@sixb/core 0.1.4 → 0.1.6

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.
Files changed (118) hide show
  1. package/dist/actions/index.d.ts +2 -2
  2. package/dist/actions/index.d.ts.map +1 -1
  3. package/dist/actions/index.js +8 -8
  4. package/dist/actions/read-facade.d.ts +10 -2
  5. package/dist/actions/read-facade.d.ts.map +1 -1
  6. package/dist/actions/types/context.d.ts +45 -2
  7. package/dist/actions/types/context.d.ts.map +1 -1
  8. package/dist/actions/types/definitions.d.ts +8 -2
  9. package/dist/actions/types/definitions.d.ts.map +1 -1
  10. package/dist/agents/index.js +13 -13
  11. package/dist/agents/types.d.ts +2 -0
  12. package/dist/agents/types.d.ts.map +1 -1
  13. package/dist/agents/validation.d.ts.map +1 -1
  14. package/dist/auth/index.d.ts +1 -1
  15. package/dist/auth/index.d.ts.map +1 -1
  16. package/dist/auth/index.js +1 -1
  17. package/dist/auth/runtime.d.ts.map +1 -1
  18. package/dist/auth/strategy.d.ts +1 -1
  19. package/dist/auth/strategy.d.ts.map +1 -1
  20. package/dist/auth/types.d.ts +13 -0
  21. package/dist/auth/types.d.ts.map +1 -1
  22. package/dist/bootstrap/index.js +17 -17
  23. package/dist/{chunk-tycbzedm.js → chunk-0hc5j867.js} +68 -3
  24. package/dist/chunk-0hc5j867.js.map +15 -0
  25. package/dist/{chunk-tkdscva6.js → chunk-1e8m6yn3.js} +6 -1
  26. package/dist/{chunk-tkdscva6.js.map → chunk-1e8m6yn3.js.map} +4 -4
  27. package/dist/{chunk-xdf9yswn.js → chunk-1g8negke.js} +1 -1
  28. package/dist/{chunk-s31d0qmq.js → chunk-5gynx4xn.js} +11 -11
  29. package/dist/{chunk-s31d0qmq.js.map → chunk-5gynx4xn.js.map} +1 -1
  30. package/dist/{chunk-pn8n8sba.js → chunk-5sxb1168.js} +7 -11
  31. package/dist/{chunk-pn8n8sba.js.map → chunk-5sxb1168.js.map} +3 -4
  32. package/dist/chunk-72q2y8hn.js.map +3 -3
  33. package/dist/{chunk-cbkm2vam.js → chunk-8gv5z0zm.js} +1 -1
  34. package/dist/{chunk-tek1qwgm.js → chunk-a44d5zgd.js} +8 -3
  35. package/dist/{chunk-tek1qwgm.js.map → chunk-a44d5zgd.js.map} +4 -3
  36. package/dist/{chunk-1yz3x65y.js → chunk-dy311asx.js} +7 -9
  37. package/dist/{chunk-1yz3x65y.js.map → chunk-dy311asx.js.map} +2 -2
  38. package/dist/{chunk-rs1wbvy6.js → chunk-e42bc6m3.js} +1 -1
  39. package/dist/{chunk-a09jx26t.js → chunk-fe5ev6mr.js} +1 -1
  40. package/dist/{chunk-nzw7sm3b.js → chunk-hceck7dq.js} +1 -1
  41. package/dist/{chunk-7gw7mepb.js → chunk-md8k0r54.js} +1 -1
  42. package/dist/{chunk-hbeh8j9w.js → chunk-n4spznws.js} +2 -2
  43. package/dist/{chunk-afjpa2hs.js → chunk-nkbfrt0c.js} +1 -1
  44. package/dist/{chunk-c43qr6s0.js → chunk-nva1xkjz.js} +16 -12
  45. package/dist/{chunk-c43qr6s0.js.map → chunk-nva1xkjz.js.map} +4 -4
  46. package/dist/{chunk-1wk20wn3.js → chunk-t624af60.js} +37 -35
  47. package/dist/{chunk-1wk20wn3.js.map → chunk-t624af60.js.map} +4 -4
  48. package/dist/{chunk-g13869yf.js → chunk-tg5mee00.js} +1 -1
  49. package/dist/{chunk-sxwk41qa.js → chunk-znagycq7.js} +1 -1
  50. package/dist/execution/agent.js +13 -13
  51. package/dist/execution/primitive.js +13 -13
  52. package/dist/execution/request.js +13 -13
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +21 -21
  56. package/dist/index.js.map +1 -1
  57. package/dist/materializer/index.js +6 -6
  58. package/dist/objects/index.js +9 -9
  59. package/dist/objects/query/index.js +6 -6
  60. package/dist/ontology/internal.d.ts +1 -1
  61. package/dist/ontology/internal.d.ts.map +1 -1
  62. package/dist/ontology/internal.js +14 -6
  63. package/dist/ontology/internal.js.map +3 -3
  64. package/dist/ontology/json-schema.d.ts +10 -0
  65. package/dist/ontology/json-schema.d.ts.map +1 -1
  66. package/dist/pipelines/index.js +7 -7
  67. package/dist/projections/internal.js +5 -5
  68. package/dist/storage/agents/provider.js +3 -3
  69. package/dist/storage/index.d.ts +1 -1
  70. package/dist/storage/index.d.ts.map +1 -1
  71. package/dist/storage/index.js +5 -5
  72. package/dist/storage/ontology/in-memory/materializations.d.ts +4 -3
  73. package/dist/storage/ontology/in-memory/materializations.d.ts.map +1 -1
  74. package/dist/storage/ontology/index.d.ts +1 -1
  75. package/dist/storage/ontology/index.d.ts.map +1 -1
  76. package/dist/storage/ontology/materializations.d.ts +7 -3
  77. package/dist/storage/ontology/materializations.d.ts.map +1 -1
  78. package/dist/storage/ontology/provider.d.ts +2 -2
  79. package/dist/storage/ontology/provider.d.ts.map +1 -1
  80. package/dist/storage/workflow-runs/provider.js +3 -3
  81. package/dist/syncs/index.js +7 -7
  82. package/dist/testing/index.js +159 -21
  83. package/dist/testing/index.js.map +3 -3
  84. package/dist/testing/materializer-storage-contract.d.ts.map +1 -1
  85. package/dist/webhooks/internal.js +5 -5
  86. package/dist/workflows/index.js +14 -14
  87. package/package.json +1 -1
  88. package/src/actions/index.ts +10 -1
  89. package/src/actions/read-facade.ts +125 -3
  90. package/src/actions/types/context.ts +69 -2
  91. package/src/actions/types/definitions.ts +24 -2
  92. package/src/agents/types.ts +2 -0
  93. package/src/agents/validation.ts +4 -0
  94. package/src/auth/index.ts +1 -0
  95. package/src/auth/runtime.ts +8 -0
  96. package/src/auth/strategy.ts +1 -0
  97. package/src/auth/types.ts +14 -0
  98. package/src/index.ts +5 -0
  99. package/src/ontology/internal.ts +1 -1
  100. package/src/ontology/json-schema.ts +27 -5
  101. package/src/storage/index.ts +1 -0
  102. package/src/storage/ontology/in-memory/materializations.ts +34 -22
  103. package/src/storage/ontology/index.ts +1 -0
  104. package/src/storage/ontology/materializations.ts +8 -3
  105. package/src/storage/ontology/provider.ts +2 -2
  106. package/src/storage/timeseries/store.ts +6 -6
  107. package/src/testing/materializer-storage-contract.ts +170 -2
  108. package/dist/chunk-tycbzedm.js.map +0 -15
  109. /package/dist/{chunk-xdf9yswn.js.map → chunk-1g8negke.js.map} +0 -0
  110. /package/dist/{chunk-cbkm2vam.js.map → chunk-8gv5z0zm.js.map} +0 -0
  111. /package/dist/{chunk-rs1wbvy6.js.map → chunk-e42bc6m3.js.map} +0 -0
  112. /package/dist/{chunk-a09jx26t.js.map → chunk-fe5ev6mr.js.map} +0 -0
  113. /package/dist/{chunk-nzw7sm3b.js.map → chunk-hceck7dq.js.map} +0 -0
  114. /package/dist/{chunk-7gw7mepb.js.map → chunk-md8k0r54.js.map} +0 -0
  115. /package/dist/{chunk-hbeh8j9w.js.map → chunk-n4spznws.js.map} +0 -0
  116. /package/dist/{chunk-afjpa2hs.js.map → chunk-nkbfrt0c.js.map} +0 -0
  117. /package/dist/{chunk-g13869yf.js.map → chunk-tg5mee00.js.map} +0 -0
  118. /package/dist/{chunk-sxwk41qa.js.map → chunk-znagycq7.js.map} +0 -0
