@substrat-run/contracts 0.67.0 → 0.69.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/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/dist/model.d.ts +69 -41
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +64 -10
- package/dist/model.js.map +1 -1
- package/dist/operations.d.ts +223 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +81 -0
- package/dist/operations.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
|
package/dist/model.d.ts
CHANGED
|
@@ -32,11 +32,18 @@ export interface EntityDef<Names extends string = string> {
|
|
|
32
32
|
/** The row shape. Field names are what `key`, `searchables` and events check against. */
|
|
33
33
|
readonly fields: z.ZodObject<z.ZodRawShape>;
|
|
34
34
|
/**
|
|
35
|
-
* The parent entity permission
|
|
36
|
-
* Checked against the declared entities — a typo is a compile error, where
|
|
37
|
-
*
|
|
35
|
+
* The parent entity types permission may flow along (design doc §4.2 rule 3).
|
|
36
|
+
* Checked against the declared entities — a typo is a compile error, where it
|
|
37
|
+
* used to be a silently dead edge.
|
|
38
|
+
*
|
|
39
|
+
* **Plural, and an array even for one.** `entityRelations` is an ALLOWLIST,
|
|
40
|
+
* not an assertion: the kernel accumulates permitted parents into a *set* per
|
|
41
|
+
* entity type and `ctx.link` checks membership. `reservation` already hangs
|
|
42
|
+
* off both `resource` and `member`; `protocol` off both `workorder` and
|
|
43
|
+
* `employee`. Singular `parent` said "the parent", which is not what the
|
|
44
|
+
* kernel means and cannot express the real cases.
|
|
38
45
|
*/
|
|
39
|
-
readonly
|
|
46
|
+
readonly parents?: readonly Names[];
|
|
40
47
|
/** Natural key, if any. Must name fields that exist. */
|
|
41
48
|
readonly key?: readonly string[];
|
|
42
49
|
/** Fields an erasure must be able to reach (§12). Must name fields that exist. */
|
|
@@ -79,7 +86,8 @@ export type EntityName<T> = keyof T & string;
|
|
|
79
86
|
export interface EmittedEntity {
|
|
80
87
|
readonly table: string;
|
|
81
88
|
readonly fields: Record<string, unknown>;
|
|
82
|
-
|
|
89
|
+
/** The permitted parent types, sorted. One shape, always. */
|
|
90
|
+
readonly parents?: readonly string[];
|
|
83
91
|
readonly key?: readonly string[];
|
|
84
92
|
readonly erasable?: readonly string[];
|
|
85
93
|
}
|
|
@@ -140,50 +148,37 @@ type EntityRefs<T extends Record<string, EntityDef>, M> = {
|
|
|
140
148
|
readonly view: string;
|
|
141
149
|
}[];
|
|
142
150
|
/**
|
|
143
|
-
*
|
|
151
|
+
* The engine registries this module composes, so relation edges naming their
|
|
152
|
+
* entities can be checked.
|
|
153
|
+
*/
|
|
154
|
+
readonly engines?: readonly Record<string, EntityDef>[];
|
|
155
|
+
/**
|
|
156
|
+
* Parent edges involving an entity this module does not own.
|
|
144
157
|
*
|
|
145
158
|
* A vertical legitimately declares these: an engine is entity-agnostic, so
|
|
146
159
|
* only the vertical knows that a work order hangs off a bike, or a protocol
|
|
147
|
-
* off a work order.
|
|
148
|
-
*
|
|
149
|
-
* up a check we hold.
|
|
160
|
+
* off a work order. **Both sides are checked** against the local entities plus
|
|
161
|
+
* every entity of every registry in `engines`.
|
|
150
162
|
*
|
|
151
|
-
*
|
|
163
|
+
* Local-to-local edges do not belong here — they are DERIVED from the
|
|
164
|
+
* entities' own `parents`, and declaring one twice is how two descriptions of
|
|
165
|
+
* a fact come to disagree.
|
|
152
166
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* { entityType: 'protocol', parentType: 'workorder' }, // parent foreign too
|
|
157
|
-
* ]
|
|
158
|
-
* ```
|
|
159
|
-
*
|
|
160
|
-
* `parentType` accepts a declared entity name or an arbitrary string, and a
|
|
161
|
-
* typo that happens to look like neither is still accepted — TypeScript cannot
|
|
162
|
-
* express "one of these, or any other string, but tell me which". What it does
|
|
163
|
-
* buy is autocomplete on the local names and a compile error on the mixed edge
|
|
164
|
-
* once `foreignParents` (below) is used instead.
|
|
165
|
-
*
|
|
166
|
-
* Both become fully checkable when engines export their entity-type constants
|
|
167
|
-
* (#696 item 3), at which point these two fields collapse back into `parent`.
|
|
167
|
+
* This replaces the `foreignChildOf` / `foreignChildren` pair, which existed
|
|
168
|
+
* only because foreign names were uncheckable. They are now, so the split has
|
|
169
|
+
* nothing left to say.
|
|
168
170
|
*/
|
|
169
|
-
readonly
|
|
170
|
-
readonly entityType: string
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
* declared entity — a typo is a compile error.
|
|
177
|
-
*
|
|
178
|
-
* `bike_shop`'s `workorder → bike` is the case: `workorder` is the engine's,
|
|
179
|
-
* `bike` is the vertical's, and the vertical is the only place that knows the
|
|
180
|
-
* edge exists.
|
|
181
|
-
*/
|
|
182
|
-
readonly foreignChildOf?: readonly {
|
|
183
|
-
readonly entityType: string;
|
|
184
|
-
readonly parentType: keyof T & string;
|
|
171
|
+
readonly relations?: readonly {
|
|
172
|
+
readonly entityType: (keyof T & string) | (M extends {
|
|
173
|
+
engines: readonly (infer R)[];
|
|
174
|
+
} ? NamesOf<R> : never);
|
|
175
|
+
readonly parentType: (keyof T & string) | (M extends {
|
|
176
|
+
engines: readonly (infer R)[];
|
|
177
|
+
} ? NamesOf<R> : never);
|
|
185
178
|
}[];
|
|
186
179
|
};
|
|
180
|
+
/** Every entity name in one registry. */
|
|
181
|
+
type NamesOf<R> = R extends Record<string, EntityDef> ? keyof R & string : never;
|
|
187
182
|
/**
|
|
188
183
|
* Compose the entity-referencing manifest fragments against the registry.
|
|
189
184
|
*
|
|
@@ -217,5 +212,38 @@ export declare function manifestEntities<const T extends Record<string, EntityDe
|
|
|
217
212
|
entityViews: M['entityViews'];
|
|
218
213
|
};
|
|
219
214
|
};
|
|
215
|
+
/**
|
|
216
|
+
* The row type of a declared entity — what `ctx.sql.query` returns for it.
|
|
217
|
+
*
|
|
218
|
+
* `ctx.sql.query` leaves `T` to the vertical, so every handler writes its own
|
|
219
|
+
* row interface and the schema ends up described three times: the DDL, the
|
|
220
|
+
* registry, and a hand-written `interface CustomerRow`. This collapses the
|
|
221
|
+
* third into the second.
|
|
222
|
+
*
|
|
223
|
+
* ```ts
|
|
224
|
+
* export type CustomerRow = EntityRow<typeof calloutEntities, 'customer'>;
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
export type EntityRow<T extends Record<string, EntityDef>, K extends keyof T> = T[K] extends {
|
|
228
|
+
fields: infer F;
|
|
229
|
+
} ? F extends z.ZodObject<z.ZodRawShape> ? z.infer<F> : never : never;
|
|
230
|
+
/**
|
|
231
|
+
* Column names per table, read out of a migration journal's SQL.
|
|
232
|
+
*
|
|
233
|
+
* **Test tooling**, exported because three engines had hand-rolled a copy and
|
|
234
|
+
* the copies had already drifted — none followed `RENAME TO`, so a journal that
|
|
235
|
+
* rebuilds a table under a temporary name would report the pre-rebuild columns
|
|
236
|
+
* forever.
|
|
237
|
+
*
|
|
238
|
+
* It exists because a registry and a journal are two descriptions of one schema
|
|
239
|
+
* until migrations are derived from the registry. Holding them to each other is
|
|
240
|
+
* what keeps that duplication safe in the meantime.
|
|
241
|
+
*
|
|
242
|
+
* Handles what real journals do: multi-line `CHECK (...)` constraints (tracked
|
|
243
|
+
* by paren depth, so a continuation line is not read as a column), `ADD COLUMN`,
|
|
244
|
+
* `DROP TABLE`, and `RENAME TO` — append-only journals rebuild a table by
|
|
245
|
+
* creating a `_new`, copying, dropping the original and renaming onto its name.
|
|
246
|
+
*/
|
|
247
|
+
export declare function journalColumns(sql: string): Map<string, Set<string>>;
|
|
220
248
|
export {};
|
|
221
249
|
//# sourceMappingURL=model.d.ts.map
|
package/dist/model.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,SAAS,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACtD,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC5C
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,SAAS,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACtD,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC5C;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;IACpC,wDAAwD;IACxD,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,uEAAuE;AACvE,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACvD,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAClC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,GACzB,KAAK,GACP,KAAK,CAAC;AAEV;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,CAAC,SAAS;IACR,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG;QACrD,GAAG,CAAC,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpC,QAAQ,CAAC,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1C;CACF,EACD,QAAQ,EAAE,CAAC,GAAG,CAAC,CAEhB;AAED,iCAAiC;AACjC,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,YAAY,CAkBxF;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EACnE,QAAQ,EAAE,CAAC,GACV;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,CAQ9C;AAMD;;;;;;;;;GASG;AACH,KAAK,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI;IACxD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS;QACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;QACtC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;KACnC,EAAE,CAAC;IACJ;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,SAAS;QAAE,WAAW,EAAE,MAAM,CAAC,CAAA;KAAE,GACrD;QACE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;YAAE,UAAU,EAAE,MAAM,CAAC,CAAA;SAAE,GACzD,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,GACxB;YAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;SAAE,GAC1E,KAAK,GACP,KAAK;KACV,GACD,KAAK,CAAC;IACV,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACnG;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;IACxD;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS;QAI5B,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS;YAAE,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,CAAA;SAAE,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAC7G,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS;YAAE,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,CAAA;SAAE,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;KAC9G,EAAE,CAAC;CACL,CAAC;AAEF,yCAAyC;AACzC,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC;AAGjF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EACzC,KAAK,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAEhC,QAAQ,EAAE,CAAC,EACX,IAAI,EAAE,CAAC,GACN;IACD,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,GAAG,EAAE,CAAC;IAC5D,WAAW,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC;IAC9B,eAAe,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9D,EAAE,EAAE;QAAE,WAAW,EAAE,CAAC,CAAC,aAAa,CAAC,CAAA;KAAE,CAAC;CACvC,CAUA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;IAC3F,MAAM,EAAE,MAAM,CAAC,CAAC;CACjB,GACG,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAClC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GACV,KAAK,GACP,KAAK,CAAC;AAEV;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAsCpE"}
|
package/dist/model.js
CHANGED
|
@@ -50,7 +50,7 @@ export function emitModel(entities) {
|
|
|
50
50
|
out[name] = {
|
|
51
51
|
table: e.table,
|
|
52
52
|
fields,
|
|
53
|
-
...(e.
|
|
53
|
+
...(e.parents?.length ? { parents: [...e.parents].sort() } : {}),
|
|
54
54
|
...(e.key ? { key: [...e.key].sort() } : {}),
|
|
55
55
|
...(e.erasable ? { erasable: [...e.erasable].sort() } : {}),
|
|
56
56
|
};
|
|
@@ -69,8 +69,10 @@ export function entityRelationsOf(entities) {
|
|
|
69
69
|
return Object.keys(entities)
|
|
70
70
|
.sort()
|
|
71
71
|
.flatMap((name) => {
|
|
72
|
-
const
|
|
73
|
-
|
|
72
|
+
const parents = entities[name]?.parents;
|
|
73
|
+
if (!parents?.length)
|
|
74
|
+
return [];
|
|
75
|
+
return [...parents].sort().map((parentType) => ({ entityType: name, parentType }));
|
|
74
76
|
});
|
|
75
77
|
}
|
|
76
78
|
/**
|
|
@@ -100,14 +102,66 @@ export function manifestEntities(entities, refs) {
|
|
|
100
102
|
attachmentTargets: (refs.attachmentTargets ?? []),
|
|
101
103
|
searchables: refs.searchables,
|
|
102
104
|
// Derived edges first, then the ones this module cannot check.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
...(refs.foreignChildOf ?? []),
|
|
107
|
-
// Neither side checkable here.
|
|
108
|
-
...(refs.foreignChildren ?? []),
|
|
109
|
-
],
|
|
105
|
+
// Local edges are derived from the entities' own `parents`; edges involving
|
|
106
|
+
// a composed engine's entity are declared, and both sides are checked.
|
|
107
|
+
entityRelations: [...entityRelationsOf(entities), ...(refs.relations ?? [])],
|
|
110
108
|
ui: { entityViews: refs.entityViews },
|
|
111
109
|
};
|
|
112
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Column names per table, read out of a migration journal's SQL.
|
|
113
|
+
*
|
|
114
|
+
* **Test tooling**, exported because three engines had hand-rolled a copy and
|
|
115
|
+
* the copies had already drifted — none followed `RENAME TO`, so a journal that
|
|
116
|
+
* rebuilds a table under a temporary name would report the pre-rebuild columns
|
|
117
|
+
* forever.
|
|
118
|
+
*
|
|
119
|
+
* It exists because a registry and a journal are two descriptions of one schema
|
|
120
|
+
* until migrations are derived from the registry. Holding them to each other is
|
|
121
|
+
* what keeps that duplication safe in the meantime.
|
|
122
|
+
*
|
|
123
|
+
* Handles what real journals do: multi-line `CHECK (...)` constraints (tracked
|
|
124
|
+
* by paren depth, so a continuation line is not read as a column), `ADD COLUMN`,
|
|
125
|
+
* `DROP TABLE`, and `RENAME TO` — append-only journals rebuild a table by
|
|
126
|
+
* creating a `_new`, copying, dropping the original and renaming onto its name.
|
|
127
|
+
*/
|
|
128
|
+
export function journalColumns(sql) {
|
|
129
|
+
const tables = new Map();
|
|
130
|
+
for (const [, table, body] of sql.matchAll(/CREATE TABLE (?:IF NOT EXISTS )?([a-z_][a-z0-9_]*)\s*\(([\s\S]*?)\n\s*\);/gi)) {
|
|
131
|
+
if (!table || !body)
|
|
132
|
+
continue;
|
|
133
|
+
const cols = new Set();
|
|
134
|
+
let depth = 0;
|
|
135
|
+
for (const raw of body.split('\n')) {
|
|
136
|
+
const line = raw.trim();
|
|
137
|
+
const atTop = depth === 0;
|
|
138
|
+
depth += (line.match(/\(/g) ?? []).length - (line.match(/\)/g) ?? []).length;
|
|
139
|
+
if (!atTop)
|
|
140
|
+
continue;
|
|
141
|
+
if (!line || line.startsWith('--') || /^(PRIMARY|FOREIGN|UNIQUE|CHECK|CONSTRAINT)\b/i.test(line))
|
|
142
|
+
continue;
|
|
143
|
+
const name = /^([a-z_][a-z0-9_]*)\b/i.exec(line)?.[1];
|
|
144
|
+
if (name)
|
|
145
|
+
cols.add(name);
|
|
146
|
+
}
|
|
147
|
+
tables.set(table, cols);
|
|
148
|
+
}
|
|
149
|
+
// Replayed in statement order: a journal may add a column and later rename the
|
|
150
|
+
// table, or rename onto a name it has just dropped.
|
|
151
|
+
for (const m of sql.matchAll(/(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+ADD COLUMN\s+([a-z_][a-z0-9_]*))|(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+RENAME TO\s+([a-z_][a-z0-9_]*))|(?:DROP TABLE (?:IF EXISTS )?([a-z_][a-z0-9_]*))/gi)) {
|
|
152
|
+
const [, addTable, addCol, fromTable, toTable, dropped] = m;
|
|
153
|
+
if (addTable && addCol)
|
|
154
|
+
tables.get(addTable)?.add(addCol);
|
|
155
|
+
else if (dropped)
|
|
156
|
+
tables.delete(dropped);
|
|
157
|
+
else if (fromTable && toTable) {
|
|
158
|
+
const cols = tables.get(fromTable);
|
|
159
|
+
if (cols) {
|
|
160
|
+
tables.delete(fromTable);
|
|
161
|
+
tables.set(toTable, cols);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return tables;
|
|
166
|
+
}
|
|
113
167
|
//# sourceMappingURL=model.js.map
|
package/dist/model.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The operation surface of the model (#707).
|
|
3
|
+
*
|
|
4
|
+
* #697 declared the entities. This declares what can be *done* to them, and
|
|
5
|
+
* checks the joins that today are unchecked strings: which permission an
|
|
6
|
+
* operation requires, which output field an event takes its subject from,
|
|
7
|
+
* whether a payload carries something an erasure must be able to reach.
|
|
8
|
+
*
|
|
9
|
+
* ## A composer, not a second `defineModel`
|
|
10
|
+
*
|
|
11
|
+
* `defineOperations` sits beside `defineEntities` rather than swallowing it.
|
|
12
|
+
* Each half stays independently adoptable — which is what let the entity half
|
|
13
|
+
* ship and be taken up by two verticals before this existed. A vertical adopts
|
|
14
|
+
* operations when it is ready, not as the price of adopting entities.
|
|
15
|
+
*
|
|
16
|
+
* ## `input`, and the transcription that is not here
|
|
17
|
+
*
|
|
18
|
+
* `input` is the Zod object the handler already parses — the same object, not a
|
|
19
|
+
* description of it. That is the whole reason the model is TypeScript (#680): a
|
|
20
|
+
* schema language would need the shape written twice, and transcription is what
|
|
21
|
+
* produced 40 wrong argument names in the one app where this was measured.
|
|
22
|
+
*/
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
import type { EntityDef } from './model.js';
|
|
25
|
+
/** `{var}` names in a literal path. */
|
|
26
|
+
type PathParams<S extends string> = S extends `${string}{${infer P}}${infer Rest}` ? P | PathParams<Rest> : never;
|
|
27
|
+
type InputKeys<O> = O extends {
|
|
28
|
+
input: infer I;
|
|
29
|
+
} ? (I extends z.ZodType ? keyof z.infer<I> & string : never) : never;
|
|
30
|
+
type OutputKeys<O> = O extends {
|
|
31
|
+
output: infer R;
|
|
32
|
+
} ? (R extends z.ZodType ? keyof z.infer<R> & string : never) : never;
|
|
33
|
+
/** Every `{var}` must name an input field, or the path type collapses. */
|
|
34
|
+
type CheckedPath<O> = O extends {
|
|
35
|
+
http: {
|
|
36
|
+
path: infer P;
|
|
37
|
+
};
|
|
38
|
+
} ? P extends string ? [PathParams<P>] extends [InputKeys<O>] ? P : never : never : string;
|
|
39
|
+
/**
|
|
40
|
+
* The erasable fields OF THE ENTITY THIS EVENT IS ABOUT.
|
|
41
|
+
*
|
|
42
|
+
* Resolving through `emits.entity` rather than matching field names across all
|
|
43
|
+
* entities is what makes the §12 check exact. A `contactPerson.email` marked
|
|
44
|
+
* erasable must not stop an event about an `office` carrying its own `email` —
|
|
45
|
+
* a rule that refuses correct code trains people to route around it, which is
|
|
46
|
+
* how a PII rule stops being obeyed.
|
|
47
|
+
*/
|
|
48
|
+
type ErasableOf<Entities, O> = O extends {
|
|
49
|
+
emits: {
|
|
50
|
+
entity: infer N;
|
|
51
|
+
};
|
|
52
|
+
} ? N extends keyof Entities ? Entities[N] extends {
|
|
53
|
+
erasable: readonly (infer F)[];
|
|
54
|
+
} ? F & string : never : never : never;
|
|
55
|
+
/**
|
|
56
|
+
* The platform's own event invariant, moved from runtime to compile time.
|
|
57
|
+
* `contracts/events.ts` enforces it with a `superRefine`: *"subjectId is
|
|
58
|
+
* required when piiClass is 'direct' — crypto-shredding must be able to key the
|
|
59
|
+
* erasure"*. Classification is mandatory here for the same reason it is there:
|
|
60
|
+
* an unclassified event type cannot be declared.
|
|
61
|
+
*/
|
|
62
|
+
type PiiShape<O, OutKeys extends string> = O extends {
|
|
63
|
+
emits: {
|
|
64
|
+
piiClass: 'none';
|
|
65
|
+
};
|
|
66
|
+
} ? {
|
|
67
|
+
readonly piiClass: 'none';
|
|
68
|
+
readonly subjectId?: never;
|
|
69
|
+
} : {
|
|
70
|
+
readonly piiClass: 'pseudonymous' | 'direct';
|
|
71
|
+
readonly subjectId: OutKeys;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* An operation carries a leading `permission` OR `narrows` with a reason, never
|
|
75
|
+
* both and never neither (rule 5 / CRM-EFF's check 14). `narrows` is the
|
|
76
|
+
* per-row proof walk: a salesperson listing their own customers must get their
|
|
77
|
+
* list, not a denial.
|
|
78
|
+
*/
|
|
79
|
+
type OpAuthority<O, PermKey extends string> = O extends {
|
|
80
|
+
narrows: unknown;
|
|
81
|
+
} ? {
|
|
82
|
+
readonly narrows: {
|
|
83
|
+
readonly reason: string;
|
|
84
|
+
};
|
|
85
|
+
readonly permission?: never;
|
|
86
|
+
} : {
|
|
87
|
+
readonly permission: PermKey;
|
|
88
|
+
readonly narrows?: never;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The per-operation constraint, self-referential in `O`.
|
|
92
|
+
*
|
|
93
|
+
* Each operation is checked against ITS OWN declared input and output rather
|
|
94
|
+
* than an erased supertype. Written the obvious way every check below compiles
|
|
95
|
+
* clean and enforces nothing — see `test/operations.test.ts`, which exists to
|
|
96
|
+
* prove they still bite.
|
|
97
|
+
*/
|
|
98
|
+
type OperationShape<O, Entities, PermKey extends string> = {
|
|
99
|
+
/** One line, imperative — what invoking this does. Feeds the API document. */
|
|
100
|
+
readonly summary: string;
|
|
101
|
+
/**
|
|
102
|
+
* The request body — the SAME Zod object the handler parses.
|
|
103
|
+
*
|
|
104
|
+
* **Omitted means no body at all**, and the handler then takes `undefined`.
|
|
105
|
+
* Found by the first adopter: three of Callout's six operations take no input,
|
|
106
|
+
* and a required `z.object({})` cannot say so — a handler accepting only
|
|
107
|
+
* `undefined` is not assignable to one accepting `{}`.
|
|
108
|
+
*
|
|
109
|
+
* This mirrors `ApiOperationDoc.input` ("Omit = no body") rather than
|
|
110
|
+
* inventing a second vocabulary for the same fact.
|
|
111
|
+
*/
|
|
112
|
+
readonly input?: z.ZodObject<z.ZodRawShape>;
|
|
113
|
+
/** True when the handler accepts a body but also accepts none (filter-style reads). */
|
|
114
|
+
readonly inputOptional?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Declared, not inferred (#695 Ask 2). Inference documents accidents: one
|
|
117
|
+
* inferred return carried `contacts?: undefined`, an artefact of an early
|
|
118
|
+
* return, which generation would have cemented into the published API.
|
|
119
|
+
*
|
|
120
|
+
* Declare a return where a caller branches on it — a UI lane is a caller that
|
|
121
|
+
* branches, which is why #682/#683 depend on this.
|
|
122
|
+
*/
|
|
123
|
+
readonly output: z.ZodType;
|
|
124
|
+
readonly http?: {
|
|
125
|
+
readonly method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
|
|
126
|
+
readonly path: CheckedPath<O>;
|
|
127
|
+
};
|
|
128
|
+
readonly emits?: {
|
|
129
|
+
/** The entity the event is about — a declared entity. */
|
|
130
|
+
readonly entity: keyof Entities & string;
|
|
131
|
+
/**
|
|
132
|
+
* Which OUTPUT field carries that entity's id.
|
|
133
|
+
*
|
|
134
|
+
* The #695 defect: 18 operations emitted `entityId: String(result.id)` on
|
|
135
|
+
* objects that answer with `contractId` / `runId` / `instanceId`. For a
|
|
136
|
+
* mutation writing a child the event is about the PARENT, so the two differ
|
|
137
|
+
* and nothing downstream would ever have flagged it.
|
|
138
|
+
*/
|
|
139
|
+
readonly entityIdFrom: OutputKeys<O>;
|
|
140
|
+
readonly type: string;
|
|
141
|
+
readonly schemaVersion: number;
|
|
142
|
+
/**
|
|
143
|
+
* Fat payload, drawn from the output — minus anything the entity marks
|
|
144
|
+
* `erasable`. Immutable events are the one place in a scope an erasure
|
|
145
|
+
* cannot reach.
|
|
146
|
+
*/
|
|
147
|
+
readonly payload?: readonly Exclude<OutputKeys<O>, ErasableOf<Entities, O>>[];
|
|
148
|
+
} & PiiShape<O, OutputKeys<O>>;
|
|
149
|
+
/**
|
|
150
|
+
* Per-field permission on the projection: omission, not denial. The caller
|
|
151
|
+
* still gets the row, without the fields they may not see.
|
|
152
|
+
*/
|
|
153
|
+
readonly gates?: {
|
|
154
|
+
readonly [F in OutputKeys<O>]?: PermKey;
|
|
155
|
+
};
|
|
156
|
+
} & OpAuthority<O, PermKey>;
|
|
157
|
+
/**
|
|
158
|
+
* Declare a module's operations against its entities and permission keys.
|
|
159
|
+
*
|
|
160
|
+
* Curried so the entities and permissions are given explicitly while each
|
|
161
|
+
* operation still infers its own input and output — a callback parameter cannot
|
|
162
|
+
* be contextually typed by a generic being inferred from the object containing
|
|
163
|
+
* it.
|
|
164
|
+
*
|
|
165
|
+
* ```ts
|
|
166
|
+
* export const ops = defineOperations(calloutEntities, PERMISSIONS)({
|
|
167
|
+
* 'customer/create': {
|
|
168
|
+
* summary: 'Register a customer',
|
|
169
|
+
* permission: 'customer:manage',
|
|
170
|
+
* input: z.object({ name: z.string() }),
|
|
171
|
+
* output: z.object({ id: z.string(), number: z.string() }),
|
|
172
|
+
* http: { method: 'POST', path: '/customers' },
|
|
173
|
+
* emits: {
|
|
174
|
+
* entity: 'customer', entityIdFrom: 'id',
|
|
175
|
+
* type: 'callout.customer-created', schemaVersion: 1, piiClass: 'none',
|
|
176
|
+
* },
|
|
177
|
+
* },
|
|
178
|
+
* });
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export declare function defineOperations<const Entities extends Record<string, EntityDef>, const Perms extends readonly string[]>(_entities: Entities, _permissions: Perms): <const Ops extends { readonly [K in keyof Ops]: OperationShape<Ops[K], Entities, Perms[number]>; }>(operations: Ops) => Ops;
|
|
182
|
+
/**
|
|
183
|
+
* The permission keys an operation set actually requires, for the manifest.
|
|
184
|
+
*
|
|
185
|
+
* Read structurally rather than through a `{ permission?: string }` parameter:
|
|
186
|
+
* a `narrows` operation has neither `permission` nor `emits`, and TypeScript's
|
|
187
|
+
* weak-type rule rejects an object sharing no properties with the parameter.
|
|
188
|
+
*/
|
|
189
|
+
export declare function permissionsUsedBy(operations: Readonly<Record<string, object>>): string[];
|
|
190
|
+
/** The event types an operation set emits, for `manifest.events.emits`. */
|
|
191
|
+
export declare function eventsEmittedBy(operations: Readonly<Record<string, object>>): {
|
|
192
|
+
type: string;
|
|
193
|
+
schemaVersion: number;
|
|
194
|
+
}[];
|
|
195
|
+
/**
|
|
196
|
+
* The handler map a declared operation set requires — CRM-EFF's `satisfies Impl`
|
|
197
|
+
* seam, which is what makes the declaration BINDING rather than decorative.
|
|
198
|
+
*
|
|
199
|
+
* ```ts
|
|
200
|
+
* export const operations = { … } satisfies OperationImpl<typeof calloutOps, OperationContext>;
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* Four things become compile errors at the exact method: a handler whose input
|
|
204
|
+
* disagrees with the declared `input`, one whose return disagrees with the
|
|
205
|
+
* declared `output`, an operation declared and not implemented, and one
|
|
206
|
+
* implemented and not declared.
|
|
207
|
+
*
|
|
208
|
+
* `Ctx` is a parameter rather than `OperationContext` because contracts is below
|
|
209
|
+
* the kernel and must not import it. The vertical supplies it.
|
|
210
|
+
*/
|
|
211
|
+
export type OperationImpl<Ops, Ctx> = {
|
|
212
|
+
[K in keyof Ops]: Ops[K] extends {
|
|
213
|
+
output: infer O;
|
|
214
|
+
} ? O extends z.ZodType ? (ctx: Ctx, input: ImplInput<Ops[K]>) => z.infer<O> | Promise<z.infer<O>> : never : never;
|
|
215
|
+
};
|
|
216
|
+
/** No declared `input` means the handler takes `undefined`. */
|
|
217
|
+
type ImplInput<O> = O extends {
|
|
218
|
+
input: infer I;
|
|
219
|
+
} ? I extends z.ZodType ? O extends {
|
|
220
|
+
inputOptional: true;
|
|
221
|
+
} ? z.infer<I> | undefined : z.infer<I> : undefined : undefined;
|
|
222
|
+
export {};
|
|
223
|
+
//# sourceMappingURL=operations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The operation surface of the model (#707).
|
|
3
|
+
*
|
|
4
|
+
* #697 declared the entities. This declares what can be *done* to them, and
|
|
5
|
+
* checks the joins that today are unchecked strings: which permission an
|
|
6
|
+
* operation requires, which output field an event takes its subject from,
|
|
7
|
+
* whether a payload carries something an erasure must be able to reach.
|
|
8
|
+
*
|
|
9
|
+
* ## A composer, not a second `defineModel`
|
|
10
|
+
*
|
|
11
|
+
* `defineOperations` sits beside `defineEntities` rather than swallowing it.
|
|
12
|
+
* Each half stays independently adoptable — which is what let the entity half
|
|
13
|
+
* ship and be taken up by two verticals before this existed. A vertical adopts
|
|
14
|
+
* operations when it is ready, not as the price of adopting entities.
|
|
15
|
+
*
|
|
16
|
+
* ## `input`, and the transcription that is not here
|
|
17
|
+
*
|
|
18
|
+
* `input` is the Zod object the handler already parses — the same object, not a
|
|
19
|
+
* description of it. That is the whole reason the model is TypeScript (#680): a
|
|
20
|
+
* schema language would need the shape written twice, and transcription is what
|
|
21
|
+
* produced 40 wrong argument names in the one app where this was measured.
|
|
22
|
+
*/
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// The composer.
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* Declare a module's operations against its entities and permission keys.
|
|
29
|
+
*
|
|
30
|
+
* Curried so the entities and permissions are given explicitly while each
|
|
31
|
+
* operation still infers its own input and output — a callback parameter cannot
|
|
32
|
+
* be contextually typed by a generic being inferred from the object containing
|
|
33
|
+
* it.
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* export const ops = defineOperations(calloutEntities, PERMISSIONS)({
|
|
37
|
+
* 'customer/create': {
|
|
38
|
+
* summary: 'Register a customer',
|
|
39
|
+
* permission: 'customer:manage',
|
|
40
|
+
* input: z.object({ name: z.string() }),
|
|
41
|
+
* output: z.object({ id: z.string(), number: z.string() }),
|
|
42
|
+
* http: { method: 'POST', path: '/customers' },
|
|
43
|
+
* emits: {
|
|
44
|
+
* entity: 'customer', entityIdFrom: 'id',
|
|
45
|
+
* type: 'callout.customer-created', schemaVersion: 1, piiClass: 'none',
|
|
46
|
+
* },
|
|
47
|
+
* },
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function defineOperations(_entities, _permissions) {
|
|
52
|
+
return (operations) => operations;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The permission keys an operation set actually requires, for the manifest.
|
|
56
|
+
*
|
|
57
|
+
* Read structurally rather than through a `{ permission?: string }` parameter:
|
|
58
|
+
* a `narrows` operation has neither `permission` nor `emits`, and TypeScript's
|
|
59
|
+
* weak-type rule rejects an object sharing no properties with the parameter.
|
|
60
|
+
*/
|
|
61
|
+
export function permissionsUsedBy(operations) {
|
|
62
|
+
const keys = Object.values(operations).flatMap((op) => {
|
|
63
|
+
const permission = op.permission;
|
|
64
|
+
return typeof permission === 'string' ? [permission] : [];
|
|
65
|
+
});
|
|
66
|
+
return [...new Set(keys)].sort();
|
|
67
|
+
}
|
|
68
|
+
/** The event types an operation set emits, for `manifest.events.emits`. */
|
|
69
|
+
export function eventsEmittedBy(operations) {
|
|
70
|
+
const seen = new Map();
|
|
71
|
+
for (const op of Object.values(operations)) {
|
|
72
|
+
const emits = op.emits;
|
|
73
|
+
if (typeof emits?.type === 'string' && typeof emits.schemaVersion === 'number') {
|
|
74
|
+
seen.set(emits.type, emits.schemaVersion);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return [...seen.entries()]
|
|
78
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
79
|
+
.map(([type, schemaVersion]) => ({ type, schemaVersion }));
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiIxB,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,gBAAgB,CAG9B,SAAmB,EAAE,YAAmB;IACxC,OAAO,CAKL,UAAe,EACV,EAAE,CAAC,UAAU,CAAC;AACvB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAA4C;IAC5E,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;QACpD,MAAM,UAAU,GAAI,EAA+B,CAAC,UAAU,CAAC;QAC/D,OAAO,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACnC,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAC7B,UAA4C;IAE5C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAI,EAA8D,CAAC,KAAK,CAAC;QACpF,IAAI,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,aAAa,KAAK,QAAQ,EAAE,CAAC;YAC/E,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;SACvB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;AAC/D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@substrat-run/contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.69.0",
|
|
4
4
|
"description": "Substrat kernel contract schemas — Zod is the source of truth (master plan D-22); OAS/JSON Schema are emitted artifacts",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|