Skip to content

Commit 34cf47f

Browse files
committed
feat: Replace nextlove sdk generator with metalsmith codegen
Replace the @seamapi/nextlove-sdk-generator based route generation with the metalsmith + handlebars + @seamapi/blueprint architecture used by seamapi/javascript-http. The generated output under seam/routes/ is byte-identical. The blueprint drives the route, endpoint, and namespace structure. The raw OpenAPI spec is still consulted wherever the nextlove generator derived output from data the blueprint normalizes differently (integer vs number types, resource schema set and order, parameter flattening); each of those spots carries a TODO to migrate to the blueprint once output is allowed to change. - Add codegen/ with the Metalsmith pipeline, plugin, context builders, and Handlebars layouts and partials - Remove generate-routes.js - Pin @seamapi/types at 1.910.0 for output parity and use @seamapi/blueprint 0.55.0 - Bump del to ^8 to satisfy the @seamapi/smith peer range - Ignore *.hbs and CODEGEN.md in a new .prettierignore since the glimmer parser mangles Python-flavored templates - Re-include codegen/lib/ in .gitignore (the Python template ignores lib/) and ignore the generated CODEGEN.md seed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iX1vWXfXKzUyRJTVm8D7s
1 parent d41bc9e commit 34cf47f

30 files changed

Lines changed: 6644 additions & 774 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,7 @@ dist
325325
# yalc
326326
.yalc/
327327
yalc.lock
328+
329+
# Codegen
330+
!codegen/lib/
331+
CODEGEN.md

.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Prettier exceptions
2+
3+
*.hbs
4+
CODEGEN.md

codegen/layouts/default.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{contents}}

codegen/layouts/models.hbs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from typing import Any, Dict, List, Optional, Union
2+
from typing_extensions import Self
3+
import abc
4+
from dataclasses import dataclass
5+
from ..utils.deep_attr_dict import DeepAttrDict
6+
7+
{{#each resources}}
8+
9+
{{> model-dataclass}}
10+
{{/each}}
11+
{{#each abstractClasses}}
12+
13+
{{> abstract-route-class}}
14+
{{/each}}
15+
16+
17+
{{> abstract-routes}}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class {{className}}(abc.ABC):
2+
{{#if showPass}}
3+
pass
4+
{{/if}}
5+
{{#each childProperties}}
6+
7+
@property
8+
@abc.abstractmethod
9+
def {{namespace}}(self) -> {{abstractClassName}}:
10+
raise NotImplementedError()
11+
{{/each}}
12+
{{#each methods}}
13+
14+
@abc.abstractmethod
15+
def {{name}}(self,{{#if hasParams}} *,{{/if}} {{signatureParams}}) -> {{returnType}}:
16+
raise NotImplementedError()
17+
{{/each}}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@dataclass
2+
class AbstractRoutes(abc.ABC):
3+
{{#each routesNamespaces}}
4+
{{namespace}}: {{abstractClassName}}
5+
{{/each}}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@dataclass
2+
class {{className}}:
3+
{{#each properties}}
4+
{{name}}: {{type}}
5+
{{/each}}
6+
7+
@staticmethod
8+
def from_dict(d: Dict[str, Any]):
9+
return {{className}}(
10+
{{#each properties}}
11+
{{name}}={{#if isDictParam}}DeepAttrDict({{/if}}d.get("{{name}}", None){{#if isDictParam}}){{/if}},
12+
{{/each}}
13+
)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
def {{name}}(self,{{#if hasParams}} *,{{/if}} {{signatureParams}}) -> {{returnType}}:
2+
json_payload = {}
3+
4+
{{#each params}}
5+
if {{name}} is not None:
6+
json_payload["{{name}}"] = {{name}}
7+
{{/each}}
8+
9+
{{#unless returnsNone}}res = {{/unless}}self.client.post("{{path}}", json=json_payload)
10+
{{#if pollsActionAttempt}}
11+
12+
wait_for_action_attempt = (
13+
self.defaults.get("wait_for_action_attempt")
14+
if wait_for_action_attempt is None
15+
else wait_for_action_attempt
16+
)
17+
18+
return resolve_action_attempt(
19+
client=self.client,
20+
action_attempt=ActionAttempt.from_dict(res["action_attempt"]),
21+
wait_for_action_attempt=wait_for_action_attempt
22+
)
23+
{{else if returnsNone}}
24+
25+
return None
26+
{{else if isList}}
27+
28+
return [{{itemType}}.from_dict(item) for item in {{resAccessor}}]
29+
{{else}}
30+
31+
return {{returnType}}.from_dict({{resAccessor}})
32+
{{/if}}

codegen/layouts/route.hbs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Optional, Any, List, Dict, Union
2+
from ..client import SeamHttpClient
3+
from .models import ({{modelImportList}})
4+
{{#each childClasses}}
5+
from .{{module}} import {{className}}
6+
{{else}}
7+
8+
{{/each}}
9+
{{#if importResolveActionAttempt}}
10+
from ..modules.action_attempts import resolve_action_attempt
11+
{{/if}}
12+
13+
14+
class {{className}}({{abstractClassName}}):
15+
def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]):
16+
self.client = client
17+
self.defaults = defaults
18+
{{#each childClasses}}
19+
self._{{namespace}} = {{className}}(client=client, defaults=defaults)
20+
{{/each}}
21+
{{#each childClasses}}
22+
23+
@property
24+
def {{namespace}}(self) -> {{className}}:
25+
return self._{{namespace}}
26+
{{/each}}
27+
{{#each methods}}
28+
29+
{{> route-method}}
30+
{{/each}}

codegen/layouts/routes-index.hbs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from typing import Any, Dict
2+
from ..client import SeamHttpClient
3+
from .models import AbstractRoutes
4+
{{#each namespaces}}
5+
from .{{namespace}} import {{className}}
6+
{{/each}}
7+
8+
9+
class Routes(AbstractRoutes):
10+
def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]):
11+
{{#each namespaces}}
12+
self.{{namespace}} = {{className}}(client=client, defaults=defaults)
13+
{{/each}}

0 commit comments

Comments
 (0)