@metaobjectsdev/codegen-ts 0.7.0-rc.7 → 0.7.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generators/index.d.ts +1 -0
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +1 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/routes-file-hono.d.ts +21 -0
- package/dist/generators/routes-file-hono.d.ts.map +1 -0
- package/dist/generators/routes-file-hono.js +38 -0
- package/dist/generators/routes-file-hono.js.map +1 -0
- package/dist/templates/routes-file-hono.d.ts +4 -0
- package/dist/templates/routes-file-hono.d.ts.map +1 -0
- package/dist/templates/routes-file-hono.js +119 -0
- package/dist/templates/routes-file-hono.js.map +1 -0
- package/package.json +5 -4
- package/src/generators/index.ts +1 -0
- package/src/generators/routes-file-hono.ts +48 -0
- package/src/templates/routes-file-hono.ts +142 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { entityFile, type EntityFileOpts } from "./entity-file.js";
|
|
2
2
|
export { queriesFile, type QueriesFileOpts } from "./queries-file.js";
|
|
3
3
|
export { routesFile, type RoutesFileOpts } from "./routes-file.js";
|
|
4
|
+
export { routesFileHono, type RoutesFileHonoOpts } from "./routes-file-hono.js";
|
|
4
5
|
export { barrel, type BarrelOpts } from "./barrel.js";
|
|
5
6
|
export { mermaidErDiagram, type MermaidErOptions } from "./mermaid-er.js";
|
|
6
7
|
export { promptRender, type PromptRenderOpts } from "./prompt-render-file.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/generators/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { entityFile } from "./entity-file.js";
|
|
2
2
|
export { queriesFile } from "./queries-file.js";
|
|
3
3
|
export { routesFile } from "./routes-file.js";
|
|
4
|
+
export { routesFileHono } from "./routes-file-hono.js";
|
|
4
5
|
export { barrel } from "./barrel.js";
|
|
5
6
|
export { mermaidErDiagram } from "./mermaid-er.js";
|
|
6
7
|
export { promptRender } from "./prompt-render-file.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAuB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAwB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAuB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAmB,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAyB,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAyB,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAuB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAwB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAuB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,cAAc,EAA2B,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,MAAM,EAAmB,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAyB,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAyB,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
2
|
+
import { type GeneratorFactory } from "../generator.js";
|
|
3
|
+
export interface RoutesFileHonoOpts {
|
|
4
|
+
filter?: (entity: MetaObject) => boolean;
|
|
5
|
+
target?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Hono variant of routesFile() — emits `<Entity>.routes.hono.ts` mounting
|
|
9
|
+
* the same five CRUD verbs (GET list / GET :id / POST / PATCH+PUT / DELETE)
|
|
10
|
+
* against `@metaobjectsdev/runtime-ts/hono` rather than `…/drizzle-fastify`.
|
|
11
|
+
*
|
|
12
|
+
* Same cross-port wire contract (envelope shape, status codes, filter +
|
|
13
|
+
* sort + withCount semantics) as the Fastify flavor. Workers / Bun / Node
|
|
14
|
+
* consumers running Hono can replace hand-written route registration with
|
|
15
|
+
* this generator output one entity at a time.
|
|
16
|
+
*
|
|
17
|
+
* Per-entity opt-out via `@emitRoutes: false` is honored. If the user
|
|
18
|
+
* supplies their own filter, both must pass (AND).
|
|
19
|
+
*/
|
|
20
|
+
export declare const routesFileHono: GeneratorFactory<RoutesFileHonoOpts>;
|
|
21
|
+
//# sourceMappingURL=routes-file-hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes-file-hono.d.ts","sourceRoot":"","sources":["../../src/generators/routes-file-hono.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKnF,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,EAuBtB,gBAAgB,CAAC,kBAAkB,CAAC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { perEntity } from "../generator.js";
|
|
2
|
+
import { renderRoutesFileHono } from "../templates/routes-file-hono.js";
|
|
3
|
+
import { formatTs } from "../format.js";
|
|
4
|
+
import { entityOutputPath } from "../import-path.js";
|
|
5
|
+
/**
|
|
6
|
+
* Hono variant of routesFile() — emits `<Entity>.routes.hono.ts` mounting
|
|
7
|
+
* the same five CRUD verbs (GET list / GET :id / POST / PATCH+PUT / DELETE)
|
|
8
|
+
* against `@metaobjectsdev/runtime-ts/hono` rather than `…/drizzle-fastify`.
|
|
9
|
+
*
|
|
10
|
+
* Same cross-port wire contract (envelope shape, status codes, filter +
|
|
11
|
+
* sort + withCount semantics) as the Fastify flavor. Workers / Bun / Node
|
|
12
|
+
* consumers running Hono can replace hand-written route registration with
|
|
13
|
+
* this generator output one entity at a time.
|
|
14
|
+
*
|
|
15
|
+
* Per-entity opt-out via `@emitRoutes: false` is honored. If the user
|
|
16
|
+
* supplies their own filter, both must pass (AND).
|
|
17
|
+
*/
|
|
18
|
+
export const routesFileHono = function routesFileHono(opts) {
|
|
19
|
+
const userFilter = opts?.filter ?? (() => true);
|
|
20
|
+
const generator = {
|
|
21
|
+
name: "routes-file-hono",
|
|
22
|
+
filter: (e) => e.ownAttr("emitRoutes") !== false && userFilter(e),
|
|
23
|
+
generate: perEntity(async (entity, ctx) => {
|
|
24
|
+
if (!ctx.renderContext) {
|
|
25
|
+
throw new Error("routes-file-hono: renderContext is required (provided by runGen)");
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
path: entityOutputPath(ctx.config.outputLayout ?? "flat", entity.package, `${entity.name}.routes.hono.ts`),
|
|
29
|
+
content: await formatTs(renderRoutesFileHono(entity, ctx.renderContext)),
|
|
30
|
+
};
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
if (opts?.target) {
|
|
34
|
+
generator.target = opts.target;
|
|
35
|
+
}
|
|
36
|
+
return generator;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=routes-file-hono.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes-file-hono.js","sourceRoot":"","sources":["../../src/generators/routes-file-hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAyC,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAOrD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,cAAc,CAAC,IAAyB;IAC7E,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC;QAC7E,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YACxC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;YACtF,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CACpB,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,EACjC,MAAM,CAAC,OAAO,EACd,GAAG,MAAM,CAAC,IAAI,iBAAiB,CAChC;gBACD,OAAO,EAAE,MAAM,QAAQ,CAAC,oBAAoB,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;aACzE,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAyC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes-file-hono.d.ts","sourceRoot":"","sources":["../../src/templates/routes-file-hono.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAM1D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CA2GnF"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Hono route template — emits a per-entity routes file that delegates
|
|
2
|
+
// CRUD verbs to helpers from @metaobjectsdev/runtime-ts/hono.
|
|
3
|
+
//
|
|
4
|
+
// Hono parallel of templates/routes-file.ts. Two emit-shape differences
|
|
5
|
+
// vs the Fastify flavor, both reflecting Hono idioms rather than contract
|
|
6
|
+
// drift:
|
|
7
|
+
//
|
|
8
|
+
// 1) Exported function shape — `registerXxxRoutes(app, deps)` instead of
|
|
9
|
+
// `xxxRoutes(fastify)`. Workers/Bun consumers typically pass `db`
|
|
10
|
+
// through a per-request deps object (so a Worker can attach the
|
|
11
|
+
// D1 client pulled off bindings) rather than reaching for a
|
|
12
|
+
// module-level singleton. The Fastify flavor uses `import { db }`
|
|
13
|
+
// because Fastify apps are long-lived Node processes where a
|
|
14
|
+
// singleton is the norm. Both flavors talk to mountCrudRoutes /
|
|
15
|
+
// mountReadOnlyCrudRoutes with identical wire behavior.
|
|
16
|
+
//
|
|
17
|
+
// 2) apiPrefix is composed into the resource path (`${apiPrefix}${path}`)
|
|
18
|
+
// rather than wrapping the registration. Hono has no fastify.register
|
|
19
|
+
// / prefix-wrapping primitive; sub-apps via `app.route(prefix, sub)`
|
|
20
|
+
// exist but bloat the generated code. String concat is simpler and
|
|
21
|
+
// produces identical URL grammar.
|
|
22
|
+
//
|
|
23
|
+
// Dispatch logic mirrors Fastify:
|
|
24
|
+
// isProjection(entity) → mountReadOnlyCrudRoutes (GET list + GET :id)
|
|
25
|
+
// vanilla / write-through entity → mountCrudRoutes (all 5 CRUD verbs)
|
|
26
|
+
import { code, imp } from "ts-poet";
|
|
27
|
+
import {} from "../render-context.js";
|
|
28
|
+
import { entityModuleSpecifier } from "../import-path.js";
|
|
29
|
+
import { GENERATED_HEADER } from "../constants.js";
|
|
30
|
+
import { variableNameFromEntity } from "../naming.js";
|
|
31
|
+
import { isProjection } from "../projection/projection-detector.js";
|
|
32
|
+
export function renderRoutesFileHono(entity, ctx) {
|
|
33
|
+
const entityName = entity.name;
|
|
34
|
+
const handlerName = `register${entityName}Routes`;
|
|
35
|
+
const entityFileSpec = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, entity.package, entityName, ctx.extStyle);
|
|
36
|
+
const header = `// ${GENERATED_HEADER} — DO NOT EDIT.\n` +
|
|
37
|
+
`// Source metadata: ${entityName} (${entity.fqn()})\n` +
|
|
38
|
+
`// Customize via ${entityName}.extra.ts in this directory (e.g., auth, additional handlers).\n`;
|
|
39
|
+
// Path composition: apiPrefix is a literal string in the URL.
|
|
40
|
+
const pathExpr = ctx.apiPrefix
|
|
41
|
+
? `\`${ctx.apiPrefix}\${${entityName}.$path}\``
|
|
42
|
+
: `${entityName}.$path`;
|
|
43
|
+
// --- Projection path: read-only routes (GET list + GET :id) ---
|
|
44
|
+
if (isProjection(entity)) {
|
|
45
|
+
const camelName = entityName.charAt(0).toLowerCase() + entityName.slice(1);
|
|
46
|
+
const HonoSym = imp("t:Hono@hono");
|
|
47
|
+
const mountReadOnlyCrudRoutesSym = imp("mountReadOnlyCrudRoutes@@metaobjectsdev/runtime-ts/hono");
|
|
48
|
+
const literalImports = code `
|
|
49
|
+
import {
|
|
50
|
+
${entityName},
|
|
51
|
+
${camelName}View,
|
|
52
|
+
${entityName}FilterAllowlist,
|
|
53
|
+
${entityName}SortAllowlist,
|
|
54
|
+
} from ${JSON.stringify(entityFileSpec)};
|
|
55
|
+
`;
|
|
56
|
+
const body = code `
|
|
57
|
+
/**
|
|
58
|
+
* Mount read-only REST endpoints for ${entityName} (projection — view-backed, no writes).
|
|
59
|
+
*
|
|
60
|
+
* Exposes GET list + GET :id only. POST/PATCH/DELETE return 405.
|
|
61
|
+
* Customize: register this as-is, or import individual route helpers from
|
|
62
|
+
* @metaobjectsdev/runtime-ts/hono.
|
|
63
|
+
*/
|
|
64
|
+
// biome-ignore lint/suspicious/noExplicitAny: consumer-defined Hono bindings/variables
|
|
65
|
+
export function ${handlerName}(app: ${HonoSym}<any, any, any>, deps: { db: unknown }): void {
|
|
66
|
+
${mountReadOnlyCrudRoutesSym}({
|
|
67
|
+
app,
|
|
68
|
+
path: ${pathExpr},
|
|
69
|
+
db: deps.db,
|
|
70
|
+
view: ${camelName}View,
|
|
71
|
+
filterAllowlist: ${entityName}FilterAllowlist,
|
|
72
|
+
sortAllowlist: ${entityName}SortAllowlist,
|
|
73
|
+
dialect: ${JSON.stringify(ctx.dialect)},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
return header + literalImports.toString() + body.toString();
|
|
78
|
+
}
|
|
79
|
+
// --- Vanilla / write-through entity path: full CRUD routes ---
|
|
80
|
+
const tableVar = variableNameFromEntity(entityName);
|
|
81
|
+
const HonoSym = imp("t:Hono@hono");
|
|
82
|
+
const mountCrudRoutesSym = imp("mountCrudRoutes@@metaobjectsdev/runtime-ts/hono");
|
|
83
|
+
const literalImports = code `
|
|
84
|
+
import {
|
|
85
|
+
${entityName},
|
|
86
|
+
${tableVar},
|
|
87
|
+
${entityName}InsertSchema,
|
|
88
|
+
${entityName}UpdateSchema,
|
|
89
|
+
${entityName}FilterAllowlist,
|
|
90
|
+
${entityName}SortAllowlist,
|
|
91
|
+
} from ${JSON.stringify(entityFileSpec)};
|
|
92
|
+
`;
|
|
93
|
+
const body = code `
|
|
94
|
+
/**
|
|
95
|
+
* Mount the 5 standard REST endpoints for ${entityName} using Drizzle directly.
|
|
96
|
+
*
|
|
97
|
+
* Customize: register this as-is for stock CRUD, OR import the per-verb
|
|
98
|
+
* helpers (mountListRoute, mountGetRoute, ...) from
|
|
99
|
+
* @metaobjectsdev/runtime-ts/hono and mix with your own handlers
|
|
100
|
+
* (auth, side effects, etc.).
|
|
101
|
+
*/
|
|
102
|
+
// biome-ignore lint/suspicious/noExplicitAny: consumer-defined Hono bindings/variables
|
|
103
|
+
export function ${handlerName}(app: ${HonoSym}<any, any, any>, deps: { db: unknown }): void {
|
|
104
|
+
${mountCrudRoutesSym}({
|
|
105
|
+
app,
|
|
106
|
+
path: ${pathExpr},
|
|
107
|
+
db: deps.db,
|
|
108
|
+
table: ${tableVar},
|
|
109
|
+
insertSchema: ${entityName}InsertSchema,
|
|
110
|
+
updateSchema: ${entityName}UpdateSchema,
|
|
111
|
+
filterAllowlist: ${entityName}FilterAllowlist,
|
|
112
|
+
sortAllowlist: ${entityName}SortAllowlist,
|
|
113
|
+
dialect: ${JSON.stringify(ctx.dialect)},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
117
|
+
return header + literalImports.toString() + body.toString();
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=routes-file-hono.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes-file-hono.js","sourceRoot":"","sources":["../../src/templates/routes-file-hono.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/codegen-ts",
|
|
3
|
-
"version": "0.7.0-rc.
|
|
3
|
+
"version": "0.7.0-rc.9",
|
|
4
4
|
"description": "TypeScript codegen engine for MetaObjects — emits Drizzle, Zod, and Fastify artifacts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@metaobjectsdev/metadata": "0.7.0-rc.
|
|
41
|
+
"@metaobjectsdev/metadata": "0.7.0-rc.9",
|
|
42
42
|
"@biomejs/js-api": "^0.7.0",
|
|
43
43
|
"@biomejs/wasm-nodejs": "^1.9.4",
|
|
44
44
|
"ts-poet": "^6.10.0"
|
|
@@ -49,10 +49,11 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@biomejs/biome": "^1.9.0",
|
|
52
|
-
"@metaobjectsdev/codegen-ts-react": "0.7.0-rc.
|
|
53
|
-
"@metaobjectsdev/render": "0.7.0-rc.
|
|
52
|
+
"@metaobjectsdev/codegen-ts-react": "0.7.0-rc.9",
|
|
53
|
+
"@metaobjectsdev/render": "0.7.0-rc.9",
|
|
54
54
|
"bun-types": "latest",
|
|
55
55
|
"drizzle-orm": "^0.36.0",
|
|
56
|
+
"hono": "^4.6.0",
|
|
56
57
|
"typescript": "^5.6.0",
|
|
57
58
|
"zod": "^3.23.0"
|
|
58
59
|
}
|
package/src/generators/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { entityFile, type EntityFileOpts } from "./entity-file.js";
|
|
2
2
|
export { queriesFile, type QueriesFileOpts } from "./queries-file.js";
|
|
3
3
|
export { routesFile, type RoutesFileOpts } from "./routes-file.js";
|
|
4
|
+
export { routesFileHono, type RoutesFileHonoOpts } from "./routes-file-hono.js";
|
|
4
5
|
export { barrel, type BarrelOpts } from "./barrel.js";
|
|
5
6
|
export { mermaidErDiagram, type MermaidErOptions } from "./mermaid-er.js";
|
|
6
7
|
export { promptRender, type PromptRenderOpts } from "./prompt-render-file.js";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
2
|
+
import { perEntity, type Generator, type GeneratorFactory } from "../generator.js";
|
|
3
|
+
import { renderRoutesFileHono } from "../templates/routes-file-hono.js";
|
|
4
|
+
import { formatTs } from "../format.js";
|
|
5
|
+
import { entityOutputPath } from "../import-path.js";
|
|
6
|
+
|
|
7
|
+
export interface RoutesFileHonoOpts {
|
|
8
|
+
filter?: (entity: MetaObject) => boolean;
|
|
9
|
+
target?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Hono variant of routesFile() — emits `<Entity>.routes.hono.ts` mounting
|
|
14
|
+
* the same five CRUD verbs (GET list / GET :id / POST / PATCH+PUT / DELETE)
|
|
15
|
+
* against `@metaobjectsdev/runtime-ts/hono` rather than `…/drizzle-fastify`.
|
|
16
|
+
*
|
|
17
|
+
* Same cross-port wire contract (envelope shape, status codes, filter +
|
|
18
|
+
* sort + withCount semantics) as the Fastify flavor. Workers / Bun / Node
|
|
19
|
+
* consumers running Hono can replace hand-written route registration with
|
|
20
|
+
* this generator output one entity at a time.
|
|
21
|
+
*
|
|
22
|
+
* Per-entity opt-out via `@emitRoutes: false` is honored. If the user
|
|
23
|
+
* supplies their own filter, both must pass (AND).
|
|
24
|
+
*/
|
|
25
|
+
export const routesFileHono = function routesFileHono(opts?: RoutesFileHonoOpts): Generator {
|
|
26
|
+
const userFilter = opts?.filter ?? (() => true);
|
|
27
|
+
const generator: Generator = {
|
|
28
|
+
name: "routes-file-hono",
|
|
29
|
+
filter: (e: MetaObject) => e.ownAttr("emitRoutes") !== false && userFilter(e),
|
|
30
|
+
generate: perEntity(async (entity, ctx) => {
|
|
31
|
+
if (!ctx.renderContext) {
|
|
32
|
+
throw new Error("routes-file-hono: renderContext is required (provided by runGen)");
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
path: entityOutputPath(
|
|
36
|
+
ctx.config.outputLayout ?? "flat",
|
|
37
|
+
entity.package,
|
|
38
|
+
`${entity.name}.routes.hono.ts`,
|
|
39
|
+
),
|
|
40
|
+
content: await formatTs(renderRoutesFileHono(entity, ctx.renderContext)),
|
|
41
|
+
};
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
if (opts?.target) {
|
|
45
|
+
generator.target = opts.target;
|
|
46
|
+
}
|
|
47
|
+
return generator;
|
|
48
|
+
} as GeneratorFactory<RoutesFileHonoOpts>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Hono route template — emits a per-entity routes file that delegates
|
|
2
|
+
// CRUD verbs to helpers from @metaobjectsdev/runtime-ts/hono.
|
|
3
|
+
//
|
|
4
|
+
// Hono parallel of templates/routes-file.ts. Two emit-shape differences
|
|
5
|
+
// vs the Fastify flavor, both reflecting Hono idioms rather than contract
|
|
6
|
+
// drift:
|
|
7
|
+
//
|
|
8
|
+
// 1) Exported function shape — `registerXxxRoutes(app, deps)` instead of
|
|
9
|
+
// `xxxRoutes(fastify)`. Workers/Bun consumers typically pass `db`
|
|
10
|
+
// through a per-request deps object (so a Worker can attach the
|
|
11
|
+
// D1 client pulled off bindings) rather than reaching for a
|
|
12
|
+
// module-level singleton. The Fastify flavor uses `import { db }`
|
|
13
|
+
// because Fastify apps are long-lived Node processes where a
|
|
14
|
+
// singleton is the norm. Both flavors talk to mountCrudRoutes /
|
|
15
|
+
// mountReadOnlyCrudRoutes with identical wire behavior.
|
|
16
|
+
//
|
|
17
|
+
// 2) apiPrefix is composed into the resource path (`${apiPrefix}${path}`)
|
|
18
|
+
// rather than wrapping the registration. Hono has no fastify.register
|
|
19
|
+
// / prefix-wrapping primitive; sub-apps via `app.route(prefix, sub)`
|
|
20
|
+
// exist but bloat the generated code. String concat is simpler and
|
|
21
|
+
// produces identical URL grammar.
|
|
22
|
+
//
|
|
23
|
+
// Dispatch logic mirrors Fastify:
|
|
24
|
+
// isProjection(entity) → mountReadOnlyCrudRoutes (GET list + GET :id)
|
|
25
|
+
// vanilla / write-through entity → mountCrudRoutes (all 5 CRUD verbs)
|
|
26
|
+
|
|
27
|
+
import { code, imp } from "ts-poet";
|
|
28
|
+
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
29
|
+
import { type RenderContext } from "../render-context.js";
|
|
30
|
+
import { entityModuleSpecifier } from "../import-path.js";
|
|
31
|
+
import { GENERATED_HEADER } from "../constants.js";
|
|
32
|
+
import { variableNameFromEntity } from "../naming.js";
|
|
33
|
+
import { isProjection } from "../projection/projection-detector.js";
|
|
34
|
+
|
|
35
|
+
export function renderRoutesFileHono(entity: MetaObject, ctx: RenderContext): string {
|
|
36
|
+
const entityName = entity.name;
|
|
37
|
+
const handlerName = `register${entityName}Routes`;
|
|
38
|
+
|
|
39
|
+
const entityFileSpec = entityModuleSpecifier(
|
|
40
|
+
ctx.selfTarget,
|
|
41
|
+
ctx.entityModuleTarget,
|
|
42
|
+
entity.package,
|
|
43
|
+
entityName,
|
|
44
|
+
ctx.extStyle,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const header =
|
|
48
|
+
`// ${GENERATED_HEADER} — DO NOT EDIT.\n` +
|
|
49
|
+
`// Source metadata: ${entityName} (${entity.fqn()})\n` +
|
|
50
|
+
`// Customize via ${entityName}.extra.ts in this directory (e.g., auth, additional handlers).\n`;
|
|
51
|
+
|
|
52
|
+
// Path composition: apiPrefix is a literal string in the URL.
|
|
53
|
+
const pathExpr = ctx.apiPrefix
|
|
54
|
+
? `\`${ctx.apiPrefix}\${${entityName}.$path}\``
|
|
55
|
+
: `${entityName}.$path`;
|
|
56
|
+
|
|
57
|
+
// --- Projection path: read-only routes (GET list + GET :id) ---
|
|
58
|
+
if (isProjection(entity)) {
|
|
59
|
+
const camelName = entityName.charAt(0).toLowerCase() + entityName.slice(1);
|
|
60
|
+
const HonoSym = imp("t:Hono@hono");
|
|
61
|
+
const mountReadOnlyCrudRoutesSym = imp(
|
|
62
|
+
"mountReadOnlyCrudRoutes@@metaobjectsdev/runtime-ts/hono",
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const literalImports = code`
|
|
66
|
+
import {
|
|
67
|
+
${entityName},
|
|
68
|
+
${camelName}View,
|
|
69
|
+
${entityName}FilterAllowlist,
|
|
70
|
+
${entityName}SortAllowlist,
|
|
71
|
+
} from ${JSON.stringify(entityFileSpec)};
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const body = code`
|
|
75
|
+
/**
|
|
76
|
+
* Mount read-only REST endpoints for ${entityName} (projection — view-backed, no writes).
|
|
77
|
+
*
|
|
78
|
+
* Exposes GET list + GET :id only. POST/PATCH/DELETE return 405.
|
|
79
|
+
* Customize: register this as-is, or import individual route helpers from
|
|
80
|
+
* @metaobjectsdev/runtime-ts/hono.
|
|
81
|
+
*/
|
|
82
|
+
// biome-ignore lint/suspicious/noExplicitAny: consumer-defined Hono bindings/variables
|
|
83
|
+
export function ${handlerName}(app: ${HonoSym}<any, any, any>, deps: { db: unknown }): void {
|
|
84
|
+
${mountReadOnlyCrudRoutesSym}({
|
|
85
|
+
app,
|
|
86
|
+
path: ${pathExpr},
|
|
87
|
+
db: deps.db,
|
|
88
|
+
view: ${camelName}View,
|
|
89
|
+
filterAllowlist: ${entityName}FilterAllowlist,
|
|
90
|
+
sortAllowlist: ${entityName}SortAllowlist,
|
|
91
|
+
dialect: ${JSON.stringify(ctx.dialect)},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
return header + literalImports.toString() + body.toString();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- Vanilla / write-through entity path: full CRUD routes ---
|
|
100
|
+
const tableVar = variableNameFromEntity(entityName);
|
|
101
|
+
|
|
102
|
+
const HonoSym = imp("t:Hono@hono");
|
|
103
|
+
const mountCrudRoutesSym = imp("mountCrudRoutes@@metaobjectsdev/runtime-ts/hono");
|
|
104
|
+
|
|
105
|
+
const literalImports = code`
|
|
106
|
+
import {
|
|
107
|
+
${entityName},
|
|
108
|
+
${tableVar},
|
|
109
|
+
${entityName}InsertSchema,
|
|
110
|
+
${entityName}UpdateSchema,
|
|
111
|
+
${entityName}FilterAllowlist,
|
|
112
|
+
${entityName}SortAllowlist,
|
|
113
|
+
} from ${JSON.stringify(entityFileSpec)};
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
const body = code`
|
|
117
|
+
/**
|
|
118
|
+
* Mount the 5 standard REST endpoints for ${entityName} using Drizzle directly.
|
|
119
|
+
*
|
|
120
|
+
* Customize: register this as-is for stock CRUD, OR import the per-verb
|
|
121
|
+
* helpers (mountListRoute, mountGetRoute, ...) from
|
|
122
|
+
* @metaobjectsdev/runtime-ts/hono and mix with your own handlers
|
|
123
|
+
* (auth, side effects, etc.).
|
|
124
|
+
*/
|
|
125
|
+
// biome-ignore lint/suspicious/noExplicitAny: consumer-defined Hono bindings/variables
|
|
126
|
+
export function ${handlerName}(app: ${HonoSym}<any, any, any>, deps: { db: unknown }): void {
|
|
127
|
+
${mountCrudRoutesSym}({
|
|
128
|
+
app,
|
|
129
|
+
path: ${pathExpr},
|
|
130
|
+
db: deps.db,
|
|
131
|
+
table: ${tableVar},
|
|
132
|
+
insertSchema: ${entityName}InsertSchema,
|
|
133
|
+
updateSchema: ${entityName}UpdateSchema,
|
|
134
|
+
filterAllowlist: ${entityName}FilterAllowlist,
|
|
135
|
+
sortAllowlist: ${entityName}SortAllowlist,
|
|
136
|
+
dialect: ${JSON.stringify(ctx.dialect)},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
|
|
141
|
+
return header + literalImports.toString() + body.toString();
|
|
142
|
+
}
|