@@ -38,6 +38,7 @@ import type {
38
38
  FinalizeMaterializationInput,
39
39
  MaterializationCardinalityOccupantWorkRecord,
40
40
  MaterializationEventWorkRecord,
41
+ MaterializationLinkScopeRevision,
41
42
  MaterializationLinkScopeState,
42
43
  MaterializationLinkState,
43
44
  MaterializationObjectExistence,
@@ -134,7 +135,7 @@ export interface SessionState {
134
135
  readonly workStreams: Record<StreamMaterializationWorkInput["order"], WorkStreamState>
135
136
  workSealed: boolean
136
137
  incidentLinksByObject: Map<string, readonly OntologyLinkRef[]> | null
137
- linkScopeStates: Map<string, MaterializationLinkScopeState> | null
138
+ linkSlotStates: Map<string, MaterializationLinkScopeState> | null
138
139
  readonly objectExistence: Map<string, MaterializationObjectExistenceWorkRecord>
139
140
  replacement: ReplacementSessionState | null
140
141
  }
@@ -209,13 +210,13 @@ export class InMemoryOntologyMaterializationStorage implements OntologyMateriali
209
210
  await this.assertObject(expected, input.commit.projectId)
210
211
  for (const expected of input.expected.links)
211
212
  await this.assertLink(expected, input.commit.projectId)
212
- const expectedScopeStates = new Map<string, MaterializationLinkScopeState>()
213
+ const expectedScopeRevisions = new Map<string, MaterializationLinkScopeRevision>()
213
214
  for (const expected of input.expected.linkScopes) {
214
215
  const key = linkScopeSortKey(expected.source, expected.linkId)
215
216
  const current =
216
- expectedScopeStates.get(key) ??
217
- this.computeLinkScopeState(input.commit.projectId, expected.source, expected.linkId)
218
- expectedScopeStates.set(key, current)
217
+ expectedScopeRevisions.get(key) ??
218
+ this.computeEffectiveLinkScope(input.commit.projectId, expected.source, expected.linkId)
219
+ expectedScopeRevisions.set(key, current)
219
220
  if (current.fingerprint !== expected.fingerprint) {
220
221
  throw new MaterializationConflictError(
221
222
  "effective-state",
@@ -247,7 +248,7 @@ export class InMemoryOntologyMaterializationStorage implements OntologyMateriali
247
248
  },
248
249
  workSealed: false,
249
250
  incidentLinksByObject: null,
250
- linkScopeStates: expectedScopeStates,
251
+ linkSlotStates: new Map(),
251
252
  objectExistence: new Map<string, MaterializationObjectExistenceWorkRecord>(),
252
253
  replacement: null,
253
254
  }
@@ -275,7 +276,7 @@ export class InMemoryOntologyMaterializationStorage implements OntologyMateriali
275
276
  session.appliedPlanItems.length = 0
276
277
  session.outboxEnvelopes.clear()
277
278
  session.incidentLinksByObject = null
278
- session.linkScopeStates = null
279
+ session.linkSlotStates = null
279
280
  session.objectExistence.clear()
280
281
  session.replacement = null
281
282
  this.liveSessions.delete(session)
@@ -329,7 +330,7 @@ export class InMemoryOntologyMaterializationStorage implements OntologyMateriali
329
330
  linkScopeSortKey(value.source, value.linkId)
330
331
  )) {
331
332
  this.requireSession(input.session)
332
- const value = this.linkScopeState(session, scope.source, scope.linkId)
333
+ const value = this.linkSlotState(session, scope.source, scope.linkId)
333
334
  // Scope rows are currently only requested for cardinality-one links, so the complete member
334
335
  // set is bounded by the ontology invariant. Replacement scope enumeration is flattened in
335
336
  // its dedicated stream and never uses this shape.
@@ -1136,25 +1137,44 @@ export class InMemoryOntologyMaterializationStorage implements OntologyMateriali
1136
1137
  }
1137
1138
  }
1138
1139
 
