@sixb/core 0.1.9 → 0.1.10

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 (279) hide show
  1. package/dist/actions/index.js +8 -8
  2. package/dist/agents/index.js +16 -15
  3. package/dist/agents/index.js.map +1 -1
  4. package/dist/bootstrap/index.js +20 -19
  5. package/dist/bootstrap/index.js.map +1 -1
  6. package/dist/broker/in-memory.d.ts +8 -2
  7. package/dist/broker/in-memory.d.ts.map +1 -1
  8. package/dist/broker/index.d.ts +4 -2
  9. package/dist/broker/index.d.ts.map +1 -1
  10. package/dist/broker/index.js +7 -3
  11. package/dist/broker/index.js.map +1 -1
  12. package/dist/broker/retention.d.ts +4 -0
  13. package/dist/broker/retention.d.ts.map +1 -0
  14. package/dist/broker/subscriber.d.ts +10 -0
  15. package/dist/broker/subscriber.d.ts.map +1 -0
  16. package/dist/broker/types.d.ts +14 -1
  17. package/dist/broker/types.d.ts.map +1 -1
  18. package/dist/{chunk-k3cqkkn2.js → chunk-1824gssz.js} +30 -65
  19. package/dist/chunk-1824gssz.js.map +12 -0
  20. package/dist/{chunk-h1gfhhbh.js → chunk-28ja2cjs.js} +1 -1
  21. package/dist/chunk-2c4j899j.js.map +1 -1
  22. package/dist/{chunk-ppytyr6w.js → chunk-358caqf5.js} +4 -4
  23. package/dist/chunk-358caqf5.js.map +12 -0
  24. package/dist/{chunk-kxwa347f.js → chunk-3qms4nax.js} +2 -2
  25. package/dist/{chunk-kxwa347f.js.map → chunk-3qms4nax.js.map} +3 -3
  26. package/dist/{chunk-emyyk7dx.js → chunk-5c7ga8zz.js} +22 -145
  27. package/dist/chunk-5c7ga8zz.js.map +12 -0
  28. package/dist/{chunk-hpr1r6av.js → chunk-5p1v802y.js} +46 -39
  29. package/dist/{chunk-hpr1r6av.js.map → chunk-5p1v802y.js.map} +4 -4
  30. package/dist/{chunk-zcnq6105.js → chunk-85cd7e8c.js} +2 -1
  31. package/dist/chunk-85cd7e8c.js.map +10 -0
  32. package/dist/{chunk-1enhpacz.js → chunk-976rxdpx.js} +5 -5
  33. package/dist/{chunk-3f7veca0.js → chunk-9x8dybsb.js} +3 -3
  34. package/dist/chunk-9x8dybsb.js.map +10 -0
  35. package/dist/{chunk-r29fxdhn.js → chunk-aa6z4a19.js} +160 -44
  36. package/dist/chunk-aa6z4a19.js.map +12 -0
  37. package/dist/{chunk-r4tfx1sf.js → chunk-agbbygvh.js} +4 -4
  38. package/dist/{chunk-jjy0fhrn.js → chunk-b0ec55a1.js} +20 -3
  39. package/dist/chunk-b0ec55a1.js.map +12 -0
  40. package/dist/{chunk-xfbydgzy.js → chunk-bn138h9w.js} +7 -8
  41. package/dist/{chunk-xfbydgzy.js.map → chunk-bn138h9w.js.map} +3 -3
  42. package/dist/{chunk-an5tt9y0.js → chunk-ftv470j8.js} +42 -4
  43. package/dist/{chunk-an5tt9y0.js.map → chunk-ftv470j8.js.map} +7 -7
  44. package/dist/{chunk-0w35wavj.js → chunk-g7knyczr.js} +1 -1
  45. package/dist/{chunk-x57ycxq0.js → chunk-gqh45d2n.js} +42 -3
  46. package/dist/{chunk-x57ycxq0.js.map → chunk-gqh45d2n.js.map} +3 -3
  47. package/dist/{chunk-0rdrecsb.js → chunk-kfee1h52.js} +116 -32
  48. package/dist/chunk-kfee1h52.js.map +13 -0
  49. package/dist/{chunk-ypbtykv6.js → chunk-nmbxcaqx.js} +1 -1
  50. package/dist/{chunk-3tgwrcyh.js → chunk-nt99sxd6.js} +2 -2
  51. package/dist/{chunk-3tgwrcyh.js.map → chunk-nt99sxd6.js.map} +3 -3
  52. package/dist/{chunk-vf691g1r.js → chunk-p5vcsw9c.js} +2 -2
  53. package/dist/{chunk-mjbk2djy.js → chunk-p6a4npdx.js} +211 -106
  54. package/dist/{chunk-mjbk2djy.js.map → chunk-p6a4npdx.js.map} +9 -8
  55. package/dist/{chunk-3frsnm7x.js → chunk-qma80ych.js} +4 -6
  56. package/dist/chunk-qma80ych.js.map +11 -0
  57. package/dist/{chunk-qhcpj9wc.js → chunk-qyeptdyv.js} +96 -2
  58. package/dist/chunk-qyeptdyv.js.map +15 -0
  59. package/dist/{chunk-52hbv0ak.js → chunk-r7fj830t.js} +101 -48
  60. package/dist/chunk-r7fj830t.js.map +36 -0
  61. package/dist/{chunk-xyr8kppc.js → chunk-rtk71jzp.js} +1 -1
  62. package/dist/{chunk-m3z3x9v0.js → chunk-t1f4f1ve.js} +1 -1
  63. package/dist/chunk-v2vcg0w6.js +151 -0
  64. package/dist/chunk-v2vcg0w6.js.map +11 -0
  65. package/dist/connectors/connections/authorizations.d.ts.map +1 -1
  66. package/dist/connectors/connections/contracts.d.ts +3 -0
  67. package/dist/connectors/connections/contracts.d.ts.map +1 -1
  68. package/dist/connectors/connections/internal.js +1 -1
  69. package/dist/connectors/connections/request.d.ts +3 -1
  70. package/dist/connectors/connections/request.d.ts.map +1 -1
  71. package/dist/connectors/connections/runs.d.ts.map +1 -1
  72. package/dist/connectors/connections/tokens.d.ts.map +1 -1
  73. package/dist/connectors/connections/validation.d.ts +5 -1
  74. package/dist/connectors/connections/validation.d.ts.map +1 -1
  75. package/dist/connectors/types.d.ts +19 -4
  76. package/dist/connectors/types.d.ts.map +1 -1
  77. package/dist/events/execution.d.ts +1 -1
  78. package/dist/events/execution.d.ts.map +1 -1
  79. package/dist/events/index.js +1 -1
  80. package/dist/events/service.d.ts +2 -2
  81. package/dist/events/service.d.ts.map +1 -1
  82. package/dist/execution/agent.js +16 -15
  83. package/dist/execution/agent.js.map +1 -1
  84. package/dist/execution/primitive.js +16 -15
  85. package/dist/execution/primitive.js.map +2 -2
  86. package/dist/execution/request.js +16 -15
  87. package/dist/execution/request.js.map +2 -2
  88. package/dist/index.d.ts +3 -3
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +30 -28
  91. package/dist/index.js.map +1 -1
  92. package/dist/lake-storage/changes.d.ts +5 -0
  93. package/dist/lake-storage/changes.d.ts.map +1 -0
  94. package/dist/lake-storage/in-memory.d.ts +2 -1
  95. package/dist/lake-storage/in-memory.d.ts.map +1 -1
  96. package/dist/lake-storage/index.d.ts +2 -1
  97. package/dist/lake-storage/index.d.ts.map +1 -1
  98. package/dist/lake-storage/index.js +4 -2
  99. package/dist/lake-storage/index.js.map +1 -1
  100. package/dist/lake-storage/types.d.ts +36 -0
  101. package/dist/lake-storage/types.d.ts.map +1 -1
  102. package/dist/logging/internal.js +1 -1
  103. package/dist/materialization/index.d.ts +1 -1
  104. package/dist/materialization/index.d.ts.map +1 -1
  105. package/dist/materialization/model.d.ts +12 -1
  106. package/dist/materialization/model.d.ts.map +1 -1
  107. package/dist/materializer/index.d.ts +1 -1
  108. package/dist/materializer/index.d.ts.map +1 -1
  109. package/dist/materializer/index.js +5 -5
  110. package/dist/materializer/materializer.d.ts +5 -1
  111. package/dist/materializer/materializer.d.ts.map +1 -1
  112. package/dist/materializer/projections/entry-validator.d.ts +2 -1
  113. package/dist/materializer/projections/entry-validator.d.ts.map +1 -1
  114. package/dist/materializer/projections/replace.d.ts.map +1 -1
  115. package/dist/materializer/projections/source-materialization.d.ts +4 -3
  116. package/dist/materializer/projections/source-materialization.d.ts.map +1 -1
  117. package/dist/materializer/shared/normalize.d.ts +2 -1
  118. package/dist/materializer/shared/normalize.d.ts.map +1 -1
  119. package/dist/models/execution/index.js +5 -5
  120. package/dist/objects/index.js +9 -9
  121. package/dist/objects/query/index.js +5 -5
  122. package/dist/pipelines/index.js +6 -6
  123. package/dist/pipelines/run-dispatch.d.ts.map +1 -1
  124. package/dist/projections/internal.js +6 -8
  125. package/dist/projections/internal.js.map +1 -1
  126. package/dist/projections/run-dispatch.d.ts.map +1 -1
  127. package/dist/queues/in-memory.d.ts +7 -3
  128. package/dist/queues/in-memory.d.ts.map +1 -1
  129. package/dist/queues/index.js +1 -1
  130. package/dist/queues/indexed-heap.d.ts +15 -0
  131. package/dist/queues/indexed-heap.d.ts.map +1 -0
  132. package/dist/runtime/host.d.ts.map +1 -1
  133. package/dist/runtime/internal.js +1 -1
  134. package/dist/runtime/ontology-mutations.d.ts +3 -1
  135. package/dist/runtime/ontology-mutations.d.ts.map +1 -1
  136. package/dist/shares/internal.js +4 -4
  137. package/dist/storage/connector-connections/in-memory.d.ts.map +1 -1
  138. package/dist/storage/connector-connections/provider/runs.d.ts.map +1 -1
  139. package/dist/storage/connector-connections/provider.js +2 -2
  140. package/dist/storage/connector-connections/provider.js.map +3 -3
  141. package/dist/storage/connector-connections/types.d.ts +3 -2
  142. package/dist/storage/connector-connections/types.d.ts.map +1 -1
  143. package/dist/storage/index.d.ts +1 -1
  144. package/dist/storage/index.d.ts.map +1 -1
  145. package/dist/storage/index.js +4 -4
  146. package/dist/storage/internal.js +1 -1
  147. package/dist/storage/ontology/in-memory/materializations-state.d.ts.map +1 -1
  148. package/dist/storage/ontology/in-memory/materializations.d.ts +1 -1
  149. package/dist/storage/ontology/in-memory/materializations.d.ts.map +1 -1
  150. package/dist/storage/ontology/in-memory/shared-state.d.ts +15 -2
  151. package/dist/storage/ontology/in-memory/shared-state.d.ts.map +1 -1
  152. package/dist/storage/ontology/in-memory/source-roots.d.ts +6 -0
  153. package/dist/storage/ontology/in-memory/source-roots.d.ts.map +1 -0
  154. package/dist/storage/ontology/in-memory/sources.d.ts.map +1 -1
  155. package/dist/storage/ontology/index.d.ts +1 -1
  156. package/dist/storage/ontology/index.d.ts.map +1 -1
  157. package/dist/storage/ontology/provider.d.ts +16 -0
  158. package/dist/storage/ontology/provider.d.ts.map +1 -1
  159. package/dist/storage/ontology/provider.js +4 -2
  160. package/dist/storage/ontology/provider.js.map +1 -1
  161. package/dist/storage/ontology/sources.d.ts +13 -4
  162. package/dist/storage/ontology/sources.d.ts.map +1 -1
  163. package/dist/storage/projection-runs/lifecycle.d.ts.map +1 -1
  164. package/dist/storage/projection-runs/provider.js +2 -2
  165. package/dist/storage/projection-runs/types.d.ts +2 -0
  166. package/dist/storage/projection-runs/types.d.ts.map +1 -1
  167. package/dist/syncs/index.js +8 -7
  168. package/dist/syncs/index.js.map +1 -1
  169. package/dist/syncs/run-dispatch.d.ts.map +1 -1
  170. package/dist/testing/broker-contract.d.ts.map +1 -1
  171. package/dist/testing/connector-connection-storage-contract.d.ts.map +1 -1
  172. package/dist/testing/index.d.ts +1 -0
  173. package/dist/testing/index.d.ts.map +1 -1
  174. package/dist/testing/index.js +1459 -951
  175. package/dist/testing/index.js.map +9 -8
  176. package/dist/testing/lake-changes-contract.d.ts +6 -0
  177. package/dist/testing/lake-changes-contract.d.ts.map +1 -0
  178. package/dist/testing/materializer-storage-contract.d.ts.map +1 -1
  179. package/dist/testing/ontology-storage-contract.d.ts.map +1 -1
  180. package/dist/testing/projection-run-storage-contract.d.ts.map +1 -1
  181. package/dist/webhooks/connections.d.ts +6 -0
  182. package/dist/webhooks/connections.d.ts.map +1 -0
  183. package/dist/webhooks/index.d.ts +1 -1
  184. package/dist/webhooks/index.d.ts.map +1 -1
  185. package/dist/webhooks/internal.d.ts +2 -0
  186. package/dist/webhooks/internal.d.ts.map +1 -1
  187. package/dist/webhooks/internal.js +54 -9
  188. package/dist/webhooks/internal.js.map +4 -3
  189. package/dist/webhooks/registry.d.ts +1 -1
  190. package/dist/webhooks/registry.d.ts.map +1 -1
  191. package/dist/webhooks/types.d.ts +15 -4
  192. package/dist/webhooks/types.d.ts.map +1 -1
  193. package/dist/workflows/index.js +17 -16
  194. package/dist/workflows/index.js.map +1 -1
  195. package/dist/workflows/run-dispatch.d.ts +3 -1
  196. package/dist/workflows/run-dispatch.d.ts.map +1 -1
  197. package/package.json +1 -1
  198. package/src/broker/in-memory.ts +104 -39
  199. package/src/broker/index.ts +4 -1
  200. package/src/broker/retention.ts +28 -0
  201. package/src/broker/subscriber.ts +21 -0
  202. package/src/broker/types.ts +15 -1
  203. package/src/connectors/builders.ts +0 -6
  204. package/src/connectors/connections/authorizations.ts +11 -5
  205. package/src/connectors/connections/contracts.ts +3 -0
  206. package/src/connectors/connections/request.ts +56 -22
  207. package/src/connectors/connections/runs.ts +1 -0
  208. package/src/connectors/connections/tokens.ts +4 -0
  209. package/src/connectors/connections/validation.ts +91 -5
  210. package/src/connectors/types.ts +19 -4
  211. package/src/events/execution.ts +2 -2
  212. package/src/events/service.ts +3 -3
  213. package/src/index.ts +10 -2
  214. package/src/lake-storage/changes.ts +43 -0
  215. package/src/lake-storage/in-memory.ts +73 -0
  216. package/src/lake-storage/index.ts +4 -0
  217. package/src/lake-storage/types.ts +40 -0
  218. package/src/logging/service.ts +1 -1
  219. package/src/materialization/index.ts +2 -0
  220. package/src/materialization/model.ts +14 -1
  221. package/src/materializer/index.ts +2 -0
  222. package/src/materializer/materializer.ts +10 -0
  223. package/src/materializer/projections/entry-validator.ts +19 -1
  224. package/src/materializer/projections/replace.ts +36 -1
  225. package/src/materializer/projections/source-materialization.ts +26 -6
  226. package/src/materializer/shared/normalize.ts +1 -1
  227. package/src/pipelines/run-dispatch.ts +21 -1
  228. package/src/projections/run-dispatch.ts +30 -68
  229. package/src/queues/in-memory.ts +105 -62
  230. package/src/queues/indexed-heap.ts +74 -0
  231. package/src/runtime/host.ts +0 -14
  232. package/src/runtime/ontology-mutations.ts +4 -0
  233. package/src/storage/connector-connections/in-memory.ts +3 -1
  234. package/src/storage/connector-connections/provider/runs.ts +3 -1
  235. package/src/storage/connector-connections/types.ts +3 -2
  236. package/src/storage/index.ts +1 -0
  237. package/src/storage/ontology/in-memory/materializations-state.ts +5 -18
  238. package/src/storage/ontology/in-memory/materializations.ts +73 -92
  239. package/src/storage/ontology/in-memory/shared-state.ts +26 -1
  240. package/src/storage/ontology/in-memory/source-roots.ts +123 -0
  241. package/src/storage/ontology/in-memory/sources.ts +78 -23
  242. package/src/storage/ontology/index.ts +1 -0
  243. package/src/storage/ontology/provider.ts +70 -0
  244. package/src/storage/ontology/sources.ts +14 -3
  245. package/src/storage/projection-runs/lifecycle.ts +5 -2
  246. package/src/storage/projection-runs/types.ts +4 -3
  247. package/src/syncs/run-dispatch.ts +19 -1
  248. package/src/testing/broker-contract.ts +95 -0
  249. package/src/testing/connector-connection-storage-contract.ts +18 -1
  250. package/src/testing/index.ts +1 -0
  251. package/src/testing/lake-changes-contract.ts +240 -0
  252. package/src/testing/materializer-storage-contract.ts +172 -1
  253. package/src/testing/ontology-storage-contract.ts +7 -0
  254. package/src/testing/projection-run-storage-contract.ts +34 -0
  255. package/src/webhooks/connections.ts +56 -0
  256. package/src/webhooks/index.ts +2 -0
  257. package/src/webhooks/internal.ts +2 -0
  258. package/src/webhooks/registry.ts +3 -4
  259. package/src/webhooks/types.ts +17 -4
  260. package/src/workflows/run-dispatch.ts +42 -29
  261. package/dist/chunk-0rdrecsb.js.map +0 -11
  262. package/dist/chunk-3f7veca0.js.map +0 -10
  263. package/dist/chunk-3frsnm7x.js.map +0 -11
  264. package/dist/chunk-52hbv0ak.js.map +0 -36
  265. package/dist/chunk-emyyk7dx.js.map +0 -14
  266. package/dist/chunk-jjy0fhrn.js.map +0 -12
  267. package/dist/chunk-k3cqkkn2.js.map +0 -12
  268. package/dist/chunk-ppytyr6w.js.map +0 -12
  269. package/dist/chunk-qhcpj9wc.js.map +0 -14
  270. package/dist/chunk-r29fxdhn.js.map +0 -11
  271. package/dist/chunk-zcnq6105.js.map +0 -10
  272. /package/dist/{chunk-h1gfhhbh.js.map → chunk-28ja2cjs.js.map} +0 -0
  273. /package/dist/{chunk-1enhpacz.js.map → chunk-976rxdpx.js.map} +0 -0
  274. /package/dist/{chunk-r4tfx1sf.js.map → chunk-agbbygvh.js.map} +0 -0
  275. /package/dist/{chunk-0w35wavj.js.map → chunk-g7knyczr.js.map} +0 -0
  276. /package/dist/{chunk-ypbtykv6.js.map → chunk-nmbxcaqx.js.map} +0 -0
  277. /package/dist/{chunk-vf691g1r.js.map → chunk-p5vcsw9c.js.map} +0 -0
  278. /package/dist/{chunk-xyr8kppc.js.map → chunk-rtk71jzp.js.map} +0 -0
  279. /package/dist/{chunk-m3z3x9v0.js.map → chunk-t1f4f1ve.js.map} +0 -0
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto"
2
+ import { isJsonObject } from "../../json"
2
3
  import type { ConnectorAuthorizationRecord } from "../../storage"
