@githolon/dsl 0.100.6 → 0.101.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/dart/CHANGELOG.md +6 -0
- package/dart/lib/src/dev_cloud.dart +46 -15
- package/dart/lib/src/test_harness.dart +78 -16
- package/dart/lib/src/webview_bridge.dart +640 -126
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/bundle_version_pairing_test.dart +11 -22
- package/dart/test/co2_product_gauntlet_test.dart +228 -0
- package/dart/test/errors_test.dart +63 -12
- package/dart/test/first_app_gauntlet_test.dart +29 -3
- package/dart/test/webview_bridge_test.dart +274 -0
- package/dart_types/CHANGELOG.md +6 -0
- package/dart_types/lib/src/errors.dart +136 -30
- package/dart_types/lib/src/surfaced.dart +48 -2
- package/dart_types/lib/src/wire.dart +3 -1
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/surfaced_test.dart +19 -0
- package/package.json +1 -1
- package/src/activity.ts +43 -4
- package/src/aggregate.ts +132 -22
- package/src/attested_core.ts +1 -1
- package/src/authoring.ts +119 -6
- package/src/birth_meter.ts +4 -4
- package/src/birth_meter_ensure.ts +9 -1
- package/src/births.ts +53 -22
- package/src/build_package.ts +55 -14
- package/src/business_birth.ts +48 -7
- package/src/codegen_dart.ts +267 -37
- package/src/codegen_dart_client.ts +921 -82
- package/src/codegen_openapi.ts +28 -5
- package/src/codegen_proof.ts +107 -11
- package/src/codegen_ts.ts +961 -135
- package/src/compile_package_main.ts +155 -45
- package/src/conflicts.ts +252 -0
- package/src/describe_model.ts +448 -0
- package/src/directive.ts +5 -38
- package/src/drivers.ts +12 -4
- package/src/engine_entry.ts +4 -0
- package/src/evolve_preview.ts +72 -11
- package/src/fields.ts +201 -55
- package/src/framework/application_home.ts +103 -0
- package/src/framework/application_home_domain.ts +2 -0
- package/src/framework/attested_receipts.ts +2 -2
- package/src/framework/auth_provider_law.ts +51 -24
- package/src/framework/business_corrections.ts +170 -0
- package/src/framework/disclosure.ts +1 -1
- package/src/framework/domain_lifecycle.ts +250 -16
- package/src/framework/encryption_identity.ts +1 -1
- package/src/framework/encryption_scope.ts +21 -13
- package/src/framework/era_certification.ts +1 -1
- package/src/framework/framework_aggregates.ts +2 -0
- package/src/framework/genesis.ts +1 -1
- package/src/framework/home.ts +22 -9
- package/src/framework/identity.ts +5 -5
- package/src/framework/impure_capability.ts +2 -2
- package/src/framework/owned_custody.ts +561 -0
- package/src/framework/platform_admin.ts +1 -1
- package/src/framework/relations.ts +9 -1
- package/src/framework/repair.ts +1 -1
- package/src/framework/signing_identity.ts +2 -2
- package/src/framework/source_roster.ts +1 -1
- package/src/framework/workspaces.ts +17 -10
- package/src/generated_client_names.ts +39 -0
- package/src/index.ts +20 -15
- package/src/kernel_capabilities.ts +25 -1
- package/src/law_check.ts +4 -1
- package/src/manifest.ts +356 -63
- package/src/read_projection_manifest.ts +8 -0
- package/src/read_projection_namespace.ts +4 -0
- package/src/relation.ts +1 -1
- package/src/shape_compat.ts +47 -2
- package/src/stable_ids.ts +38 -6
- package/src/stable_ids_types.ts +30 -0
- package/src/surfaced_lint.ts +0 -0
- package/src/usd.ts +62 -11
- package/src/usd_layers.ts +54 -3
- package/src/value_object.ts +23 -19
- package/src/wire.ts +1 -0
- package/src/wire_encode.ts +48 -4
- package/src/workspace_routing.ts +57 -0
- package/src/workspace_sharding.ts +9 -9
- package/src/certified_read.ts +0 -313
package/src/fields.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* the value type (via Zod) is preserved.
|
|
16
16
|
*/
|
|
17
17
|
import { z } from "zod";
|
|
18
|
-
import { Conflict, Counter,
|
|
18
|
+
import { Conflict, Counter, FirstWriterWins, LastPosition, type Driver } from "./drivers.js";
|
|
19
19
|
import type { AggregateHandle } from "./aggregate.js";
|
|
20
20
|
import type { ContractHandle } from "./contract.js";
|
|
21
21
|
import type { FieldFacet } from "./principal_evidence.js";
|
|
@@ -47,8 +47,9 @@ export type JsonValue =
|
|
|
47
47
|
* `nullReason` records why (rides docs/openapi).
|
|
48
48
|
* The EXPLICIT-CLEAR semantic is ORTHOGONAL, carried by the separate `isClearable` flag (`t.clearable(X)`
|
|
49
49
|
* + `.clear(field)` / the generated `clearX(...)` lane, lowered to a Set of json `null`) — a field can be
|
|
50
|
-
* BOTH clearable and nullable.
|
|
51
|
-
*
|
|
50
|
+
* BOTH clearable and nullable. Neither enters the driver-only kernel wire `Schema`, but the compiler
|
|
51
|
+
* includes value-shaping semantics in the stable field contract. Changing them therefore changes the
|
|
52
|
+
* field/law identity used to judge whether observations can be merged. Codegen reads
|
|
52
53
|
* `nullSemantic`/`nullReason`/`isClearable` to shape the tri-state client surface; the runtime enforcement
|
|
53
54
|
* is the payload zod (no-null refine) + the encode-path null guard (`wire_encode.ts`).
|
|
54
55
|
*/
|
|
@@ -212,11 +213,11 @@ export interface ValueObjectDescriptor {
|
|
|
212
213
|
|
|
213
214
|
/**
|
|
214
215
|
* The value-object metadata a VO-typed `Field` carries (`ValueObject.field()` sets it).
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* USD key (built by `composeDomainModule`/`usd.ts` FROM this metadata)
|
|
219
|
-
* the structure visible to tooling.
|
|
216
|
+
* It never enters the driver-only kernel wire `Schema`, but its canonical descriptor is
|
|
217
|
+
* part of the compiler-derived stable field value contract. A VO shape change therefore
|
|
218
|
+
* changes the field/law identity used by schema-aware conflicts. The additive
|
|
219
|
+
* `nomosValueObjects` USD key (built by `composeDomainModule`/`usd.ts` FROM this metadata)
|
|
220
|
+
* also makes the structure visible to tooling.
|
|
220
221
|
*/
|
|
221
222
|
export interface ValueObjectFieldMeta {
|
|
222
223
|
/** The VO's declared name (the generated TS interface / Dart class name). */
|
|
@@ -232,6 +233,15 @@ export interface ValueObjectFieldMeta {
|
|
|
232
233
|
readonly example?: unknown;
|
|
233
234
|
}
|
|
234
235
|
|
|
236
|
+
/** One business-value evolution declared on a field. This is author-time metadata only:
|
|
237
|
+
* the compiler replaces the source field with stable contract identities and a deterministic
|
|
238
|
+
* identity/case table in the law manifest. No function source reaches the runtime. */
|
|
239
|
+
export interface FieldEvolutionDeclaration {
|
|
240
|
+
readonly source: Field;
|
|
241
|
+
readonly mode: "identity" | "cases";
|
|
242
|
+
readonly cases?: readonly { readonly from: unknown; readonly to: unknown }[];
|
|
243
|
+
}
|
|
244
|
+
|
|
235
245
|
export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
236
246
|
/** The kind tag. `K` is the LITERAL kind so the query DSL can constrain `.ref`
|
|
237
247
|
* to ONLY `kind:'ref'` fields at compile time (`t.ref` → `Field<string,'ref'>`).
|
|
@@ -249,8 +259,9 @@ export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
|
249
259
|
* NULL SEMANTICS (`architecture/null_semantics.md`): what `null` MEANS on this field. Set by the
|
|
250
260
|
* `t.json(...)` null option and the `t.optional`/`t.clearable`/`t.nullable` wrappers. `undefined`
|
|
251
261
|
* ⇒ the field carries no null declaration (a scalar/collection whose null-ness never arises); a
|
|
252
|
-
* `json` field with no wrapper defaults to `"forbid"` at construction.
|
|
253
|
-
*
|
|
262
|
+
* `json` field with no wrapper defaults to `"forbid"` at construction. It does not change the
|
|
263
|
+
* driver-only kernel wire `Schema`, but it is part of the stable field value contract. See
|
|
264
|
+
* {@link NullSemantic}.
|
|
254
265
|
*/
|
|
255
266
|
readonly nullSemantic?: NullSemantic;
|
|
256
267
|
/** The REQUIRED reason string for `nullable`/`explicit-empty` (law documentation — rides docs/openapi).
|
|
@@ -260,7 +271,7 @@ export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
|
260
271
|
* explicit-clear write (`clear(handle, "field")` / `.clear("field")` / the generated `clearX(...)`
|
|
261
272
|
* lane), lowered to a Set of json `null`. ORTHOGONAL to `nullSemantic` (a field may be both). A
|
|
262
273
|
* cleared field reads back as json null, so codegen surfaces it tri-state (see {@link admitsNull}).
|
|
263
|
-
*
|
|
274
|
+
* It leaves the kernel driver unchanged but participates in the stable field value contract. */
|
|
264
275
|
readonly isClearable?: true;
|
|
265
276
|
/** The AUTHZ relation the GENERATED clear directive requires (`t.clearable(field, { requires })`). When
|
|
266
277
|
* set, the compose-seam-injected `clear<Agg><Field>` directive carries `.requires(relation)`; when
|
|
@@ -296,7 +307,7 @@ export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
|
296
307
|
* For a `t.map(inner)` field: the inner VALUE field's kind (e.g. `"string"` for
|
|
297
308
|
* `t.map(t.string())`, `"json"` for `t.map(t.json())`). The wire DRIVER is
|
|
298
309
|
* identical for both (`MapOf(Lww)` — a json leaf encodes to a `Str` value just like
|
|
299
|
-
* a string),
|
|
310
|
+
* a string), but the inner value kind is part of the stable field value contract. It also tells codegen how to SURFACE each
|
|
300
311
|
* entry value: a `string`-valued map reads `Map<String,String>` (flat strings),
|
|
301
312
|
* whereas a `json`-valued map reads `Map<String,Object?>` (each entry JSON-decoded
|
|
302
313
|
* back to its object) so a value-object map (e.g. a site's `customFields`,
|
|
@@ -315,26 +326,26 @@ export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
|
315
326
|
* a `scaleX` double or an `estimatedCost` authored as a JSON leaf). `"object"` is the
|
|
316
327
|
* EXPLICIT assertion "this json leaf is always a JSON OBJECT" (set by `t.jsonObject()`);
|
|
317
328
|
* `undefined` (a plain `t.json()`) means the shape is unknown/non-object → the
|
|
318
|
-
* projection defaults it to the permissive `Object?`. This flag
|
|
319
|
-
*
|
|
320
|
-
* `encodeKernelSchema`) — so it changes NO hash (identical to `mapValueKind`/`isOptional`).
|
|
329
|
+
* projection defaults it to the permissive `Object?`. This flag does not change the
|
|
330
|
+
* driver-only kernel wire `Schema`, but it is part of the stable field value contract.
|
|
321
331
|
*/
|
|
322
332
|
readonly jsonShape?: "object";
|
|
323
333
|
/**
|
|
324
334
|
* For a `json`-kind field lowered from a NAMED VALUE OBJECT (`ValueObject.field()`):
|
|
325
335
|
* the VO metadata (name + canonical descriptor + nested-VO dependencies). Drives the
|
|
326
336
|
* generated TS interface / Dart class and the additive `nomosValueObjects` USD key.
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
337
|
+
* It never changes the driver-only kernel wire `Schema`; its canonical descriptor does
|
|
338
|
+
* participate in the stable field value contract (see {@link ValueObjectFieldMeta}).
|
|
339
|
+
* `undefined` for every non-VO field.
|
|
330
340
|
*/
|
|
331
341
|
readonly valueObject?: ValueObjectFieldMeta;
|
|
332
342
|
/**
|
|
333
343
|
* For a `t.map(inner)` whose INNER value is a NAMED VALUE OBJECT (`t.map(VO.field())`):
|
|
334
344
|
* the inner VO's metadata, so codegen surfaces the map as `Map<String, VoClass>` /
|
|
335
345
|
* `Record<string, VoClass>` (each entry decoded via `VoClass.fromProjected`) instead of
|
|
336
|
-
* the raw `Map<String, Object?>` / `Record<string, unknown>`.
|
|
337
|
-
*
|
|
346
|
+
* the raw `Map<String, Object?>` / `Record<string, unknown>`. The wire driver remains
|
|
347
|
+
* `MapOf(Lww)` exactly as for a plain json-valued map, while the VO descriptor participates
|
|
348
|
+
* in the stable field value contract.
|
|
338
349
|
* `undefined` for a non-map field or a map of scalars.
|
|
339
350
|
*/
|
|
340
351
|
readonly mapValueVo?: ValueObjectFieldMeta;
|
|
@@ -345,8 +356,8 @@ export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
|
345
356
|
* `.optional()` so the generated Dart projection type makes it NULLABLE and decodes
|
|
346
357
|
* a minimal freshly-folded aggregate (DSL codegen GAP 1). COLLECTION fields
|
|
347
358
|
* (`set`/`map`) are ALWAYS empty-defaultable and never need this. This flag affects
|
|
348
|
-
*
|
|
349
|
-
*
|
|
359
|
+
* the generated frontend type's required-vs-optional and the stable field value contract;
|
|
360
|
+
* it does not change the driver-only kernel wire `Schema`.
|
|
350
361
|
*/
|
|
351
362
|
readonly isOptional: boolean;
|
|
352
363
|
/**
|
|
@@ -390,8 +401,11 @@ export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
|
390
401
|
* unchanged — the facet governs WRITES, not what's stored.
|
|
391
402
|
*/
|
|
392
403
|
readonly facet?: FieldFacet;
|
|
393
|
-
/**
|
|
394
|
-
|
|
404
|
+
/** Business-declared source contracts this field can understand during a gradual rollout.
|
|
405
|
+
* Compiler-owned metadata; generated clients and tenant runtime code never see contract ids. */
|
|
406
|
+
readonly evolutions?: readonly FieldEvolutionDeclaration[];
|
|
407
|
+
/** Returns a copy with the chosen driver while preserving fluent law markers already applied. */
|
|
408
|
+
merge(driver: Driver): this;
|
|
395
409
|
/** Tag this field's facet, threaded through merge/optional (the canonical form is the
|
|
396
410
|
* `authorityField(...)`/`attributionField(...)`/… wrappers, which call this). */
|
|
397
411
|
withFacet<Fa extends FieldFacet>(facet: Fa): Field<T, K> & { readonly facet: Fa };
|
|
@@ -411,7 +425,7 @@ export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
|
411
425
|
optional(): Field<T | undefined, K>;
|
|
412
426
|
/** Declare BIRTH-PRESENCE LAW: aggregate() synthesizes a gate invariant requiring this field
|
|
413
427
|
* present on every event touching the type; read models type it NON-NULLABLE. */
|
|
414
|
-
fromBirth():
|
|
428
|
+
fromBirth(): this & { readonly isFromBirth: true };
|
|
415
429
|
/**
|
|
416
430
|
* INTERNAL (the `t.optional`/`t.clearable`/`t.nullable` wrappers call this): tag this field's null
|
|
417
431
|
* semantic (+ optional reason). Threaded through merge/optional like every other flag. Not for
|
|
@@ -421,6 +435,11 @@ export interface Field<T = unknown, K extends FieldKind = FieldKind> {
|
|
|
421
435
|
/** INTERNAL (`t.clearable` calls this): mark this field EXPLICIT-CLEARABLE (sets {@link Field.isClearable}
|
|
422
436
|
* + optional {@link Field.clearRequires}). Threaded through merge/optional like every other flag. */
|
|
423
437
|
clearable(requires?: string): Field<T, K> & { readonly isClearable: true };
|
|
438
|
+
/** Declare how values authored by an earlier field contract become this field's current
|
|
439
|
+
* business value. With no function, the compiler proves identity widening. With a function,
|
|
440
|
+
* the source must be finite (enum/boolean/literal union), so Nomos can exhaustively compile
|
|
441
|
+
* and validate the complete case table rather than executing tenant code during replay. */
|
|
442
|
+
evolvesFrom<From>(source: Field<From>, project?: (value: From) => T): Field<T, K>;
|
|
424
443
|
/** Phantom to keep T load-bearing for inference. */
|
|
425
444
|
readonly _t?: T;
|
|
426
445
|
}
|
|
@@ -454,6 +473,7 @@ function makeField<T, K extends FieldKind = FieldKind>(
|
|
|
454
473
|
nullReason?: string;
|
|
455
474
|
isClearable?: true;
|
|
456
475
|
clearRequires?: string;
|
|
476
|
+
evolutions?: readonly FieldEvolutionDeclaration[];
|
|
457
477
|
} = {},
|
|
458
478
|
): Field<T, K> {
|
|
459
479
|
const isOptional = extra.isOptional ?? false;
|
|
@@ -479,8 +499,11 @@ function makeField<T, K extends FieldKind = FieldKind>(
|
|
|
479
499
|
...(extra.nullReason !== undefined ? { nullReason: extra.nullReason } : {}),
|
|
480
500
|
...(extra.isClearable ? { isClearable: true as const } : {}),
|
|
481
501
|
...(extra.clearRequires !== undefined ? { clearRequires: extra.clearRequires } : {}),
|
|
482
|
-
|
|
483
|
-
|
|
502
|
+
...(extra.evolutions !== undefined && extra.evolutions.length > 0
|
|
503
|
+
? { evolutions: extra.evolutions }
|
|
504
|
+
: {}),
|
|
505
|
+
merge(d: Driver): typeof f {
|
|
506
|
+
return makeField<T, K>(kind, zod, d, extra) as typeof f;
|
|
484
507
|
},
|
|
485
508
|
optional(): Field<T | undefined, K> {
|
|
486
509
|
if (extra.fromBirth) {
|
|
@@ -494,7 +517,7 @@ function makeField<T, K extends FieldKind = FieldKind>(
|
|
|
494
517
|
isOptional: true,
|
|
495
518
|
});
|
|
496
519
|
},
|
|
497
|
-
fromBirth():
|
|
520
|
+
fromBirth(): typeof f & { readonly isFromBirth: true } {
|
|
498
521
|
// BIRTH-PRESENCE LAW: only scalar Lww-class kinds — a set/map/counter/position reads as
|
|
499
522
|
// an empty/zero default (presence is meaningless), a hasMany is virtual. Presence is
|
|
500
523
|
// MONOTONE for the allowed kinds (no op unsets a value leaf), which is exactly what makes
|
|
@@ -506,7 +529,9 @@ function makeField<T, K extends FieldKind = FieldKind>(
|
|
|
506
529
|
if (isOptional) {
|
|
507
530
|
throw new Error("a .optional() field cannot be .fromBirth() — birth-presence and optionality contradict.");
|
|
508
531
|
}
|
|
509
|
-
return makeField<T, K>(kind, zod, driver, { ...extra, fromBirth: true })
|
|
532
|
+
return makeField<T, K>(kind, zod, driver, { ...extra, fromBirth: true }) as typeof f & {
|
|
533
|
+
readonly isFromBirth: true;
|
|
534
|
+
};
|
|
510
535
|
},
|
|
511
536
|
withNull(semantic: NullSemantic, reason?: string): Field<T, K> {
|
|
512
537
|
// Threaded through merge/optional like every other flag (it rides `extra`). Reason is carried
|
|
@@ -524,6 +549,44 @@ function makeField<T, K extends FieldKind = FieldKind>(
|
|
|
524
549
|
...(requires !== undefined ? { clearRequires: requires } : {}),
|
|
525
550
|
}) as Field<T, K> & { readonly isClearable: true };
|
|
526
551
|
},
|
|
552
|
+
evolvesFrom<From>(source: Field<From>, project?: (value: From) => T): Field<T, K> {
|
|
553
|
+
if (source === null || typeof source !== "object" || source.zod === undefined) {
|
|
554
|
+
throw new Error(".evolvesFrom(source, project) needs an earlier Nomos field declaration as its source");
|
|
555
|
+
}
|
|
556
|
+
let declaration: FieldEvolutionDeclaration;
|
|
557
|
+
if (project === undefined) {
|
|
558
|
+
declaration = { source, mode: "identity" };
|
|
559
|
+
} else {
|
|
560
|
+
const values = finiteEvolutionValues(source.zod);
|
|
561
|
+
if (values === undefined) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
`.evolvesFrom(source, project) cannot prove project is total because the source value space is infinite. ` +
|
|
564
|
+
`Use the no-function identity form for a compatible widening, or model the transition as a finite ` +
|
|
565
|
+
`business enum/union so Nomos can compile every case.`,
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
const cases = values.map((from) => {
|
|
569
|
+
const projected = project(from as From);
|
|
570
|
+
const parsed = zod.safeParse(projected);
|
|
571
|
+
if (!parsed.success) {
|
|
572
|
+
throw new Error(
|
|
573
|
+
`.evolvesFrom() maps ${JSON.stringify(from)} to a value the target field refuses: ` +
|
|
574
|
+
parsed.error.issues.map((issue) => issue.message).join("; "),
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return { from, to: parsed.data } as const;
|
|
578
|
+
}).sort((a, b) => {
|
|
579
|
+
const left = JSON.stringify(a.from);
|
|
580
|
+
const right = JSON.stringify(b.from);
|
|
581
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
582
|
+
});
|
|
583
|
+
declaration = { source, mode: "cases", cases };
|
|
584
|
+
}
|
|
585
|
+
return makeField<T, K>(kind, zod, driver, {
|
|
586
|
+
...extra,
|
|
587
|
+
evolutions: [...(extra.evolutions ?? []), declaration],
|
|
588
|
+
});
|
|
589
|
+
},
|
|
527
590
|
encrypted(): Field<T, K> {
|
|
528
591
|
// Carry the flag through merge/optional (extra is threaded), so `.merge(Lww).encrypted()`
|
|
529
592
|
// and `.encrypted().merge(Lww)` agree. The wire schema is UNCHANGED (driver-only) — the
|
|
@@ -565,6 +628,36 @@ function makeField<T, K extends FieldKind = FieldKind>(
|
|
|
565
628
|
return f;
|
|
566
629
|
}
|
|
567
630
|
|
|
631
|
+
/** Enumerate a finite JSON-schema value language. Deliberately conservative: failure to
|
|
632
|
+
* prove finiteness refuses the function form, so a partial transform can never masquerade
|
|
633
|
+
* as rollout coverage. */
|
|
634
|
+
function finiteEvolutionValues(schema: z.ZodTypeAny): unknown[] | undefined {
|
|
635
|
+
let json: unknown;
|
|
636
|
+
try {
|
|
637
|
+
json = z.toJSONSchema(schema);
|
|
638
|
+
} catch {
|
|
639
|
+
return undefined;
|
|
640
|
+
}
|
|
641
|
+
const walk = (node: unknown): unknown[] | undefined => {
|
|
642
|
+
if (node === null || typeof node !== "object") return undefined;
|
|
643
|
+
const o = node as Record<string, unknown>;
|
|
644
|
+
if (Array.isArray(o.enum)) return [...o.enum];
|
|
645
|
+
if (Object.prototype.hasOwnProperty.call(o, "const")) return [o.const];
|
|
646
|
+
if (o.type === "boolean") return [false, true];
|
|
647
|
+
for (const key of ["anyOf", "oneOf"] as const) {
|
|
648
|
+
if (!Array.isArray(o[key])) continue;
|
|
649
|
+
const all = o[key]!.flatMap((arm) => walk(arm) ?? [FINITE_EVOLUTION_UNKNOWN]);
|
|
650
|
+
if (all.includes(FINITE_EVOLUTION_UNKNOWN)) return undefined;
|
|
651
|
+
const byJson = new Map(all.map((value) => [JSON.stringify(value), value]));
|
|
652
|
+
return [...byJson.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)).map(([, value]) => value);
|
|
653
|
+
}
|
|
654
|
+
return undefined;
|
|
655
|
+
};
|
|
656
|
+
return walk(json);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const FINITE_EVOLUTION_UNKNOWN = Symbol("nomos-finite-evolution-unknown");
|
|
660
|
+
|
|
568
661
|
// ── FIELD FACETS (security_model.md — the endorse model) ─────────────────────────────────────
|
|
569
662
|
// Canonical wrappers that tag a field's facet; `set()` then gates which PrincipalEvidence
|
|
570
663
|
// conversions it accepts. The field's read/wire type is unchanged — only WRITES are governed.
|
|
@@ -615,10 +708,10 @@ function assertNullEligible(field: Field<unknown, FieldKind>, wrapper: "clearabl
|
|
|
615
708
|
);
|
|
616
709
|
}
|
|
617
710
|
const dk = (field.driver as { kind?: string }).kind;
|
|
618
|
-
if (dk !== "Lww" && dk !== "LwwCausal" && dk !== "LwwBy") {
|
|
711
|
+
if (dk !== "FirstWriterWins" && dk !== "Lww" && dk !== "LwwCausal" && dk !== "LwwBy") {
|
|
619
712
|
throw new Error(
|
|
620
|
-
`t.${wrapper}(...) needs
|
|
621
|
-
`value WHOLESALE, which a '${dk}' merge cannot express
|
|
713
|
+
`t.${wrapper}(...) needs a wholesale scalar field (FirstWriterWins / Lww / LwwCausal / LwwBy) — ` +
|
|
714
|
+
`a null/clear replaces the value WHOLESALE, which a '${dk}' merge cannot express.`,
|
|
622
715
|
);
|
|
623
716
|
}
|
|
624
717
|
if (field.isFromBirth === true) {
|
|
@@ -628,18 +721,70 @@ function assertNullEligible(field: Field<unknown, FieldKind>, wrapper: "clearabl
|
|
|
628
721
|
}
|
|
629
722
|
}
|
|
630
723
|
|
|
724
|
+
/** Common business validation carried by a string field itself. This is deliberately small: a field
|
|
725
|
+
* declaration is the one source for stored shape, birth input, generated clients and admission. */
|
|
726
|
+
export interface StringRules {
|
|
727
|
+
readonly nonempty?: true;
|
|
728
|
+
readonly length?: number;
|
|
729
|
+
readonly minLength?: number;
|
|
730
|
+
readonly maxLength?: number;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/** Common integer validation carried by the field itself. */
|
|
734
|
+
export interface IntRules {
|
|
735
|
+
readonly nonnegative?: true;
|
|
736
|
+
readonly minimum?: number;
|
|
737
|
+
readonly maximum?: number;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function naturalRule(value: number | undefined, name: string): void {
|
|
741
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
|
|
742
|
+
throw new Error(`${name} must be a non-negative safe integer; got ${JSON.stringify(value)}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
631
746
|
export const t = {
|
|
632
|
-
string(): Field<string, "string"> {
|
|
633
|
-
|
|
747
|
+
string(rules: StringRules = {}): Field<string, "string"> {
|
|
748
|
+
naturalRule(rules.length, "t.string() length");
|
|
749
|
+
naturalRule(rules.minLength, "t.string() minLength");
|
|
750
|
+
naturalRule(rules.maxLength, "t.string() maxLength");
|
|
751
|
+
const minimum = Math.max(rules.nonempty === true ? 1 : 0, rules.minLength ?? 0);
|
|
752
|
+
const maximum = rules.maxLength;
|
|
753
|
+
if (rules.length !== undefined && (rules.length < minimum || (maximum !== undefined && rules.length > maximum))) {
|
|
754
|
+
throw new Error("t.string() length contradicts its minLength/maxLength/nonempty rule");
|
|
755
|
+
}
|
|
756
|
+
if (maximum !== undefined && minimum > maximum) {
|
|
757
|
+
throw new Error("t.string() minLength/nonempty rule exceeds maxLength");
|
|
758
|
+
}
|
|
759
|
+
let schema = z.string();
|
|
760
|
+
if (rules.length !== undefined) schema = schema.length(rules.length);
|
|
761
|
+
else {
|
|
762
|
+
if (minimum > 0) schema = schema.min(minimum);
|
|
763
|
+
if (maximum !== undefined) schema = schema.max(maximum);
|
|
764
|
+
}
|
|
765
|
+
return makeField("string", schema, FirstWriterWins);
|
|
634
766
|
},
|
|
635
|
-
int(): Field<number, "int"> {
|
|
636
|
-
|
|
767
|
+
int(rules: IntRules = {}): Field<number, "int"> {
|
|
768
|
+
for (const [name, value] of [["minimum", rules.minimum], ["maximum", rules.maximum]] as const) {
|
|
769
|
+
if (value !== undefined && !Number.isSafeInteger(value)) {
|
|
770
|
+
throw new Error(`t.int() ${name} must be a safe integer; got ${JSON.stringify(value)}`);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const minimum = Math.max(rules.nonnegative === true ? 0 : Number.NEGATIVE_INFINITY, rules.minimum ?? Number.NEGATIVE_INFINITY);
|
|
774
|
+
const maximum = rules.maximum;
|
|
775
|
+
if (maximum !== undefined && minimum > maximum) {
|
|
776
|
+
throw new Error("t.int() minimum/nonnegative rule exceeds maximum");
|
|
777
|
+
}
|
|
778
|
+
let schema = z.number().int();
|
|
779
|
+
if (Number.isFinite(minimum)) schema = schema.min(minimum);
|
|
780
|
+
if (maximum !== undefined) schema = schema.max(maximum);
|
|
781
|
+
return makeField("int", schema, FirstWriterWins);
|
|
637
782
|
},
|
|
638
783
|
/** A boolean flag (Lww). Stored as the kernel scalar `"true"`/`"false"` and decoded back to a real
|
|
639
784
|
* JSON boolean on read — so a flag is `t.bool()`, not a 2-value `t.enum`. Counts/filters key on the
|
|
640
785
|
* same string form. (Not encryptable — only Lww-class scalars are, and bool already is.) */
|
|
641
786
|
bool(): Field<boolean, "bool"> {
|
|
642
|
-
return makeField("bool", z.boolean(),
|
|
787
|
+
return makeField("bool", z.boolean(), FirstWriterWins);
|
|
643
788
|
},
|
|
644
789
|
/** A PN-COUNTER (`Driver::Counter`) — a number that CONVERGES under concurrent edits: two replicas
|
|
645
790
|
* that each `increment` while offline both land (the merge is the per-replica sum), so nobody's count
|
|
@@ -673,7 +818,7 @@ export const t = {
|
|
|
673
818
|
return makeField<E[number], "enum">(
|
|
674
819
|
"enum",
|
|
675
820
|
z.enum(values) as unknown as z.ZodType<E[number]>,
|
|
676
|
-
|
|
821
|
+
FirstWriterWins,
|
|
677
822
|
);
|
|
678
823
|
},
|
|
679
824
|
/** A set of items. Defaults to AddWins is NOT assumed — caller picks via .merge; default Lww-on-set is wrong, so we default to Conflict-free AddWins-friendly Lww only when overridden. */
|
|
@@ -688,7 +833,7 @@ export const t = {
|
|
|
688
833
|
* `json`-valued map (`t.map(t.json())`) as `Map<String,Object?>` (each entry decoded
|
|
689
834
|
* back to its object — loss-less value-object round-trip) while a `string`-valued map
|
|
690
835
|
* stays `Map<String,String>`. The wire driver is identical (`MapOf(Lww)` — both encode
|
|
691
|
-
* a leaf to `Str`),
|
|
836
|
+
* a leaf to `Str`), while the inner value contract deliberately changes. */
|
|
692
837
|
map<T>(inner: Field<T>): Field<Record<string, T>, "map"> {
|
|
693
838
|
return makeField<Record<string, T>, "map">(
|
|
694
839
|
"map",
|
|
@@ -697,8 +842,8 @@ export const t = {
|
|
|
697
842
|
{
|
|
698
843
|
mapValueKind: inner.kind,
|
|
699
844
|
// A map of a NAMED value object (`t.map(VO.field())`) carries the inner VO's
|
|
700
|
-
// metadata so codegen types each entry as the VO class
|
|
701
|
-
//
|
|
845
|
+
// metadata so codegen types each entry as the VO class. The wire driver is
|
|
846
|
+
// `MapOf(Lww)` either way; the stable field value contract includes the VO shape.
|
|
702
847
|
...(inner.valueObject !== undefined ? { mapValueVo: inner.valueObject } : {}),
|
|
703
848
|
},
|
|
704
849
|
);
|
|
@@ -715,8 +860,8 @@ export const t = {
|
|
|
715
860
|
// guard (`wire_encode.ts`) refuses a plan-injected null in the lump on every peer, and a payload
|
|
716
861
|
// declared with the exported {@link jsonNoNullSchema} refuses a null at `.parse()`. `{ null:
|
|
717
862
|
// "explicit-empty", reason }` admits nulls as a documented domain value (reason REQUIRED — a
|
|
718
|
-
// meaning-free null is the whole footgun); the
|
|
719
|
-
//
|
|
863
|
+
// meaning-free null is the whole footgun); codegen shapes the tri-state client surface from the
|
|
864
|
+
// metadata and the stable field value contract includes it, even though the kernel driver does not.
|
|
720
865
|
const mode = opts?.null ?? "forbid";
|
|
721
866
|
if (mode === "explicit-empty") {
|
|
722
867
|
if (typeof opts?.reason !== "string" || opts.reason.trim() === "") {
|
|
@@ -726,12 +871,12 @@ export const t = {
|
|
|
726
871
|
`Give a reason, or use the default t.json() which forbids null.`,
|
|
727
872
|
);
|
|
728
873
|
}
|
|
729
|
-
return makeField<string, "json">("json", z.string(),
|
|
874
|
+
return makeField<string, "json">("json", z.string(), FirstWriterWins, {
|
|
730
875
|
nullSemantic: "explicit-empty",
|
|
731
876
|
nullReason: opts.reason,
|
|
732
877
|
}) as unknown as Field<JsonValue, "json">;
|
|
733
878
|
}
|
|
734
|
-
return makeField<string, "json">("json", z.string(),
|
|
879
|
+
return makeField<string, "json">("json", z.string(), FirstWriterWins, {
|
|
735
880
|
nullSemantic: "forbid",
|
|
736
881
|
}) as unknown as Field<JsonValue, "json">;
|
|
737
882
|
},
|
|
@@ -780,16 +925,17 @@ export const t = {
|
|
|
780
925
|
};
|
|
781
926
|
},
|
|
782
927
|
/** A JSON-OBJECT value-object leaf: an arbitrary JSON-string leaf (identical wire +
|
|
783
|
-
* driver to `t.json()` — a `Str` at op time
|
|
928
|
+
* driver to `t.json()` — a `Str` at op time) whose decoded value is
|
|
784
929
|
* asserted to ALWAYS be a JSON OBJECT (e.g. `location`, `address`, `blobRef`,
|
|
785
930
|
* `structuralAssignment`). The v1-FREE projection surfaces it as a STRUCTURED
|
|
786
931
|
* `Map<String,Object?>` with a STRICT reader (a present-but-non-object value throws —
|
|
787
932
|
* the strict-decode contract), as opposed to a plain `t.json()` (decoded shape unknown
|
|
788
933
|
* → permissive `Object?`). Marks `jsonShape:"object"`; the kind stays `"json"`. */
|
|
789
934
|
jsonObject(): Field<JsonValue, "json"> {
|
|
790
|
-
// Same JsonValue authoring widening as `t.json()` (structured value in, `Str` wire out
|
|
791
|
-
//
|
|
792
|
-
|
|
935
|
+
// Same JsonValue authoring widening as `t.json()` (structured value in, `Str` wire out).
|
|
936
|
+
// `jsonShape:"object"` also enters the stable field value contract: the generated strict
|
|
937
|
+
// object reader must never be applied to an observation authored as arbitrary JSON.
|
|
938
|
+
return makeField<string, "json">("json", z.string(), FirstWriterWins, { jsonShape: "object" }) as unknown as Field<JsonValue, "json">;
|
|
793
939
|
},
|
|
794
940
|
/**
|
|
795
941
|
* Reference to another aggregate (stored as its id string).
|
|
@@ -814,7 +960,7 @@ export const t = {
|
|
|
814
960
|
domain?: string;
|
|
815
961
|
}): Field<string, "ref"> {
|
|
816
962
|
const isContract = (opts.target as { __isContract?: unknown }).__isContract === true;
|
|
817
|
-
return makeField<string, "ref">("ref", z.string(),
|
|
963
|
+
return makeField<string, "ref">("ref", z.string(), FirstWriterWins, {
|
|
818
964
|
refAggregateId: opts.target.id,
|
|
819
965
|
foreignRef: {
|
|
820
966
|
workspaceField: opts.workspace,
|
|
@@ -865,7 +1011,7 @@ export const t = {
|
|
|
865
1011
|
* value, like a string), overridable via `.merge(...)`.
|
|
866
1012
|
*/
|
|
867
1013
|
evidence(): Field<EvidenceRefValue, "evidence"> {
|
|
868
|
-
return makeField<EvidenceRefValue, "evidence">("evidence", evidenceRefSchema,
|
|
1014
|
+
return makeField<EvidenceRefValue, "evidence">("evidence", evidenceRefSchema, FirstWriterWins);
|
|
869
1015
|
},
|
|
870
1016
|
};
|
|
871
1017
|
|
|
@@ -890,16 +1036,16 @@ function refImpl(
|
|
|
890
1036
|
): Field<string, "ref"> {
|
|
891
1037
|
if (typeof a === "string") {
|
|
892
1038
|
const target = b!;
|
|
893
|
-
return makeField<string, "ref">("ref", z.string(),
|
|
1039
|
+
return makeField<string, "ref">("ref", z.string(), FirstWriterWins, {
|
|
894
1040
|
refWorkspace: a,
|
|
895
1041
|
refAggregateId: target.id,
|
|
896
1042
|
});
|
|
897
1043
|
}
|
|
898
1044
|
// THUNK form (`t.ref(() => Agg)`) — resolve lazily to break a bidirectional declaration cycle.
|
|
899
1045
|
if (typeof a === "function") {
|
|
900
|
-
return makeField<string, "ref">("ref", z.string(),
|
|
1046
|
+
return makeField<string, "ref">("ref", z.string(), FirstWriterWins, { refAggregateIdThunk: () => a().id });
|
|
901
1047
|
}
|
|
902
|
-
return makeField<string, "ref">("ref", z.string(),
|
|
1048
|
+
return makeField<string, "ref">("ref", z.string(), FirstWriterWins, { refAggregateId: a.id });
|
|
903
1049
|
}
|
|
904
1050
|
|
|
905
1051
|
/**
|
|
@@ -916,5 +1062,5 @@ export function valueObjectField<T>(
|
|
|
916
1062
|
zod: z.ZodType<T>,
|
|
917
1063
|
valueObject: ValueObjectFieldMeta,
|
|
918
1064
|
): Field<T, "json"> {
|
|
919
|
-
return makeField<T, "json">("json", zod,
|
|
1065
|
+
return makeField<T, "json">("json", zod, FirstWriterWins, { jsonShape: "object", valueObject });
|
|
920
1066
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// NOMOS — compiler-owned application/home law for one-business-birth packages.
|
|
2
|
+
//
|
|
3
|
+
// A tenant declaring `birth(Estate, ...)` is declaring a complete product entrypoint,
|
|
4
|
+
// not merely a child recipe. The compiler privately composes this module into the same
|
|
5
|
+
// package so NomosScope.home(application:) can create/recover the signed-in person's home,
|
|
6
|
+
// and that home can immediately call the tenant's generated `birth(...)`.
|
|
7
|
+
//
|
|
8
|
+
// Every declaration is presentation-internal: it remains certified, hash-bearing and
|
|
9
|
+
// routable through the runtime OpenAPI, while TS/Dart/OpenAPI business surfaces omit it.
|
|
10
|
+
|
|
11
|
+
import type { AggregateHandle } from "../aggregate.js";
|
|
12
|
+
import type { Directive } from "../directive.js";
|
|
13
|
+
import type { QueryDecl } from "../query.js";
|
|
14
|
+
import { APPLICATION_HOME_DOMAIN } from "./application_home_domain.js";
|
|
15
|
+
import {
|
|
16
|
+
AuthProvider,
|
|
17
|
+
authProvider,
|
|
18
|
+
authProviderSeedBeforeWarrant,
|
|
19
|
+
seedAuthProvider,
|
|
20
|
+
setAuthProvider,
|
|
21
|
+
} from "./auth_provider_law.js";
|
|
22
|
+
import { GenesisWarrant, seedGenesis } from "./genesis.js";
|
|
23
|
+
import {
|
|
24
|
+
BirthAttestation,
|
|
25
|
+
HomeOwner,
|
|
26
|
+
SharedWithMe,
|
|
27
|
+
birthAttestation,
|
|
28
|
+
birthHomeForDomain,
|
|
29
|
+
homeNameBindsOwner,
|
|
30
|
+
homeRelations,
|
|
31
|
+
parentAttestedBirth,
|
|
32
|
+
pruneShare,
|
|
33
|
+
recordShare,
|
|
34
|
+
seedBirthAttestation,
|
|
35
|
+
seedHomeOwner,
|
|
36
|
+
sharedByWorkspace,
|
|
37
|
+
} from "./home.js";
|
|
38
|
+
import { RelationTuple, relationTuplesByObject } from "./relations.js";
|
|
39
|
+
import {
|
|
40
|
+
SignerDelegation,
|
|
41
|
+
SignerEpoch,
|
|
42
|
+
advanceSignerEpoch,
|
|
43
|
+
enrollDelegation,
|
|
44
|
+
enrollFirstDevice,
|
|
45
|
+
enrollSigner,
|
|
46
|
+
enrollSignerSelf,
|
|
47
|
+
enrollSignerSelfRequiresVerified,
|
|
48
|
+
identityRelations,
|
|
49
|
+
signerDelegationByUser,
|
|
50
|
+
signerEpochCurrent,
|
|
51
|
+
signerEpochStrictlyIncreases,
|
|
52
|
+
} from "./signing_identity.js";
|
|
53
|
+
|
|
54
|
+
export { APPLICATION_HOME_DOMAIN };
|
|
55
|
+
|
|
56
|
+
type Internal<T extends object> = T & { readonly businessBirthInternal: true };
|
|
57
|
+
const internal = <T extends object>(value: T): Internal<T> => ({
|
|
58
|
+
...value,
|
|
59
|
+
businessBirthInternal: true,
|
|
60
|
+
});
|
|
61
|
+
const aggregate = (value: AggregateHandle): Internal<AggregateHandle> => internal(value);
|
|
62
|
+
const directive = <P>(value: Directive<P>): Internal<Directive<P>> => internal(value);
|
|
63
|
+
const query = (value: QueryDecl): Internal<QueryDecl> => internal(value);
|
|
64
|
+
|
|
65
|
+
// Parent primitive + the law its born home uses.
|
|
66
|
+
export const __nomos_birthHome = directive(birthHomeForDomain(APPLICATION_HOME_DOMAIN));
|
|
67
|
+
export const __nomos_HomeOwner = aggregate(HomeOwner);
|
|
68
|
+
export const __nomos_seedHomeOwner = directive(seedHomeOwner);
|
|
69
|
+
export const __nomos_homeNameBindsOwner = internal(homeNameBindsOwner);
|
|
70
|
+
export const __nomos_homeRelations = internal(homeRelations);
|
|
71
|
+
|
|
72
|
+
// Subject-side discovery/share custody.
|
|
73
|
+
export const __nomos_SharedWithMe = aggregate(SharedWithMe);
|
|
74
|
+
export const __nomos_recordShare = directive(recordShare);
|
|
75
|
+
export const __nomos_pruneShare = directive(pruneShare);
|
|
76
|
+
export const __nomos_sharedByWorkspace = query(sharedByWorkspace);
|
|
77
|
+
|
|
78
|
+
// Warrant, provenance, signer seating, recovery and verifier state.
|
|
79
|
+
export const __nomos_GenesisWarrant = aggregate(GenesisWarrant);
|
|
80
|
+
export const __nomos_seedGenesis = directive(seedGenesis);
|
|
81
|
+
export const __nomos_BirthAttestation = aggregate(BirthAttestation);
|
|
82
|
+
export const __nomos_seedBirthAttestation = directive(seedBirthAttestation);
|
|
83
|
+
export const __nomos_birthAttestation = query(birthAttestation);
|
|
84
|
+
export const __nomos_parentAttestedBirth = internal(parentAttestedBirth);
|
|
85
|
+
export const __nomos_RelationTuple = aggregate(RelationTuple);
|
|
86
|
+
export const __nomos_relationTuplesByObject = query(relationTuplesByObject);
|
|
87
|
+
export const __nomos_identityRelations = internal(identityRelations);
|
|
88
|
+
export const __nomos_enrollSigner = directive(enrollSigner);
|
|
89
|
+
export const __nomos_enrollFirstDevice = directive(enrollFirstDevice);
|
|
90
|
+
export const __nomos_enrollSignerSelf = directive(enrollSignerSelf);
|
|
91
|
+
export const __nomos_enrollSignerSelfRequiresVerified = internal(enrollSignerSelfRequiresVerified);
|
|
92
|
+
export const __nomos_SignerEpoch = aggregate(SignerEpoch);
|
|
93
|
+
export const __nomos_signerEpochCurrent = query(signerEpochCurrent);
|
|
94
|
+
export const __nomos_advanceSignerEpoch = directive(advanceSignerEpoch);
|
|
95
|
+
export const __nomos_signerEpochStrictlyIncreases = internal(signerEpochStrictlyIncreases);
|
|
96
|
+
export const __nomos_SignerDelegation = aggregate(SignerDelegation);
|
|
97
|
+
export const __nomos_enrollDelegation = directive(enrollDelegation);
|
|
98
|
+
export const __nomos_signerDelegationByUser = query(signerDelegationByUser);
|
|
99
|
+
export const __nomos_AuthProvider = aggregate(AuthProvider);
|
|
100
|
+
export const __nomos_seedAuthProvider = directive(seedAuthProvider);
|
|
101
|
+
export const __nomos_authProviderSeedBeforeWarrant = internal(authProviderSeedBeforeWarrant);
|
|
102
|
+
export const __nomos_setAuthProvider = directive(setAuthProvider);
|
|
103
|
+
export const __nomos_authProvider = query(authProvider);
|
|
@@ -75,7 +75,7 @@ export const NomosAttestationReceipt = aggregate("NomosAttestationReceipt", {
|
|
|
75
75
|
foldRootAtSeal: t.string().merge(Lww),
|
|
76
76
|
/** Which read-attestor key signed every member of this batch (`sha256hex(pubkey)`). */
|
|
77
77
|
signerKeyHash: t.string().merge(Lww),
|
|
78
|
-
});
|
|
78
|
+
}).public();
|
|
79
79
|
|
|
80
80
|
/** Audit read: the source's sealed receipt stream, by batch id. */
|
|
81
81
|
export const attestationReceiptByBatch = query("attestationReceiptByBatch")
|
|
@@ -138,7 +138,7 @@ export const NomosSourceDishonor = aggregate("NomosSourceDishonor", {
|
|
|
138
138
|
foldRoot: t.string().merge(Lww),
|
|
139
139
|
/** When the reporter proved it (ms — the reporter's clock, informational). */
|
|
140
140
|
provenAtMs: t.int().merge(Lww),
|
|
141
|
-
});
|
|
141
|
+
}).public();
|
|
142
142
|
|
|
143
143
|
/** Audit read: the folded dishonor facts, by source genesis. */
|
|
144
144
|
export const sourceDishonorByGenesis = query("sourceDishonorByGenesis")
|