@substrat-run/model-emit 0.5.2 → 0.6.0
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/emit-client.d.ts +107 -0
- package/dist/emit-client.d.ts.map +1 -0
- package/dist/emit-client.js +566 -0
- package/dist/emit-client.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A vertical's browser client, rendered from its model.
|
|
3
|
+
*
|
|
4
|
+
* The pure half of `tools/client-emit.mts` — everything from "a Zod schema" to "a
|
|
5
|
+
* string of TypeScript", with no filesystem and no `process.exit` in it. It lives here
|
|
6
|
+
* rather than in the tool for one reason: a Zod→TypeScript printer is the kind of code
|
|
7
|
+
* that is wrong in ways a drift check cannot see. `--check` re-emits and compares, so it
|
|
8
|
+
* catches a client that fell behind its model; it cannot catch a client that has been
|
|
9
|
+
* confidently mis-spelling `z.union([...])` inside an array since the day it was written.
|
|
10
|
+
* That needs a test with the schema on one side and the expected string on the other,
|
|
11
|
+
* and a test needs something importable.
|
|
12
|
+
*
|
|
13
|
+
* Its home is this package because that is already this package's job: build-time
|
|
14
|
+
* tooling over a Substrat model. `emitTables` turns entities into DDL; this turns
|
|
15
|
+
* entities and operations into a typed fetch client.
|
|
16
|
+
*
|
|
17
|
+
* ## What it emits
|
|
18
|
+
*
|
|
19
|
+
* Standalone TypeScript with no imports at all. A vertical's SPA is a separate Vite
|
|
20
|
+
* package that depends on neither `@substrat-run/contracts` nor zod and must keep
|
|
21
|
+
* depending on neither — and a checked-in artifact that re-exports its meaning from
|
|
22
|
+
* another package is not reviewable in a diff.
|
|
23
|
+
*
|
|
24
|
+
* ## What it refuses
|
|
25
|
+
*
|
|
26
|
+
* A schema it cannot spell raises `ClientEmitError` naming the operation and the field.
|
|
27
|
+
* Never `unknown`, never a skipped method: a generated client that quietly degrades to
|
|
28
|
+
* `any` is more dangerous than the hand-written one it replaced, because the green light
|
|
29
|
+
* is now mechanical.
|
|
30
|
+
*/
|
|
31
|
+
/** Raised for anything the caller must fix. The CLI turns it into exit 2 and a remedy. */
|
|
32
|
+
export declare class ClientEmitError extends Error {
|
|
33
|
+
constructor(message: string);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* How a vertical declares its client, from `package.json` under `substrat.client`.
|
|
37
|
+
*/
|
|
38
|
+
export interface ClientConfig {
|
|
39
|
+
/**
|
|
40
|
+
* The module(s) the model is declared in.
|
|
41
|
+
*
|
|
42
|
+
* A list because a vertical is free to split it: todo keeps entities and operations
|
|
43
|
+
* in one `spec/model.ts`, callout has `src/entities.ts` and `src/operations.ts`, and
|
|
44
|
+
* an entry that is not a path is a package specifier — which is how a composed
|
|
45
|
+
* engine's published schemas are reached. Resolving the names across them is the
|
|
46
|
+
* caller's job (`tools/client-emit.mts`); this module is handed the values.
|
|
47
|
+
*/
|
|
48
|
+
readonly model: string | readonly string[];
|
|
49
|
+
/**
|
|
50
|
+
* The export(s) holding entity declarations — `{ name: { fields, table } }`.
|
|
51
|
+
*
|
|
52
|
+
* A list because a vertical that composes engines wants the ENGINE's entities named
|
|
53
|
+
* too: without them `protocolGet`'s return type inlines four hundred characters of
|
|
54
|
+
* protocol row on one line, which is a diff nobody reads.
|
|
55
|
+
*/
|
|
56
|
+
readonly entities: string | readonly string[];
|
|
57
|
+
/**
|
|
58
|
+
* Extra schemas to name, as `InterfaceName` → export name.
|
|
59
|
+
*
|
|
60
|
+
* An engine publishes types that are NOT its stored rows — `workOrder` is the
|
|
61
|
+
* engine's published shape, with the two `facility_*` columns folded into one
|
|
62
|
+
* `EntityRef`, and no entity's `fields` is that object. Those have nowhere else to
|
|
63
|
+
* be declared, so they are named here.
|
|
64
|
+
*/
|
|
65
|
+
readonly schemas?: Readonly<Record<string, string>>;
|
|
66
|
+
/**
|
|
67
|
+
* The export(s) holding this vertical's operations.
|
|
68
|
+
*
|
|
69
|
+
* An array because a vertical that COMPOSES engines has more than one: callout
|
|
70
|
+
* declares its own six and then binds three engines' operations to its own URLs
|
|
71
|
+
* with `defineEngineRoutes`, which returns the same operation objects with `http`
|
|
72
|
+
* attached. Those are as much part of the app's surface as the vertical's own —
|
|
73
|
+
* the SPA calls `/workorders/{id}/complete` without caring whose operation it is —
|
|
74
|
+
* so a generator that read only the first bag would emit a third of the client and
|
|
75
|
+
* look complete doing it.
|
|
76
|
+
*/
|
|
77
|
+
readonly operations: string | readonly string[];
|
|
78
|
+
readonly out: string;
|
|
79
|
+
readonly name: string;
|
|
80
|
+
/**
|
|
81
|
+
* Hand-stated types for schemas the printer cannot spell, keyed by the path the
|
|
82
|
+
* refusal prints (`<operation>.input.<field>`). Every entry must be reached — a
|
|
83
|
+
* stale escape hatch is worse than none, so an unused one is exit 2.
|
|
84
|
+
*/
|
|
85
|
+
readonly types?: Readonly<Record<string, string>>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* One schema as TypeScript, with no operation around it.
|
|
89
|
+
*
|
|
90
|
+
* The printer's entry point for a test. `named` is the identity map an entity's
|
|
91
|
+
* `fields` would be in — pass one to check that a reference prints as its interface
|
|
92
|
+
* name rather than as an inline shape.
|
|
93
|
+
*/
|
|
94
|
+
export declare function tsTypeOf(schema: unknown, named?: Map<unknown, string>): string;
|
|
95
|
+
/**
|
|
96
|
+
* `todo/create-list` → `createList`; `protocol/get` → `protocolGet`.
|
|
97
|
+
*
|
|
98
|
+
* The vertical's OWN prefix is dropped — it is the client's identity and repeating it
|
|
99
|
+
* on every method says nothing (`api.callout.calloutCreateOrder()`). A composed
|
|
100
|
+
* engine's prefix stays, and has to: callout binds `workorder/get`, `protocol/get` and
|
|
101
|
+
* `invoicing/get` at three URLs, and a client with one `get()` would reach whichever
|
|
102
|
+
* bag was read last. Renaming an engine's operation to suit a vertical's client is not
|
|
103
|
+
* a thing a vertical may do, so the disambiguation belongs here.
|
|
104
|
+
*/
|
|
105
|
+
export declare const methodName: (operation: string, ownPrefix: string) => string;
|
|
106
|
+
export declare function renderClient(vertical: string, config: ClientConfig, source: string, entities: Record<string, unknown>, operations: Record<string, unknown>, extras: Record<string, unknown>): string;
|
|
107
|
+
//# sourceMappingURL=emit-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-client.d.ts","sourceRoot":"","sources":["../src/emit-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,0FAA0F;AAC1F,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAMD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC9C;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAChD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAmLD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,GAAE,GAAG,CAAC,OAAO,EAAE,MAAM,CAAa,GAAG,MAAM,CAEzF;AAkCD;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,cAAe,MAAM,aAAa,MAAM,WAI9D,CAAC;AAsGF,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAwSR"}
|
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A vertical's browser client, rendered from its model.
|
|
3
|
+
*
|
|
4
|
+
* The pure half of `tools/client-emit.mts` — everything from "a Zod schema" to "a
|
|
5
|
+
* string of TypeScript", with no filesystem and no `process.exit` in it. It lives here
|
|
6
|
+
* rather than in the tool for one reason: a Zod→TypeScript printer is the kind of code
|
|
7
|
+
* that is wrong in ways a drift check cannot see. `--check` re-emits and compares, so it
|
|
8
|
+
* catches a client that fell behind its model; it cannot catch a client that has been
|
|
9
|
+
* confidently mis-spelling `z.union([...])` inside an array since the day it was written.
|
|
10
|
+
* That needs a test with the schema on one side and the expected string on the other,
|
|
11
|
+
* and a test needs something importable.
|
|
12
|
+
*
|
|
13
|
+
* Its home is this package because that is already this package's job: build-time
|
|
14
|
+
* tooling over a Substrat model. `emitTables` turns entities into DDL; this turns
|
|
15
|
+
* entities and operations into a typed fetch client.
|
|
16
|
+
*
|
|
17
|
+
* ## What it emits
|
|
18
|
+
*
|
|
19
|
+
* Standalone TypeScript with no imports at all. A vertical's SPA is a separate Vite
|
|
20
|
+
* package that depends on neither `@substrat-run/contracts` nor zod and must keep
|
|
21
|
+
* depending on neither — and a checked-in artifact that re-exports its meaning from
|
|
22
|
+
* another package is not reviewable in a diff.
|
|
23
|
+
*
|
|
24
|
+
* ## What it refuses
|
|
25
|
+
*
|
|
26
|
+
* A schema it cannot spell raises `ClientEmitError` naming the operation and the field.
|
|
27
|
+
* Never `unknown`, never a skipped method: a generated client that quietly degrades to
|
|
28
|
+
* `any` is more dangerous than the hand-written one it replaced, because the green light
|
|
29
|
+
* is now mechanical.
|
|
30
|
+
*/
|
|
31
|
+
/** Raised for anything the caller must fix. The CLI turns it into exit 2 and a remedy. */
|
|
32
|
+
export class ClientEmitError extends Error {
|
|
33
|
+
constructor(message) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = 'ClientEmitError';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function fail(message) {
|
|
39
|
+
throw new ClientEmitError(message);
|
|
40
|
+
}
|
|
41
|
+
function defOf(schema) {
|
|
42
|
+
return (schema?._zod?.def ??
|
|
43
|
+
schema?._def);
|
|
44
|
+
}
|
|
45
|
+
/** The wrappers that do not change a type, only its optionality or its parsing. */
|
|
46
|
+
const TRANSPARENT = new Set(['default', 'prefault', 'catch', 'readonly', 'nonoptional', 'pipe']);
|
|
47
|
+
/** A field is optional when its own outermost wrapper says so — `a?: T`, not `a: T | undefined`. */
|
|
48
|
+
function isOptional(schema) {
|
|
49
|
+
const def = defOf(schema);
|
|
50
|
+
if (def?.type === 'optional' || def?.type === 'nullish')
|
|
51
|
+
return true;
|
|
52
|
+
if (def && TRANSPARENT.has(def.type ?? '')) {
|
|
53
|
+
// A defaulted field is optional TO THE CALLER — the server fills it in.
|
|
54
|
+
return def.type === 'default' || def.type === 'prefault' || isOptional(def.innerType ?? def.in);
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The type a `?` property carries, with the optionality itself taken off.
|
|
60
|
+
*
|
|
61
|
+
* `limit?: number | undefined` says the same thing twice and reads as though the
|
|
62
|
+
* two halves might differ. A `nullish` field keeps its `| null`, because that half
|
|
63
|
+
* is a value the server can actually send.
|
|
64
|
+
*/
|
|
65
|
+
function unwrapOptional(schema) {
|
|
66
|
+
const def = defOf(schema);
|
|
67
|
+
if (def?.type === 'optional')
|
|
68
|
+
return unwrapOptional(def.innerType);
|
|
69
|
+
if (def?.type === 'default' || def?.type === 'prefault')
|
|
70
|
+
return unwrapOptional(def.innerType);
|
|
71
|
+
return schema;
|
|
72
|
+
}
|
|
73
|
+
function literalOf(value) {
|
|
74
|
+
if (typeof value === 'string')
|
|
75
|
+
return JSON.stringify(value);
|
|
76
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
77
|
+
return String(value);
|
|
78
|
+
if (value === null)
|
|
79
|
+
return 'null';
|
|
80
|
+
return fail(`a literal of type ${typeof value} has no TypeScript spelling here`);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A schema as a TypeScript type.
|
|
84
|
+
*
|
|
85
|
+
* An entity's `fields` object is matched by IDENTITY, not by shape: `spec/model.ts`
|
|
86
|
+
* writes `output: todoEntities.item.fields`, so the very same object arrives here
|
|
87
|
+
* and prints as `Item`. Two entities that happen to share a shape stay two names,
|
|
88
|
+
* and an inline object that happens to match an entity stays inline — which is
|
|
89
|
+
* what the model actually said in both cases.
|
|
90
|
+
*/
|
|
91
|
+
function tsType(schema, ctx) {
|
|
92
|
+
const override = ctx.overrides.get(ctx.where);
|
|
93
|
+
if (override !== undefined) {
|
|
94
|
+
ctx.used.add(ctx.where);
|
|
95
|
+
return override;
|
|
96
|
+
}
|
|
97
|
+
const named = ctx.named.get(schema);
|
|
98
|
+
if (named)
|
|
99
|
+
return named;
|
|
100
|
+
const def = defOf(schema);
|
|
101
|
+
switch (def?.type) {
|
|
102
|
+
case 'string':
|
|
103
|
+
return 'string';
|
|
104
|
+
case 'number':
|
|
105
|
+
return 'number';
|
|
106
|
+
case 'boolean':
|
|
107
|
+
return 'boolean';
|
|
108
|
+
case 'bigint':
|
|
109
|
+
return 'bigint';
|
|
110
|
+
case 'unknown':
|
|
111
|
+
case 'any':
|
|
112
|
+
return 'unknown';
|
|
113
|
+
case 'null':
|
|
114
|
+
return 'null';
|
|
115
|
+
case 'literal': {
|
|
116
|
+
const values = def.values ?? [];
|
|
117
|
+
if (values.length === 0)
|
|
118
|
+
return fail(`${ctx.where}: a literal with no values`);
|
|
119
|
+
return values.map(literalOf).join(' | ');
|
|
120
|
+
}
|
|
121
|
+
case 'enum': {
|
|
122
|
+
const values = Object.values(def.entries ?? {});
|
|
123
|
+
if (values.length === 0)
|
|
124
|
+
return fail(`${ctx.where}: an enum with no members`);
|
|
125
|
+
return values.map(literalOf).join(' | ');
|
|
126
|
+
}
|
|
127
|
+
case 'array':
|
|
128
|
+
return `${maybeParen(tsType(def.element, ctx), ctx)}[]`;
|
|
129
|
+
case 'object':
|
|
130
|
+
return tsObject(def.shape ?? {}, ctx);
|
|
131
|
+
case 'record':
|
|
132
|
+
return `Record<${tsType(def.keyType, ctx)}, ${tsType(def.valueType, ctx)}>`;
|
|
133
|
+
case 'union': {
|
|
134
|
+
const options = def.options ?? [];
|
|
135
|
+
if (options.length === 0)
|
|
136
|
+
return fail(`${ctx.where}: a union with no options`);
|
|
137
|
+
return [...new Set(options.map((o) => tsType(o, ctx)))].join(' | ');
|
|
138
|
+
}
|
|
139
|
+
case 'optional':
|
|
140
|
+
// Reached only where optionality has no key to hang off — inside an array,
|
|
141
|
+
// say. As a property it is spelled `a?: T` by `tsObject` instead.
|
|
142
|
+
return `${tsType(def.innerType, ctx)} | undefined`;
|
|
143
|
+
case 'nullable':
|
|
144
|
+
return `${tsType(def.innerType, ctx)} | null`;
|
|
145
|
+
case 'nullish':
|
|
146
|
+
return `${tsType(def.innerType, ctx)} | null | undefined`;
|
|
147
|
+
case 'default':
|
|
148
|
+
case 'prefault':
|
|
149
|
+
case 'catch':
|
|
150
|
+
case 'readonly':
|
|
151
|
+
case 'nonoptional':
|
|
152
|
+
return tsType(def.innerType, ctx);
|
|
153
|
+
case 'pipe':
|
|
154
|
+
// `z.coerce.number()` is a pipe; the caller supplies its INPUT side.
|
|
155
|
+
return tsType(def.in, ctx);
|
|
156
|
+
default:
|
|
157
|
+
return fail(`${ctx.where}: no TypeScript spelling for a Zod '${def?.type ?? 'unrecognised'}' schema.\n` +
|
|
158
|
+
` A generator that answered 'unknown' here would hand the app a green light over a\n` +
|
|
159
|
+
` type it never checked.\n` +
|
|
160
|
+
` Remedy, in order of preference: teach tsType() this case; declare the field with a\n` +
|
|
161
|
+
` shape the client can carry; or, when the schema genuinely has no inferable input\n` +
|
|
162
|
+
` type (a preprocess does not), state the type yourself in package.json —\n` +
|
|
163
|
+
` "substrat": { "client": { "types": { ${JSON.stringify(ctx.where.split('.').slice(1).join('.'))}: "unknown" } } }`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** Parenthesise a union before `[]`, so `(A | B)[]` never prints as `A | B[]`. */
|
|
167
|
+
function maybeParen(printed, _ctx) {
|
|
168
|
+
return printed.includes('|') && !printed.startsWith('(') ? `(${printed})` : printed;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* One schema as TypeScript, with no operation around it.
|
|
172
|
+
*
|
|
173
|
+
* The printer's entry point for a test. `named` is the identity map an entity's
|
|
174
|
+
* `fields` would be in — pass one to check that a reference prints as its interface
|
|
175
|
+
* name rather than as an inline shape.
|
|
176
|
+
*/
|
|
177
|
+
export function tsTypeOf(schema, named = new Map()) {
|
|
178
|
+
return tsType(schema, { named, where: 'schema', overrides: new Map(), used: new Set() });
|
|
179
|
+
}
|
|
180
|
+
function tsObject(shape, ctx) {
|
|
181
|
+
const keys = Object.keys(shape);
|
|
182
|
+
if (keys.length === 0)
|
|
183
|
+
return 'Record<string, never>';
|
|
184
|
+
const fields = keys.map((key) => {
|
|
185
|
+
const optional = isOptional(shape[key]);
|
|
186
|
+
const value = optional ? unwrapOptional(shape[key]) : shape[key];
|
|
187
|
+
return `${key}${optional ? '?' : ''}: ${tsType(value, { ...ctx, where: `${ctx.where}.${key}` })}`;
|
|
188
|
+
});
|
|
189
|
+
return `{ ${fields.join('; ')} }`;
|
|
190
|
+
}
|
|
191
|
+
/** An entity's fields printed as a multi-line interface body. */
|
|
192
|
+
function interfaceBody(shape, ctx) {
|
|
193
|
+
return Object.keys(shape)
|
|
194
|
+
.map((key) => {
|
|
195
|
+
const optional = isOptional(shape[key]);
|
|
196
|
+
const value = optional ? unwrapOptional(shape[key]) : shape[key];
|
|
197
|
+
return ` ${key}${optional ? '?' : ''}: ${tsType(value, { ...ctx, where: `${ctx.where}.${key}` })};`;
|
|
198
|
+
})
|
|
199
|
+
.join('\n');
|
|
200
|
+
}
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Names.
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
const pascal = (s) => s.replace(/(^|[-_/])(\w)/g, (_, __, c) => c.toUpperCase());
|
|
205
|
+
const camel = (s) => {
|
|
206
|
+
const p = pascal(s);
|
|
207
|
+
return p.charAt(0).toLowerCase() + p.slice(1);
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* `todo/create-list` → `createList`; `protocol/get` → `protocolGet`.
|
|
211
|
+
*
|
|
212
|
+
* The vertical's OWN prefix is dropped — it is the client's identity and repeating it
|
|
213
|
+
* on every method says nothing (`api.callout.calloutCreateOrder()`). A composed
|
|
214
|
+
* engine's prefix stays, and has to: callout binds `workorder/get`, `protocol/get` and
|
|
215
|
+
* `invoicing/get` at three URLs, and a client with one `get()` would reach whichever
|
|
216
|
+
* bag was read last. Renaming an engine's operation to suit a vertical's client is not
|
|
217
|
+
* a thing a vertical may do, so the disambiguation belongs here.
|
|
218
|
+
*/
|
|
219
|
+
export const methodName = (operation, ownPrefix) => {
|
|
220
|
+
const [prefix, ...rest] = operation.split('/');
|
|
221
|
+
const tail = rest.join('/');
|
|
222
|
+
return prefix === ownPrefix ? camel(tail) : camel(`${prefix}/${tail}`);
|
|
223
|
+
};
|
|
224
|
+
const pathParams = (path) => [...path.matchAll(/\{(\w+)\}/g)].map((m) => m[1]);
|
|
225
|
+
/**
|
|
226
|
+
* The fixed runtime, which is a template rather than a derivation — but it is
|
|
227
|
+
* emitted rather than shipped as a package for the same reason the types are:
|
|
228
|
+
* the app depends on nothing, and one file is one thing to review.
|
|
229
|
+
*/
|
|
230
|
+
function preamble(name, source, anyPaged, stated) {
|
|
231
|
+
return `// GENERATED by tools/client-emit.mts from ${source} — do not edit by hand.${stated.length
|
|
232
|
+
? `
|
|
233
|
+
//
|
|
234
|
+
// HAND-STATED TYPES (substrat.client.types). These schemas have no inferable input
|
|
235
|
+
// type — a \`z.preprocess\` infers \`unknown\` by construction — so the type below was
|
|
236
|
+
// decided by a person, not derived, and is not checked against the schema:
|
|
237
|
+
${stated.map((line) => `// ${line}`).join('\n')}`
|
|
238
|
+
: ''}
|
|
239
|
+
//
|
|
240
|
+
// Re-emit with \`pnpm lint:client\`; CI runs \`pnpm lint:client --check\` and fails on
|
|
241
|
+
// drift, so the client cannot fall behind the model the way a hand-written one did.
|
|
242
|
+
//
|
|
243
|
+
// Standalone on purpose: no imports, so the browser bundle carries no schema library
|
|
244
|
+
// and \`tools/declared-deps.mjs\` has nothing to object to.
|
|
245
|
+
|
|
246
|
+
/** A failed request. \`status\` is the interesting field — 403 is the permission model answering. */
|
|
247
|
+
export class ApiError extends Error {
|
|
248
|
+
constructor(
|
|
249
|
+
readonly status: number,
|
|
250
|
+
message: string,
|
|
251
|
+
readonly body: unknown,
|
|
252
|
+
) {
|
|
253
|
+
super(message);
|
|
254
|
+
this.name = 'ApiError';
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
${anyPaged
|
|
258
|
+
? `
|
|
259
|
+
/**
|
|
260
|
+
* One page of a paged read, reassembled from the response.
|
|
261
|
+
*
|
|
262
|
+
* The BODY is the entries and always was; the walk rides in headers (#829), which is
|
|
263
|
+
* what let paging be adopted without renaming a live endpoint's response. This puts
|
|
264
|
+
* the two back together so a caller sees a page rather than a header protocol.
|
|
265
|
+
*
|
|
266
|
+
* \`next\` is an absolute URL to FOLLOW (RFC 8288), not a cursor to reassemble — this
|
|
267
|
+
* request's filters and page size travel with it. Its absence is how the walk ends.
|
|
268
|
+
*/
|
|
269
|
+
export interface Paged<T> {
|
|
270
|
+
entries: T[];
|
|
271
|
+
/** Hand back to \`follow()\`. \`null\` when there is no next page. */
|
|
272
|
+
next: string | null;
|
|
273
|
+
/** From \`X-Total-Count\`, and only for a read that declares \`total\`. */
|
|
274
|
+
total: number | null;
|
|
275
|
+
}
|
|
276
|
+
`
|
|
277
|
+
: ''}
|
|
278
|
+
export interface ClientOptions {
|
|
279
|
+
/** Prefixed to every declared path. Matches \`mountOperations\`' own default. */
|
|
280
|
+
baseUrl?: string;
|
|
281
|
+
/** Per-request headers — an auth token, or this demo's \`x-principal\` persona seam. */
|
|
282
|
+
headers?: () => Record<string, string>;
|
|
283
|
+
/** Injectable so the client works under a test harness, not only in a browser. */
|
|
284
|
+
fetch?: typeof globalThis.fetch;
|
|
285
|
+
/**
|
|
286
|
+
* Pull a message out of an error body.
|
|
287
|
+
*
|
|
288
|
+
* The one thing here the model does NOT declare: a vertical picks its error
|
|
289
|
+
* envelope in its own \`app.onError\`, so this is a best effort over the shapes in
|
|
290
|
+
* use — \`{ error }\`, and problem+json's \`detail\`/\`title\` — and an app that
|
|
291
|
+
* answers something else says so here rather than being guessed at.
|
|
292
|
+
*/
|
|
293
|
+
errorMessage?: (body: unknown, response: Response) => string | undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function defaultErrorMessage(body: unknown): string | undefined {
|
|
297
|
+
if (typeof body !== 'object' || body === null) return undefined;
|
|
298
|
+
const b = body as Record<string, unknown>;
|
|
299
|
+
for (const key of ['error', 'detail', 'title', 'message']) {
|
|
300
|
+
if (typeof b[key] === 'string') return b[key] as string;
|
|
301
|
+
}
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
export function renderClient(vertical, config, source, entities, operations, extras) {
|
|
307
|
+
const named = new Map();
|
|
308
|
+
const claimedBy = new Map();
|
|
309
|
+
const claim = (schema, name, origin) => {
|
|
310
|
+
const already = claimedBy.get(name);
|
|
311
|
+
if (already !== undefined && named.get(schema) !== name) {
|
|
312
|
+
fail(`${vertical}: two different schemas both want the interface name \`${name}\` ` +
|
|
313
|
+
`(${already}, ${origin}).\n` +
|
|
314
|
+
` Remedy: rename one in substrat.client.schemas.`);
|
|
315
|
+
}
|
|
316
|
+
claimedBy.set(name, origin);
|
|
317
|
+
named.set(schema, name);
|
|
318
|
+
};
|
|
319
|
+
for (const [key, def] of Object.entries(entities)) {
|
|
320
|
+
const fields = def?.fields;
|
|
321
|
+
if (!fields)
|
|
322
|
+
fail(`${vertical}: entity '${key}' has no \`fields\` schema`);
|
|
323
|
+
claim(fields, pascal(key), `entity ${key}`);
|
|
324
|
+
}
|
|
325
|
+
for (const [name, exportName] of Object.entries(config.schemas ?? {})) {
|
|
326
|
+
const schema = extras[exportName];
|
|
327
|
+
if (schema === undefined)
|
|
328
|
+
fail(`${vertical}: substrat.client.schemas names \`${exportName}\`, which nothing exports`);
|
|
329
|
+
claim(schema, name, `schema ${exportName}`);
|
|
330
|
+
}
|
|
331
|
+
// Keys are declared relative to the operation, so the vertical's own name is not
|
|
332
|
+
// repeated in every entry; `where` carries the prefix, so add it here.
|
|
333
|
+
const overrides = new Map(Object.entries(config.types ?? {}).map(([k, v]) => [`${vertical}.${k}`, v]));
|
|
334
|
+
const used = new Set();
|
|
335
|
+
const ctx = { named, where: vertical, overrides, used };
|
|
336
|
+
// Every named type gets a block: an entity's `fields`, and each extra schema. A name
|
|
337
|
+
// in the identity map with no interface behind it is a file that references a type it
|
|
338
|
+
// never declares — which typechecks nowhere and would only be found by building.
|
|
339
|
+
const blocks = [
|
|
340
|
+
...Object.entries(entities).map(([key, def]) => ({
|
|
341
|
+
name: pascal(key),
|
|
342
|
+
note: `\`${def.table ?? key}\` — declared in ${source}`,
|
|
343
|
+
schema: def.fields,
|
|
344
|
+
where: `${vertical}.${key}`,
|
|
345
|
+
})),
|
|
346
|
+
...Object.entries(config.schemas ?? {}).map(([name, exportName]) => ({
|
|
347
|
+
name,
|
|
348
|
+
note: `\`${exportName}\` — a published schema, not a stored row`,
|
|
349
|
+
schema: extras[exportName],
|
|
350
|
+
where: `${vertical}.${exportName}`,
|
|
351
|
+
})),
|
|
352
|
+
];
|
|
353
|
+
const entityBlocks = blocks.map(({ name, note, schema, where }) => {
|
|
354
|
+
const def = defOf(schema);
|
|
355
|
+
if (def?.type !== 'object') {
|
|
356
|
+
fail(`${vertical}: \`${name}\` is not an object schema, so it cannot be an interface.\n` +
|
|
357
|
+
` Remedy: drop it from substrat.client.schemas — a union or an array is spelled\n` +
|
|
358
|
+
` inline where it is used.`);
|
|
359
|
+
}
|
|
360
|
+
// Printed with this schema temporarily UNNAMED, or the body would be `Foo` referring
|
|
361
|
+
// to itself. Every other reference to it still resolves to the name.
|
|
362
|
+
const named2 = new Map(ctx.named);
|
|
363
|
+
named2.delete(schema);
|
|
364
|
+
return `/** ${note}. */\nexport interface ${name} {\n${interfaceBody(def.shape ?? {}, { ...ctx, named: named2, where })}\n}`;
|
|
365
|
+
});
|
|
366
|
+
// Then one method per operation that declares an HTTP binding. An operation
|
|
367
|
+
// without one is not part of this app's surface — a composed engine's, say.
|
|
368
|
+
const declared = Object.entries(operations)
|
|
369
|
+
.map(([operation, op]) => [operation, op])
|
|
370
|
+
.filter((entry) => Boolean(entry[1]?.http))
|
|
371
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
372
|
+
if (declared.length === 0)
|
|
373
|
+
fail(`${vertical}: no operation declares an \`http\` binding — a client over nothing`);
|
|
374
|
+
// The first named bag is the vertical's own `defineOperations`; the rest are engine
|
|
375
|
+
// route bindings. Taken from INSERTION order (the bags are merged in the order
|
|
376
|
+
// package.json names them), never from `declared`, which is sorted for a stable
|
|
377
|
+
// artifact — reading it there would make the answer depend on the alphabet, and it
|
|
378
|
+
// happens to be right for callout, which is the worst way for it to be wrong.
|
|
379
|
+
const ownPrefix = Object.keys(operations)[0]?.split('/')[0] ?? '';
|
|
380
|
+
const seen = new Map();
|
|
381
|
+
const methods = [];
|
|
382
|
+
const impls = [];
|
|
383
|
+
let anyPaged = false;
|
|
384
|
+
for (const [operation, op] of declared) {
|
|
385
|
+
const method = methodName(operation, ownPrefix);
|
|
386
|
+
const clash = seen.get(method);
|
|
387
|
+
if (clash) {
|
|
388
|
+
fail(`${vertical}: '${clash}' and '${operation}' both name the client method \`${method}\`.\n` +
|
|
389
|
+
` Two methods with one name is a call site that silently reaches the wrong\n` +
|
|
390
|
+
` endpoint. A composed engine's prefix is kept precisely to avoid this, so a\n` +
|
|
391
|
+
` collision here means two operations of the SAME module share a name.\n` +
|
|
392
|
+
` Remedy: rename one of them.`);
|
|
393
|
+
}
|
|
394
|
+
seen.set(method, operation);
|
|
395
|
+
const where = `${vertical}.${operation}`;
|
|
396
|
+
const params = pathParams(op.http.path);
|
|
397
|
+
const inputShape = (defOf(op.input)?.shape ?? {});
|
|
398
|
+
const hasInput = op.input !== undefined || params.length > 0;
|
|
399
|
+
// The parameter type is the input schema WHOLE — path parameters included.
|
|
400
|
+
// `mountOperations` reads them off the URL, but they are declared input fields
|
|
401
|
+
// and the compile-checked `{var}` join says so, so the caller passes one object.
|
|
402
|
+
const inputType = op.input ? tsObject(inputShape, { ...ctx, where: `${where}.input` }) : '{}';
|
|
403
|
+
const entryType = tsType(op.output, { ...ctx, where: `${where}.output` });
|
|
404
|
+
const returnType = op.paged ? `Paged<${maybeParen(entryType, ctx)}>` : entryType;
|
|
405
|
+
if (op.paged)
|
|
406
|
+
anyPaged = true;
|
|
407
|
+
const signature = hasInput ? `input: ${inputType}` : '';
|
|
408
|
+
const doc = [
|
|
409
|
+
' /**',
|
|
410
|
+
` * ${op.summary ?? operation}`,
|
|
411
|
+
' *',
|
|
412
|
+
` * \`${op.http.method} ${op.http.path}\` — \`${operation}\``,
|
|
413
|
+
...(op.paged ? [' *', ' * Paged: walk it with `follow(page.next)` until `next` is `null`.'] : []),
|
|
414
|
+
' */',
|
|
415
|
+
].join('\n');
|
|
416
|
+
methods.push(`${doc}\n ${method}(${signature}): Promise<${returnType}>;`);
|
|
417
|
+
// Path parameters are spliced out of the payload; what is left is a body for a
|
|
418
|
+
// writing method and a query string for a read — exactly what mountOperations
|
|
419
|
+
// expects on the other side, which is the only reason this can be derived.
|
|
420
|
+
const takesBody = ['POST', 'PUT', 'PATCH'].includes(op.http.method);
|
|
421
|
+
const pathExpr = params.length
|
|
422
|
+
? '`' + op.http.path.replace(/\{(\w+)\}/g, (_, p) => `\${encodeURIComponent(String(input.${p}))}`) + '`'
|
|
423
|
+
: JSON.stringify(op.http.path);
|
|
424
|
+
const rest = params.length ? `omit(input, ${JSON.stringify(params)})` : hasInput ? 'input' : 'undefined';
|
|
425
|
+
const call = op.paged ? 'page' : 'send';
|
|
426
|
+
impls.push(` ${method}: (${hasInput ? 'input: Args' : ''}) =>\n` +
|
|
427
|
+
` ${call}(${pathExpr}, ${JSON.stringify(op.http.method)}, ${takesBody ? `${rest}, undefined` : `undefined, ${rest}`}),`);
|
|
428
|
+
}
|
|
429
|
+
const runtime = `
|
|
430
|
+
/**
|
|
431
|
+
* The plumbing below is deliberately untyped against the interface above, and cast
|
|
432
|
+
* once at the end.
|
|
433
|
+
*
|
|
434
|
+
* \`send\` cannot know an operation's return type — it has a \`Response\`, not a schema
|
|
435
|
+
* — so every method would otherwise need its own \`as\` and the file would carry
|
|
436
|
+
* fourteen casts instead of one. The interface is what a caller sees, and it IS
|
|
437
|
+
* derived from the model; this is the seam where a runtime that speaks \`unknown\`
|
|
438
|
+
* meets it.
|
|
439
|
+
*/
|
|
440
|
+
type Args = Record<string, unknown>;
|
|
441
|
+
|
|
442
|
+
const omit = (input: Record<string, unknown>, keys: string[]): Record<string, unknown> => {
|
|
443
|
+
const out: Record<string, unknown> = {};
|
|
444
|
+
for (const [k, v] of Object.entries(input ?? {})) if (!keys.includes(k)) out[k] = v;
|
|
445
|
+
return out;
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
/** Declared fields become a query string; \`undefined\` is absent rather than the string "undefined". */
|
|
449
|
+
const query = (values: Record<string, unknown> | undefined): string => {
|
|
450
|
+
if (!values) return '';
|
|
451
|
+
const params = new URLSearchParams();
|
|
452
|
+
for (const [k, v] of Object.entries(values)) {
|
|
453
|
+
if (v === undefined || v === null) continue;
|
|
454
|
+
params.set(k, String(v));
|
|
455
|
+
}
|
|
456
|
+
const s = params.toString();
|
|
457
|
+
return s ? \`?\${s}\` : '';
|
|
458
|
+
};
|
|
459
|
+
${anyPaged
|
|
460
|
+
? `
|
|
461
|
+
/**
|
|
462
|
+
* The \`rel="next"\` URL out of an RFC 8288 \`Link\` header, or null.
|
|
463
|
+
*
|
|
464
|
+
* Only \`next\` is looked for because only \`next\` is sent: keyset paging walks one
|
|
465
|
+
* way and does not know its own offset, so there is no \`prev\`, \`first\` or \`last\`
|
|
466
|
+
* to honour. A header naming some other relation is not this walk's.
|
|
467
|
+
*/
|
|
468
|
+
const nextFrom = (header: string | null): string | null => {
|
|
469
|
+
if (!header) return null;
|
|
470
|
+
for (const part of header.split(',')) {
|
|
471
|
+
const match = /^\\s*<([^>]+)>\\s*;\\s*(.+)$/.exec(part);
|
|
472
|
+
if (match && /rel\\s*=\\s*"?next"?/.test(match[2] as string)) return match[1] as string;
|
|
473
|
+
}
|
|
474
|
+
return null;
|
|
475
|
+
};
|
|
476
|
+
`
|
|
477
|
+
: ''}
|
|
478
|
+
export function createClient(options: ClientOptions = {}): ${config.name}Client {
|
|
479
|
+
const baseUrl = options.baseUrl ?? '/api';
|
|
480
|
+
const doFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
481
|
+
const readMessage = options.errorMessage ?? defaultErrorMessage;
|
|
482
|
+
|
|
483
|
+
/** One request, against a path that is ALREADY prefixed and query-stringed. */
|
|
484
|
+
const raw = async (fullPath: string, method: string, body: unknown): Promise<Response> => {
|
|
485
|
+
const hasBody = body !== undefined && method !== 'GET' && method !== 'DELETE';
|
|
486
|
+
return await doFetch(fullPath, {
|
|
487
|
+
method,
|
|
488
|
+
headers: {
|
|
489
|
+
...(hasBody ? { 'content-type': 'application/json' } : {}),
|
|
490
|
+
...(options.headers?.() ?? {}),
|
|
491
|
+
},
|
|
492
|
+
...(hasBody ? { body: JSON.stringify(body) } : {}),
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
const parse = async (res: Response): Promise<unknown> => {
|
|
497
|
+
const text = await res.text();
|
|
498
|
+
const body: unknown = text ? JSON.parse(text) : null;
|
|
499
|
+
if (!res.ok) throw new ApiError(res.status, readMessage(body, res) ?? res.statusText, body);
|
|
500
|
+
return body;
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const send = async (path: string, method: string, body: unknown, params: unknown): Promise<unknown> =>
|
|
504
|
+
await parse(await raw(\`\${baseUrl}\${path}\${query(params as Record<string, unknown>)}\`, method, body));
|
|
505
|
+
${anyPaged
|
|
506
|
+
? `
|
|
507
|
+
/**
|
|
508
|
+
* A paged read: the entries come from the body, the walk from the headers.
|
|
509
|
+
*
|
|
510
|
+
* A cross-origin caller reads neither header without \`Access-Control-Expose-Headers\`,
|
|
511
|
+
* and the symptom is not an error — it is a list that looks like it has one page. Under
|
|
512
|
+
* a same-origin dev proxy (this demo) or a same-origin deployment, both are readable.
|
|
513
|
+
*/
|
|
514
|
+
const readPage = async (res: Response): Promise<Paged<unknown>> => {
|
|
515
|
+
const entries = (await parse(res)) as unknown[];
|
|
516
|
+
const total = res.headers.get('X-Total-Count');
|
|
517
|
+
return { entries, next: nextFrom(res.headers.get('Link')), total: total === null ? null : Number(total) };
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const page = async (path: string, method: string, body: unknown, params: unknown): Promise<Paged<unknown>> =>
|
|
521
|
+
await readPage(await raw(\`\${baseUrl}\${path}\${query(params as Record<string, unknown>)}\`, method, body));
|
|
522
|
+
`
|
|
523
|
+
: ''}
|
|
524
|
+
return {
|
|
525
|
+
${impls.join('\n')}${anyPaged
|
|
526
|
+
? `
|
|
527
|
+
follow: async (next: string) => {
|
|
528
|
+
// The link names the API's OWN origin, which under a dev proxy is not the origin
|
|
529
|
+
// this page was served from. So the path is kept and the origin is taken from
|
|
530
|
+
// whatever this client was configured to talk to: relative for a browser, which
|
|
531
|
+
// keeps the request same-origin, absolute for a harness that has no page.
|
|
532
|
+
const link = new URL(next, 'http://substrat.invalid');
|
|
533
|
+
const target = /^https?:\\/\\//.test(baseUrl)
|
|
534
|
+
? new URL(link.pathname + link.search, baseUrl).href
|
|
535
|
+
: link.pathname + link.search;
|
|
536
|
+
return await readPage(await raw(target, 'GET', undefined));
|
|
537
|
+
},`
|
|
538
|
+
: ''}
|
|
539
|
+
} as unknown as ${config.name}Client;
|
|
540
|
+
}
|
|
541
|
+
`;
|
|
542
|
+
const followSignature = anyPaged
|
|
543
|
+
? `
|
|
544
|
+
/**
|
|
545
|
+
* Fetch the next page of any paged read, given a previous page's \`next\`.
|
|
546
|
+
*
|
|
547
|
+
* One method for every paged read rather than one per read: \`next\` is a URL that
|
|
548
|
+
* already carries the filters, so there is nothing left for a caller to restate.
|
|
549
|
+
*/
|
|
550
|
+
follow<T>(next: string): Promise<Paged<T>>;
|
|
551
|
+
`
|
|
552
|
+
: '';
|
|
553
|
+
const unused = [...overrides.keys()].filter((k) => !used.has(k));
|
|
554
|
+
if (unused.length) {
|
|
555
|
+
fail(`${vertical}: substrat.client.types names ${unused.map((k) => k.slice(vertical.length + 1)).join(', ')}, ` +
|
|
556
|
+
`which the printer never reached.\n` +
|
|
557
|
+
` A stale escape hatch reads as a live decision. Remedy: remove the entry.`);
|
|
558
|
+
}
|
|
559
|
+
return [
|
|
560
|
+
preamble(config.name, source, anyPaged, [...used].sort().map((k) => `${k.slice(vertical.length + 1)} → ${overrides.get(k)}`)),
|
|
561
|
+
entityBlocks.join('\n\n'),
|
|
562
|
+
`/** Every operation this vertical binds to HTTP, one method each. */\nexport interface ${config.name}Client {\n${methods.join('\n\n')}\n${followSignature}}`,
|
|
563
|
+
runtime.trimStart(),
|
|
564
|
+
].join('\n\n');
|
|
565
|
+
}
|
|
566
|
+
//# sourceMappingURL=emit-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-client.js","sourceRoot":"","sources":["../src/emit-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { emitTables, columnsOf, primaryKeyConstraint, uniqueConstraints, type Em
|
|
|
2
2
|
export { journalColumns, journalUniques, journalPrimaryKeys } from './journal.js';
|
|
3
3
|
export { readSchema, statements, type TableSchema } from './replay.js';
|
|
4
4
|
export { planMigration, parseJournal, type Journal, type JournalEntry, type MigrationPlan, } from './plan.js';
|
|
5
|
+
export { renderClient, ClientEmitError, tsTypeOf, methodName, type ClientConfig, } from './emit-client.js';
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EACL,aAAa,EACb,YAAY,EACZ,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EACL,aAAa,EACb,YAAY,EACZ,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,UAAU,EACV,KAAK,YAAY,GAClB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,5 @@ export { emitTables, columnsOf, primaryKeyConstraint, uniqueConstraints, } from
|
|
|
2
2
|
export { journalColumns, journalUniques, journalPrimaryKeys } from './journal.js';
|
|
3
3
|
export { readSchema, statements } from './replay.js';
|
|
4
4
|
export { planMigration, parseJournal, } from './plan.js';
|
|
5
|
+
export { renderClient, ClientEmitError, tsTypeOf, methodName, } from './emit-client.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAoB,MAAM,aAAa,CAAC;AACvE,OAAO,EACL,aAAa,EACb,YAAY,GAIb,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAoB,MAAM,aAAa,CAAC;AACvE,OAAO,EACL,aAAa,EACb,YAAY,GAIb,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,UAAU,GAEX,MAAM,kBAAkB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@substrat-run/model-emit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Build-time tooling over a Substrat model — DDL emitted from the entity registry, and the journal reader that holds it honest",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@substrat-run/contracts": "0.
|
|
25
|
+
"@substrat-run/contracts": "0.83.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.0.0",
|