3
4
  import { createConnectorCodedError } from "../errors"
4
5
  import type { ConnectorAccountCandidate, ConnectorOAuthCredentials } from "../types"
@@ -26,6 +27,7 @@ export function validateCredentials(
26
27
  tokenType?: string
27
28
  scopes?: string[]
28
29
  expiresAt?: Date
30
+ authorizationContext?: ConnectorOAuthCredentials["authorizationContext"]
29
31
  } = {
30
32
  accessToken: nonblank(
31
33
  credentials.accessToken,
@@ -76,6 +78,16 @@ export function validateCredentials(
76
78
  normalized.expiresAt = new Date(credentials.expiresAt)
77
79
  }
78
80
 
81
+ if (credentials.authorizationContext !== undefined) {
82
+ if (!isJsonObject(credentials.authorizationContext)) {
83
+ throw createConnectorCodedError(
84
+ "connector.adapter_invalid",
85
+ "OAuth connector adapter returned invalid authorization context; expected a JSON object."
86
+ )
87
+ }
88
+ normalized.authorizationContext = structuredClone(credentials.authorizationContext)
89
+ }
90
+
79
91
  return normalized
80
92
  }
81
93
 
@@ -135,6 +147,9 @@ export function serializeCredentials(credentials: ConnectorOAuthCredentials): st
135
147
  ...(credentials.expiresAt === undefined
136
148
  ? {}
137
149
  : { expiresAt: credentials.expiresAt.toISOString() }),
150
+ ...(credentials.authorizationContext === undefined
151
+ ? {}
152
+ : { authorizationContext: credentials.authorizationContext }),
138
153
  })
