@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.
- package/dist/actions/index.js +8 -8
- package/dist/agents/index.js +16 -15
- package/dist/agents/index.js.map +1 -1
- package/dist/bootstrap/index.js +20 -19
- package/dist/bootstrap/index.js.map +1 -1
- package/dist/broker/in-memory.d.ts +8 -2
- package/dist/broker/in-memory.d.ts.map +1 -1
- package/dist/broker/index.d.ts +4 -2
- package/dist/broker/index.d.ts.map +1 -1
- package/dist/broker/index.js +7 -3
- package/dist/broker/index.js.map +1 -1
- package/dist/broker/retention.d.ts +4 -0
- package/dist/broker/retention.d.ts.map +1 -0
- package/dist/broker/subscriber.d.ts +10 -0
- package/dist/broker/subscriber.d.ts.map +1 -0
- package/dist/broker/types.d.ts +14 -1
- package/dist/broker/types.d.ts.map +1 -1
- package/dist/{chunk-k3cqkkn2.js → chunk-1824gssz.js} +30 -65
- package/dist/chunk-1824gssz.js.map +12 -0
- package/dist/{chunk-h1gfhhbh.js → chunk-28ja2cjs.js} +1 -1
- package/dist/chunk-2c4j899j.js.map +1 -1
- package/dist/{chunk-ppytyr6w.js → chunk-358caqf5.js} +4 -4
- package/dist/chunk-358caqf5.js.map +12 -0
- package/dist/{chunk-kxwa347f.js → chunk-3qms4nax.js} +2 -2
- package/dist/{chunk-kxwa347f.js.map → chunk-3qms4nax.js.map} +3 -3
- package/dist/{chunk-emyyk7dx.js → chunk-5c7ga8zz.js} +22 -145
- package/dist/chunk-5c7ga8zz.js.map +12 -0
- package/dist/{chunk-hpr1r6av.js → chunk-5p1v802y.js} +46 -39
- package/dist/{chunk-hpr1r6av.js.map → chunk-5p1v802y.js.map} +4 -4
- package/dist/{chunk-zcnq6105.js → chunk-85cd7e8c.js} +2 -1
- package/dist/chunk-85cd7e8c.js.map +10 -0
- package/dist/{chunk-1enhpacz.js → chunk-976rxdpx.js} +5 -5
- package/dist/{chunk-3f7veca0.js → chunk-9x8dybsb.js} +3 -3
- package/dist/chunk-9x8dybsb.js.map +10 -0
- package/dist/{chunk-r29fxdhn.js → chunk-aa6z4a19.js} +160 -44
- package/dist/chunk-aa6z4a19.js.map +12 -0
- package/dist/{chunk-r4tfx1sf.js → chunk-agbbygvh.js} +4 -4
- package/dist/{chunk-jjy0fhrn.js → chunk-b0ec55a1.js} +20 -3
- package/dist/chunk-b0ec55a1.js.map +12 -0
- package/dist/{chunk-xfbydgzy.js → chunk-bn138h9w.js} +7 -8
- package/dist/{chunk-xfbydgzy.js.map → chunk-bn138h9w.js.map} +3 -3
- package/dist/{chunk-an5tt9y0.js → chunk-ftv470j8.js} +42 -4
- package/dist/{chunk-an5tt9y0.js.map → chunk-ftv470j8.js.map} +7 -7
- package/dist/{chunk-0w35wavj.js → chunk-g7knyczr.js} +1 -1
- package/dist/{chunk-x57ycxq0.js → chunk-gqh45d2n.js} +42 -3
- package/dist/{chunk-x57ycxq0.js.map → chunk-gqh45d2n.js.map} +3 -3
- package/dist/{chunk-0rdrecsb.js → chunk-kfee1h52.js} +116 -32
- package/dist/chunk-kfee1h52.js.map +13 -0
- package/dist/{chunk-ypbtykv6.js → chunk-nmbxcaqx.js} +1 -1
- package/dist/{chunk-3tgwrcyh.js → chunk-nt99sxd6.js} +2 -2
- package/dist/{chunk-3tgwrcyh.js.map → chunk-nt99sxd6.js.map} +3 -3
- package/dist/{chunk-vf691g1r.js → chunk-p5vcsw9c.js} +2 -2
- package/dist/{chunk-mjbk2djy.js → chunk-p6a4npdx.js} +211 -106
- package/dist/{chunk-mjbk2djy.js.map → chunk-p6a4npdx.js.map} +9 -8
- package/dist/{chunk-3frsnm7x.js → chunk-qma80ych.js} +4 -6
- package/dist/chunk-qma80ych.js.map +11 -0
- package/dist/{chunk-qhcpj9wc.js → chunk-qyeptdyv.js} +96 -2
- package/dist/chunk-qyeptdyv.js.map +15 -0
- package/dist/{chunk-52hbv0ak.js → chunk-r7fj830t.js} +101 -48
- package/dist/chunk-r7fj830t.js.map +36 -0
- package/dist/{chunk-xyr8kppc.js → chunk-rtk71jzp.js} +1 -1
- package/dist/{chunk-m3z3x9v0.js → chunk-t1f4f1ve.js} +1 -1
- package/dist/chunk-v2vcg0w6.js +151 -0
- package/dist/chunk-v2vcg0w6.js.map +11 -0
- package/dist/connectors/connections/authorizations.d.ts.map +1 -1
- package/dist/connectors/connections/contracts.d.ts +3 -0
- package/dist/connectors/connections/contracts.d.ts.map +1 -1
- package/dist/connectors/connections/internal.js +1 -1
- package/dist/connectors/connections/request.d.ts +3 -1
- package/dist/connectors/connections/request.d.ts.map +1 -1
- package/dist/connectors/connections/runs.d.ts.map +1 -1
- package/dist/connectors/connections/tokens.d.ts.map +1 -1
- package/dist/connectors/connections/validation.d.ts +5 -1
- package/dist/connectors/connections/validation.d.ts.map +1 -1
- package/dist/connectors/types.d.ts +19 -4
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/events/execution.d.ts +1 -1
- package/dist/events/execution.d.ts.map +1 -1
- package/dist/events/index.js +1 -1
- package/dist/events/service.d.ts +2 -2
- package/dist/events/service.d.ts.map +1 -1
- package/dist/execution/agent.js +16 -15
- package/dist/execution/agent.js.map +1 -1
- package/dist/execution/primitive.js +16 -15
- package/dist/execution/primitive.js.map +2 -2
- package/dist/execution/request.js +16 -15
- package/dist/execution/request.js.map +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -28
- package/dist/index.js.map +1 -1
- package/dist/lake-storage/changes.d.ts +5 -0
- package/dist/lake-storage/changes.d.ts.map +1 -0
- package/dist/lake-storage/in-memory.d.ts +2 -1
- package/dist/lake-storage/in-memory.d.ts.map +1 -1
- package/dist/lake-storage/index.d.ts +2 -1
- package/dist/lake-storage/index.d.ts.map +1 -1
- package/dist/lake-storage/index.js +4 -2
- package/dist/lake-storage/index.js.map +1 -1
- package/dist/lake-storage/types.d.ts +36 -0
- package/dist/lake-storage/types.d.ts.map +1 -1
- package/dist/logging/internal.js +1 -1
- package/dist/materialization/index.d.ts +1 -1
- package/dist/materialization/index.d.ts.map +1 -1
- package/dist/materialization/model.d.ts +12 -1
- package/dist/materialization/model.d.ts.map +1 -1
- package/dist/materializer/index.d.ts +1 -1
- package/dist/materializer/index.d.ts.map +1 -1
- package/dist/materializer/index.js +5 -5
- package/dist/materializer/materializer.d.ts +5 -1
- package/dist/materializer/materializer.d.ts.map +1 -1
- package/dist/materializer/projections/entry-validator.d.ts +2 -1
- package/dist/materializer/projections/entry-validator.d.ts.map +1 -1
- package/dist/materializer/projections/replace.d.ts.map +1 -1
- package/dist/materializer/projections/source-materialization.d.ts +4 -3
- package/dist/materializer/projections/source-materialization.d.ts.map +1 -1
- package/dist/materializer/shared/normalize.d.ts +2 -1
- package/dist/materializer/shared/normalize.d.ts.map +1 -1
- package/dist/models/execution/index.js +5 -5
- package/dist/objects/index.js +9 -9
- package/dist/objects/query/index.js +5 -5
- package/dist/pipelines/index.js +6 -6
- package/dist/pipelines/run-dispatch.d.ts.map +1 -1
- package/dist/projections/internal.js +6 -8
- package/dist/projections/internal.js.map +1 -1
- package/dist/projections/run-dispatch.d.ts.map +1 -1
- package/dist/queues/in-memory.d.ts +7 -3
- package/dist/queues/in-memory.d.ts.map +1 -1
- package/dist/queues/index.js +1 -1
- package/dist/queues/indexed-heap.d.ts +15 -0
- package/dist/queues/indexed-heap.d.ts.map +1 -0
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/internal.js +1 -1
- package/dist/runtime/ontology-mutations.d.ts +3 -1
- package/dist/runtime/ontology-mutations.d.ts.map +1 -1
- package/dist/shares/internal.js +4 -4
- package/dist/storage/connector-connections/in-memory.d.ts.map +1 -1
- package/dist/storage/connector-connections/provider/runs.d.ts.map +1 -1
- package/dist/storage/connector-connections/provider.js +2 -2
- package/dist/storage/connector-connections/provider.js.map +3 -3
- package/dist/storage/connector-connections/types.d.ts +3 -2
- package/dist/storage/connector-connections/types.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +4 -4
- package/dist/storage/internal.js +1 -1
- package/dist/storage/ontology/in-memory/materializations-state.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/materializations.d.ts +1 -1
- package/dist/storage/ontology/in-memory/materializations.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/shared-state.d.ts +15 -2
- package/dist/storage/ontology/in-memory/shared-state.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/source-roots.d.ts +6 -0
- package/dist/storage/ontology/in-memory/source-roots.d.ts.map +1 -0
- package/dist/storage/ontology/in-memory/sources.d.ts.map +1 -1
- package/dist/storage/ontology/index.d.ts +1 -1
- package/dist/storage/ontology/index.d.ts.map +1 -1
- package/dist/storage/ontology/provider.d.ts +16 -0
- package/dist/storage/ontology/provider.d.ts.map +1 -1
- package/dist/storage/ontology/provider.js +4 -2
- package/dist/storage/ontology/provider.js.map +1 -1
- package/dist/storage/ontology/sources.d.ts +13 -4
- package/dist/storage/ontology/sources.d.ts.map +1 -1
- package/dist/storage/projection-runs/lifecycle.d.ts.map +1 -1
- package/dist/storage/projection-runs/provider.js +2 -2
- package/dist/storage/projection-runs/types.d.ts +2 -0
- package/dist/storage/projection-runs/types.d.ts.map +1 -1
- package/dist/syncs/index.js +8 -7
- package/dist/syncs/index.js.map +1 -1
- package/dist/syncs/run-dispatch.d.ts.map +1 -1
- package/dist/testing/broker-contract.d.ts.map +1 -1
- package/dist/testing/connector-connection-storage-contract.d.ts.map +1 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1459 -951
- package/dist/testing/index.js.map +9 -8
- package/dist/testing/lake-changes-contract.d.ts +6 -0
- package/dist/testing/lake-changes-contract.d.ts.map +1 -0
- package/dist/testing/materializer-storage-contract.d.ts.map +1 -1
- package/dist/testing/ontology-storage-contract.d.ts.map +1 -1
- package/dist/testing/projection-run-storage-contract.d.ts.map +1 -1
- package/dist/webhooks/connections.d.ts +6 -0
- package/dist/webhooks/connections.d.ts.map +1 -0
- package/dist/webhooks/index.d.ts +1 -1
- package/dist/webhooks/index.d.ts.map +1 -1
- package/dist/webhooks/internal.d.ts +2 -0
- package/dist/webhooks/internal.d.ts.map +1 -1
- package/dist/webhooks/internal.js +54 -9
- package/dist/webhooks/internal.js.map +4 -3
- package/dist/webhooks/registry.d.ts +1 -1
- package/dist/webhooks/registry.d.ts.map +1 -1
- package/dist/webhooks/types.d.ts +15 -4
- package/dist/webhooks/types.d.ts.map +1 -1
- package/dist/workflows/index.js +17 -16
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/run-dispatch.d.ts +3 -1
- package/dist/workflows/run-dispatch.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/broker/in-memory.ts +104 -39
- package/src/broker/index.ts +4 -1
- package/src/broker/retention.ts +28 -0
- package/src/broker/subscriber.ts +21 -0
- package/src/broker/types.ts +15 -1
- package/src/connectors/builders.ts +0 -6
- package/src/connectors/connections/authorizations.ts +11 -5
- package/src/connectors/connections/contracts.ts +3 -0
- package/src/connectors/connections/request.ts +56 -22
- package/src/connectors/connections/runs.ts +1 -0
- package/src/connectors/connections/tokens.ts +4 -0
- package/src/connectors/connections/validation.ts +91 -5
- package/src/connectors/types.ts +19 -4
- package/src/events/execution.ts +2 -2
- package/src/events/service.ts +3 -3
- package/src/index.ts +10 -2
- package/src/lake-storage/changes.ts +43 -0
- package/src/lake-storage/in-memory.ts +73 -0
- package/src/lake-storage/index.ts +4 -0
- package/src/lake-storage/types.ts +40 -0
- package/src/logging/service.ts +1 -1
- package/src/materialization/index.ts +2 -0
- package/src/materialization/model.ts +14 -1
- package/src/materializer/index.ts +2 -0
- package/src/materializer/materializer.ts +10 -0
- package/src/materializer/projections/entry-validator.ts +19 -1
- package/src/materializer/projections/replace.ts +36 -1
- package/src/materializer/projections/source-materialization.ts +26 -6
- package/src/materializer/shared/normalize.ts +1 -1
- package/src/pipelines/run-dispatch.ts +21 -1
- package/src/projections/run-dispatch.ts +30 -68
- package/src/queues/in-memory.ts +105 -62
- package/src/queues/indexed-heap.ts +74 -0
- package/src/runtime/host.ts +0 -14
- package/src/runtime/ontology-mutations.ts +4 -0
- package/src/storage/connector-connections/in-memory.ts +3 -1
- package/src/storage/connector-connections/provider/runs.ts +3 -1
- package/src/storage/connector-connections/types.ts +3 -2
- package/src/storage/index.ts +1 -0
- package/src/storage/ontology/in-memory/materializations-state.ts +5 -18
- package/src/storage/ontology/in-memory/materializations.ts +73 -92
- package/src/storage/ontology/in-memory/shared-state.ts +26 -1
- package/src/storage/ontology/in-memory/source-roots.ts +123 -0
- package/src/storage/ontology/in-memory/sources.ts +78 -23
- package/src/storage/ontology/index.ts +1 -0
- package/src/storage/ontology/provider.ts +70 -0
- package/src/storage/ontology/sources.ts +14 -3
- package/src/storage/projection-runs/lifecycle.ts +5 -2
- package/src/storage/projection-runs/types.ts +4 -3
- package/src/syncs/run-dispatch.ts +19 -1
- package/src/testing/broker-contract.ts +95 -0
- package/src/testing/connector-connection-storage-contract.ts +18 -1
- package/src/testing/index.ts +1 -0
- package/src/testing/lake-changes-contract.ts +240 -0
- package/src/testing/materializer-storage-contract.ts +172 -1
- package/src/testing/ontology-storage-contract.ts +7 -0
- package/src/testing/projection-run-storage-contract.ts +34 -0
- package/src/webhooks/connections.ts +56 -0
- package/src/webhooks/index.ts +2 -0
- package/src/webhooks/internal.ts +2 -0
- package/src/webhooks/registry.ts +3 -4
- package/src/webhooks/types.ts +17 -4
- package/src/workflows/run-dispatch.ts +42 -29
- package/dist/chunk-0rdrecsb.js.map +0 -11
- package/dist/chunk-3f7veca0.js.map +0 -10
- package/dist/chunk-3frsnm7x.js.map +0 -11
- package/dist/chunk-52hbv0ak.js.map +0 -36
- package/dist/chunk-emyyk7dx.js.map +0 -14
- package/dist/chunk-jjy0fhrn.js.map +0 -12
- package/dist/chunk-k3cqkkn2.js.map +0 -12
- package/dist/chunk-ppytyr6w.js.map +0 -12
- package/dist/chunk-qhcpj9wc.js.map +0 -14
- package/dist/chunk-r29fxdhn.js.map +0 -11
- package/dist/chunk-zcnq6105.js.map +0 -10
- /package/dist/{chunk-h1gfhhbh.js.map → chunk-28ja2cjs.js.map} +0 -0
- /package/dist/{chunk-1enhpacz.js.map → chunk-976rxdpx.js.map} +0 -0
- /package/dist/{chunk-r4tfx1sf.js.map → chunk-agbbygvh.js.map} +0 -0
- /package/dist/{chunk-0w35wavj.js.map → chunk-g7knyczr.js.map} +0 -0
- /package/dist/{chunk-ypbtykv6.js.map → chunk-nmbxcaqx.js.map} +0 -0
- /package/dist/{chunk-vf691g1r.js.map → chunk-p5vcsw9c.js.map} +0 -0
- /package/dist/{chunk-xyr8kppc.js.map → chunk-rtk71jzp.js.map} +0 -0
- /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
|
|
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
|
-
|
|
247
|
-
|
|
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
|
-
|
|
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,
|
package/src/connectors/types.ts
CHANGED
|
@@ -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
|
-
|
|
79
|
-
readonly
|
|
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
|
-
|
|
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?:
|
|
149
|
+
readonly webhooks?: readonly WebhookDefinition<unknown, TClient>[]
|
|
135
150
|
discoverAccounts(
|
|
136
151
|
context: ConnectorContext,
|
|
137
152
|
credentials: ConnectorOAuthCredentials
|
package/src/events/execution.ts
CHANGED
|
@@ -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[]) =>
|
|
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
|
}
|
package/src/events/service.ts
CHANGED
|
@@ -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[]) =>
|
|
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[]) =>
|
|
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 {
|
|
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
|
}
|
package/src/logging/service.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
|
@@ -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)])
|