@githolon/dsl 0.101.5 → 0.102.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 +13 -0
- package/dart/lib/nomos_client.dart +3 -52
- package/dart/lib/src/dev_cloud.dart +5 -0
- package/dart/lib/src/home.dart +10 -15
- package/dart/lib/src/nms_conformance_runtime.dart +12 -0
- package/dart/lib/src/nms_generated_runtime.dart +29 -0
- package/dart/lib/src/nms_test_environment.dart +8 -0
- package/dart/lib/src/node_runner_transport.dart +56 -18
- package/dart/lib/src/provider.dart +5 -1
- package/dart/lib/src/record_origin.dart +55 -0
- package/dart/lib/src/runner/node-entry.mjs +9105 -0
- package/dart/lib/src/runner/offline-install.mjs +4729 -0
- package/dart/lib/src/session_bound.dart +23 -10
- package/dart/lib/src/source_trust.dart +51 -0
- package/dart/lib/src/test_harness.dart +490 -58
- package/dart/lib/src/webview_bridge.dart +167 -58
- package/dart/lib/src/workspace_name.dart +7 -1
- package/dart/pubspec.lock +3 -3
- package/dart/pubspec.yaml +4 -2
- package/dart/test/acquire_session_credentials_test.dart +34 -17
- package/dart/test/acquire_session_test.dart +109 -24
- package/dart/test/adv_realm_interference_test.dart +227 -99
- package/dart/test/bundle_version_pairing_test.dart +1 -1
- package/dart/test/cloud_bound_topology_test.dart +1 -1
- package/dart/test/cluster_attested_test.dart +52 -35
- package/dart/test/cluster_births_test.dart +5 -2
- package/dart/test/cluster_device_keyed_acceptance_test.dart +5 -2
- package/dart/test/cluster_local_autosync_test.dart +5 -2
- package/dart/test/cluster_scale_test.dart +5 -2
- package/dart/test/compat_test.dart +1 -1
- package/dart/test/dlq_session_surface_test.dart +2 -1
- package/dart/test/errors_test.dart +1 -1
- package/dart/test/first_app_gauntlet_test.dart +78 -51
- package/dart/test/four_layer_nested_topology_test.dart +1 -1
- package/dart/test/harness_phase_timeout_test.dart +1 -1
- package/dart/test/home_parity_test.dart +9 -5
- package/dart/test/local_sync_inert_test.dart +22 -14
- package/dart/test/node_runner_transport_test.dart +9 -5
- package/dart/test/null_slot_test.dart +1 -1
- package/dart/test/parent_signer_recovery_test.dart +5 -2
- package/dart/test/schema_validation_test.dart +1 -1
- package/dart/test/sealing_client_test.dart +2 -1
- package/dart/test/session_bound_test.dart +92 -119
- package/dart/test/stale_bundle_test.dart +1 -1
- package/dart/test/test_harness_test.dart +77 -22
- package/dart/test/vo_read_typing_test.dart +32 -14
- package/dart/test/watch_after_offer_stall_test.dart +2 -1
- package/dart/test/webview_bridge_test.dart +17 -4
- package/dart/test/wire_test.dart +1 -1
- package/dart_types/CHANGELOG.md +8 -0
- package/dart_types/lib/nomos_types.dart +7 -6
- package/dart_types/lib/src/activity.dart +23 -13
- package/dart_types/lib/src/foreign_ref.dart +61 -112
- package/dart_types/lib/src/invitation.dart +96 -0
- package/dart_types/lib/src/nomos_ref.dart +97 -59
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/surfaced_test.dart +125 -19
- package/package.json +7 -3
- package/runtime/generated-application-host.js +8851 -0
- package/src/activity.ts +2 -2
- package/src/aggregate.ts +24 -1
- package/src/application_composition.ts +282 -0
- package/src/application_topology.ts +151 -0
- package/src/attested_core.ts +2 -2
- package/src/authoring_context.ts +9 -0
- package/src/birth_meter_ensure.ts +1 -1
- package/src/births.ts +53 -5
- package/src/build_package.ts +79 -71
- package/src/business_birth.ts +93 -10
- package/src/clear_directive.ts +3 -1
- package/src/codegen_dart.ts +369 -420
- package/src/codegen_dart_application.ts +1508 -0
- package/src/codegen_dart_client.ts +156 -95
- package/src/codegen_dart_scenario.ts +299 -0
- package/src/codegen_openapi.ts +8 -3
- package/src/codegen_proof.ts +1113 -1137
- package/src/codegen_provider_dart.ts +12 -1
- package/src/codegen_ts.ts +1176 -496
- package/src/compat_package_main.ts +22 -10
- package/src/compile_package_main.ts +1536 -337
- package/src/contract.ts +10 -4
- package/src/dart_barrel_guard.ts +5 -1
- package/src/describe_model.ts +13 -5
- package/src/directive.ts +244 -5
- package/src/engine_entry.ts +56 -13
- package/src/engine_javascript.ts +31 -0
- package/src/evolve_preview.ts +3 -12
- package/src/framework/application_home.ts +24 -10
- package/src/framework/capability.ts +15 -1
- package/src/framework/domain_lifecycle.ts +8 -0
- package/src/framework/framework_aggregates.ts +2 -2
- package/src/framework/genesis.ts +11 -1
- package/src/framework/home.ts +178 -75
- package/src/framework/impure_capability.ts +24 -7
- package/src/framework/relations.ts +13 -5
- package/src/framework/workspaces.ts +13 -12
- package/src/generated_client_names.ts +42 -3
- package/src/idof_lint.ts +65 -13
- package/src/index.ts +46 -12
- package/src/intent.ts +587 -0
- package/src/law_check.ts +64 -16
- package/src/precondition.ts +16 -4
- package/src/read_projection_namespace.ts +2 -7
- package/src/recipient.ts +33 -0
- package/src/relation_schema.ts +93 -0
- package/src/secondary_ensure.ts +11 -2
- package/src/shape_compat.ts +11 -1
- package/src/usd.ts +30 -8
- package/src/usd_layers.ts +447 -246
- package/src/usd_state.ts +1 -1
- package/src/value_object.ts +49 -13
- package/src/wire_encode.ts +124 -26
- package/src/workspace_sharing.ts +269 -0
- package/src/zod_introspection.ts +84 -0
- package/dart/example/nomos_secure_key_store.dart +0 -82
- package/dart/example/nomos_webview_transport.dart +0 -63
- package/dart/lib/nomos_headless.dart +0 -50
- package/dart/lib/testing.dart +0 -14
- package/dart/test/co2_product_gauntlet_test.dart +0 -244
- package/src/framework/identity.ts +0 -537
|
@@ -74,6 +74,7 @@ import { directive, type RequirableDirective } from "../directive.js";
|
|
|
74
74
|
import { executeDirectiveToIntent } from "../wire_encode.js";
|
|
75
75
|
import type { WireIntent } from "../wire.js";
|
|
76
76
|
import type { Ports } from "../ctx.js";
|
|
77
|
+
import { idOf } from "../value_object.js";
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
80
|
* The impure-capability lifecycle vocabulary — framework-fixed. A task is `requested` when
|
|
@@ -455,6 +456,12 @@ export function impureCapability<
|
|
|
455
456
|
* the Rust reactor's `result_fields` (`HandlerSuccess::with_result_field`).
|
|
456
457
|
*/
|
|
457
458
|
completeResult?: (taskInstanceId: string, resultRef: string) => PlannedOp[];
|
|
459
|
+
/**
|
|
460
|
+
* OPTIONAL validation for the result reference before the plan runs. Typed capabilities use this
|
|
461
|
+
* to make malformed encoded receipts impossible to admit at the payload boundary; receipt-free
|
|
462
|
+
* capabilities retain the ordinary opaque-string result reference.
|
|
463
|
+
*/
|
|
464
|
+
completeResultSchema?: z.ZodString;
|
|
458
465
|
/**
|
|
459
466
|
* OPTIONAL: the capability-credential name the provider host must hold a
|
|
460
467
|
* law-state `CapabilityBinding` for (see {@link ImpureCapability.capability}).
|
|
@@ -463,9 +470,19 @@ export function impureCapability<
|
|
|
463
470
|
capability?: string;
|
|
464
471
|
}): ImpureCapability<Id, ParamsShape, F> {
|
|
465
472
|
const taskAggregate = impureCapabilityAggregate(spec.aggregateId, spec.params);
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
473
|
+
const typedRequestId = { requestId: idOf(taskAggregate) };
|
|
474
|
+
const obligationPayloadBase = obligationEnvelopeSchema.extend(spec.paramsSchema);
|
|
475
|
+
// `idOf()` deliberately has the same parsed value (`string`) as the schema it
|
|
476
|
+
// replaces; its extra aggregate identity lives in Zod metadata for lint/codegen.
|
|
477
|
+
// Preserve the generic payload type while installing that richer runtime schema.
|
|
478
|
+
const obligationPayload = obligationPayloadBase.extend(typedRequestId) as typeof obligationPayloadBase;
|
|
479
|
+
const typedCompleteBase = completeSchema.extend(typedRequestId);
|
|
480
|
+
const typedCompleteSchema = spec.completeResultSchema !== undefined
|
|
481
|
+
? typedCompleteBase.extend({ resultRef: spec.completeResultSchema })
|
|
482
|
+
: typedCompleteBase;
|
|
483
|
+
const typedFailSchema = failSchema.extend(typedRequestId);
|
|
484
|
+
const typedBlockSchema = blockSchema.extend(typedRequestId);
|
|
485
|
+
const typedDeadLetterSchema = deadLetterSchema.extend(typedRequestId);
|
|
469
486
|
|
|
470
487
|
const obligation: RequirableDirective<ObligationPayload<ParamsShape>> = directive(
|
|
471
488
|
spec.obligationDirectiveId,
|
|
@@ -488,7 +505,7 @@ export function impureCapability<
|
|
|
488
505
|
|
|
489
506
|
const complete = directive(spec.completeDirectiveId)
|
|
490
507
|
.mutates(taskAggregate)
|
|
491
|
-
.payload(
|
|
508
|
+
.payload(typedCompleteSchema)
|
|
492
509
|
.plan((p) =>
|
|
493
510
|
completeOps(
|
|
494
511
|
spec.aggregateId,
|
|
@@ -500,17 +517,17 @@ export function impureCapability<
|
|
|
500
517
|
|
|
501
518
|
const fail = directive(spec.failDirectiveId)
|
|
502
519
|
.mutates(taskAggregate)
|
|
503
|
-
.payload(
|
|
520
|
+
.payload(typedFailSchema)
|
|
504
521
|
.plan((p) => failOps(spec.aggregateId, p));
|
|
505
522
|
|
|
506
523
|
const block = directive(spec.blockDirectiveId)
|
|
507
524
|
.mutates(taskAggregate)
|
|
508
|
-
.payload(
|
|
525
|
+
.payload(typedBlockSchema)
|
|
509
526
|
.plan((p) => blockOps(spec.aggregateId, p));
|
|
510
527
|
|
|
511
528
|
const deadLetter = directive(spec.deadLetterDirectiveId)
|
|
512
529
|
.mutates(taskAggregate)
|
|
513
|
-
.payload(
|
|
530
|
+
.payload(typedDeadLetterSchema)
|
|
514
531
|
.plan((p) => deadLetterOps(spec.aggregateId, p));
|
|
515
532
|
|
|
516
533
|
return {
|
|
@@ -244,7 +244,10 @@ interface WriteTupleInput {
|
|
|
244
244
|
* un-endorsed identity (`asAttribution()`) cannot be a subject (it won't typecheck), so identity
|
|
245
245
|
* never SILENTLY decides a grant.
|
|
246
246
|
*/
|
|
247
|
-
subject
|
|
247
|
+
/** A principal/userset wire subject, endorsed current principal, or an object token for a typed parent
|
|
248
|
+
* edge. `objectOf(Relations, id)` is the canonical parent-reference form: Nomos qualifies its type to the
|
|
249
|
+
* dispatching domain, so law authors never hand-assemble a namespaced relation identity. */
|
|
250
|
+
subject: string | EndorsedAuthority<string> | RelationObjectToken;
|
|
248
251
|
/** ATTRIBUTION (free): who recorded the grant. A plain string OR `ctx.principal().asAttribution()`. */
|
|
249
252
|
grantedBy: string | AttributionPrincipal;
|
|
250
253
|
grantedAt: string;
|
|
@@ -261,7 +264,7 @@ interface WriteTupleInput {
|
|
|
261
264
|
function lowerTuple(
|
|
262
265
|
p: WriteTupleInput & {
|
|
263
266
|
status: RelationStatus;
|
|
264
|
-
revokedBy?: string | undefined;
|
|
267
|
+
revokedBy?: string | AttributionPrincipal | undefined;
|
|
265
268
|
revokedAt?: string | undefined;
|
|
266
269
|
},
|
|
267
270
|
): PlannedOp[] {
|
|
@@ -278,13 +281,18 @@ function lowerTuple(
|
|
|
278
281
|
// host-stamped actor — the SAME form the write gate parses via `Subject::parse`), so an endorsed
|
|
279
282
|
// authority subject lowers to that leaf verbatim and the gate's `engine.check` matches it.
|
|
280
283
|
const subjectE = endorsementOf(p.subject);
|
|
281
|
-
const subjectRaw = subjectE
|
|
284
|
+
const subjectRaw = subjectE
|
|
285
|
+
? subjectE.principal
|
|
286
|
+
: isRelationObjectToken(p.subject)
|
|
287
|
+
? resolveTupleObject(p.subject, p.relation)
|
|
288
|
+
: p.subject as string;
|
|
282
289
|
const grantedByRaw = endorsementOf(p.grantedBy)?.principal ?? (p.grantedBy as string);
|
|
290
|
+
const revokedByRaw = endorsementOf(p.revokedBy)?.principal ?? (p.revokedBy as string | undefined);
|
|
283
291
|
const leaf = compact({
|
|
284
292
|
status: p.status,
|
|
285
293
|
grantedBy: grantedByRaw,
|
|
286
294
|
grantedAt: p.grantedAt,
|
|
287
|
-
revokedBy:
|
|
295
|
+
revokedBy: revokedByRaw,
|
|
288
296
|
revokedAt: p.revokedAt,
|
|
289
297
|
// VA-core signer-provenance leaf fields — `compact` strips an `undefined`, so a non-signer relation
|
|
290
298
|
// (these never set) stays byte-identical to a pre-VA leaf (the era rule on the wire shape).
|
|
@@ -316,7 +324,7 @@ export function writeTuple(p: WriteTupleInput): PlannedOp[] {
|
|
|
316
324
|
* gate's remove-wins check (slice 4) refuses a later re-grant of an ever-revoked subject.
|
|
317
325
|
*/
|
|
318
326
|
export function removeTuple(
|
|
319
|
-
p: WriteTupleInput & { revokedBy: string; revokedAt: string },
|
|
327
|
+
p: WriteTupleInput & { revokedBy: string | AttributionPrincipal; revokedAt: string },
|
|
320
328
|
): PlannedOp[] {
|
|
321
329
|
return lowerTuple({
|
|
322
330
|
...p,
|
|
@@ -78,6 +78,7 @@ import { workspaceInvariant, refAs, type WorkspaceInvariantVerdict } from "./wor
|
|
|
78
78
|
import { writeTuple, removeTuple, RelationTuple, relationTupleInstanceId } from "./relations.js";
|
|
79
79
|
import { relationType, self, user, key } from "../relation_schema.js";
|
|
80
80
|
import { GENESIS_WARRANT_ID } from "./genesis.js";
|
|
81
|
+
import { idOf } from "../value_object.js";
|
|
81
82
|
import { signerObject, SIGNER_RELATION } from "./signing_identity.js";
|
|
82
83
|
// THE GENESIS BOOTSTRAP (platform-birth genesis bootstrap, Approach A): governance carries the
|
|
83
84
|
// warrant-flip primitives so the birth door can lawfully bootstrap a virgin platform's founding admin —
|
|
@@ -812,7 +813,7 @@ export const provisionWorkspace = directive("provisionWorkspace")
|
|
|
812
813
|
.mutates(CloudWorkspace)
|
|
813
814
|
.payload(
|
|
814
815
|
z.object({
|
|
815
|
-
workspaceId:
|
|
816
|
+
workspaceId: idOf(CloudWorkspace),
|
|
816
817
|
ownerGrantHash: sha256Hex,
|
|
817
818
|
provisionedAt: epochMs,
|
|
818
819
|
provisionedBy: z.string().min(1),
|
|
@@ -836,7 +837,7 @@ export const rotateWorkspaceOwner = directive("rotateWorkspaceOwner")
|
|
|
836
837
|
.mutates(CloudWorkspace)
|
|
837
838
|
.payload(
|
|
838
839
|
z.object({
|
|
839
|
-
workspaceId:
|
|
840
|
+
workspaceId: idOf(CloudWorkspace),
|
|
840
841
|
ownerGrantHash: sha256Hex,
|
|
841
842
|
rotatedBy: z.string().min(1),
|
|
842
843
|
rotatedAt: epochMs,
|
|
@@ -857,7 +858,7 @@ export const setWorkspaceTier = directive("setWorkspaceTier")
|
|
|
857
858
|
.mutates(CloudWorkspace)
|
|
858
859
|
.payload(
|
|
859
860
|
z.object({
|
|
860
|
-
workspaceId:
|
|
861
|
+
workspaceId: idOf(CloudWorkspace),
|
|
861
862
|
tier: z.enum(WORKSPACE_TIERS),
|
|
862
863
|
changedBy: z.string().min(1),
|
|
863
864
|
changedAt: epochMs,
|
|
@@ -878,7 +879,7 @@ export const retireWorkspace = directive("retireWorkspace")
|
|
|
878
879
|
.mutates(CloudWorkspace)
|
|
879
880
|
.payload(
|
|
880
881
|
z.object({
|
|
881
|
-
workspaceId:
|
|
882
|
+
workspaceId: idOf(CloudWorkspace),
|
|
882
883
|
retiredBy: z.string().min(1),
|
|
883
884
|
retiredAt: epochMs,
|
|
884
885
|
}),
|
|
@@ -900,7 +901,7 @@ export const retireOwnWorkspace = directive("retireOwnWorkspace")
|
|
|
900
901
|
.mutates(CloudWorkspace)
|
|
901
902
|
.payload(
|
|
902
903
|
z.object({
|
|
903
|
-
workspaceId:
|
|
904
|
+
workspaceId: idOf(CloudWorkspace),
|
|
904
905
|
retiredBy: z.string().min(1),
|
|
905
906
|
retiredAt: epochMs,
|
|
906
907
|
}),
|
|
@@ -956,7 +957,7 @@ export const reclaimWorkspace = directive("reclaimWorkspace")
|
|
|
956
957
|
.mutates(CloudWorkspace)
|
|
957
958
|
.payload(
|
|
958
959
|
z.object({
|
|
959
|
-
workspaceId:
|
|
960
|
+
workspaceId: idOf(CloudWorkspace),
|
|
960
961
|
reclaimedBy: z.string().min(1),
|
|
961
962
|
reclaimedAt: epochMs,
|
|
962
963
|
}),
|
|
@@ -994,7 +995,7 @@ export const rebirthWorkspace = directive("rebirthWorkspace")
|
|
|
994
995
|
.payload(
|
|
995
996
|
z.object({
|
|
996
997
|
/** The target CloudWorkspace AGGREGATE id (the minted lineage-record id; root-custody registry). */
|
|
997
|
-
workspaceId:
|
|
998
|
+
workspaceId: idOf(CloudWorkspace),
|
|
998
999
|
/** MANDATORY — the custodian's justification for superseding this chain (audit, on the ledger forever). */
|
|
999
1000
|
reason: z.string().min(1),
|
|
1000
1001
|
/** Epoch-ms the supersession was authored (caller-stamped — the timestamp doctrine). Its presence on the
|
|
@@ -1322,7 +1323,7 @@ export const setPlatformPool = directive("setPlatformPool")
|
|
|
1322
1323
|
.mutates(CloudWorkspace)
|
|
1323
1324
|
.payload(
|
|
1324
1325
|
z.object({
|
|
1325
|
-
workspaceId:
|
|
1326
|
+
workspaceId: idOf(CloudWorkspace),
|
|
1326
1327
|
pool: z.number().int().positive(),
|
|
1327
1328
|
setBy: z.string().min(1),
|
|
1328
1329
|
setAt: epochMs,
|
|
@@ -1437,7 +1438,7 @@ export const releaseHostname = directive("releaseHostname")
|
|
|
1437
1438
|
.mutates(HostnameClaim)
|
|
1438
1439
|
.payload(
|
|
1439
1440
|
z.object({
|
|
1440
|
-
claimId:
|
|
1441
|
+
claimId: idOf(HostnameClaim),
|
|
1441
1442
|
releasedBy: z.string().min(1),
|
|
1442
1443
|
releasedAt: epochMs,
|
|
1443
1444
|
}),
|
|
@@ -1499,7 +1500,7 @@ export const revokeCapability = directive("revokeCapability")
|
|
|
1499
1500
|
.mutates(CapabilityBinding)
|
|
1500
1501
|
.payload(
|
|
1501
1502
|
z.object({
|
|
1502
|
-
bindingId:
|
|
1503
|
+
bindingId: idOf(CapabilityBinding),
|
|
1503
1504
|
revokedBy: z.string().min(1),
|
|
1504
1505
|
revokedAt: epochMs,
|
|
1505
1506
|
}),
|
|
@@ -1574,7 +1575,7 @@ export const setWorkspaceByteCap = directive("setWorkspaceByteCap")
|
|
|
1574
1575
|
.mutates(CloudWorkspace)
|
|
1575
1576
|
.payload(
|
|
1576
1577
|
z.object({
|
|
1577
|
-
workspaceId:
|
|
1578
|
+
workspaceId: idOf(CloudWorkspace),
|
|
1578
1579
|
byteCap: z.number().int().positive(),
|
|
1579
1580
|
setBy: z.string().min(1),
|
|
1580
1581
|
setAt: epochMs,
|
|
@@ -1807,7 +1808,7 @@ export const founderSignerWriteOnce = workspaceInvariant("FounderSignerWriteOnce
|
|
|
1807
1808
|
refAs(
|
|
1808
1809
|
"tuple",
|
|
1809
1810
|
"RelationTuple",
|
|
1810
|
-
"pre:" + relationTupleInstanceId(signerObject(String(
|
|
1811
|
+
"pre:" + relationTupleInstanceId(signerObject(String(ctx.principal ?? "")), SIGNER_RELATION),
|
|
1811
1812
|
),
|
|
1812
1813
|
])
|
|
1813
1814
|
.assert(({ tuple }, ctx) => {
|
|
@@ -16,7 +16,45 @@ type ClientDomain = {
|
|
|
16
16
|
const camel = (value: string): string =>
|
|
17
17
|
value.replace(/[_\s-]+(\w)/g, (_match, letter: string) => letter.toUpperCase());
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const lowerFirst = (value: string): string =>
|
|
20
|
+
value.length === 0 ? value : value[0]!.toLowerCase() + value.slice(1);
|
|
21
|
+
|
|
22
|
+
/** One shared public collection spelling for generators and their generated proof (`Entry` -> `entries`). */
|
|
23
|
+
export function generatedAggregateCollectionName(aggregateId: string): string {
|
|
24
|
+
const name = camel(aggregateId);
|
|
25
|
+
const plural = /[^aeiou]y$/i.test(name)
|
|
26
|
+
? `${name.slice(0, -1)}ies`
|
|
27
|
+
: /(?:s|x|z|ch|sh)$/i.test(name)
|
|
28
|
+
? `${name}es`
|
|
29
|
+
: `${name}s`;
|
|
30
|
+
return lowerFirst(plural);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const GENERATED_APPLICATION_MEMBERS = new Set([
|
|
34
|
+
"domain",
|
|
35
|
+
"domainHash",
|
|
36
|
+
"interface",
|
|
37
|
+
"model",
|
|
38
|
+
"encryption",
|
|
39
|
+
"recovery",
|
|
40
|
+
"devices",
|
|
41
|
+
"share",
|
|
42
|
+
"unshare",
|
|
43
|
+
"transfer",
|
|
44
|
+
"corrections",
|
|
45
|
+
"conflicts",
|
|
46
|
+
"past",
|
|
47
|
+
"watchActivity",
|
|
48
|
+
"activityLog",
|
|
49
|
+
"describe",
|
|
50
|
+
"describeHistory",
|
|
51
|
+
"currentStatus",
|
|
52
|
+
"watchStatus",
|
|
53
|
+
"currentUser",
|
|
54
|
+
"refresh",
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
/** Refuse tenant callables that would overwrite a generated application concept. */
|
|
20
58
|
export function assertModelClientMemberAvailable(mod: ClientDomain): void {
|
|
21
59
|
const domain = mod.domain ?? mod.name;
|
|
22
60
|
const surfaces: readonly [string, readonly Named[]][] = [
|
|
@@ -28,9 +66,10 @@ export function assertModelClientMemberAvailable(mod: ClientDomain): void {
|
|
|
28
66
|
];
|
|
29
67
|
for (const [kind, entries] of surfaces) {
|
|
30
68
|
for (const entry of entries) {
|
|
31
|
-
if (typeof entry.id !== "string" || camel(entry.id)
|
|
69
|
+
if (typeof entry.id !== "string" || !GENERATED_APPLICATION_MEMBERS.has(camel(entry.id))) continue;
|
|
70
|
+
const reserved = camel(entry.id);
|
|
32
71
|
throw new Error(
|
|
33
|
-
`Nomos generated
|
|
72
|
+
`Nomos generated application concept '${reserved}' is already taken, but business ${kind} ` +
|
|
34
73
|
`'${entry.id}' in domain '${domain}' would use the same name. Sorry mate: Nomos will not ` +
|
|
35
74
|
`silently reinterpret either concept; rename the business ${kind}.`,
|
|
36
75
|
);
|
package/src/idof_lint.ts
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
/**
|
|
9
9
|
* THE PAYLOAD-REF idOf LINT (`architecture/dart_codegen_parity.md` P2 — ref ergonomics). A
|
|
10
10
|
* `t.ref(Target)` / `t.foreignRef(Target)` field on the AGGREGATE/read-model side already
|
|
11
|
-
* generates a
|
|
11
|
+
* generates a canonical `NomosRef<Site>`. Nothing enforced the payload SIDE
|
|
12
12
|
* carried the matching brand — a directive author can write `siteId: z.string()` in the
|
|
13
13
|
* payload and never discover `idOf(Site)` exists, so the generated Dart payload class carries a
|
|
14
|
-
* raw `String` while the read model carries `
|
|
15
|
-
*
|
|
14
|
+
* raw `String` while the read model carries `NomosRef<Site>`: an asymmetry that forces manual
|
|
15
|
+
* identity reconstruction at the UI boundary and lets a mistyped id compile silently.
|
|
16
16
|
*
|
|
17
17
|
* THIS PASS closes that gap at COMPILE time: for every directive, plan-PROBE it (reusing the
|
|
18
18
|
* exact primitive the proof generator's own discovery legs use — `probePlanOps` /
|
|
@@ -37,17 +37,15 @@
|
|
|
37
37
|
* - A directive whose plan can't be probed (unsynthesizable payload, or the plan throws under
|
|
38
38
|
* the synthesized sample) is SKIPPED — best-effort, matching every other plan-probe
|
|
39
39
|
* consumer in `codegen_proof.ts` (never a false refusal from an exotic shape).
|
|
40
|
-
* - `idOf(
|
|
41
|
-
*
|
|
42
|
-
* refuses.
|
|
40
|
+
* - Only `idOf(the exact target aggregate)` satisfies a reference edge. A standalone
|
|
41
|
+
* `brandedId(Name)` is intentionally insufficient because it has no workspace or aggregate type.
|
|
43
42
|
*
|
|
44
43
|
* NOT era-gated: this is a pure Dart/TS-codegen build-time guard (like the domain-contracts
|
|
45
44
|
* circular-import guard in `compile_package_main.ts`) — it has no wire/manifest/kernel effect,
|
|
46
45
|
* changes no hash, and never touches already-deployed law. It only judges whether `nomos-compile`
|
|
47
46
|
* accepts a directive's SOURCE shape, exactly the same posture as that circular-dependency guard.
|
|
48
|
-
* A domain already using `idOf
|
|
49
|
-
* exactly as before;
|
|
50
|
-
* newly refuses — which is the intended fail-closed behaviour, not an accidental regression.
|
|
47
|
+
* A domain already using the exact `idOf(Target)` (or one with no ref-fed payload field at all) compiles
|
|
48
|
+
* exactly as before; bare strings, standalone brands and `idOf(AnotherAggregate)` refuse.
|
|
51
49
|
*/
|
|
52
50
|
import type { z } from "zod";
|
|
53
51
|
|
|
@@ -101,6 +99,12 @@ function refTargetOf(op: FieldOp): string | undefined {
|
|
|
101
99
|
* (unsynthesizable payload / plan throws) is silently skipped — best-effort, no false refusal.
|
|
102
100
|
*/
|
|
103
101
|
export function assertIdOfLintForDirective(dir: Directive<unknown>, where: string): void {
|
|
102
|
+
// Framework-authored implementation directives deliberately carry wire-level record ids.
|
|
103
|
+
// They are never emitted into the generated tenant API, whose public wrapper accepts and
|
|
104
|
+
// validates NomosRef<TAggregate> before privately lowering it to this payload. Applying the
|
|
105
|
+
// tenant-facing idOf lint here would force framework wire schemas to masquerade as public
|
|
106
|
+
// application schemas and, worse, reject every aggregate that opts into owned custody.
|
|
107
|
+
if ((dir as { businessBirthInternal?: unknown }).businessBirthInternal === true) return;
|
|
104
108
|
const probed = probePlanOps(dir, where);
|
|
105
109
|
if (probed === undefined) return;
|
|
106
110
|
let shape: Record<string, z.ZodTypeAny>;
|
|
@@ -117,6 +121,31 @@ export function assertIdOfLintForDirective(dir: Directive<unknown>, where: strin
|
|
|
117
121
|
if (typeof value === "string") byValue.set(value, name);
|
|
118
122
|
}
|
|
119
123
|
for (const op of probed.ops) {
|
|
124
|
+
// A bound mutation addresses one existing aggregate instance. The payload field carrying that
|
|
125
|
+
// address is therefore not "some string" or merely "some branded id": it must be idOf(the exact
|
|
126
|
+
// aggregate being mutated). This is the identity spine of the generated application API. Without
|
|
127
|
+
// this guard a model can compile `entryId: z.string()`, after which every generated caller can mix
|
|
128
|
+
// unrelated record ids and is encouraged to reconstruct identity as application state.
|
|
129
|
+
const effectiveMarker = op.marker ?? (op.aggregateType === dir.aggregateId ? dir.marker : "mutates");
|
|
130
|
+
if (op.aggregateType !== undefined && (effectiveMarker === "mutates" || effectiveMarker === "archives")) {
|
|
131
|
+
const payloadField = byValue.get(op.aggregateId);
|
|
132
|
+
if (payloadField !== undefined) {
|
|
133
|
+
const fieldSchema = shape[payloadField];
|
|
134
|
+
const actualTarget = fieldSchema === undefined ? undefined : idOfMetaOf(fieldSchema);
|
|
135
|
+
if (actualTarget !== op.aggregateType) {
|
|
136
|
+
const expected = shortAggName(op.aggregateType);
|
|
137
|
+
const actual = actualTarget === undefined
|
|
138
|
+
? "an untyped string"
|
|
139
|
+
: `idOf(${shortAggName(actualTarget)})`;
|
|
140
|
+
const verb = effectiveMarker === "archives" ? "archive" : "mutate";
|
|
141
|
+
throw new IdOfLintError(
|
|
142
|
+
`directive '${dir.id}': payload field '${payloadField}' selects the ${expected} record to ${verb} ` +
|
|
143
|
+
`but carries ${actual} — declare it as idOf(${expected}) so create, update, find and watch use ` +
|
|
144
|
+
`the same generated NomosRef<${expected}>.`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
120
149
|
if (op.kind !== "set" && op.kind !== "setEntry") continue;
|
|
121
150
|
if (typeof op.value !== "string") continue;
|
|
122
151
|
const targetAgg = refTargetOf(op);
|
|
@@ -124,13 +153,23 @@ export function assertIdOfLintForDirective(dir: Directive<unknown>, where: strin
|
|
|
124
153
|
const payloadField = byValue.get(op.value);
|
|
125
154
|
if (payloadField === undefined) continue; // not traceable to a top-level payload field — no verdict
|
|
126
155
|
const fieldSchema = shape[payloadField];
|
|
127
|
-
if (fieldSchema === undefined
|
|
156
|
+
if (fieldSchema === undefined) continue;
|
|
128
157
|
const onAgg = shortAggName(op.aggregateType ?? op.aggregateId);
|
|
129
158
|
const targetName = shortAggName(targetAgg);
|
|
159
|
+
const actualTarget = idOfMetaOf(fieldSchema);
|
|
160
|
+
if (actualTarget === targetAgg) continue;
|
|
161
|
+
if (actualTarget !== undefined) {
|
|
162
|
+
throw new IdOfLintError(
|
|
163
|
+
`directive '${dir.id}': payload field '${payloadField}' feeds ref field '${onAgg}.${op.field}' ` +
|
|
164
|
+
`(-> ${targetName}) but carries idOf(${shortAggName(actualTarget)}) — declare it as ` +
|
|
165
|
+
`idOf(${targetName}) so generated callers carry NomosRef<${targetName}>.`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
if (!isBareString(fieldSchema) && brandedIdMetaOf(fieldSchema) === undefined) continue;
|
|
130
169
|
throw new IdOfLintError(
|
|
131
170
|
`directive '${dir.id}': payload field '${payloadField}' feeds ref field '${onAgg}.${op.field}' ` +
|
|
132
|
-
`(-> ${targetName}) but is
|
|
133
|
-
`Dart/TS carry
|
|
171
|
+
`(-> ${targetName}) but is not idOf(${targetName}) — declare it as idOf(${targetName}) so generated ` +
|
|
172
|
+
`Dart/TS carry NomosRef<${targetName}>, matching the read model. (dart_codegen_parity.md P2)`,
|
|
134
173
|
);
|
|
135
174
|
}
|
|
136
175
|
}
|
|
@@ -138,7 +177,20 @@ export function assertIdOfLintForDirective(dir: Directive<unknown>, where: strin
|
|
|
138
177
|
/** Run the idOf lint over every directive in a domain. `domain` names the module for the probe's
|
|
139
178
|
* error-context strings (`synthesizePayload`'s `where`). Throws on the FIRST violation. */
|
|
140
179
|
export function assertIdOfLint(directives: readonly Directive<unknown>[], domain: string): void {
|
|
180
|
+
const violations: string[] = [];
|
|
141
181
|
for (const dir of directives) {
|
|
142
|
-
|
|
182
|
+
try {
|
|
183
|
+
assertIdOfLintForDirective(dir, `${domain}/${dir.id}`);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (!(error instanceof IdOfLintError)) throw error;
|
|
186
|
+
violations.push(error.message);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (violations.length === 1) throw new IdOfLintError(violations[0]!);
|
|
190
|
+
if (violations.length > 1) {
|
|
191
|
+
throw new IdOfLintError(
|
|
192
|
+
`${violations.length} typed identity violations in domain '${domain}':\n` +
|
|
193
|
+
violations.map((message) => ` - ${message}`).join("\n"),
|
|
194
|
+
);
|
|
143
195
|
}
|
|
144
196
|
}
|
package/src/index.ts
CHANGED
|
@@ -60,6 +60,7 @@ export {
|
|
|
60
60
|
unit,
|
|
61
61
|
idOf,
|
|
62
62
|
idOfMetaOf,
|
|
63
|
+
idOfTargetKindOf,
|
|
63
64
|
brandedId,
|
|
64
65
|
brandedIdMetaOf,
|
|
65
66
|
voMetaOf,
|
|
@@ -110,6 +111,32 @@ export {
|
|
|
110
111
|
type ConflictPolicy,
|
|
111
112
|
type ConflictRule,
|
|
112
113
|
} from "./conflicts.js";
|
|
114
|
+
export {
|
|
115
|
+
intent,
|
|
116
|
+
language,
|
|
117
|
+
isBusinessIntent,
|
|
118
|
+
isApplicationLanguage,
|
|
119
|
+
applicationLanguageOf,
|
|
120
|
+
executableDirectivesForLanguage,
|
|
121
|
+
assertApplicationLanguageCoversAuthoredDirectives,
|
|
122
|
+
remapWorkspaceInvariantsForLanguage,
|
|
123
|
+
type BusinessIntent,
|
|
124
|
+
type IntentContract,
|
|
125
|
+
type IntentContractDeclaration,
|
|
126
|
+
type IntentStep,
|
|
127
|
+
type ApplicationLanguage,
|
|
128
|
+
} from "./intent.js";
|
|
129
|
+
export {
|
|
130
|
+
workspaceSharing,
|
|
131
|
+
type WorkspaceSharingOptions,
|
|
132
|
+
type WorkspaceSharingPolicy,
|
|
133
|
+
type WorkspaceSharingReceipt,
|
|
134
|
+
} from "./workspace_sharing.js";
|
|
135
|
+
export {
|
|
136
|
+
recipient,
|
|
137
|
+
recipientMetaOf,
|
|
138
|
+
NOMOS_RECIPIENT_BRAND,
|
|
139
|
+
} from "./recipient.js";
|
|
113
140
|
export {
|
|
114
141
|
set,
|
|
115
142
|
clear,
|
|
@@ -131,6 +158,7 @@ export {
|
|
|
131
158
|
export { clearDirectiveId, makeClearDirective, withClearDirectives } from "./clear_directive.js";
|
|
132
159
|
export {
|
|
133
160
|
directive,
|
|
161
|
+
nomosImplementationDetail,
|
|
134
162
|
recordObject,
|
|
135
163
|
type Directive,
|
|
136
164
|
type ReferentialMarker,
|
|
@@ -148,7 +176,7 @@ export {
|
|
|
148
176
|
// — naming no id. A directive's `.plan` calls these and returns `[]` (the recorded graph is the write
|
|
149
177
|
// surface). The dev never sees a "prim" — that is the GitHolon's internal OpenUSD IR.
|
|
150
178
|
export { create, existing, ensure, type AggregateRef } from "./authoring.js";
|
|
151
|
-
export {
|
|
179
|
+
export { recordContract, isContractHandle, satisfiesContract, type ContractHandle, type ContractField, type ContractSatisfaction } from "./contract.js";
|
|
152
180
|
export { normalizeActivity, renderActivityTemplate, activityKey, type ActivityInput, type ActivityDescriptor, type ActivityRefInput, type ActivitySeverity } from "./activity.js";
|
|
153
181
|
// The REBIRTH-boundary repair DSL (architecture/repair_dsl.md) — distinct from framework/repair.js (the
|
|
154
182
|
// IN-CHAIN compensating-intent lane): repair() binds a typed replay refusal to a declared re-fold remedy.
|
|
@@ -444,22 +472,28 @@ export {
|
|
|
444
472
|
setAuthProvider,
|
|
445
473
|
authProvider,
|
|
446
474
|
} from "./framework/auth_provider_law.js";
|
|
447
|
-
// THE
|
|
448
|
-
// index + the owner lane to maintain it (the key directory is the `signer` relation above).
|
|
475
|
+
// THE RECEIVING-GROUP HOLON — the framework-private shared signer roster + access manifest.
|
|
449
476
|
export {
|
|
450
477
|
SharedWithMe,
|
|
451
478
|
recordShare,
|
|
452
479
|
pruneShare,
|
|
453
480
|
sharedByWorkspace,
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
481
|
+
ReceivingGroup,
|
|
482
|
+
ReceivingGroupPolicy,
|
|
483
|
+
RECEIVING_GROUP_ID,
|
|
484
|
+
RECEIVING_GROUP_NAME_PREFIX,
|
|
485
|
+
RECEIVING_GROUP_NAME_HASH_LEN,
|
|
486
|
+
RECEIVING_GROUP_POLICY_ID,
|
|
487
|
+
DEFAULT_RECEIVING_GROUP_POLICY,
|
|
488
|
+
seedReceivingGroup,
|
|
489
|
+
seedReceivingGroupPolicy,
|
|
490
|
+
writeReceivingGroup,
|
|
491
|
+
receivingGroupNameBindsPrincipal,
|
|
492
|
+
receivingGroupRejectsKeylessAdditionalDevice,
|
|
493
|
+
receivingGroupAdditionalDeviceFollowsPolicy,
|
|
494
|
+
receivingGroupRevocationFollowsPolicy,
|
|
495
|
+
receivingGroupNameOf,
|
|
496
|
+
birthReceivingGroup,
|
|
463
497
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain).
|
|
464
498
|
BirthAttestation,
|
|
465
499
|
BIRTH_ATTESTATION_ID,
|