139
154
  }
140
155
 
@@ -160,7 +175,8 @@ export function parseCredentials(serialized: string): ConnectorOAuthCredentials
160
175
  (value.tokenType !== undefined && typeof value.tokenType !== "string") ||
161
176
  (value.scopes !== undefined &&
162
177
  (!Array.isArray(value.scopes) || value.scopes.some((scope) => typeof scope !== "string"))) ||
163
- (value.expiresAt !== undefined && typeof value.expiresAt !== "string")
178
+ (value.expiresAt !== undefined && typeof value.expiresAt !== "string") ||
179
+ (value.authorizationContext !== undefined && !isJsonObject(value.authorizationContext))
164
180
  ) {
165
181
  throw createConnectorCodedError(
166
182
  "connector.credentials_unavailable",
@@ -175,6 +191,9 @@ export function parseCredentials(serialized: string): ConnectorOAuthCredentials
175
191
  ...(value.tokenType === undefined ? {} : { tokenType: value.tokenType }),
176
192
  ...(value.scopes === undefined ? {} : { scopes: value.scopes as string[] }),
177
193
  ...(expiresAt === undefined ? {} : { expiresAt }),
194
+ ...(value.authorizationContext === undefined
195
+ ? {}
196
+ : { authorizationContext: value.authorizationContext }),
178
197
  })