1139
- private linkScopeState(
1140
+ private linkSlotState(
1140
1141
  session: SessionState,
1141
1142
  source: OntologyObjectRef,
1142
1143
  linkId: string
1143
1144
  ): MaterializationLinkScopeState {
1144
- session.linkScopeStates ??= new Map()
1145
+ session.linkSlotStates ??= new Map()
1145
1146
  const key = linkScopeSortKey(source, linkId)
1146
- const existing = session.linkScopeStates.get(key)
1147
+ const existing = session.linkSlotStates.get(key)
1147
1148
  if (existing) return structuredClone(existing)
1148
- const computed = this.computeLinkScopeState(session.header.commit.projectId, source, linkId)
1149
- session.linkScopeStates.set(key, computed)
1149
+ const computed = this.computeLinkSlotState(session.header.commit.projectId, source, linkId)
1150
+ session.linkSlotStates.set(key, computed)
1150
1151
  return structuredClone(computed)
1151
1152
  }
1152
1153
 
1153
- private computeLinkScopeState(
1154
+ private computeLinkSlotState(
1154
1155
  projectId: string,
1155
1156
  source: OntologyObjectRef,
1156
1157
  linkId: string
1157
1158
  ): MaterializationLinkScopeState {
1159
+ const value = this.computeEffectiveLinkScope(projectId, source, linkId)
1160
+ return {
1161
+ ...value,
1162
+ sourceAssertion: this.findActiveLinkScopeSource(projectId, source, linkId),
1163
+ override: structuredClone(
1164
+ publicLinkSlotOverride(
1165
+ this.state.linkSlotOverrides.get(
1166
+ projectEntityKey(projectId, linkScopeKey(source, linkId))
1167
+ )
1168
+ )
1169
+ ),
1170
+ }
1171
+ }
1172
+
1173
+ private computeEffectiveLinkScope(
1174
+ projectId: string,
1175
+ source: OntologyObjectRef,
1176
+ linkId: string
1177
+ ): MaterializationLinkScopeRevision & Pick<MaterializationLinkScopeState, "effective"> {
1158
1178
  const rows: ObjectLinkRow[] = []
1159
1179
  getInMemoryObjectMaterializerAdapter(this.objects).visitExactScopeLinks(
1160
1180
  projectId,
@@ -1177,14 +1197,6 @@ export class InMemoryOntologyMaterializationStorage implements OntologyMateriali
1177
1197
  const effective = rows.length === 1 ? linkSnapshot(rows[0]!) : null
1178
1198
  return {
1179
1199
  ...finishScopeAccumulator(accumulator),
1180
- sourceAssertion: this.findActiveLinkScopeSource(projectId, source, linkId),
1181
- override: structuredClone(
1182
- publicLinkSlotOverride(
1183
- this.state.linkSlotOverrides.get(
1184
- projectEntityKey(projectId, linkScopeKey(source, linkId))
1185
- )
1186
- )
1187
- ),
1188
1200
  effective,
1189
1201
  }
1190
1202
  }
@@ -45,6 +45,7 @@ export type {
45
45
  MaterializationClassificationWorkRecord,
46
46
  MaterializationEventWorkRecord,
47
47
  MaterializationIncidentObjectWorkRecord,
48
+ MaterializationLinkScopeRevision,
48
49
  MaterializationLinkScopeState,
49
50
  MaterializationLinkState,
50
51
  MaterializationObjectExistence,
@@ -77,17 +77,22 @@ export interface MaterializationLinkState {
77
77
  readonly effective: EffectiveLinkSnapshot | null
78
78
  }
79
79
 
80
- export interface MaterializationLinkScopeState {
80
+ /** Revision of the complete effective `(source, linkId)` member set, for either cardinality. */
81
+ export interface MaterializationLinkScopeRevision {
81
82
  readonly source: OntologyObjectRef
82
83
  readonly linkId: string
84
+ readonly effectiveCount: number
85
+ readonly fingerprint: string
86
+ }
87
+
88
+ /** Cardinality-one slot state used while resolving managed and projected authority. */
89
+ export interface MaterializationLinkScopeState extends MaterializationLinkScopeRevision {
83
90
  /** Complete active source value for the link slot. */
84
91
  readonly sourceAssertion: StoredSourceLinkAssertion | null
85
92
  /** Managed slot authority; absence delegates to source authority. */
86
93
  readonly override: StoredLinkSlotOverride | null
87
94
  /** The single effective edge, if its endpoints are effective. */
88
95
  readonly effective: EffectiveLinkSnapshot | null
89
- readonly effectiveCount: number
90
- readonly fingerprint: string
91
96
  }
92
97
 
93
98
  export interface MaterializationStatePage {
@@ -14,7 +14,7 @@ import type { OntologyCommitOriginSelector, OntologyCommitWrite } from "./commit
14
14
  import type {
15
15
  FinalizeMaterializationInput,
16
16
  MaterializationEventWorkRecord,
17
- MaterializationLinkScopeState,
17
+ MaterializationLinkScopeRevision,
18
18
  MaterializationPlanChunk,
19
19
  MaterializationPlanHeader,
20
20
  MaterializationPlanWorkItem,
@@ -99,7 +99,7 @@ export function appendScopeSnapshot(
99
99
 
100
100
  export function finishScopeAccumulator(
101
101
  accumulator: LinkScopeAccumulator
102
- ): Pick<MaterializationLinkScopeState, "source" | "linkId" | "effectiveCount" | "fingerprint"> {
102
+ ): MaterializationLinkScopeRevision {
103
103
  accumulator.hash.update("]")
104
104
  return {
105
105
  source: accumulator.source,
@@ -139,9 +139,10 @@ export class InMemoryTimeseriesStorage implements TimeseriesStorage {
139
139
  points: readonly TimeseriesPoint[]
140
140
  }[]
141
141
  > {
142
- const results = []
143
- for (const series of input.series) {
144
- results.push({
142
+ // Start every read before yielding so one batch observes one in-memory snapshot. The SQL
143
+ // providers get the same guarantee from their single query.
144
+ return Promise.all(
145
+ input.series.map(async (series) => ({
145
146
  ...series,
146
147
  points: await this.getHistory({
147
148
  projectId: input.projectId,
@@ -153,9 +154,8 @@ export class InMemoryTimeseriesStorage implements TimeseriesStorage {
153
154
  limit: input.limitPerSeries,
154
155
  order: input.order,
155
156
  }),
156
- })
157
- }
158
- return results
157
+ }))
158
+ )
159
159
  }
160
160
 
161
161
  async getLatest(params: {
@@ -15,6 +15,7 @@ import { restoreTrustedPrimitiveExecutionScope } from "../execution/durable"
15
15
  import { createTestingScope } from "../execution/scopes"
16
16
  import type { OntologyMaterializer } from "../materializer"
17
17
  import {
18
+ createLinkScopeFingerprint,
18
19
  createOntologyMaterializer,
19
20
  type OntologyEditOperation,
20
21
  ProjectionRegistry,
@@ -42,7 +43,7 @@ const Device = defineObjectType({
42
43
  prop("name", "string", { required: true }),
43
44
  prop("temperature", "double", { mode: "telemetry" }),
44
45
  ],
45
- links: [link.self("parent", { cardinality: "one" })],
46
+ links: [link.self("parent", { cardinality: "one" }), link.self("peers", { cardinality: "many" })],
46
47
  })
47
48
 
48
49
  const devices = defineDataset("storage_contract_devices", {
@@ -55,6 +56,9 @@ const devices = defineDataset("storage_contract_devices", {
55
56
  const readings = defineDataset("storage_contract_readings", {
56
57
  schema: [col("device_id", "string"), col("at", "timestamp"), col("value", "float64")],
57
58
  })
59
+ const devicePeers = defineDataset("storage_contract_device_peers", {
60
+ schema: [col("source_id", "string"), col("target_id", "string")],
61
+ })
58
62
  const ConflictDevice = defineObjectType({
59
63
  id: "StorageContractConflictDevice",
60
64
  name: "Conflict device",
@@ -88,6 +92,10 @@ const temperatureProjection = defineProjection(
88
92
  )
89
93
  .fromDataset(readings)
90
94
  .points({ objectId: "device_id", at: "at", value: "value" })
95
+ const devicePeersProjection = defineProjection("storage_contract_device_peers", Device.l.peers)
96
+ .fromDataset(devicePeers)
97
+ .sourceField("source_id")
98
+ .targetField("target_id")
91
99
  const conflictDeviceProjection = defineProjection(
92
100
  "storage_contract_conflict_devices",
93
101
  ConflictDevice
@@ -98,11 +106,17 @@ const conflictDeviceProjection = defineProjection(
98
106
 
99
107
  const ontology = new OntologyRegistry({ sources: [Device, ConflictDevice] })
100
108
  const projections = new ProjectionRegistry({
101
- projections: [deviceProjection, temperatureProjection, conflictDeviceProjection],
109
+ projections: [
110
+ deviceProjection,
111
+ temperatureProjection,
112
+ devicePeersProjection,
113
+ conflictDeviceProjection,
114
+ ],
102
115
  ontology,
103
116
  datasetsById: new Map<string, DatasetDefinition>([
104
117
  [devices.id, devices],
105
118
  [readings.id, readings],
119
+ [devicePeers.id, devicePeers],
106
120
  [conflictDevices.id, conflictDevices],
107
121
  ]),
108
122
  })
@@ -561,6 +575,160 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
561
575
  await provider.cleanup?.(createdStorage)
562
576
  }
563
577
  })
578
+
579
+ test(`${name} fences projected cardinality-many link scopes by fingerprint`, async () => {
580
+ const createdStorage = await provider.createStorage()
581
+ const storage = requireContractStorage(createdStorage)
582
+ let materializationOrdinal = 0
583
+ const materializer = createOntologyMaterializer({
584
+ projectId: "materializer-storage-contract",
585
+ ontology,
586
+ projections,
587
+ storage,
588
+ dependencies: {
589
+ batching: { sourceStageRows: 1, statePageRows: 1, planChunkRows: 1 },
590
+ clock: () => new Date("2026-02-01T12:00:00.000Z"),
591
+ materializationId: () => `many-scope-contract-candidate-${++materializationOrdinal}`,
592
+ },
593
+ })
594
+ const runtimeMaterializer = materializer.withScope(runtimeScope())
595
+ const source = { objectTypeId: Device.id, primaryId: "source" }
596
+ const peer = (primaryId: string) => ({ objectTypeId: Device.id, primaryId })
597
+ const linkRef = (targetId: string) => ({
598
+ source,
599
+ linkId: "peers",
600
+ target: peer(targetId),
601
+ })
602
+
603
+ try {
604
+ await runtimeMaterializer.edits.commit({
605
+ mode: "atomic",
606
+ source: { kind: "runtime", requestId: "many-scope-endpoints" },
607
+ operations: ["source", "peer-a", "peer-b", "peer-c"].map((primaryId) => ({
608
+ id: `create-${primaryId}`,
609
+ kind: "object.create" as const,
610
+ ref: peer(primaryId),
611
+ properties: { name: primaryId },
612
+ })),
613
+ expectedObjects: [],
614
+ expectedLinks: [],
615
+ expectedLinkScopes: [],
616
+ })
617
+
618
+ const datasetVersion = {
619
+ datasetId: devicePeers.id,
620
+ versionId: "many-links-v1",
621
+ createdAt: "2026-02-01T00:00:00.000Z",
622
+ }
623
+ const execution = await claim(storage, {
624
+ runId: "many-links-v1",
625
+ projectionId: devicePeersProjection.id,
626
+ protocol: "replacement",
627
+ datasetVersion,
628
+ })
629
+ const projectedLinks = [linkRef("peer-a"), linkRef("peer-b")]
630
+ await (
631
+ await projectionMaterializer(
632
+ materializer,
633
+ storage,
634
+ devicePeersProjection.id,
635
+ "many-links-v1"
636
+ )
637
+ ).projections.replace({
638
+ source: { projectionId: devicePeersProjection.id },
639
+ datasetVersion,
640
+ execution,
641
+ entries: entries(
642
+ projectedLinks.map((ref) => ({
643
+ root: { kind: "link", ref },
644
+ assertions: [{ kind: "link", ref }],
645
+ }))
646
+ ),
647
+ })
648
+
649
+ const observedLinks = await storage.objects.listLinks({
650
+ projectId: "materializer-storage-contract",
651
+ objectTypeId: Device.id,
652
+ objectId: source.primaryId,
653
+ linkId: "peers",
654
+ })
655
+ const fingerprint = createLinkScopeFingerprint(
656
+ observedLinks.map((row) => {
657
+ if (row.lastCommitId === undefined) {
658
+ throw new Error("Projected contract link is missing materializer provenance.")
659
+ }
660
+ return {
661
+ ref: linkRef(row.targetId),
662
+ createdAt: row.createdAt.toISOString(),
663
+ updatedAt: row.updatedAt.toISOString(),
664
+ lastCommitId: row.lastCommitId,
665
+ }
666
+ })
667
+ )
668
+ const commitObservation = async (runId: string, name: string) => {
669
+ await queueTestActionRun(storage, {
670
+ id: runId,
671
+ projectId: "materializer-storage-contract",
672
+ actionId: "observePeers",
673
+ subject: { kind: "object", objectTypeId: Device.id, primaryId: source.primaryId },
674
+ params: {},
675
+ idempotencyKey: runId,
676
+ })
677
+ await storage.actionRuns.start({ id: runId, projectId: "materializer-storage-contract" })
678
+ const actionMaterializer = await primitiveMaterializer(materializer, storage, {
679
+ kind: "action",
680
+ id: "observePeers",
681
+ runId,
682
+ })
683
+ return actionMaterializer.edits.commit({
684
+ mode: "atomic",
685
+ source: { kind: "action", actionId: "observePeers", runId },
686
+ operations: [
687
+ {
688
+ id: `record-${runId}`,
689
+ kind: "object.patch",
690
+ ref: source,
691
+ set: { name },
692
+ unset: [],
693
+ reset: [],
694
+ },
695
+ ],
696
+ expectedObjects: [],
697
+ expectedLinks: [],
698
+ expectedLinkScopes: [{ source, linkId: "peers", fingerprint }],
699
+ })
700
+ }
701
+
702
+ // Regression guard for G-021: make begin() hydrate the expected scope through the
703
+ // cardinality-one slot reader again and this otherwise unrelated Action commit fails before
704
+ // its already-stable fingerprint can be compared.
705
+ await expect(commitObservation("many-scope-action", "observed peers")).resolves.toMatchObject(
706
+ { kind: "edit", created: true }
707
+ )
708
+
709
+ await runtimeMaterializer.edits.commit({
710
+ mode: "atomic",
711
+ source: { kind: "runtime", requestId: "many-scope-change" },
712
+ operations: [{ id: "add-peer", kind: "link.upsert", ref: linkRef("peer-c") }],
713
+ expectedObjects: [],
714
+ expectedLinks: [],
715
+ expectedLinkScopes: [],
716
+ })
717
+
718
+ await expect(commitObservation("stale-many-scope-action", "must not commit")).rejects.toThrow(
719
+ "Expected link scope changed"
720
+ )
721
+ expect(
722
+ await storage.objects.getByPrimaryId({
723
+ projectId: "materializer-storage-contract",
724
+ objectTypeId: Device.id,
725
+ primaryId: source.primaryId,
726
+ })
727
+ ).toMatchObject({ properties: { name: "observed peers" } })
728
+ } finally {
729
+ await provider.cleanup?.(createdStorage)
730
+ }
731
+ })
564
732
  }
565
733
 
566
734
  function runtimeScope() {
@@ -1,15 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/actions/builders/define-action.ts", "../src/actions/builders/params.ts", "../src/actions/commit-edits.ts", "../src/actions/read-facade.ts", "../src/actions/registry.ts", "../src/actions/validators.ts"],
4
- "sourcesContent": [
5
- "import type { ObjectTypeWithPropertyTokens } from \"../../ontology/tokens\"\nimport { ActionDefinitionError, effectsWithoutEditsMessage } from \"../errors\"\nimport type {\n ActionBuilder,\n ActionEditsHandler,\n ActionEffectsHandler,\n ActionParamsConfig,\n ActionValidator,\n ActionWritebackHandler,\n GlobalActionDefinition,\n GlobalActionEditsHandler,\n GlobalActionEffectsHandler,\n GlobalActionPhaseBuilder,\n GlobalActionValidator,\n GlobalActionWritebackHandler,\n ObjectActionDefinition,\n ObjectActionPhaseBuilder,\n} from \"../types\"\nimport { assertNonEmpty } from \"../validation\"\n\ntype ActionOptions = {\n description?: string\n}\n\nexport function defineAction<const TId extends string>(\n id: TId,\n options?: ActionOptions\n): ActionBuilder<TId> {\n assertNonEmpty(id, \"id\")\n\n return asPublicActionType<ActionBuilder<TId>>(createActionBuilder(id, options))\n}\n\nfunction createActionBuilder(id: string, options?: ActionOptions) {\n const builder = {\n params(params: ActionParamsConfig) {\n return createGlobalActionPhaseBuilder(id, params, options)\n },\n on(objectType: ObjectTypeWithPropertyTokens) {\n assertNonEmpty(objectType.id, \"target id\")\n return createObjectActionParamsBuilder(id, objectType, options)\n },\n }\n\n return builder\n}\n\nfunction createGlobalActionPhaseBuilder(\n id: string,\n params: ActionParamsConfig,\n options?: ActionOptions\n): GlobalActionPhaseBuilder<string, ActionParamsConfig> {\n const validators: GlobalActionValidator<Record<string, unknown>>[] = []\n\n const builder = {\n validate(validator: GlobalActionValidator<Record<string, unknown>>) {\n validators.push(validator)\n return builder\n },\n writeback(handler: GlobalActionWritebackHandler<Record<string, unknown>, unknown>) {\n return createGlobalDefinition({\n id,\n params,\n description: options?.description,\n validators,\n writeback: handler,\n })\n },\n edits(handler: GlobalActionEditsHandler<Record<string, unknown>, undefined>) {\n return createGlobalDefinition({\n id,\n params,\n description: options?.description,\n validators,\n edits: handler,\n })\n },\n }\n\n return asPublicActionType<GlobalActionPhaseBuilder<string, ActionParamsConfig>>(builder)\n}\n\nfunction createObjectActionParamsBuilder(\n id: string,\n objectType: ObjectTypeWithPropertyTokens,\n options?: ActionOptions\n) {\n return {\n params(params: ActionParamsConfig) {\n return createObjectActionPhaseBuilder(id, objectType, params, options)\n },\n }\n}\n\nfunction createObjectActionPhaseBuilder(\n id: string,\n objectType: ObjectTypeWithPropertyTokens,\n params: ActionParamsConfig,\n options?: ActionOptions\n): ObjectActionPhaseBuilder<string, ObjectTypeWithPropertyTokens, ActionParamsConfig> {\n const validators: ActionValidator<ObjectTypeWithPropertyTokens, Record<string, unknown>>[] = []\n\n const builder = {\n validate(validator: ActionValidator<ObjectTypeWithPropertyTokens, Record<string, unknown>>) {\n validators.push(validator)\n return builder\n },\n writeback(\n handler: ActionWritebackHandler<\n ObjectTypeWithPropertyTokens,\n Record<string, unknown>,\n unknown\n >\n ) {\n return createObjectDefinition({\n id,\n objectType,\n params,\n description: options?.description,\n validators,\n writeback: handler,\n })\n },\n edits(\n handler: ActionEditsHandler<ObjectTypeWithPropertyTokens, Record<string, unknown>, undefined>\n ) {\n return createObjectDefinition({\n id,\n objectType,\n params,\n description: options?.description,\n validators,\n edits: handler,\n })\n },\n }\n\n return asPublicActionType<\n ObjectActionPhaseBuilder<string, ObjectTypeWithPropertyTokens, ActionParamsConfig>\n >(builder)\n}\n\nfunction createGlobalDefinition(input: {\n readonly id: string\n readonly params: ActionParamsConfig\n readonly description?: string\n readonly validators: readonly unknown[]\n readonly writeback?: unknown\n readonly edits?: unknown\n readonly effects?: unknown\n}): GlobalActionDefinition {\n const definition = {\n kind: \"action\",\n id: input.id,\n binding: { kind: \"global\" },\n params: input.params,\n phases: {\n validate: [...input.validators],\n ...(input.writeback ? { writeback: input.writeback } : {}),\n ...(input.edits ? { edits: input.edits } : {}),\n ...(input.effects ? { effects: input.effects } : {}),\n },\n ...(input.description !== undefined ? { description: input.description } : {}),\n }\n\n return asPublicActionType<GlobalActionDefinition>(\n Object.assign(definition, {\n edits(handler: GlobalActionEditsHandler<Record<string, unknown>, unknown>) {\n return createGlobalDefinition({\n ...input,\n edits: handler,\n })\n },\n effects(handler: GlobalActionEffectsHandler<Record<string, unknown>, unknown>) {\n if (!input.edits) {\n throw new ActionDefinitionError(effectsWithoutEditsMessage(input.id))\n }\n return createGlobalDefinition({\n ...input,\n effects: handler,\n })\n },\n })\n )\n}\n\nfunction createObjectDefinition(input: {\n readonly id: string\n readonly objectType: ObjectTypeWithPropertyTokens\n readonly params: ActionParamsConfig\n readonly description?: string\n readonly validators: readonly unknown[]\n readonly writeback?: unknown\n readonly edits?: unknown\n readonly effects?: unknown\n}): ObjectActionDefinition {\n const definition = {\n kind: \"action\",\n id: input.id,\n binding: { kind: \"object\", objectType: input.objectType },\n params: input.params,\n phases: {\n validate: [...input.validators],\n ...(input.writeback ? { writeback: input.writeback } : {}),\n ...(input.edits ? { edits: input.edits } : {}),\n ...(input.effects ? { effects: input.effects } : {}),\n },\n ...(input.description !== undefined ? { description: input.description } : {}),\n }\n\n return asPublicActionType<ObjectActionDefinition>(\n Object.assign(definition, {\n edits(\n handler: ActionEditsHandler<ObjectTypeWithPropertyTokens, Record<string, unknown>, unknown>\n ) {\n return createObjectDefinition({\n ...input,\n edits: handler,\n })\n },\n effects(\n handler: ActionEffectsHandler<\n ObjectTypeWithPropertyTokens,\n Record<string, unknown>,\n unknown\n >\n ) {\n if (!input.edits) {\n throw new ActionDefinitionError(effectsWithoutEditsMessage(input.id))\n }\n return createObjectDefinition({\n ...input,\n effects: handler,\n })\n },\n })\n )\n}\n\n// Runtime builders are plain objects; public builder types carry phase-order inference.\nfunction asPublicActionType<TPublic>(value: unknown): TPublic {\n return value as TPublic\n}\n",
6
- "import type { SchemaOrRef } from \"../../ontology\"\nimport type { QuantitativeTypeId } from \"../../ontology/units\"\nimport type { ActionParamConfig } from \"../types\"\n\ntype ActionParamOptions = {\n description?: string\n semanticType?: QuantitativeTypeId\n nullable?: boolean\n}\n\ntype FieldFromOptions<TOptions, TKey extends string, TFallback> =\n TOptions extends Record<TKey, infer TValue>\n ? { [K in TKey]: TValue }\n : { [K in TKey]?: TFallback }\n\ntype ActionParamResult<\n TSchema extends SchemaOrRef,\n TRequired extends boolean,\n TOptions extends ActionParamOptions | undefined,\n> = {\n schema: TSchema\n required: TRequired\n} & FieldFromOptions<TOptions, \"description\", string> &\n FieldFromOptions<TOptions, \"semanticType\", QuantitativeTypeId> &\n FieldFromOptions<TOptions, \"nullable\", boolean>\n\nexport function param<\n const TSchema extends SchemaOrRef,\n const TOptions extends ActionParamOptions | undefined = undefined,\n>(schema: TSchema, options?: TOptions): ActionParamResult<TSchema, true, TOptions> {\n const result: ActionParamConfig & { required: true } = {\n schema,\n required: true,\n ...(options?.description !== undefined ? { description: options.description } : {}),\n ...(options?.semanticType !== undefined ? { semanticType: options.semanticType } : {}),\n ...(options?.nullable !== undefined ? { nullable: options.nullable } : {}),\n }\n return result as ActionParamResult<TSchema, true, TOptions>\n}\n\nexport function optional<const TParam extends ActionParamConfig>(\n paramConfig: TParam\n): Omit<TParam, \"required\"> & { readonly required: false } {\n return {\n ...paramConfig,\n required: false,\n }\n}\n",
7
- "import type { EditBatch } from \"../edits\"\nimport { lowerEditBatch } from \"../edits\"\nimport type {\n EditCommitResult,\n EffectiveLinkChange,\n EffectiveObjectChange,\n ExpectedLinkRevision,\n ExpectedLinkScopeRevision,\n ExpectedObjectRevision,\n OntologyOperationOutcome,\n} from \"../materializer\"\nimport type { OntologyMutationRuntime } from \"../runtime/ontology-mutations\"\nimport type { Storage } from \"../storage/types\"\nimport { ActionEditCommitError } from \"./errors\"\n\n/** Exact reads an Action handler depended on, protected by row-level CAS at commit time. */\nexport interface ActionReadDependencies {\n readonly objects: readonly ExpectedObjectRevision[]\n readonly links: readonly ExpectedLinkRevision[]\n readonly linkScopes: readonly ExpectedLinkScopeRevision[]\n}\n\nconst NO_READ_DEPENDENCIES: ActionReadDependencies = { objects: [], links: [], linkScopes: [] }\n\nexport interface CommitActionEditsInput {\n readonly mutations: Pick<OntologyMutationRuntime, \"commitEdits\">\n readonly projectId: string\n readonly runId: string\n readonly actionId: string\n readonly batch: EditBatch\n readonly dependencies?: ActionReadDependencies\n}\n\n/** The authoritative ontology commit an Action run produced. */\nexport interface ActionEditCommitResult {\n readonly commitId: string\n readonly created: boolean\n readonly eventCount: number\n readonly outcomes: readonly OntologyOperationOutcome[]\n readonly changes: {\n readonly objects: readonly EffectiveObjectChange[]\n readonly links: readonly EffectiveLinkChange[]\n }\n readonly committedAt: Date\n}\n\nexport interface FindActionEditCommitInput {\n readonly storage: Pick<Storage, \"ontology\">\n readonly projectId: string\n readonly runId: string\n}\n\n/**\n * Commits one Action run's recorded edits as a single atomic ontology commit.\n *\n * The batch lowers to canonical Materializer operations and the Materializer validates the Action run\n * identity, applies managed authority, resolves effective state, writes the authoritative commit, and\n * inserts outbox facts in one transaction. Repeating the call for the same run replays that commit;\n * a divergent request for the same run is a typed idempotency conflict.\n */\nexport async function commitActionEdits(\n input: CommitActionEditsInput\n): Promise<ActionEditCommitResult> {\n const dependencies = input.dependencies ?? NO_READ_DEPENDENCIES\n const commit = await input.mutations.commitEdits({\n mode: \"atomic\",\n source: { kind: \"action\", actionId: input.actionId, runId: input.runId },\n operations: lowerEditBatch(input.batch),\n expectedObjects: dependencies.objects,\n expectedLinks: dependencies.links,\n expectedLinkScopes: dependencies.linkScopes,\n })\n return toActionEditCommitResult(commit)\n}\n\n/**\n * Resolves the authoritative commit an Action run already produced, if any.\n *\n * Resume uses the exact `{ actionRunId }` origin instead of rerunning handlers, so a redelivered run\n * returns the persisted result without recomputing an idempotency hash.\n */\nexport async function findActionEditCommit(\n input: FindActionEditCommitInput\n): Promise<ActionEditCommitResult | null> {\n const record = await input.storage.ontology.commits.getByOrigin({\n projectId: input.projectId,\n origin: { kind: \"action\", actionRunId: input.runId },\n })\n if (!record) return null\n if (record.result.kind !== \"edit\") {\n throw new ActionEditCommitError(\n `[Sixb] Action run '${input.runId}' resolved a '${record.result.kind}' commit instead of an edit commit.`\n )\n }\n return toActionEditCommitResult({ ...record.result, created: false })\n}\n\nfunction toActionEditCommitResult(commit: EditCommitResult): ActionEditCommitResult {\n return {\n commitId: commit.commitId,\n created: commit.created,\n eventCount: commit.eventCount,\n outcomes: commit.outcomes,\n changes: commit.changes,\n committedAt: new Date(commit.committedAt),\n }\n}\n",
8
- "import { linkRefKey, linkScopeKey, objectRefKey } from \"../materialization/refs\"\nimport type {\n EffectiveLinkSnapshot,\n ExpectedLinkRevision,\n ExpectedLinkScopeRevision,\n ExpectedObjectRevision,\n OntologyLinkRef,\n OntologyObjectRef,\n} from \"../materializer\"\nimport { createLinkScopeFingerprint } from \"../materializer\"\nimport type { ObjectTypeWithPropertyTokens } from \"../ontology/tokens\"\nimport type { ValueType } from \"../ontology/types\"\nimport type { ObjectSetListInput } from \"../runtime/types\"\nimport type { ObjectLinkRow, ObjectRow } from \"../storage\"\nimport type { ActionReadDependencies } from \"./commit-edits\"\nimport type { ActionReadFacade, ActionReadObjectSet } from \"./types\"\n\nexport type ActionReadObjectSetSource = {\n get(id: string): Promise<unknown>\n query(): unknown\n list(input?: ObjectSetListInput): Promise<unknown>\n byId(id: string): {\n get(): Promise<unknown>\n listLinks(link?: { readonly id: string }): Promise<unknown>\n }\n}\n\n/**\n * Records the exact state an Action handler observed while reading.\n *\n * A handler that reads one dependency twice keeps the first observation: the commit must fail when\n * current state no longer matches what the decision was made against, and the later read may already\n * reflect the handler's own intent. Arbitrary query phantoms stay out of this release — `query()` and\n * `list()` results are not turned into dependencies.\n */\nexport class ActionReadRecorder {\n private readonly objects = new Map<string, ExpectedObjectRevision>()\n private readonly links = new Map<string, ExpectedLinkRevision>()\n private readonly linkScopes = new Map<string, ExpectedLinkScopeRevision>()\n\n /** Records a concrete object read, including exact absence. */\n observeObject(ref: OntologyObjectRef, row: ObjectRow | null): void {\n const key = objectRefKey(ref)\n if (this.objects.has(key)) return\n if (!row) {\n this.objects.set(key, { ref, exists: false })\n return\n }\n // Rows without materializer provenance predate the commit ledger and cannot be fenced.\n if (row.lastCommitId === undefined) return\n this.objects.set(key, {\n ref,\n exists: true,\n version: row.version,\n lastCommitId: row.lastCommitId,\n })\n }\n\n /**\n * Records the complete `(source, linkId)` scopes a link listing observed.\n *\n * `linkIds` names every scope the read covered so an empty scope is recorded as an empty\n * fingerprint rather than being silently omitted.\n */\n observeLinkScopes(\n source: OntologyObjectRef,\n linkIds: readonly string[],\n rows: readonly ObjectLinkRow[]\n ): void {\n const snapshotsByLinkId = new Map<string, EffectiveLinkSnapshot[]>()\n for (const linkId of linkIds) snapshotsByLinkId.set(linkId, [])\n /** Scopes holding a row that cannot be fenced, so no expectation may be recorded for them. */\n const unfenceable = new Set<string>()\n\n for (const row of rows) {\n if (row.sourceTypeId !== source.objectTypeId || row.sourceId !== source.primaryId) continue\n const snapshots = snapshotsByLinkId.get(row.linkId)\n if (!snapshots) continue\n const snapshot = toLinkSnapshot(row)\n if (!snapshot) {\n // Same rule `observeObject` applies to rows without materializer provenance: record no\n // expectation at all. A fingerprint computed over the remaining rows would describe a scope\n // the store can never reproduce, turning every such read into a guaranteed conflict.\n unfenceable.add(row.linkId)\n continue\n }\n snapshots.push(snapshot)\n this.observeLink(snapshot.ref, snapshot)\n }\n\n for (const [linkId, snapshots] of snapshotsByLinkId) {\n if (unfenceable.has(linkId)) continue\n const key = linkScopeKey(source, linkId)\n if (this.linkScopes.has(key)) continue\n this.linkScopes.set(key, {\n source,\n linkId,\n fingerprint: createLinkScopeFingerprint(snapshots),\n })\n }\n }\n\n /** Records a concrete link read, including exact absence. */\n observeLink(ref: OntologyLinkRef, snapshot: EffectiveLinkSnapshot | null): void {\n const key = linkRefKey(ref)\n if (this.links.has(key)) return\n this.links.set(\n key,\n snapshot ? { ref, exists: true, lastCommitId: snapshot.lastCommitId } : { ref, exists: false }\n )\n }\n\n dependencies(): ActionReadDependencies {\n return {\n objects: [...this.objects.values()],\n links: [...this.links.values()],\n linkScopes: [...this.linkScopes.values()],\n }\n }\n}\n\nexport interface ActionReadFacadeOptions {\n /** Records what the handler observed, so the commit can be fenced against it. */\n readonly recorder: ActionReadRecorder\n /**\n * Every link the object type carries, inherited links included.\n *\n * Inheritance is resolved by the Ontology registry, not by the imported definition, so an\n * `extends` type would otherwise report only its own links and drop inherited ones from the\n * recorded scopes.\n */\n readonly resolveLinkIds: (objectTypeId: string) => readonly string[]\n}\n\nexport function createActionReadFacade(\n createObjectSet: <const TObjectType extends ObjectTypeWithPropertyTokens>(\n objectType: TObjectType\n ) => ActionReadObjectSetSource,\n options?: ActionReadFacadeOptions\n): ActionReadFacade {\n const facade = {\n objects<const TObjectType extends ObjectTypeWithPropertyTokens>(objectType: TObjectType) {\n return createActionReadObjectSetAdapter<TObjectType>(\n objectType,\n createObjectSet(objectType),\n options\n )\n },\n }\n\n return facade as ActionReadFacade\n}\n\nfunction createActionReadObjectSetAdapter<TObjectType extends ObjectTypeWithPropertyTokens>(\n objectType: TObjectType,\n objectSet: ActionReadObjectSetSource,\n options: ActionReadFacadeOptions | undefined\n): ActionReadObjectSet<TObjectType, readonly ValueType[], ObjectTypeWithPropertyTokens> {\n type TypedReadObjectSet = ActionReadObjectSet<\n TObjectType,\n readonly ValueType[],\n ObjectTypeWithPropertyTokens\n >\n\n async function readObject(id: string, read: () => Promise<unknown>): Promise<unknown> {\n const row = await read()\n options?.recorder.observeObject(\n { objectTypeId: objectType.id, primaryId: id },\n (row ?? null) as ObjectRow | null\n )\n return row\n }\n\n return {\n get(id) {\n return readObject(id, () => objectSet.get(id)) as ReturnType<TypedReadObjectSet[\"get\"]>\n },\n query() {\n return objectSet.query() as ReturnType<TypedReadObjectSet[\"query\"]>\n },\n list(input) {\n return objectSet.list(input) as ReturnType<TypedReadObjectSet[\"list\"]>\n },\n byId(id) {\n const handle = objectSet.byId(id)\n return {\n get() {\n return readObject(id, () => handle.get()) as ReturnType<\n ReturnType<TypedReadObjectSet[\"byId\"]>[\"get\"]\n >\n },\n async listLinks(link) {\n const rows = (await handle.listLinks(link)) as readonly ObjectLinkRow[]\n options?.recorder.observeLinkScopes(\n { objectTypeId: objectType.id, primaryId: id },\n link ? [link.id] : options.resolveLinkIds(objectType.id),\n rows\n )\n return rows as unknown as Awaited<\n ReturnType<ReturnType<TypedReadObjectSet[\"byId\"]>[\"listLinks\"]>\n >\n },\n }\n },\n }\n}\n\nfunction toLinkSnapshot(row: ObjectLinkRow): EffectiveLinkSnapshot | null {\n // Rows without materializer provenance predate the commit ledger and cannot be fenced.\n if (row.lastCommitId === undefined) return null\n return {\n ref: {\n source: { objectTypeId: row.sourceTypeId, primaryId: row.sourceId },\n linkId: row.linkId,\n target: { objectTypeId: row.targetTypeId, primaryId: row.targetId },\n },\n ...(row.properties !== undefined\n ? { properties: row.properties as EffectiveLinkSnapshot[\"properties\"] }\n : {}),\n createdAt: row.createdAt.toISOString(),\n updatedAt: row.updatedAt.toISOString(),\n lastCommitId: row.lastCommitId,\n }\n}\n",
9
- "import type { ObjectType, OntologyDefinitionCatalog } from \"../ontology\"\nimport {\n ActionDefinitionError,\n effectsWithoutEditsMessage,\n missingActionMutationMessage,\n} from \"./errors\"\nimport type { ActionDefinition, ObjectActionDefinition } from \"./types\"\nimport { isObjectActionDefinition } from \"./validation\"\n\nexport interface ActionDefinitionCatalog {\n list(): readonly ActionDefinition[]\n getById(id: string): ActionDefinition | null\n listGlobal(): readonly ActionDefinition[]\n listForType(type: ObjectType): readonly ObjectActionDefinition[]\n}\n\nexport interface ActionRegistryOptions {\n readonly actions: readonly ActionDefinition[]\n readonly ontology: OntologyDefinitionCatalog\n}\n\nexport class ActionRegistry implements ActionDefinitionCatalog {\n private readonly byId = new Map<string, ActionDefinition>()\n private readonly globalActions: ActionDefinition[] = []\n private readonly byTargetId = new Map<string, ObjectActionDefinition[]>()\n\n private readonly ontology: OntologyDefinitionCatalog\n\n constructor(options: ActionRegistryOptions) {\n this.ontology = options.ontology\n\n for (const action of options.actions) {\n const previous = this.byId.get(action.id)\n if (previous) {\n const chainDuplicate = this.getInheritanceDuplicate(action, previous)\n if (chainDuplicate) {\n throw new ActionDefinitionError(\n `Duplicate action id \"${action.id}\" in inheritance chain of \"${chainDuplicate.objectTypeId}\": defined on both \"${chainDuplicate.firstTargetId}\" and \"${chainDuplicate.secondTargetId}\".`\n )\n }\n\n throw new ActionDefinitionError(\n `Duplicate action id \"${action.id}\" declared in multiple files.`\n )\n }\n\n this.validatePhases(action)\n this.byId.set(action.id, action)\n\n if (!isObjectActionDefinition(action)) {\n this.globalActions.push(action)\n continue\n }\n\n const objectType = action.binding.objectType\n const target = this.ontology.getObjectTypeById(objectType.id)\n if (!target) {\n throw new ActionDefinitionError(\n `Action \"${action.id}\" targets unknown object type \"${objectType.id}\". Add the object type to ontology before registering the action.`\n )\n }\n\n const bucket = this.byTargetId.get(objectType.id) ?? []\n bucket.push(action)\n this.byTargetId.set(objectType.id, bucket)\n }\n }\n\n private validatePhases(action: ActionDefinition): void {\n const hasWriteback = action.phases.writeback !== undefined\n const hasEdits = action.phases.edits !== undefined\n const hasEffects = action.phases.effects !== undefined\n\n if (!hasWriteback && !hasEdits) {\n throw new ActionDefinitionError(missingActionMutationMessage(action.id))\n }\n\n if (hasEffects && !hasEdits) {\n throw new ActionDefinitionError(effectsWithoutEditsMessage(action.id))\n }\n }\n\n list(): readonly ActionDefinition[] {\n return [...this.byId.values()]\n }\n\n getById(id: string): ActionDefinition | null {\n return this.byId.get(id) ?? null\n }\n\n listGlobal(): readonly ActionDefinition[] {\n return [...this.globalActions]\n }\n\n listForType(type: ObjectType): readonly ObjectActionDefinition[] {\n this.ontology.resolveObjectType(type.id)\n\n const seen = new Set<string>()\n const result: ObjectActionDefinition[] = []\n\n for (const ancestor of this.ontology.listAncestorChain(type)) {\n for (const action of this.byTargetId.get(ancestor.id) ?? []) {\n if (seen.has(action.id)) continue\n seen.add(action.id)\n result.push(action)\n }\n }\n\n return result\n }\n\n private getInheritanceDuplicate(\n action: ActionDefinition,\n previous: ActionDefinition\n ): { objectTypeId: string; firstTargetId: string; secondTargetId: string } | null {\n if (!isObjectActionDefinition(action) || !isObjectActionDefinition(previous)) {\n return null\n }\n\n const actionObjectType = action.binding.objectType\n const previousObjectType = previous.binding.objectType\n\n if (actionObjectType.id === previousObjectType.id) {\n return null\n }\n\n const actionChain = this.ontology.listAncestorChain(actionObjectType)\n if (actionChain.some((ancestor) => ancestor.id === previousObjectType.id)) {\n return {\n objectTypeId: actionObjectType.id,\n firstTargetId: previousObjectType.id,\n secondTargetId: actionObjectType.id,\n }\n }\n\n const previousChain = this.ontology.listAncestorChain(previousObjectType)\n if (previousChain.some((ancestor) => ancestor.id === actionObjectType.id)) {\n return {\n objectTypeId: previousObjectType.id,\n firstTargetId: actionObjectType.id,\n secondTargetId: previousObjectType.id,\n }\n }\n\n return null\n }\n}\n",
10
- "import { ActionValidationError } from \"../objects/action/errors\"\nimport type { ActionDefinition, ActionSubject, ActionTargetObject } from \"./types\"\nimport { isObjectActionDefinition } from \"./validation\"\n\ntype RuntimePhaseHandler = (ctx: unknown) => unknown | Promise<unknown>\n\nexport async function runActionValidators<TBaseContext extends object>(input: {\n readonly action: ActionDefinition\n readonly subject: ActionSubject\n readonly baseContext: TBaseContext\n readonly target?: ActionTargetObject | null\n}): Promise<void> {\n const validators = input.action.phases.validate as readonly RuntimePhaseHandler[]\n if (validators.length === 0) {\n return\n }\n\n if (!isObjectActionDefinition(input.action)) {\n for (const validator of validators) {\n const result = await validator(input.baseContext)\n if (isValidatorErrorResult(result)) {\n throw new ActionValidationError(result.error, {\n actionId: input.action.id,\n subject: input.subject,\n })\n }\n }\n return\n }\n\n if (!input.target) {\n throw new Error(`[Sixb] Action '${input.action.id}' requires an object target.`)\n }\n\n for (const validator of validators) {\n const result = await validator({\n ...input.baseContext,\n target: input.target,\n })\n if (isValidatorErrorResult(result)) {\n throw new ActionValidationError(result.error, {\n actionId: input.action.id,\n subject: input.subject,\n })\n }\n }\n}\n\nfunction isValidatorErrorResult(value: unknown): value is { readonly error: string } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"error\" in value &&\n typeof value.error === \"string\"\n )\n}\n"
11
- ],
12
- "mappings": "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",
13
- "debugId": "F2505FFF52F07A6564756E2164756E21",
14
- "names": []
15
- }