@githolon/dsl 0.100.6 → 0.101.1
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/birth_meter.ts
CHANGED
|
@@ -83,7 +83,7 @@ export const NomosBirthRecord = aggregate("NomosBirthRecord", {
|
|
|
83
83
|
childGenesisHash: t.string().merge(Lww),
|
|
84
84
|
/** Caller-stamped birth time from the payload (`bornAt`, else `requestedAt`) — the timestamp doctrine. */
|
|
85
85
|
bornAt: t.string().merge(Lww),
|
|
86
|
-
});
|
|
86
|
+
}).public();
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
89
|
* NomosChildLifecycle — additive lifecycle for a child witnessed by NomosBirthRecord. It is deliberately
|
|
@@ -98,7 +98,7 @@ export const NomosChildLifecycle = aggregate("NomosChildLifecycle", {
|
|
|
98
98
|
retiredAt: t.int().merge(Lww),
|
|
99
99
|
reclaimedBy: t.string().merge(Lww),
|
|
100
100
|
reclaimedAt: t.int().merge(Lww),
|
|
101
|
-
});
|
|
101
|
+
}).public();
|
|
102
102
|
|
|
103
103
|
/** BirthPolicy — the workspace's birth-allowance singleton (id `birth-policy:workspace`), the
|
|
104
104
|
* `CloudPolicy` shape one level down: the default max live births per subject in THIS workspace. */
|
|
@@ -110,7 +110,7 @@ export const BirthPolicy = aggregate("BirthPolicy", {
|
|
|
110
110
|
setBy: t.string().merge(Lww),
|
|
111
111
|
/** Caller-stamped (ISO or epoch-ms string) — the timestamp doctrine. */
|
|
112
112
|
setAt: t.string().merge(Lww),
|
|
113
|
-
});
|
|
113
|
+
}).public();
|
|
114
114
|
|
|
115
115
|
/** BirthGrant — a per-subject allowance override (id `birth-grant:<principal>`), the `QuotaGrant`
|
|
116
116
|
* shape: the LATEST grant (max grantedAt) for a subject is the allowance in force. */
|
|
@@ -123,7 +123,7 @@ export const BirthGrant = aggregate("BirthGrant", {
|
|
|
123
123
|
grantedBy: t.string().merge(Lww),
|
|
124
124
|
/** epoch-ms — the recency tiebreak (latest grant wins), caller-stamped. */
|
|
125
125
|
grantedAt: t.int().merge(Lww),
|
|
126
|
-
});
|
|
126
|
+
}).public();
|
|
127
127
|
|
|
128
128
|
// ── the meter's declared queries (the #58 captured-read recipes) ────────────────────────────────────
|
|
129
129
|
|
|
@@ -59,7 +59,13 @@ import {
|
|
|
59
59
|
signerDelegationByUser,
|
|
60
60
|
signerObject,
|
|
61
61
|
} from "./framework/signing_identity.js";
|
|
62
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
AuthProvider,
|
|
64
|
+
authProvider,
|
|
65
|
+
authProviderSeedBeforeWarrant,
|
|
66
|
+
seedAuthProvider,
|
|
67
|
+
setAuthProvider,
|
|
68
|
+
} from "./framework/auth_provider_law.js";
|
|
63
69
|
import { refAs, workspaceInvariant } from "./framework/workspace_invariant.js";
|
|
64
70
|
|
|
65
71
|
/** Lightweight copy of the framework's founding-signer guard, kept here to avoid importing the whole
|
|
@@ -191,6 +197,8 @@ export function withBirthMeterExports<M extends Record<string, unknown>>(mod: M)
|
|
|
191
197
|
internalize("__nomos_seedBirthAttestation", seedBirthAttestation);
|
|
192
198
|
internalize("__nomos_birthAttestation", birthAttestation);
|
|
193
199
|
internalize("__nomos_AuthProvider", AuthProvider);
|
|
200
|
+
internalize("__nomos_seedAuthProvider", seedAuthProvider);
|
|
201
|
+
reserve("__nomos_authProviderSeedBeforeWarrant", authProviderSeedBeforeWarrant);
|
|
194
202
|
internalize("__nomos_setAuthProvider", setAuthProvider);
|
|
195
203
|
internalize("__nomos_authProvider", authProvider);
|
|
196
204
|
reserve("__nomos_founderSignerWriteOnce", bornFounderSignerWriteOnce);
|
package/src/births.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
// wasm32-wasip1 artifact {kernel · projection · embedded
|
|
4
4
|
// QuickJS engine}. If a file isn't this / hosting this / authoring for this / proving this — it's gone.
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import { z } from "zod";
|
|
7
7
|
import type { AggregateHandle } from "./aggregate.js";
|
|
8
8
|
import type { AggregateRef } from "./authoring.js";
|
|
9
|
-
import type { Field } from "./fields.js";
|
|
9
|
+
import type { Field, FieldValue } from "./fields.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* THE BIRTH EFFECT — birth is an EXPLICIT, DECLARED kernel primitive (G3, the unix model).
|
|
@@ -75,19 +75,29 @@ export interface BusinessBirthDecl<
|
|
|
75
75
|
readonly payloadSchema: z.ZodObject<z.ZodRawShape>;
|
|
76
76
|
/** A stable business key the caller already knows (an estate reference, account number, etc.). */
|
|
77
77
|
readonly key: (payload: P) => string;
|
|
78
|
-
/**
|
|
78
|
+
/** The declared business fact used by {@link key}; retained for generated clients to pre-address custody. */
|
|
79
|
+
readonly keyField: string;
|
|
80
|
+
/** Framework-derived initial authoring. Tenant code marks aggregate fields `.fromBirth()` instead. */
|
|
79
81
|
readonly plan: (root: AggregateRef<F>, payload: P) => unknown;
|
|
80
82
|
/** Optional business noun used only to make the durable address readable. Defaults to the aggregate id. */
|
|
81
83
|
readonly namespace?: string;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
export
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
export type BirthFactKeys<F extends Record<string, Field>> = {
|
|
87
|
+
[K in keyof F]-?: F[K] extends { readonly isFromBirth: true } ? K : never;
|
|
88
|
+
}[keyof F] & string;
|
|
89
|
+
|
|
90
|
+
export type BirthStringFactKeys<F extends Record<string, Field>> = {
|
|
91
|
+
[K in BirthFactKeys<F>]: FieldValue<F[K]> extends string ? K : never;
|
|
92
|
+
}[BirthFactKeys<F>] & string;
|
|
93
|
+
|
|
94
|
+
export type BusinessBirthPayload<F extends Record<string, Field>> = {
|
|
95
|
+
readonly [K in BirthFactKeys<F>]: FieldValue<F[K]>;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export interface BusinessBirthSpec<F extends Record<string, Field>> {
|
|
99
|
+
/** One declared birth fact that gives this business life its stable address. */
|
|
100
|
+
readonly key: BirthStringFactKeys<F>;
|
|
91
101
|
readonly namespace?: string;
|
|
92
102
|
}
|
|
93
103
|
|
|
@@ -95,30 +105,51 @@ export interface BusinessBirthSpec<
|
|
|
95
105
|
* declares `birth(Aggregate, spec)` and leaves the recipe, identity, and recovery machinery to Nomos. */
|
|
96
106
|
export function birth(recipe: BirthRecipe): void;
|
|
97
107
|
/** Declare that one business aggregate is given its own durable life. */
|
|
98
|
-
export function birth<
|
|
108
|
+
export function birth<F extends Record<string, Field>>(
|
|
99
109
|
aggregate: AggregateHandle<string, F>,
|
|
100
|
-
spec: BusinessBirthSpec<
|
|
101
|
-
): BusinessBirthDecl<
|
|
102
|
-
export function birth<
|
|
110
|
+
spec: BusinessBirthSpec<F>,
|
|
111
|
+
): BusinessBirthDecl<BusinessBirthPayload<F>, F>;
|
|
112
|
+
export function birth<F extends Record<string, Field>>(
|
|
103
113
|
recipeOrAggregate: BirthRecipe | AggregateHandle<string, F>,
|
|
104
|
-
spec?: BusinessBirthSpec<
|
|
105
|
-
): void | BusinessBirthDecl<
|
|
114
|
+
spec?: BusinessBirthSpec<F>,
|
|
115
|
+
): void | BusinessBirthDecl<BusinessBirthPayload<F>, F> {
|
|
106
116
|
if (spec !== undefined) {
|
|
107
117
|
const aggregate = recipeOrAggregate as AggregateHandle<string, F>;
|
|
108
118
|
if (!aggregate || aggregate.__isAggregateHandle !== true) {
|
|
109
119
|
throw new Error("birth(Aggregate, spec): the first argument must be an aggregate handle");
|
|
110
120
|
}
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
const internalFields = new Set(aggregate.internalFields ?? []);
|
|
122
|
+
const facts = Object.entries(aggregate.fields as Record<string, Field>).filter(
|
|
123
|
+
([name, field]) => field.isFromBirth === true && !internalFields.has(name),
|
|
124
|
+
);
|
|
125
|
+
if (facts.length === 0) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`birth(${aggregate.id}): no birth facts are declared — mark every required initial business field with .fromBirth()`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const keyField = (aggregate.fields as Record<string, Field>)[spec.key];
|
|
131
|
+
if (keyField === undefined || keyField.isFromBirth !== true) {
|
|
132
|
+
throw new Error(`birth(${aggregate.id}): key '${String(spec.key)}' must be a declared .fromBirth() field`);
|
|
133
|
+
}
|
|
134
|
+
if (keyField.kind !== "string" && keyField.kind !== "enum" && keyField.kind !== "ref") {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`birth(${aggregate.id}): key '${String(spec.key)}' must be a string-like business fact; got ${keyField.kind}`,
|
|
137
|
+
);
|
|
113
138
|
}
|
|
139
|
+
const payloadSchema = z.object(Object.fromEntries(facts.map(([name, field]) => [name, field.zod])));
|
|
140
|
+
type Payload = BusinessBirthPayload<F>;
|
|
114
141
|
return {
|
|
115
142
|
__isBusinessBirth: true,
|
|
116
143
|
aggregate,
|
|
117
|
-
payloadSchema
|
|
118
|
-
key: spec.key,
|
|
119
|
-
|
|
144
|
+
payloadSchema,
|
|
145
|
+
key: (payload: Payload) => String(payload[spec.key]),
|
|
146
|
+
keyField: spec.key,
|
|
147
|
+
plan: (root: AggregateRef<F>, payload: Payload) => {
|
|
148
|
+
const writable = root as unknown as { set(field: string, value: unknown): unknown };
|
|
149
|
+
for (const [name] of facts) writable.set(name, payload[name as keyof Payload]);
|
|
150
|
+
},
|
|
120
151
|
...(spec.namespace !== undefined ? { namespace: spec.namespace } : {}),
|
|
121
|
-
}
|
|
152
|
+
} as BusinessBirthDecl<Payload, F>;
|
|
122
153
|
}
|
|
123
154
|
footprint.push(recipeOrAggregate as BirthRecipe);
|
|
124
155
|
}
|
package/src/build_package.ts
CHANGED
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
type TrustSourceDecl,
|
|
70
70
|
type TrustPolicyDecl,
|
|
71
71
|
} from "./attested.js";
|
|
72
|
-
import type
|
|
72
|
+
import { isRelationTypeDecl, type RelationTypeDecl } from "./relation_schema.js";
|
|
73
73
|
import { domainHash, emitManifestBytes } from "./manifest.js";
|
|
74
74
|
import { collectValueObjectDescriptors } from "./value_object.js";
|
|
75
75
|
import { emitUsd } from "./usd.js";
|
|
@@ -81,7 +81,14 @@ import { parentAttestedBirthMarker } from "./births.js";
|
|
|
81
81
|
import { discoverUnsanctionedEnsures, sanctioningEnsureDirective } from "./secondary_ensure.js";
|
|
82
82
|
import { assertIdOfLint } from "./idof_lint.js";
|
|
83
83
|
import { assertSurfacedResolvers } from "./surfaced_lint.js";
|
|
84
|
+
import {
|
|
85
|
+
applyConflictPoliciesToAggregates,
|
|
86
|
+
applyConflictPolicyResolvers,
|
|
87
|
+
conflictPoliciesOf,
|
|
88
|
+
} from "./conflicts.js";
|
|
84
89
|
import { expandBusinessBirthExports } from "./business_birth.js";
|
|
90
|
+
import { withOwnedCustodyExports } from "./framework/owned_custody.js";
|
|
91
|
+
import { withBusinessCorrectionExports } from "./framework/business_corrections.js";
|
|
85
92
|
import {
|
|
86
93
|
reclaimChildAsAdminRequiresRetired,
|
|
87
94
|
reclaimChildRequiresRetired,
|
|
@@ -184,9 +191,13 @@ export interface DomainModuleSpec {
|
|
|
184
191
|
* (an empty optional key must be ABSENT or every domain's canonical manifest hash drifts).
|
|
185
192
|
*/
|
|
186
193
|
export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
194
|
+
const domain = spec.domain ?? spec.name;
|
|
195
|
+
const conflictPolicies = conflictPoliciesOf(spec.modules, domain);
|
|
187
196
|
let merged: Mod = {};
|
|
188
197
|
for (const m of spec.modules) merged = { ...merged, ...m };
|
|
189
198
|
merged = expandBusinessBirthExports(merged, spec.domain ?? spec.name);
|
|
199
|
+
merged = withOwnedCustodyExports(merged);
|
|
200
|
+
merged = withBusinessCorrectionExports(merged);
|
|
190
201
|
// `capability()` bundles expand into discoverable entries (the package plane's
|
|
191
202
|
// merge point) — the quartet validation below then passes without re-exports.
|
|
192
203
|
merged = expandCapabilityExports(merged);
|
|
@@ -211,7 +222,7 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
211
222
|
|
|
212
223
|
const byId = new Map<string, AggregateHandle>();
|
|
213
224
|
for (const a of [...aggregatesOf(merged), ...(spec.extraAggregates ?? [])]) byId.set(a.id, a);
|
|
214
|
-
const directives = directivesOf(merged);
|
|
225
|
+
const directives = applyConflictPolicyResolvers(directivesOf(merged), conflictPolicies);
|
|
215
226
|
// RELATIONS AUTO-ENSURE + AUTO-COMPOSE (co2 recovery DX): a domain that USES relation law — declares a
|
|
216
227
|
// relation schema, a `.requires(...)`-gated directive, or relate-family/signer directives ensuring
|
|
217
228
|
// RelationTuple — needs the framework RelationTuple aggregate IN the schema on TWO counts: (1) the deploy
|
|
@@ -236,9 +247,20 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
236
247
|
const declaresPrivateRead = [...byId.values()].some(
|
|
237
248
|
(a) => (a as { visibility?: unknown }).visibility !== undefined,
|
|
238
249
|
);
|
|
250
|
+
const declaresOwnedRead = [...byId.values()].some(
|
|
251
|
+
(a) => (a as { audience?: { kind?: unknown } }).audience?.kind === "owned",
|
|
252
|
+
);
|
|
239
253
|
if (declaresPrivateRead) {
|
|
240
|
-
if (!byId.has(AuthProvider.id))
|
|
241
|
-
|
|
254
|
+
if (!byId.has(AuthProvider.id)) {
|
|
255
|
+
byId.set(AuthProvider.id, declaresOwnedRead
|
|
256
|
+
? { ...AuthProvider, businessBirthInternal: true } as AggregateHandle
|
|
257
|
+
: AuthProvider);
|
|
258
|
+
}
|
|
259
|
+
if (!directives.some((d) => d.id === setAuthProvider.id)) {
|
|
260
|
+
directives.push(declaresOwnedRead
|
|
261
|
+
? { ...setAuthProvider, businessBirthInternal: true } as Directive<any>
|
|
262
|
+
: setAuthProvider);
|
|
263
|
+
}
|
|
242
264
|
}
|
|
243
265
|
const usesRelations =
|
|
244
266
|
declaresPrivateRead ||
|
|
@@ -246,7 +268,9 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
246
268
|
directives.some((d) => (d as { requiresRelationGate?: unknown }).requiresRelationGate !== undefined) ||
|
|
247
269
|
directives.some((d) => d.aggregateId === RelationTuple.id);
|
|
248
270
|
if (usesRelations && !byId.has(RelationTuple.id)) {
|
|
249
|
-
byId.set(RelationTuple.id,
|
|
271
|
+
byId.set(RelationTuple.id, declaresOwnedRead
|
|
272
|
+
? { ...RelationTuple, businessBirthInternal: true } as AggregateHandle
|
|
273
|
+
: RelationTuple);
|
|
250
274
|
}
|
|
251
275
|
// BIRTH-METER AUTO-ENSURE (metered birth, slice A — the same "discoverable without re-exports"
|
|
252
276
|
// courtesy as RelationTuple): a domain declaring ≥1 `.births()` directive gets the meter aggregates
|
|
@@ -296,7 +320,11 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
296
320
|
directives.push(dir);
|
|
297
321
|
autoDeclaredEnsures.push(ty);
|
|
298
322
|
}
|
|
299
|
-
const aggregates =
|
|
323
|
+
const aggregates = applyConflictPoliciesToAggregates(
|
|
324
|
+
[...byId.values()],
|
|
325
|
+
conflictPolicies,
|
|
326
|
+
domain,
|
|
327
|
+
);
|
|
300
328
|
const directiveIds = new Set(directives.map((d) => d.id));
|
|
301
329
|
for (const task of spec.impureCapabilities ?? []) {
|
|
302
330
|
for (const directive of [task.obligation, task.complete, task.fail, task.block, task.deadLetter]) {
|
|
@@ -383,7 +411,14 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
383
411
|
queries.push(internalDelegationQuery);
|
|
384
412
|
}
|
|
385
413
|
}
|
|
386
|
-
const
|
|
414
|
+
const relationTypeByKey = new Map<string, RelationTypeDecl>();
|
|
415
|
+
for (const value of Object.values(merged)) {
|
|
416
|
+
if (isRelationTypeDecl(value)) relationTypeByKey.set(`${value.global === true ? "global" : "local"}:${value.type}`, value);
|
|
417
|
+
}
|
|
418
|
+
for (const value of spec.relationTypes ?? []) {
|
|
419
|
+
relationTypeByKey.set(`${value.global === true ? "global" : "local"}:${value.type}`, value);
|
|
420
|
+
}
|
|
421
|
+
const relationTypes = [...relationTypeByKey.values()];
|
|
387
422
|
// RELATION-USING LAW AUTO-COMPOSES ITS AUDIT READ (co2 recovery DX — same idiom as the meter
|
|
388
423
|
// queries above): a domain that declares relation law (a relation schema, a `.requires(...)` gated
|
|
389
424
|
// directive, or relate-family/signer directives ensuring RelationTuple) gets the framework's
|
|
@@ -398,10 +433,14 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
398
433
|
// Auth-provider auto-compose (sibling of the RelationTuple query carry): a private package gets the
|
|
399
434
|
// `authProvider` read so the workspace can surface its declared verifier.
|
|
400
435
|
if (declaresPrivateRead && !queries.some((q) => q.id === authProvider.id)) {
|
|
401
|
-
queries.push(
|
|
436
|
+
queries.push(declaresOwnedRead
|
|
437
|
+
? { ...authProvider, businessBirthInternal: true } as QueryDecl
|
|
438
|
+
: authProvider);
|
|
402
439
|
}
|
|
403
440
|
if (usesRelations && !queries.some((q) => q.id === relationTuplesByObject.id)) {
|
|
404
|
-
queries.push(
|
|
441
|
+
queries.push(declaresOwnedRead
|
|
442
|
+
? { ...relationTuplesByObject, businessBirthInternal: true } as QueryDecl
|
|
443
|
+
: relationTuplesByObject);
|
|
405
444
|
}
|
|
406
445
|
// FIRST-CLASS VALUE OBJECTS (value_object.ts): the deduped, sorted descriptors of every
|
|
407
446
|
// named VO the domain's aggregates carry (transitive nested VOs included) — the input to
|
|
@@ -527,10 +566,11 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
527
566
|
// closed is dissolved by construction. See `dsl/src/framework/signing_identity.ts::enrollSigner`.)
|
|
528
567
|
|
|
529
568
|
// THE SURFACED-RESOLVER LINT (surfaced_lint.ts — the fail-closed teeth): every
|
|
530
|
-
//
|
|
569
|
+
// Surfaced field must have EXACTLY ONE designated merge directive, whose plan
|
|
531
570
|
// provably set()s the field from a payload field. Returns the resolver bindings the typed
|
|
532
|
-
//
|
|
533
|
-
//
|
|
571
|
+
// chained conflict-reference codegen consumes AND the canonical manifest records as the
|
|
572
|
+
// hash-bearing conflictResolution contract. A Surfaced-free domain runs zero checks and is
|
|
573
|
+
// byte-identical; a resolver policy deliberately becomes part of business law.
|
|
534
574
|
const surfacedResolvers = assertSurfacedResolvers(
|
|
535
575
|
aggregates,
|
|
536
576
|
directives,
|
|
@@ -547,8 +587,9 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
547
587
|
// ROLE SUGAR codegen metadata — NOT law (not hashed): drives the `canEdit<Agg>`/`canView<Agg>`
|
|
548
588
|
// UI-hint helpers. Omit-when-empty (a role-free domain is byte-identical).
|
|
549
589
|
...(roles.length > 0 ? { roles } : {}),
|
|
550
|
-
// SURFACED RESOLVER BINDINGS —
|
|
551
|
-
//
|
|
590
|
+
// SURFACED RESOLVER BINDINGS — the aggregate/field/directive tuple is lowered into the
|
|
591
|
+
// hash-bearing manifest; payload-field tracing also drives generated typed references.
|
|
592
|
+
// Omit-when-empty because ordinary domains need no resolver metadata.
|
|
552
593
|
...(surfacedResolvers.length > 0 ? { surfacedResolvers } : {}),
|
|
553
594
|
...(queries.length > 0 ? { queries } : {}),
|
|
554
595
|
...(counts.length > 0 ? { counts } : {}),
|
package/src/business_birth.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// NOMOS — one business birth at the tenant surface; compute-science mechanics lower here.
|
|
2
2
|
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { BUSINESS_BIRTH_SEED_PAYLOAD_FIELD } from "./aggregate.js";
|
|
4
5
|
import { ensure } from "./authoring.js";
|
|
5
6
|
import { directive, type Directive } from "./directive.js";
|
|
6
7
|
import {
|
|
@@ -11,7 +12,13 @@ import {
|
|
|
11
12
|
type ParentAttestedBirthMarker,
|
|
12
13
|
} from "./births.js";
|
|
13
14
|
import { readFrameworkDeclared } from "./read.js";
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
AuthProvider,
|
|
17
|
+
authProvider,
|
|
18
|
+
authProviderSeedBeforeWarrant,
|
|
19
|
+
seedAuthProvider,
|
|
20
|
+
setAuthProvider,
|
|
21
|
+
} from "./framework/auth_provider_law.js";
|
|
15
22
|
import {
|
|
16
23
|
BirthGrant,
|
|
17
24
|
BirthPolicy,
|
|
@@ -49,6 +56,7 @@ export interface BusinessBirthMetadata {
|
|
|
49
56
|
readonly publicPayloadSchema: z.ZodObject<z.ZodRawShape>;
|
|
50
57
|
readonly rootId: string;
|
|
51
58
|
readonly namespace: string;
|
|
59
|
+
readonly keyField: string;
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
export interface BusinessBirthDirective extends Directive<Record<string, unknown>> {
|
|
@@ -68,7 +76,13 @@ function internalSurface<T extends object>(value: T): T & { readonly businessBir
|
|
|
68
76
|
return { ...value, businessBirthInternal: true };
|
|
69
77
|
}
|
|
70
78
|
|
|
71
|
-
const INTERNAL_FIELDS = [
|
|
79
|
+
const INTERNAL_FIELDS = [
|
|
80
|
+
"principal",
|
|
81
|
+
"bornAt",
|
|
82
|
+
"ownerKeyHash",
|
|
83
|
+
"birthCert",
|
|
84
|
+
BUSINESS_BIRTH_SEED_PAYLOAD_FIELD,
|
|
85
|
+
] as const;
|
|
72
86
|
const sha256Hex = z.string().regex(/^[0-9a-f]{64}$/);
|
|
73
87
|
|
|
74
88
|
/**
|
|
@@ -107,16 +121,20 @@ function lowerOne(decl: BusinessBirthDecl, domain: string): Record<string, unkno
|
|
|
107
121
|
const seedId = `nomosSeed${stem}`;
|
|
108
122
|
const birthId = `nomosBirth${stem}`;
|
|
109
123
|
const publicSchema = decl.payloadSchema as z.ZodObject<z.ZodRawShape>;
|
|
110
|
-
const
|
|
124
|
+
const encryptedBirth = (decl.aggregate.encryptedFields?.length ?? 0) > 0;
|
|
125
|
+
const seedSchema = publicSchema.extend({
|
|
111
126
|
principal: z.string().min(1),
|
|
112
127
|
bornAt: z.string().min(1),
|
|
113
128
|
ownerKeyHash: sha256Hex.optional(),
|
|
114
129
|
birthCert: z.string().optional(),
|
|
115
130
|
});
|
|
131
|
+
const parentSchema = seedSchema.extend({
|
|
132
|
+
...(encryptedBirth ? { [BUSINESS_BIRTH_SEED_PAYLOAD_FIELD]: z.string().min(1) } : {}),
|
|
133
|
+
});
|
|
116
134
|
|
|
117
135
|
const seedBase = directive(seedId)
|
|
118
136
|
.ensures(decl.aggregate)
|
|
119
|
-
.payload(
|
|
137
|
+
.payload(seedSchema)
|
|
120
138
|
.plan((p) => {
|
|
121
139
|
decl.plan(ensure(decl.aggregate, rootId), p);
|
|
122
140
|
return [];
|
|
@@ -128,10 +146,30 @@ function lowerOne(decl: BusinessBirthDecl, domain: string): Record<string, unkno
|
|
|
128
146
|
|
|
129
147
|
const birthBase = directive(birthId)
|
|
130
148
|
.creates(decl.aggregate)
|
|
131
|
-
.payload(
|
|
149
|
+
.payload(parentSchema)
|
|
132
150
|
.plan((p) => {
|
|
133
151
|
const founder = p.principal as string;
|
|
134
152
|
const child = workspaceNameForKey(namespace, String(decl.key(p)));
|
|
153
|
+
const childPayload = { ...(p as Record<string, unknown>) };
|
|
154
|
+
if (encryptedBirth) {
|
|
155
|
+
const rawSeed = childPayload[BUSINESS_BIRTH_SEED_PAYLOAD_FIELD];
|
|
156
|
+
if (typeof rawSeed !== "string") {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`birth(${decl.aggregate.id}): Nomos did not carry the internally sealed genesis facts`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
let sealed: unknown;
|
|
162
|
+
try {
|
|
163
|
+
sealed = JSON.parse(rawSeed);
|
|
164
|
+
} catch {
|
|
165
|
+
throw new Error(`birth(${decl.aggregate.id}): the internally sealed genesis facts are malformed`);
|
|
166
|
+
}
|
|
167
|
+
if (sealed === null || typeof sealed !== "object" || Array.isArray(sealed)) {
|
|
168
|
+
throw new Error(`birth(${decl.aggregate.id}): the internally sealed genesis facts are malformed`);
|
|
169
|
+
}
|
|
170
|
+
Object.assign(childPayload, sealed as Record<string, unknown>);
|
|
171
|
+
}
|
|
172
|
+
delete childPayload[BUSINESS_BIRTH_SEED_PAYLOAD_FIELD];
|
|
135
173
|
const chain: Array<Record<string, unknown>> = [
|
|
136
174
|
{
|
|
137
175
|
domain: "bootstrap",
|
|
@@ -153,7 +191,7 @@ function lowerOne(decl: BusinessBirthDecl, domain: string): Record<string, unkno
|
|
|
153
191
|
domain,
|
|
154
192
|
directiveId: seedId,
|
|
155
193
|
actor: founder,
|
|
156
|
-
payload:
|
|
194
|
+
payload: childPayload,
|
|
157
195
|
},
|
|
158
196
|
];
|
|
159
197
|
|
|
@@ -184,7 +222,7 @@ function lowerOne(decl: BusinessBirthDecl, domain: string): Record<string, unkno
|
|
|
184
222
|
if (apStr("issuer")) {
|
|
185
223
|
chain.push({
|
|
186
224
|
domain,
|
|
187
|
-
directiveId: "
|
|
225
|
+
directiveId: "seedAuthProvider",
|
|
188
226
|
actor: founder,
|
|
189
227
|
payload: {
|
|
190
228
|
issuer: apStr("issuer"),
|
|
@@ -212,6 +250,7 @@ function lowerOne(decl: BusinessBirthDecl, domain: string): Record<string, unkno
|
|
|
212
250
|
publicPayloadSchema: publicSchema,
|
|
213
251
|
rootId,
|
|
214
252
|
namespace,
|
|
253
|
+
keyField: decl.keyField,
|
|
215
254
|
};
|
|
216
255
|
const parent = { ...birthBase, businessBirth: metadata } as BusinessBirthDirective;
|
|
217
256
|
return {
|
|
@@ -236,6 +275,8 @@ function lowerOne(decl: BusinessBirthDecl, domain: string): Record<string, unkno
|
|
|
236
275
|
__nomos_enrollDelegation: internalDirective(enrollDelegation),
|
|
237
276
|
__nomos_signerDelegationByUser: internalSurface(signerDelegationByUser),
|
|
238
277
|
__nomos_AuthProvider: internalSurface(AuthProvider),
|
|
278
|
+
__nomos_seedAuthProvider: internalDirective(seedAuthProvider),
|
|
279
|
+
__nomos_authProviderSeedBeforeWarrant: internalSurface(authProviderSeedBeforeWarrant),
|
|
239
280
|
__nomos_setAuthProvider: internalDirective(setAuthProvider),
|
|
240
281
|
__nomos_authProvider: internalSurface(authProvider),
|
|
241
282
|
// Obscure framework export keys minimize accidental tenant collisions. Stable law ids remain the real
|