179
198
  } catch (error) {
180
199
  throw createConnectorCodedError(
@@ -238,21 +257,88 @@ export function normalizedHttpUrl(
238
257
 
239
258
  export function assertAuthorizationUrlParameters(
240
259
  authorizationUrl: string,
241
- expected: { readonly state: string; readonly codeChallenge: string }
260
+ expected: { readonly state: string; readonly codeChallenge?: string }
242
261
  ): void {
243
262
  const parameters = new URL(authorizationUrl).searchParams
244
263
  if (
245
264
  !sameSingleParameter(parameters, "state", expected.state) ||
246
- !sameSingleParameter(parameters, "code_challenge", expected.codeChallenge) ||
247
- !sameSingleParameter(parameters, "code_challenge_method", "S256")
265
+ (expected.codeChallenge === undefined
266
+ ? parameters.has("code_challenge") || parameters.has("code_challenge_method")
267
+ : !sameSingleParameter(parameters, "code_challenge", expected.codeChallenge) ||
268
+ !sameSingleParameter(parameters, "code_challenge_method", "S256"))
248
269
  ) {
249
270
  throw createConnectorCodedError(
250
271
  "connector.adapter_invalid",
251
- "OAuth connector authorization URL must preserve the framework-provided state and PKCE S256 parameters."
272
+ expected.codeChallenge === undefined
273
+ ? "OAuth connector authorization URL must preserve the framework-provided state and omit disabled PKCE parameters."
274
+ : "OAuth connector authorization URL must preserve the framework-provided state and PKCE S256 parameters."
252
275
  )
253
276
  }
254
277
  }
255
278
 
279
+ export function connectorPkceEnabled(pkce: "S256" | "disabled" | undefined): boolean {
280
+ if (pkce === undefined || pkce === "S256") return true
281
+ if (pkce === "disabled") return false
282
+ throw createConnectorCodedError(
283
+ "connector.adapter_invalid",
284
+ "OAuth connector PKCE must be S256 or disabled."
285
+ )
286
+ }
287
+
288
+ const reservedCallbackParameters = new Set([
289
+ "state",
290
+ "code",
291
+ "auth_code",
292
+ "error",
293
+ "error_description",
294
+ "error_uri",
295
+ ])
296
+
297
+ export function validateCallbackParameterNames(names: readonly string[] = []): void {
298
+ if (
299
+ !Array.isArray(names) ||
300
+ names.some(
301
+ (name) =>
302
+ typeof name !== "string" ||
303
+ !/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(name) ||
304
+ reservedCallbackParameters.has(name)
305
+ ) ||
306
+ new Set(names).size !== names.length
307
+ ) {
308
+ throw createConnectorCodedError(
309
+ "connector.adapter_invalid",
310
+ "OAuth callback parameter names must be unique, non-reserved parameter names."
311
+ )
312
+ }
313
+ }
314
+
315
+ /** Select only declared provider values after the callback's state and browser binding are proved. */
316
+ export function selectCallbackParameters(
317
+ names: readonly string[] = [],
318
+ parameters: Readonly<Record<string, string | readonly string[]>> = {}
319
+ ): Readonly<Record<string, string>> {
320
+ validateCallbackParameterNames(names)
321
+ if (!isRecord(parameters)) {
322
+ throw createConnectorCodedError(
323
+ "connector.authorization_invalid",
324
+ "OAuth provider callback parameters must be an object."
325
+ )
326
+ }
327
+ return Object.fromEntries(
328
+ names.flatMap((name) => {
329
+ if (!Object.hasOwn(parameters, name)) return []
330
+ const value = parameters[name]
331
+ if (typeof value !== "string") {
332
+ throw createConnectorCodedError(
333
+ "connector.authorization_invalid",
334
+ "OAuth provider callback parameters must have a single string value."
335
+ )
336
+ }
337
+ return [[name, value]]
338
+ })
339
+ )
340
+ }
341
+
256
342
  export function nonblank(
257
343
  value: unknown,
258
344
  field: string,
@@ -1,3 +1,4 @@
1
+ import type { ReadonlyJsonObject } from "../json"
1
2
  import type { WebhookDefinition } from "../webhooks/types"
2
3
 
3
4
  /**
@@ -33,6 +34,12 @@ export interface ConnectorOAuthCredentials {
33
34
  readonly tokenType?: string
34
35
  readonly scopes?: readonly string[]
35
36
  readonly expiresAt?: Date
37
+ /**
38
+ * Provider-owned grant context, encrypted with the tokens and never exposed in connection views.
39
+ * Available to account discovery, refresh, and revoke. Refresh preserves it when omitted;
40
+ * returning an object replaces it (use {} to clear it). Code exchange starts fresh on reauthorization.
41
+ */
42
+ readonly authorizationContext?: ReadonlyJsonObject
36
43
  }
37
44
 
38
45
  /** Public, non-secret account information returned for framework-owned selection UI. */
@@ -75,13 +82,17 @@ export interface OAuthConnectorAuthorizationContext extends ConnectorContext {
75
82
 
76
83
  export interface OAuthConnectorAuthorizationUrlInput {
77
84
  readonly state: string
78
- readonly codeChallenge: string
79
- readonly codeChallengeMethod: "S256"
85
+ /** Present only when the adapter enables PKCE (S256 by default). */
86
+ readonly codeChallenge?: string
87
+ readonly codeChallengeMethod?: "S256"
80
88
  }
81
89
 
82
90
  export interface OAuthConnectorCodeExchangeInput {
83
91
  readonly code: string
84
- readonly codeVerifier: string
92
+ /** Present only when this authorization attempt used PKCE. */
93
+ readonly codeVerifier?: string
94
+ /** Allowlisted callback values. Untrusted until the adapter validates them with the provider. */
95
+ readonly callbackParameters?: Readonly<Record<string, string>>
85
96
  }
86
97
 
87
98
  export interface ConnectorConnectionClientContext extends ConnectorContext {
@@ -109,6 +120,10 @@ export interface ConnectorTokenSource {
109
120
  */
110
121
  export interface ConnectorOAuth2Authentication {
111
122
  readonly type: "oauth2"
123
+ /** Defaults to S256. Disable only for providers whose flow does not support PKCE. */
124
+ readonly pkce?: "S256" | "disabled"
125
+ /** Additional callback parameter names accepted by exchangeCode; framework OAuth fields are reserved. */
126
+ readonly callbackParameters?: readonly string[]
112
127
  authorizationUrl(
113
128
  context: OAuthConnectorAuthorizationContext,
114
129
  input: OAuthConnectorAuthorizationUrlInput
@@ -131,7 +146,7 @@ export interface ConnectorOAuth2Authentication {
131
146
  export interface OAuthConnectorAdapter<TType extends string = string, TClient = unknown> {
132
147
  readonly type: TType
133
148
  readonly authentication: ConnectorOAuth2Authentication
134
- readonly webhooks?: undefined
149
+ readonly webhooks?: readonly WebhookDefinition<unknown, TClient>[]
135
150
  discoverAccounts(
136
151
  context: ConnectorContext,
137
152
  credentials: ConnectorOAuthCredentials
@@ -17,7 +17,7 @@ export interface EventsRuntime {
17
17
  latestCursor(): Promise<string | undefined>
18
18
  subscribe(
19
19
  input: EventsSubscribeInput,
20
- handler: (events: readonly StoredDomainEvent[]) => void
20
+ handler: (events: readonly StoredDomainEvent[]) => unknown
21
21
  ): Promise<() => void>
22
22
  }
23
23
 
@@ -88,7 +88,7 @@ export function createEventsRuntime(runtime: SixbRuntimeContext): EventsRuntime
88
88
  }
89
89
  return runtime.events.subscribe(input, (events) => {
90
90
  const visible = visibleEvents(events)
91
- if (visible.length > 0) handler(visible)
91
+ if (visible.length > 0) return handler(visible)
92
92
  })
93
93
  },
94
94
  }
@@ -79,7 +79,7 @@ export interface DomainEventLog {
79
79
  latestCursor(): Promise<string | undefined>
80
80
  subscribe(
81
81
  input: EventsSubscribeInput,
82
- handler: (events: readonly StoredDomainEvent[]) => void
82
+ handler: (events: readonly StoredDomainEvent[]) => unknown
83
83
  ): Promise<() => void>
84
84
  }
85
85
 
@@ -251,7 +251,7 @@ export class DomainEventService implements DomainEventLog, StableEventPublisher
251
251
 
252
252
  async subscribe(
253
253
  input: EventsSubscribeInput,
254
- handler: (events: readonly StoredDomainEvent[]) => void
254
+ handler: (events: readonly StoredDomainEvent[]) => unknown
255
255
  ): Promise<() => void> {
256
256
  await this.ensureStream()
257
257
  return this.broker.subscribe(
@@ -269,7 +269,7 @@ export class DomainEventService implements DomainEventLog, StableEventPublisher
269
269
  }
270
270
 
271
271
  try {
272
- handler(events)
272
+ return handler(events)
273
273
  } catch {
274
274
  // Preserve fire-and-forget subscriber semantics.
275
275
  }
package/src/index.ts CHANGED
@@ -163,9 +163,15 @@ export {
163
163
 
164
164
  // ── Broker ─────────────────────────────────────────────────
165
165
 
166
- export type { Broker, BrokerCursor } from "./broker"
166
+ export type {
167
+ Broker,
168
+ BrokerCursor,
169
+ BrokerRetention,
170
+ BrokerStreamRetention,
171
+ InMemoryBrokerOptions,
172
+ } from "./broker"
167
173
  export { InMemoryBroker } from "./broker"
168
- export type { JsonValue, ReadonlyJsonValue } from "./json"
174
+ export type { JsonValue, ReadonlyJsonObject, ReadonlyJsonValue } from "./json"
169
175
  export {
170
176
  assertJsonValue,
171
177
  cloneJsonValue,
@@ -681,6 +687,8 @@ export type {
681
687
  WebhookBodyFormat,
682
688
  WebhookBodyParser,
683
689
  WebhookBodySchema,
690
+ WebhookConnection,
691
+ WebhookConnections,
684
692
  WebhookConnectorClient,
685
693
  WebhookDefinition,
686
694
  WebhookHandlerContext,
@@ -0,0 +1,43 @@
1
+ import type { DatasetColumnDefinition } from "../datasets"
2
+ import { LakeStorageError } from "./errors"
3
+ import type { DatasetVersion, ReadDatasetChangesInput } from "./types"
4
+
5
+ /** Shared eligibility checks for providers comparing pinned row images. */
6
+ export function resolveDatasetChangeColumns(
7
+ input: ReadDatasetChangesInput,
8
+ before: DatasetVersion | null,
9
+ after: DatasetVersion | null
10
+ ): readonly DatasetColumnDefinition[] | null {
11
+ for (const value of [input.datasetId, input.fromVersionId, input.toVersionId]) {
12
+ if (typeof value !== "string" || value.trim().length === 0) {
13
+ throw new LakeStorageError(
14
+ "[LakeStorage] Change reads require explicit dataset and version ids."
15
+ )
16
+ }
17
+ }
18
+ if (
19
+ input.keyColumns.length === 0 ||
20
+ new Set(input.keyColumns).size !== input.keyColumns.length ||
21
+ input.columns.length === 0 ||
22
+ new Set(input.columns).size !== input.columns.length ||
23
+ input.columns.some((column) => typeof column !== "string" || column.length === 0) ||
24
+ input.keyColumns.some((column) => !input.columns.includes(column))
25
+ ) {
26
+ throw new LakeStorageError(
27
+ "[LakeStorage] Change reads require distinct columns including every key column."
28
+ )
29
+ }
30
+ if (!before || !after) return null
31
+ const oldColumns = new Map(before.schema.columns.map((column) => [column.name, column]))
32
+ const newColumns = new Map(after.schema.columns.map((column) => [column.name, column]))
33
+ const columns: DatasetColumnDefinition[] = []
34
+ for (const name of input.columns) {
35
+ const previous = oldColumns.get(name)
36
+ const next = newColumns.get(name)
37
+ if (!previous || !next || previous.type !== next.type) return null
38
+ // Projection identities are nonblank strings. Other key types keep the full-read path.
39
+ if (input.keyColumns.includes(name) && next.type !== "string") return null
40
+ columns.push(next)
41
+ }
42
+ return columns
43
+ }
@@ -1,6 +1,8 @@
1
1
  import { randomUUID } from "node:crypto"
2
+ import { isDeepStrictEqual } from "node:util"
2
3
  import type { DatasetDefinition, DatasetSchema, MergeChange } from "../datasets"
3
4
  import { getDatasetRowValidationError } from "../datasets/validation"
5
+ import { resolveDatasetChangeColumns } from "./changes"
4
6
  import { mergeStrictDatasetDefinition } from "./definition-updates"
5
7
  import { LakeConcurrencyError, LakeStorageError } from "./errors"
6
8
  import type {
@@ -26,12 +28,15 @@ import type {
26
28
  BeginDatasetWriteInput,
27
29
  CommitDatasetWriteInput,
28
30
  DatasetCatalogState,
31
+ DatasetChanges,
29
32
  DatasetRow,
33
+ DatasetRowChange,
30
34
  DatasetVersion,
31
35
  DatasetWriteCommitResult,
32
36
  DatasetWriteMode,
33
37
  LakeStorage,
34
38
  LakeWriteSession,
39
+ ReadDatasetChangesInput,
35
40
  ReadDatasetRowsInput,
36
41
  } from "./types"
37
42
  import { hasDatasetInputChanges } from "./version-inputs"
@@ -389,6 +394,74 @@ export class InMemoryLakeStorage implements LakeStorage {
389
394
  }
390
395
  }
391
396
 
397
+ async readChanges(input: ReadDatasetChangesInput): Promise<DatasetChanges | null> {
398
+ input.signal?.throwIfAborted()
399
+ const before = await this.getVersion(input.datasetId, input.fromVersionId)
400
+ const after = await this.getVersion(input.datasetId, input.toVersionId)
401
+ if (!resolveDatasetChangeColumns(input, before, after)) return null
402
+ const previous = this.rowsByVersionId.get(input.fromVersionId)!
403
+ const next = this.rowsByVersionId.get(input.toVersionId)!
404
+ const index = (rows: readonly DatasetRow[]) => {
405
+ const result = new Map<string, DatasetRow>()
406
+ for (const row of rows) {
407
+ input.signal?.throwIfAborted()
408
+ const values = input.keyColumns.map((column) => row[column])
409
+ if (values.some((value) => typeof value !== "string" || value.trim().length === 0))
410
+ return null
411
+ const key = JSON.stringify(values)
412
+ if (result.has(key)) return null
413
+ result.set(key, row)
414
+ }
415
+ return result
416
+ }
417
+ const oldRows = index(previous)
418
+ const newRows = index(next)
419
+ if (!oldRows || !newRows) return null
420
+ let pending: DatasetRowChange[] = []
421
+ for (const [key, row] of oldRows) {
422
+ const newRow = newRows.get(key)
423
+ if (
424
+ !newRow ||
425
+ input.columns.some(
426
+ (column) => !isDeepStrictEqual(row[column] ?? null, newRow[column] ?? null)
427
+ )
428
+ ) {
429
+ pending.push({
430
+ before: selectColumns(row, input.columns),
431
+ after: newRow ? selectColumns(newRow, input.columns) : null,
432
+ })
433
+ }
434
+ }
435
+ for (const [key, row] of newRows) {
436
+ if (!oldRows.has(key))
437
+ pending.push({ before: null, after: selectColumns(row, input.columns) })
438
+ }
439
+ let closed = false
440
+ const changeCount = pending.length
441
+ return {
442
+ fromRowCount: previous.length,
443
+ toRowCount: next.length,
444
+ changeCount,
445
+ changes: (async function* () {
446
+ if (closed) throw new LakeStorageError("[LakeStorage] Change reader is closed.")
447
+ try {
448
+ for (let i = 0; i < changeCount; i++) {
449
+ input.signal?.throwIfAborted()
450
+ if (closed) throw new LakeStorageError("[LakeStorage] Change reader is closed.")
451
+ yield structuredClone(pending[i]!)
452
+ }
453
+ } finally {
454
+ closed = true
455
+ pending = []
456
+ }
457
+ })(),
458
+ async close() {
459
+ closed = true
460
+ pending = []
461
+ },
462
+ }
463
+ }
464
+
392
465
  async commitWrite(options: {
393
466
  write: BeginDatasetWriteInput
394
467
  rows: readonly DatasetRow[]
@@ -8,6 +8,7 @@ export type {
8
8
  } from "../datasets"
9
9
  export { assertDatasetDefinition } from "../datasets/validation"
10
10
  export type { JsonValue } from "../json"
11
+ export { resolveDatasetChangeColumns } from "./changes"
11
12
  export type {
12
13
  DatasetDefinitionUpdatePlan,
13
14
  DatasetMetadataUpdatePlan,
@@ -54,9 +55,11 @@ export type {
54
55
  BeginDatasetWriteInput,
55
56
  CommitDatasetWriteInput,
56
57
  DatasetCatalogState,
58
+ DatasetChanges,
57
59
  DatasetLatestVersionSummary,
58
60
  DatasetProducer,
59
61
  DatasetRow,
62
+ DatasetRowChange,
60
63
  DatasetVersion,
61
64
  DatasetVersionMode,
62
65
  DatasetVersionRef,
@@ -66,6 +69,7 @@ export type {
66
69
  LakeStandardId,
67
70
  LakeStorage,
68
71
  LakeWriteSession,
72
+ ReadDatasetChangesInput,
69
73
  ReadDatasetRowsInput,
70
74
  } from "./types"
71
75
  export { hasDatasetInputChanges } from "./version-inputs"
@@ -73,6 +73,39 @@ export interface ReadDatasetRowsInput {
73
73
  readonly offset?: number
74
74
  }
75
75
 
76
+ export interface ReadDatasetChangesInput {
77
+ readonly datasetId: string
78
+ readonly fromVersionId: string
79
+ readonly toVersionId: string
80
+ /** Stable, unique row identity in both versions; it need not be the dataset primary key. */
81
+ readonly keyColumns: readonly string[]
82
+ /** Complete inputs of the consumer's calculation, including key columns and timestamps. */
83
+ readonly columns: readonly string[]
84
+ readonly signal?: AbortSignal
85
+ }
86
+
87
+ export interface DatasetRowChange {
88
+ /** Null for an insertion. Otherwise includes every requested column. */
89
+ readonly before: DatasetRow | null
90
+ /** Null for a deletion. Otherwise includes every requested column. */
91
+ readonly after: DatasetRow | null
92
+ }
93
+
94
+ /** A complete, pinned change set. Call close in finally, including when iteration never starts. */
95
+ export interface DatasetChanges {
96
+ readonly fromRowCount: number
97
+ readonly toRowCount: number
98
+ /** Exact number of records the iterator must deliver before EOF. */
99
+ readonly changeCount: number
100
+ /**
101
+ * At most one record per key, with at least one non-null image. Includes every changed key;
102
+ * providers may conservatively include equal images (for example, reordered JSON fields).
103
+ * Order is unspecified. Consumers must exhaust the stream before publishing its result.
104
+ */
105
+ readonly changes: AsyncIterable<DatasetRowChange>
106
+ close(): Promise<void>
107
+ }
108
+
76
109
  export interface CommitDatasetWriteInput {
77
110
  /** Atomic version guard; null requires that no version exists, undefined leaves it unguarded. */
78
111
  readonly expectedLatestVersionId?: string | null
@@ -138,6 +171,13 @@ export interface LakeStorage {
138
171
  /** Explicit immutable-version reads have stable physical order and stable offset semantics. */
139
172
  readRows(input: ReadDatasetRowsInput): AsyncIterable<DatasetRow>
140
173
 
174
+ /**
175
+ * Optional comparison of two immutable versions. Null means the snapshots, schemas or unique
176
+ * keys cannot support a complete delta: use a full read. Operational errors must propagate.
177
+ * Iteration releases the provider connection between bounded pages, allowing other lake work.
178
+ */
179
+ readChanges?(input: ReadDatasetChangesInput): Promise<DatasetChanges | null>
180
+
141
181
  /** Release external resources. Optional: a provider that owns none omits it. */
142
182
  close?(): void | Promise<void>
143
183
  }
@@ -213,7 +213,7 @@ export class LoggingService {
213
213
  const lines = records.map(hydrateLogRecord)
214
214
  if (lines.length === 0) return
215
215
  try {
216
- handler(lines)
216
+ return handler(lines)
217
217
  } catch {
218
218
  // Subscriber failures never break broker delivery.
219
219
  }
@@ -51,6 +51,8 @@ export type {
51
51
  ProjectionRunFinishInput,
52
52
  ProjectionRunTerminalDecision,
53
53
  ProjectionSourceAssertion,
54
+ ProjectionSourceBase,
55
+ ProjectionSourceDeletion,
54
56
  ProjectionSourceEntry,
55
57
  ProjectionSourceRef,
56
58
  ProjectionSourceReplacement,
@@ -169,11 +169,24 @@ export interface ProjectionSourceEntry {
169
169
  readonly assertions: readonly ProjectionSourceAssertion[]
170
170
  }
171
171
 
172
+ export interface ProjectionSourceDeletion {
173
+ readonly root: ProjectionEntityRef
174
+ readonly deleted: true
175
+ }
176
+
177
+ /** Exact committed source state against which a complete delta was calculated. */
178
+ export interface ProjectionSourceBase {
179
+ readonly materializationId: string
180
+ readonly lastCommitId: string
181
+ }
182
+
172
183
  export interface ProjectionSourceReplacement {
173
184
  readonly source: ProjectionSourceRef
174
185
  readonly datasetVersion: PinnedDatasetVersion
175
186
  readonly execution: ProjectionExecution
176
- readonly entries: AsyncIterable<ProjectionSourceEntry>
187
+ /** Without a base, entries replace the whole source. With a base, unchanged roots are retained. */
188
+ readonly entries: AsyncIterable<ProjectionSourceEntry | ProjectionSourceDeletion>
189
+ readonly base?: ProjectionSourceBase
177
190
  readonly signal?: AbortSignal
178
191
  }
179
192
 
@@ -42,6 +42,8 @@ export type {
42
42
  ProjectionProtocolIdentity,
43
43
  ProjectionRunFinishInput,
44
44
  ProjectionSourceAssertion,
45
+ ProjectionSourceBase,
46
+ ProjectionSourceDeletion,
45
47
  ProjectionSourceEntry,
46
48
  ProjectionSourceRef,
47
49
  ProjectionSourceReplacement,
@@ -4,12 +4,14 @@ import type {
4
4
  OntologyEditCommit,
5
5
  ProjectionCommitResult,
6
6
  ProjectionRunFinishInput,
7
+ ProjectionSourceRef,
7
8
  ProjectionSourceReplacement,
8
9
  TelemetryAppend,
9
10
  TelemetryCommitResult,
10
11
  } from "../materialization/model"
11
12
  import type { OntologyRegistry } from "../ontology"
12
13
  import type { ProjectionRegistry } from "../projections/registry"
14
+ import type { OntologySourceRecord } from "../storage/ontology"
13
15
  import {
14
16
  createMaterializerContext,
15
17
  type MaterializerContext,
@@ -34,6 +36,7 @@ export interface OntologyMaterializerContract {
34
36
  commit(input: MaterializerCommand<OntologyEditCommit>): Promise<EditCommitResult>
35
37
  }
36
38
  readonly projections: {
39
+ getActive(source: ProjectionSourceRef): Promise<OntologySourceRecord | null>
37
40
  replace(
38
41
  input: MaterializerCommand<ProjectionSourceReplacement>
39
42
  ): Promise<ProjectionCommitResult>
@@ -50,6 +53,7 @@ export interface BoundOntologyMaterializer {
50
53
  commit(input: OntologyEditCommit): Promise<EditCommitResult>
51
54
  }
52
55
  readonly projections: {
56
+ getActive(source: ProjectionSourceRef): Promise<OntologySourceRecord | null>
53
57
  replace(input: ProjectionSourceReplacement): Promise<ProjectionCommitResult>
54
58
  finishRun(input: ProjectionRunFinishInput): Promise<void>
55
59
  }
@@ -64,6 +68,11 @@ export class OntologyMaterializer implements OntologyMaterializerContract {
64
68
  commitEdits(this.context, command),
65
69
  }
66
70
  readonly projections = {
71
+ getActive: (source: ProjectionSourceRef) =>
72
+ this.context.storage.ontology.sources.getActive({
73
+ projectId: this.context.projectId,
74
+ source,
75
+ }),
67
76
  replace: (command: MaterializerCommand<ProjectionSourceReplacement>) =>
68
77
  replaceProjection(this.context, command),
69
78
  finishRun: (command: MaterializerCommand<ProjectionRunFinishInput>) =>
@@ -80,6 +89,7 @@ export class OntologyMaterializer implements OntologyMaterializerContract {
80
89
  commit: (input: OntologyEditCommit) => this.edits.commit({ scope, input }),
81
90
  }),
82
91
  projections: Object.freeze({
92
+ getActive: (source: ProjectionSourceRef) => this.projections.getActive(source),
83
93
  replace: (input: ProjectionSourceReplacement) => this.projections.replace({ scope, input }),
84
94
  finishRun: (input: ProjectionRunFinishInput) =>
85
95
  this.projections.finishRun({ scope, input }),
@@ -1,5 +1,5 @@
1
1
  import { MaterializationValidationError } from "../../materialization/errors"
2
- import type { ProjectionSourceEntry } from "../../materialization/model"
2
+ import type { ProjectionEntityRef, ProjectionSourceEntry } from "../../materialization/model"
3
3
  import { linkOwnershipKey } from "../../materialization/refs"
4
4
  import type { OntologyRegistry } from "../../ontology"
5
5
  import type { ProjectionRegistry } from "../../projections/registry"
@@ -30,6 +30,24 @@ export function createProjectionEntryValidator(
30
30
  return (entry) => validateProjectionEntry(ontology, resolved, policy, entry)
31
31
  }
32
32
 
33
+ export function validateProjectionRootRef(
34
+ resolved: ResolvedSourceProjection,
35
+ root: ProjectionEntityRef
36
+ ): void {
37
+ const definition = resolved.definition
38
+ const owned =
39
+ definition._tag === "ObjectProjectionDefinition"
40
+ ? root.kind === "object" && root.ref.objectTypeId === definition.objectTypeId
41
+ : root.kind === "link" &&
42
+ root.ref.source.objectTypeId === definition.sourceObjectTypeId &&
43
+ root.ref.target.objectTypeId === definition.targetObjectTypeId &&
44
+ root.ref.linkId === definition.linkId
45
+ if (!owned)
46
+ throw new MaterializationValidationError(
47
+ "Projection root is outside its owned type or link scope."
48
+ )
49
+ }
50
+
33
51
  function createValidationPolicy(resolved: ResolvedSourceProjection): ProjectionValidationPolicy {
34
52
  const ownedProperties = new Map(
35
53
  resolved.ownership.objects.map((object) => [object.objectTypeId, new Set(object.propertyIds)])