@githolon/dsl 0.48.1 → 0.50.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/check_package.mjs +46 -0
- package/dart/lib/src/test_harness.dart +46 -14
- package/dart/lib/src/webview_bridge.dart +160 -6
- package/dart/test/test_harness_test.dart +42 -0
- package/package.json +3 -2
- package/src/build_package.ts +44 -1
- package/src/check_package_main.ts +83 -0
- package/src/codegen_dart.ts +8 -0
- package/src/codegen_dart_client.ts +103 -8
- package/src/codegen_proof.ts +28 -1
- package/src/codegen_ts.ts +289 -14
- package/src/compile_package_main.ts +75 -3
- package/src/fields.ts +12 -0
- package/src/law_check.ts +375 -0
- package/src/secondary_ensure.ts +86 -0
- package/src/value_object.ts +76 -6
package/src/value_object.ts
CHANGED
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
*/
|
|
53
53
|
import { z } from "zod";
|
|
54
54
|
|
|
55
|
+
import type { Driver } from "./drivers.js";
|
|
55
56
|
import {
|
|
56
57
|
valueObjectField,
|
|
57
58
|
type Field,
|
|
@@ -129,6 +130,17 @@ export interface ValueObject<T = unknown> {
|
|
|
129
130
|
* whole), `.optional()`, `.encrypted()` (under the existing `requires(cap)` rules).
|
|
130
131
|
*/
|
|
131
132
|
field(): Field<T, "json">;
|
|
133
|
+
/**
|
|
134
|
+
* SUGAR for `.field().merge(driver)` — lower this VO DIRECTLY to an aggregate field with
|
|
135
|
+
* the chosen merge driver: `location: SiteLocation.merge(Lww).optional()`. A VO field is
|
|
136
|
+
* ONE Lww-class json leaf replaced WHOLESALE, so only the Lww-class family
|
|
137
|
+
* (`Lww` / `LwwCausal` / `LwwBy`) is accepted — any other driver throws (field-level merge
|
|
138
|
+
* composition of a VO's subfields is a later slice; model those as aggregate fields / a
|
|
139
|
+
* `t.map(...)`). The returned `Field` chains the usual modifiers (`.optional()`,
|
|
140
|
+
* `.encrypted()`, …), so `SiteLocation.merge(Lww).optional()` == `SiteLocation.field()
|
|
141
|
+
* .merge(Lww).optional()` byte-for-byte.
|
|
142
|
+
*/
|
|
143
|
+
merge(driver: Driver): Field<T, "json">;
|
|
132
144
|
/** Phantom to keep T load-bearing for inference. */
|
|
133
145
|
readonly _t?: T;
|
|
134
146
|
}
|
|
@@ -255,12 +267,48 @@ export function idOfMetaOf(zt: z.ZodTypeAny): string | undefined {
|
|
|
255
267
|
return undefined;
|
|
256
268
|
}
|
|
257
269
|
|
|
270
|
+
/** Thread a nested VO's own descriptor + transitive dependencies into `deps` (divergent
|
|
271
|
+
* same-name shapes throw — one name, one shape), then return its ref field descriptor. This
|
|
272
|
+
* is what makes `z.array(SomeVo.zod)` / a raw `SomeVo.zod` entry plug-and-play: the VO brand
|
|
273
|
+
* rides the `.zod`, so the descriptor TREE is threaded rather than lost (vo_first_class.md
|
|
274
|
+
* slice 0). */
|
|
275
|
+
function takeVoField(
|
|
276
|
+
meta: ValueObjectFieldMeta,
|
|
277
|
+
deps: Map<string, ValueObjectDescriptor>,
|
|
278
|
+
): ValueObjectFieldDescriptor {
|
|
279
|
+
for (const d of [meta.descriptor, ...meta.dependencies]) {
|
|
280
|
+
const existing = deps.get(d.name);
|
|
281
|
+
if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(d)) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`valueObject: two DIVERGENT value objects share the name '${d.name}' — one name, one shape. ` +
|
|
284
|
+
`Rename one of them (the descriptor is identity-bearing).`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
deps.set(d.name, d);
|
|
288
|
+
}
|
|
289
|
+
return meta.geometry !== undefined
|
|
290
|
+
? { type: "geometry", ref: meta.name, geometry: meta.geometry }
|
|
291
|
+
: { type: "object", ref: meta.name };
|
|
292
|
+
}
|
|
293
|
+
|
|
258
294
|
/**
|
|
259
295
|
* Describe one CONSTRAINED zod schema as a descriptor field (NO FALLBACK — an
|
|
260
296
|
* out-of-set shape throws with the field's path so the author fixes the declaration,
|
|
261
|
-
* never ships an undescribed structure).
|
|
297
|
+
* never ships an undescribed structure). `deps` collects every nested VO the schema
|
|
298
|
+
* references BY BRAND (`z.array(SomeVo.zod)`, a raw `SomeVo.zod`) so its descriptor rides
|
|
299
|
+
* alongside — the caller unions it into the VO's `dependencies`.
|
|
262
300
|
*/
|
|
263
|
-
function describeZod(
|
|
301
|
+
function describeZod(
|
|
302
|
+
voName: string,
|
|
303
|
+
path: string,
|
|
304
|
+
schema: z.ZodTypeAny,
|
|
305
|
+
deps: Map<string, ValueObjectDescriptor>,
|
|
306
|
+
): ValueObjectFieldDescriptor {
|
|
307
|
+
// A VO referenced through its RAW zod (its own brand, no optional wrapper) — emit the
|
|
308
|
+
// ref + thread its descriptor tree. (Optional wrappers are handled by the switch below,
|
|
309
|
+
// which recurses to the branded inner schema.)
|
|
310
|
+
const ownBrand = (schema as unknown as Record<symbol, unknown>)[NOMOS_VO_BRAND];
|
|
311
|
+
if (ownBrand !== undefined) return takeVoField(ownBrand as ValueObjectFieldMeta, deps);
|
|
264
312
|
switch (zodKind(schema)) {
|
|
265
313
|
case "string":
|
|
266
314
|
return { type: "string" };
|
|
@@ -285,7 +333,7 @@ function describeZod(voName: string, path: string, schema: z.ZodTypeAny): ValueO
|
|
|
285
333
|
return { type: "literal", value: v };
|
|
286
334
|
}
|
|
287
335
|
case "array": {
|
|
288
|
-
const items = describeZod(voName, `${path}[]`, zodArrayElement(schema));
|
|
336
|
+
const items = describeZod(voName, `${path}[]`, zodArrayElement(schema), deps);
|
|
289
337
|
if (items.optional === true) {
|
|
290
338
|
throw new Error(
|
|
291
339
|
`valueObject '${voName}': field '${path}' is an array of optionals — not expressible in the v1 set.`,
|
|
@@ -295,7 +343,7 @@ function describeZod(voName: string, path: string, schema: z.ZodTypeAny): ValueO
|
|
|
295
343
|
}
|
|
296
344
|
case "optional":
|
|
297
345
|
case "default": {
|
|
298
|
-
const inner = describeZod(voName, path, zodDef(schema).innerType
|
|
346
|
+
const inner = describeZod(voName, path, zodDef(schema).innerType!, deps);
|
|
299
347
|
return { ...inner, optional: true };
|
|
300
348
|
}
|
|
301
349
|
default:
|
|
@@ -452,7 +500,29 @@ function makeValueObject<T>(
|
|
|
452
500
|
field(): Field<T, "json"> {
|
|
453
501
|
return valueObjectField(zodSchema, meta);
|
|
454
502
|
},
|
|
503
|
+
merge(driver: Driver): Field<T, "json"> {
|
|
504
|
+
const k = driver.kind;
|
|
505
|
+
if (k !== "Lww" && k !== "LwwCausal" && k !== "LwwBy") {
|
|
506
|
+
throw new Error(
|
|
507
|
+
`valueObject '${name}': .merge(${k}) is not a valid value-object field merge — a VO is ONE ` +
|
|
508
|
+
`Lww-class json leaf replaced WHOLESALE, so only Lww / LwwCausal / LwwBy apply. Field-level ` +
|
|
509
|
+
`merge composition of a VO's subfields is a later slice; model independently-merging subfields ` +
|
|
510
|
+
`as aggregate fields or a t.map(...).`,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
return valueObjectField(zodSchema, meta).merge(driver);
|
|
514
|
+
},
|
|
455
515
|
};
|
|
516
|
+
// THE HANDLE IS A FIRST-CLASS ZOD SCHEMA (vo_first_class.md slice 1): delegate zod's
|
|
517
|
+
// internals so the raw HANDLE composes DIRECTLY where a zod is expected — `z.array(Vo)`,
|
|
518
|
+
// `z.object({ f: Vo })`, a directive payload — with NO `.zod` peeling. Non-enumerable (never
|
|
519
|
+
// serialized, domainHash-inert) and stamped with the VO brand so describeZod / voMetaOf resolve
|
|
520
|
+
// the handle exactly like its `.zod`.
|
|
521
|
+
const inner = zodSchema as unknown as { _zod?: unknown; _def?: unknown; def?: unknown };
|
|
522
|
+
Object.defineProperty(vo, "_zod", { get: () => inner._zod, enumerable: false });
|
|
523
|
+
Object.defineProperty(vo, "_def", { get: () => inner._def, enumerable: false });
|
|
524
|
+
Object.defineProperty(vo, "def", { get: () => inner.def, enumerable: false });
|
|
525
|
+
Object.defineProperty(vo, NOMOS_VO_BRAND, { value: meta, enumerable: false });
|
|
456
526
|
return vo;
|
|
457
527
|
}
|
|
458
528
|
|
|
@@ -495,7 +565,7 @@ export function valueObject<const Name extends string, S extends Record<string,
|
|
|
495
565
|
: { type: "object", ref: entry.name };
|
|
496
566
|
collectDescriptors(entry, deps);
|
|
497
567
|
} else {
|
|
498
|
-
fields[key] = describeZod(name, key, entry);
|
|
568
|
+
fields[key] = describeZod(name, key, entry, deps);
|
|
499
569
|
zodShape[key] = entry;
|
|
500
570
|
}
|
|
501
571
|
}
|
|
@@ -584,7 +654,7 @@ export function variant<const Name extends string, Arms extends Record<string, U
|
|
|
584
654
|
: { type: "object", ref: entry.name };
|
|
585
655
|
collectDescriptors(entry, deps);
|
|
586
656
|
} else {
|
|
587
|
-
fields[fieldKey] = describeZod(name, `${key}.${fieldKey}`, entry);
|
|
657
|
+
fields[fieldKey] = describeZod(name, `${key}.${fieldKey}`, entry, deps);
|
|
588
658
|
zodShape[fieldKey] = entry;
|
|
589
659
|
}
|
|
590
660
|
}
|