@sixb/core 0.1.13 → 0.1.15
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 +22 -22
- package/dist/agents/index.js +34 -34
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/streams/index.js +2 -2
- package/dist/agents/streams/internal.js +2 -2
- package/dist/auth/errors.d.ts +12 -0
- package/dist/auth/errors.d.ts.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +7 -5
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/runtime.d.ts.map +1 -1
- package/dist/auth/strategy.d.ts +2 -0
- package/dist/auth/strategy.d.ts.map +1 -1
- package/dist/auth/strategy.js +4 -2
- package/dist/auth/strategy.js.map +1 -1
- package/dist/auth/types.d.ts +8 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/authorization/index.js +2 -2
- package/dist/bootstrap/index.js +37 -37
- package/dist/{chunk-693f3zn8.js → chunk-1jvq82rp.js} +3 -3
- package/dist/{chunk-0321g649.js → chunk-2jntasmh.js} +13 -2
- package/dist/{chunk-0321g649.js.map → chunk-2jntasmh.js.map} +3 -3
- package/dist/{chunk-cvrbhvtr.js → chunk-2tz5vnvz.js} +28 -28
- package/dist/{chunk-cvrbhvtr.js.map → chunk-2tz5vnvz.js.map} +1 -1
- package/dist/{chunk-898y24dd.js → chunk-3c90jvfj.js} +2 -2
- package/dist/{chunk-vn3yh259.js → chunk-4gmng3vf.js} +1 -1
- package/dist/{chunk-91wthfwe.js → chunk-4jph1t3v.js} +7 -7
- package/dist/{chunk-znw0zvy4.js → chunk-4wn7bxxa.js} +25 -7
- package/dist/chunk-4wn7bxxa.js.map +12 -0
- package/dist/{chunk-3qpcrcj8.js → chunk-6aawea28.js} +2 -4
- package/dist/chunk-6aawea28.js.map +10 -0
- package/dist/{chunk-rzkr70gr.js → chunk-6fsr7y93.js} +5 -1
- package/dist/{chunk-rzkr70gr.js.map → chunk-6fsr7y93.js.map} +3 -3
- package/dist/{chunk-y487ywrv.js → chunk-6q7c68yx.js} +4 -4
- package/dist/{chunk-ypj2wqtj.js → chunk-70jmg2zx.js} +2 -2
- package/dist/{chunk-s24qbp32.js → chunk-7jc21943.js} +3 -3
- package/dist/{chunk-rzcjp5m2.js → chunk-8fjmqjyx.js} +1 -1
- package/dist/{chunk-m8zkvep1.js → chunk-8w27xf9e.js} +1 -1
- package/dist/{chunk-z9h698s8.js → chunk-a4g5yjm0.js} +1 -1
- package/dist/{chunk-gmt22nxx.js → chunk-adbve9zt.js} +1 -1
- package/dist/{chunk-mf6hwpzh.js → chunk-dqkd3kdj.js} +41 -23
- package/dist/{chunk-mf6hwpzh.js.map → chunk-dqkd3kdj.js.map} +3 -3
- package/dist/{chunk-gbcrz0gr.js → chunk-ex8s8wsk.js} +6 -6
- package/dist/{chunk-4tnrzqqh.js → chunk-fj1dvp37.js} +1 -1
- package/dist/{chunk-gc226ten.js → chunk-fjw417jf.js} +127 -69
- package/dist/{chunk-gc226ten.js.map → chunk-fjw417jf.js.map} +4 -4
- package/dist/{chunk-ht4a7mjg.js → chunk-g4rbm4a6.js} +42 -8
- package/dist/{chunk-ht4a7mjg.js.map → chunk-g4rbm4a6.js.map} +3 -3
- package/dist/{chunk-qma80ych.js → chunk-hn2r7z41.js} +3 -2
- package/dist/chunk-hn2r7z41.js.map +11 -0
- package/dist/{chunk-bdhfn0ww.js → chunk-kkqwzy6h.js} +1 -1
- package/dist/{chunk-bep5rrdq.js → chunk-mmac1mp6.js} +2 -2
- package/dist/{chunk-w9bp61e7.js → chunk-n5t4mq6z.js} +5 -5
- package/dist/{chunk-n33e8dz8.js → chunk-p0nbkys4.js} +1 -1
- package/dist/{chunk-04h4t2b5.js → chunk-py2dw8cw.js} +3 -3
- package/dist/{chunk-jhyc777j.js → chunk-q3grgzp1.js} +2 -2
- package/dist/{chunk-9pfw47qx.js → chunk-qne45xqq.js} +53 -48
- package/dist/{chunk-9pfw47qx.js.map → chunk-qne45xqq.js.map} +7 -7
- package/dist/{chunk-cbn96bve.js → chunk-r5sc1d2b.js} +2 -2
- package/dist/{chunk-y6vnvayj.js → chunk-vjcd8d27.js} +1 -1
- package/dist/{chunk-jmwy25mf.js → chunk-wd34wnsy.js} +6 -6
- package/dist/{chunk-63fp9x3q.js → chunk-wem5vqjw.js} +7 -7
- package/dist/{chunk-darm7eeg.js → chunk-xr7y35yj.js} +3 -3
- package/dist/{chunk-a69fkrtr.js → chunk-y0zmae99.js} +5 -5
- package/dist/{chunk-xgptywp0.js → chunk-y2aq6ag9.js} +1 -1
- package/dist/{chunk-2hbhn1ff.js → chunk-yfppyaty.js} +1 -1
- package/dist/{chunk-rqtdtm43.js → chunk-z708gemf.js} +129 -70
- package/dist/{chunk-rqtdtm43.js.map → chunk-z708gemf.js.map} +5 -5
- package/dist/deploy/define.d.ts +4 -0
- package/dist/deploy/define.d.ts.map +1 -0
- package/dist/deploy/index.d.ts +3 -0
- package/dist/deploy/index.d.ts.map +1 -0
- package/dist/deploy/index.js +10 -0
- package/dist/deploy/index.js.map +10 -0
- package/dist/deploy/internal.d.ts +3 -0
- package/dist/deploy/internal.d.ts.map +1 -0
- package/dist/deploy/internal.js +244 -0
- package/dist/deploy/internal.js.map +11 -0
- package/dist/deploy/services.d.ts +8 -0
- package/dist/deploy/services.d.ts.map +1 -0
- package/dist/deploy/types.d.ts +309 -0
- package/dist/deploy/types.d.ts.map +1 -0
- package/dist/deploy/validation.d.ts +7 -0
- package/dist/deploy/validation.d.ts.map +1 -0
- package/dist/error-reporting/internal.js +3 -3
- package/dist/errors/catalog.d.ts +4 -0
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/internal.js +1 -1
- package/dist/events/index.js +3 -3
- package/dist/execution/agent.js +33 -33
- package/dist/execution/primitive.js +29 -29
- package/dist/execution/request.js +31 -31
- package/dist/execution/request.js.map +1 -1
- package/dist/index.js +49 -49
- package/dist/index.js.map +1 -1
- package/dist/maintenance/ontology-maintenance.d.ts +2 -0
- package/dist/maintenance/ontology-maintenance.d.ts.map +1 -1
- package/dist/maintenance/types.d.ts +8 -2
- package/dist/maintenance/types.d.ts.map +1 -1
- package/dist/materialization/dataset-watermark.d.ts +6 -1
- package/dist/materialization/dataset-watermark.d.ts.map +1 -1
- package/dist/materialization/index.js +1 -1
- package/dist/materialization/model.d.ts +6 -0
- package/dist/materialization/model.d.ts.map +1 -1
- package/dist/materializer/index.js +18 -18
- package/dist/materializer/index.js.map +1 -1
- package/dist/materializer/projections/finish-run.d.ts.map +1 -1
- package/dist/materializer/projections/replace.d.ts.map +1 -1
- package/dist/materializer/projections/source-materialization.d.ts +6 -2
- package/dist/materializer/projections/source-materialization.d.ts.map +1 -1
- package/dist/models/execution/index.js +14 -14
- package/dist/objects/index.js +21 -21
- package/dist/objects/query/index.js +15 -15
- package/dist/pipelines/index.js +19 -19
- package/dist/projections/internal.js +16 -16
- package/dist/projections/types.d.ts +1 -1
- package/dist/projections/types.d.ts.map +1 -1
- package/dist/queues/in-memory.d.ts +1 -1
- package/dist/queues/in-memory.d.ts.map +1 -1
- package/dist/runtime/internal.js +19 -19
- package/dist/shares/internal.js +14 -14
- package/dist/shares/internal.js.map +1 -1
- package/dist/storage/action-runs/failure.js +2 -2
- package/dist/storage/ai-limits/enforcement.js +2 -2
- package/dist/storage/auth/errors.d.ts +1 -1
- package/dist/storage/auth/errors.d.ts.map +1 -1
- package/dist/storage/auth/in-memory/auth-storage.d.ts +2 -1
- package/dist/storage/auth/in-memory/auth-storage.d.ts.map +1 -1
- package/dist/storage/auth/types.d.ts +15 -7
- package/dist/storage/auth/types.d.ts.map +1 -1
- package/dist/storage/connector-connections/provider.js +1 -1
- package/dist/storage/file-upload-sessions/provider.js +5 -5
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +29 -29
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/internal.js +1 -1
- package/dist/storage/ontology/in-memory/sources.d.ts +6 -3
- 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 -3
- package/dist/storage/ontology/provider.d.ts.map +1 -1
- package/dist/storage/ontology/provider.js +8 -4
- package/dist/storage/ontology/provider.js.map +1 -1
- package/dist/storage/ontology/sources.d.ts +36 -8
- package/dist/storage/ontology/sources.d.ts.map +1 -1
- package/dist/storage/projection-runs/in-memory.d.ts +3 -1
- package/dist/storage/projection-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/projection-runs/index.d.ts +1 -1
- package/dist/storage/projection-runs/index.d.ts.map +1 -1
- package/dist/storage/projection-runs/lifecycle.d.ts +12 -0
- package/dist/storage/projection-runs/lifecycle.d.ts.map +1 -1
- package/dist/storage/projection-runs/provider.js +10 -4
- package/dist/storage/projection-runs/provider.js.map +1 -1
- package/dist/storage/projection-runs/types.d.ts +32 -1
- package/dist/storage/projection-runs/types.d.ts.map +1 -1
- package/dist/storage/workflow-runs/provider.js +5 -5
- package/dist/syncs/index.js +20 -20
- package/dist/testing/auth-storage-contract.d.ts.map +1 -1
- package/dist/testing/index.js +381 -171
- package/dist/testing/index.js.map +5 -5
- 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/internal.js +15 -15
- package/dist/webhooks/internal.js.map +1 -1
- package/dist/workers/index.js +1 -1
- package/dist/workflows/index.js +34 -34
- package/package.json +13 -1
- package/src/auth/errors.ts +23 -0
- package/src/auth/index.ts +1 -1
- package/src/auth/runtime.ts +56 -22
- package/src/auth/strategy.ts +2 -0
- package/src/auth/types.ts +8 -0
- package/src/deploy/define.ts +6 -0
- package/src/deploy/index.ts +41 -0
- package/src/deploy/internal.ts +7 -0
- package/src/deploy/services.ts +51 -0
- package/src/deploy/types.ts +338 -0
- package/src/deploy/validation.ts +261 -0
- package/src/errors/catalog.ts +4 -0
- package/src/maintenance/ontology-maintenance.ts +32 -2
- package/src/maintenance/types.ts +8 -2
- package/src/materialization/dataset-watermark.ts +6 -4
- package/src/materialization/model.ts +7 -0
- package/src/materializer/README.md +26 -6
- package/src/materializer/projections/finish-run.ts +129 -26
- package/src/materializer/projections/replace.ts +67 -49
- package/src/materializer/projections/source-materialization.ts +11 -6
- package/src/projections/types.ts +1 -0
- package/src/storage/auth/errors.ts +0 -1
- package/src/storage/auth/in-memory/auth-storage.ts +44 -65
- package/src/storage/auth/types.ts +15 -7
- package/src/storage/index.ts +6 -1
- package/src/storage/ontology/in-memory/sources.ts +119 -55
- package/src/storage/ontology/index.ts +4 -1
- package/src/storage/ontology/provider.ts +64 -7
- package/src/storage/ontology/sources.ts +42 -8
- package/src/storage/projection-runs/in-memory.ts +41 -2
- package/src/storage/projection-runs/index.ts +2 -0
- package/src/storage/projection-runs/lifecycle.ts +61 -4
- package/src/storage/projection-runs/types.ts +41 -1
- package/src/testing/auth-storage-contract.ts +104 -130
- package/src/testing/ontology-storage-contract.ts +141 -15
- package/src/testing/projection-run-storage-contract.ts +174 -1
- package/dist/chunk-3qpcrcj8.js.map +0 -10
- package/dist/chunk-qma80ych.js.map +0 -11
- package/dist/chunk-znw0zvy4.js.map +0 -12
- /package/dist/{chunk-693f3zn8.js.map → chunk-1jvq82rp.js.map} +0 -0
- /package/dist/{chunk-898y24dd.js.map → chunk-3c90jvfj.js.map} +0 -0
- /package/dist/{chunk-vn3yh259.js.map → chunk-4gmng3vf.js.map} +0 -0
- /package/dist/{chunk-91wthfwe.js.map → chunk-4jph1t3v.js.map} +0 -0
- /package/dist/{chunk-y487ywrv.js.map → chunk-6q7c68yx.js.map} +0 -0
- /package/dist/{chunk-ypj2wqtj.js.map → chunk-70jmg2zx.js.map} +0 -0
- /package/dist/{chunk-s24qbp32.js.map → chunk-7jc21943.js.map} +0 -0
- /package/dist/{chunk-rzcjp5m2.js.map → chunk-8fjmqjyx.js.map} +0 -0
- /package/dist/{chunk-m8zkvep1.js.map → chunk-8w27xf9e.js.map} +0 -0
- /package/dist/{chunk-z9h698s8.js.map → chunk-a4g5yjm0.js.map} +0 -0
- /package/dist/{chunk-gmt22nxx.js.map → chunk-adbve9zt.js.map} +0 -0
- /package/dist/{chunk-gbcrz0gr.js.map → chunk-ex8s8wsk.js.map} +0 -0
- /package/dist/{chunk-4tnrzqqh.js.map → chunk-fj1dvp37.js.map} +0 -0
- /package/dist/{chunk-bdhfn0ww.js.map → chunk-kkqwzy6h.js.map} +0 -0
- /package/dist/{chunk-bep5rrdq.js.map → chunk-mmac1mp6.js.map} +0 -0
- /package/dist/{chunk-w9bp61e7.js.map → chunk-n5t4mq6z.js.map} +0 -0
- /package/dist/{chunk-n33e8dz8.js.map → chunk-p0nbkys4.js.map} +0 -0
- /package/dist/{chunk-04h4t2b5.js.map → chunk-py2dw8cw.js.map} +0 -0
- /package/dist/{chunk-jhyc777j.js.map → chunk-q3grgzp1.js.map} +0 -0
- /package/dist/{chunk-cbn96bve.js.map → chunk-r5sc1d2b.js.map} +0 -0
- /package/dist/{chunk-y6vnvayj.js.map → chunk-vjcd8d27.js.map} +0 -0
- /package/dist/{chunk-jmwy25mf.js.map → chunk-wd34wnsy.js.map} +0 -0
- /package/dist/{chunk-63fp9x3q.js.map → chunk-wem5vqjw.js.map} +0 -0
- /package/dist/{chunk-darm7eeg.js.map → chunk-xr7y35yj.js.map} +0 -0
- /package/dist/{chunk-a69fkrtr.js.map → chunk-y0zmae99.js.map} +0 -0
- /package/dist/{chunk-xgptywp0.js.map → chunk-y2aq6ag9.js.map} +0 -0
- /package/dist/{chunk-2hbhn1ff.js.map → chunk-yfppyaty.js.map} +0 -0
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
CompleteOidcSignInInput,
|
|
8
8
|
CompleteSignInResult,
|
|
9
9
|
GroupMembershipRecord,
|
|
10
|
+
GroupMembershipSource,
|
|
10
11
|
InvitationRecord,
|
|
11
12
|
SessionRecord,
|
|
12
13
|
SuspendUserAndRevokeSessionsInput,
|
|
@@ -42,6 +43,7 @@ import {
|
|
|
42
43
|
normalizeEmail,
|
|
43
44
|
normalizeGroupIds,
|
|
44
45
|
oidcAttemptKey,
|
|
46
|
+
removeGroupMembershipRecord,
|
|
45
47
|
revokeActiveSessionsForUser,
|
|
46
48
|
upsertGroupMembershipRecord,
|
|
47
49
|
userKey,
|
|
@@ -219,25 +221,6 @@ export class InMemoryAuthStorage implements AuthStorage {
|
|
|
219
221
|
)
|
|
220
222
|
}
|
|
221
223
|
|
|
222
|
-
if (
|
|
223
|
-
shouldCreateUser &&
|
|
224
|
-
!activeInvitation &&
|
|
225
|
-
input.allowUserCreationWithoutInvitation &&
|
|
226
|
-
input.requireNoActiveUsersForUserCreation &&
|
|
227
|
-
hasActiveUsers(this.state, projectId)
|
|
228
|
-
) {
|
|
229
|
-
consumeMagicLinkRecord(this.state, {
|
|
230
|
-
projectId,
|
|
231
|
-
id: input.magicLinkId,
|
|
232
|
-
tokenHash: input.tokenHash,
|
|
233
|
-
consumedAt: completedAt,
|
|
234
|
-
})
|
|
235
|
-
throw new AuthStorageError(
|
|
236
|
-
"user_creation_not_allowed",
|
|
237
|
-
`[Sixb] Magic link '${input.magicLinkId}' cannot create a user for project '${projectId}'.`
|
|
238
|
-
)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
224
|
validateCompleteSessionInput(this.state, projectId, input.session)
|
|
242
225
|
assertSignInSessionAudience(projectId, input.session.audience, magicLink.audience)
|
|
243
226
|
|
|
@@ -280,8 +263,9 @@ export class InMemoryAuthStorage implements AuthStorage {
|
|
|
280
263
|
projectId,
|
|
281
264
|
user,
|
|
282
265
|
})
|
|
283
|
-
const memberships = this.
|
|
266
|
+
const memberships = this.applyGroups({
|
|
284
267
|
completedAt,
|
|
268
|
+
source: "manual",
|
|
285
269
|
groupIds: manualGroupIds,
|
|
286
270
|
projectId,
|
|
287
271
|
userId: user.id,
|
|
@@ -352,14 +336,6 @@ export class InMemoryAuthStorage implements AuthStorage {
|
|
|
352
336
|
)
|
|
353
337
|
}
|
|
354
338
|
|
|
355
|
-
if (!identity && user && (!input.autoLinkByVerifiedEmail || !input.emailVerified)) {
|
|
356
|
-
this.consumeOidcAttempt(input, completedAt, projectId)
|
|
357
|
-
throw new AuthStorageError(
|
|
358
|
-
"email_link_not_allowed",
|
|
359
|
-
`[Sixb] OIDC identity cannot auto-link to user '${user.id}' for project '${projectId}'.`
|
|
360
|
-
)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
339
|
if (user?.status === "suspended") {
|
|
364
340
|
this.consumeOidcAttempt(input, completedAt, projectId)
|
|
365
341
|
throw new AuthStorageError(
|
|
@@ -368,14 +344,6 @@ export class InMemoryAuthStorage implements AuthStorage {
|
|
|
368
344
|
)
|
|
369
345
|
}
|
|
370
346
|
|
|
371
|
-
if (shouldCreateUser && !input.emailVerified) {
|
|
372
|
-
this.consumeOidcAttempt(input, completedAt, projectId)
|
|
373
|
-
throw new AuthStorageError(
|
|
374
|
-
"user_creation_not_allowed",
|
|
375
|
-
`[Sixb] OIDC authorization attempt '${input.oidcAuthorizationAttemptId}' cannot create a user for project '${projectId}'.`
|
|
376
|
-
)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
347
|
if (shouldCreateUser && !activeInvitation && !input.allowUserCreationWithoutInvitation) {
|
|
380
348
|
this.consumeOidcAttempt(input, completedAt, projectId)
|
|
381
349
|
throw new AuthStorageError(
|
|
@@ -384,20 +352,6 @@ export class InMemoryAuthStorage implements AuthStorage {
|
|
|
384
352
|
)
|
|
385
353
|
}
|
|
386
354
|
|
|
387
|
-
if (
|
|
388
|
-
shouldCreateUser &&
|
|
389
|
-
!activeInvitation &&
|
|
390
|
-
input.allowUserCreationWithoutInvitation &&
|
|
391
|
-
input.requireNoActiveUsersForUserCreation &&
|
|
392
|
-
hasActiveUsers(this.state, projectId)
|
|
393
|
-
) {
|
|
394
|
-
this.consumeOidcAttempt(input, completedAt, projectId)
|
|
395
|
-
throw new AuthStorageError(
|
|
396
|
-
"user_creation_not_allowed",
|
|
397
|
-
`[Sixb] OIDC authorization attempt '${input.oidcAuthorizationAttemptId}' cannot create a user for project '${projectId}'.`
|
|
398
|
-
)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
355
|
validateCompleteSessionInput(this.state, projectId, input.session)
|
|
402
356
|
assertSignInSessionAudience(projectId, input.session.audience, attempt.audience)
|
|
403
357
|
|
|
@@ -434,12 +388,19 @@ export class InMemoryAuthStorage implements AuthStorage {
|
|
|
434
388
|
projectId,
|
|
435
389
|
user,
|
|
436
390
|
})
|
|
437
|
-
const memberships = this.
|
|
391
|
+
const memberships = this.syncDirectoryGroups({
|
|
438
392
|
completedAt,
|
|
439
|
-
groupIds:
|
|
393
|
+
groupIds: input.directoryGroupIds,
|
|
440
394
|
projectId,
|
|
441
395
|
userId: user.id,
|
|
442
|
-
existing:
|
|
396
|
+
existing: this.applyGroups({
|
|
397
|
+
completedAt,
|
|
398
|
+
source: "manual",
|
|
399
|
+
groupIds: manualGroupIds,
|
|
400
|
+
projectId,
|
|
401
|
+
userId: user.id,
|
|
402
|
+
existing: invitation.memberships,
|
|
403
|
+
}),
|
|
443
404
|
})
|
|
444
405
|
|
|
445
406
|
const nextIdentity: UserIdentityRecord = {
|
|
@@ -533,10 +494,11 @@ export class InMemoryAuthStorage implements AuthStorage {
|
|
|
533
494
|
return { invitation, memberships }
|
|
534
495
|
}
|
|
535
496
|
|
|
536
|
-
private
|
|
497
|
+
private applyGroups(input: {
|
|
537
498
|
readonly completedAt: Date
|
|
538
499
|
readonly groupIds: readonly string[]
|
|
539
500
|
readonly projectId: string
|
|
501
|
+
readonly source: GroupMembershipSource
|
|
540
502
|
readonly userId: string
|
|
541
503
|
readonly existing: readonly GroupMembershipRecord[]
|
|
542
504
|
}): readonly GroupMembershipRecord[] {
|
|
@@ -547,13 +509,40 @@ export class InMemoryAuthStorage implements AuthStorage {
|
|
|
547
509
|
projectId: input.projectId,
|
|
548
510
|
userId: input.userId,
|
|
549
511
|
groupId,
|
|
550
|
-
source:
|
|
512
|
+
source: input.source,
|
|
551
513
|
createdAt: input.completedAt,
|
|
552
514
|
})
|
|
553
515
|
)
|
|
554
516
|
}
|
|
555
517
|
return memberships
|
|
556
518
|
}
|
|
519
|
+
// Directory memberships become exactly `groupIds`. A group the user already holds from another
|
|
520
|
+
// source keeps that source, and memberships from other sources are never removed.
|
|
521
|
+
private syncDirectoryGroups(input: {
|
|
522
|
+
readonly completedAt: Date
|
|
523
|
+
readonly groupIds?: readonly string[]
|
|
524
|
+
readonly projectId: string
|
|
525
|
+
readonly userId: string
|
|
526
|
+
readonly existing: readonly GroupMembershipRecord[]
|
|
527
|
+
}): readonly GroupMembershipRecord[] {
|
|
528
|
+
if (!input.groupIds) {
|
|
529
|
+
return input.existing
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const groupIds = normalizeGroupIds(input.groupIds)
|
|
533
|
+
const stale = [...this.state.groupMemberships.values()].filter(
|
|
534
|
+
(membership) =>
|
|
535
|
+
membership.projectId === input.projectId &&
|
|
536
|
+
membership.userId === input.userId &&
|
|
537
|
+
membership.source === "directory" &&
|
|
538
|
+
!groupIds.includes(membership.groupId)
|
|
539
|
+
)
|
|
540
|
+
for (const membership of stale) {
|
|
541
|
+
removeGroupMembershipRecord(this.state, membership)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return this.applyGroups({ ...input, groupIds, source: "directory" })
|
|
545
|
+
}
|
|
557
546
|
|
|
558
547
|
private createSignInSession(input: {
|
|
559
548
|
readonly projectId: string
|
|
@@ -599,16 +588,6 @@ function restoreMap<TKey, TValue>(target: Map<TKey, TValue>, snapshot: Map<TKey,
|
|
|
599
588
|
}
|
|
600
589
|
}
|
|
601
590
|
|
|
602
|
-
function hasActiveUsers(state: AuthStorageState, projectId: string): boolean {
|
|
603
|
-
for (const user of state.users.values()) {
|
|
604
|
-
if (user.projectId === projectId && user.status === "active") {
|
|
605
|
-
return true
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
return false
|
|
610
|
-
}
|
|
611
|
-
|
|
612
591
|
function assertSignInSessionAudience(
|
|
613
592
|
projectId: string,
|
|
614
593
|
sessionAudience: string,
|
|
@@ -4,7 +4,8 @@ import type { AuthorizablePrincipal } from "../../execution/types"
|
|
|
4
4
|
export type UserStatus = "active" | "suspended"
|
|
5
5
|
export type ServiceAccountStatus = "active" | "suspended"
|
|
6
6
|
export type InvitationStatus = "pending" | "accepted" | "revoked"
|
|
7
|
-
|
|
7
|
+
/** `directory`: owned by the identity provider, and synced to its groups on every sign-in. */
|
|
8
|
+
export type GroupMembershipSource = "invitation" | "manual" | "agent" | "directory"
|
|
8
9
|
export type DeviceAuthorizationStatus = "pending" | "approved" | "denied" | "consumed"
|
|
9
10
|
export const MAX_PENDING_DEVICE_AUTHORIZATIONS = 100
|
|
10
11
|
|
|
@@ -45,7 +46,8 @@ export interface ServiceAccountGroupMembershipRecord {
|
|
|
45
46
|
readonly projectId: string
|
|
46
47
|
readonly serviceAccountId: string
|
|
47
48
|
readonly groupId: string
|
|
48
|
-
|
|
49
|
+
/** Service accounts never sign in, so no identity provider owns their memberships. */
|
|
50
|
+
readonly source: Exclude<GroupMembershipSource, "directory">
|
|
49
51
|
readonly createdAt: Date
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -269,7 +271,7 @@ export interface UpsertAuthServiceAccountGroupMembershipInput {
|
|
|
269
271
|
readonly projectId: string
|
|
270
272
|
readonly serviceAccountId: string
|
|
271
273
|
readonly groupId: string
|
|
272
|
-
readonly source:
|
|
274
|
+
readonly source: ServiceAccountGroupMembershipRecord["source"]
|
|
273
275
|
readonly createdAt?: Date
|
|
274
276
|
}
|
|
275
277
|
|
|
@@ -431,7 +433,6 @@ export interface CompleteMagicLinkSignInInput {
|
|
|
431
433
|
readonly newUserDisplayName?: string
|
|
432
434
|
readonly newUserAvatarUrl?: string
|
|
433
435
|
readonly allowUserCreationWithoutInvitation?: boolean
|
|
434
|
-
readonly requireNoActiveUsersForUserCreation?: boolean
|
|
435
436
|
readonly manualGroupIds?: readonly string[]
|
|
436
437
|
readonly session: CompleteAuthSessionInput
|
|
437
438
|
}
|
|
@@ -442,15 +443,22 @@ export interface CompleteOidcSignInInput {
|
|
|
442
443
|
readonly stateHash: string
|
|
443
444
|
readonly completedAt: Date
|
|
444
445
|
readonly subject: string
|
|
446
|
+
/**
|
|
447
|
+
* An address the strategy has already decided to trust. Without an identity for `subject`, it
|
|
448
|
+
* links the user who has this email, or claims this email's invitation for a new user.
|
|
449
|
+
*/
|
|
445
450
|
readonly email: string
|
|
446
|
-
readonly emailVerified?: boolean
|
|
447
451
|
readonly displayName?: string
|
|
448
452
|
readonly avatarUrl?: string
|
|
449
453
|
readonly claims?: Readonly<Record<string, unknown>>
|
|
450
|
-
readonly autoLinkByVerifiedEmail?: boolean
|
|
451
454
|
readonly allowUserCreationWithoutInvitation?: boolean
|
|
452
|
-
readonly requireNoActiveUsersForUserCreation?: boolean
|
|
453
455
|
readonly manualGroupIds?: readonly string[]
|
|
456
|
+
/**
|
|
457
|
+
* The groups the identity provider grants this user. When present, the user's `directory`
|
|
458
|
+
* memberships become exactly these; memberships from any other source are never touched, and a
|
|
459
|
+
* group the user already has from another source keeps it. Omitted, nothing is synced.
|
|
460
|
+
*/
|
|
461
|
+
readonly directoryGroupIds?: readonly string[]
|
|
454
462
|
readonly newUserId: string
|
|
455
463
|
readonly session: CompleteAuthSessionInput
|
|
456
464
|
}
|
package/src/storage/index.ts
CHANGED
|
@@ -449,8 +449,11 @@ export {
|
|
|
449
449
|
snapshotObjectReadExecutionLimits,
|
|
450
450
|
} from "./objects"
|
|
451
451
|
export type {
|
|
452
|
+
AbandonRunSourceMaterializationInput,
|
|
452
453
|
AbandonSourceMaterializationCandidateInput,
|
|
453
454
|
AbandonSourceMaterializationInput,
|
|
455
|
+
AdoptedSourceMaterialization,
|
|
456
|
+
AdoptSourceMaterializationInput,
|
|
454
457
|
ApplyMaterializationChunkInput,
|
|
455
458
|
ApplyMaterializationResult,
|
|
456
459
|
AssertSourceMaterializationExecution,
|
|
@@ -530,9 +533,9 @@ export type {
|
|
|
530
533
|
OntologyStorage,
|
|
531
534
|
OntologyVectorStorage,
|
|
532
535
|
ProjectionOntologyCommitIntent,
|
|
536
|
+
PurgeAbandonedSourceMaterializationsInput,
|
|
533
537
|
PurgePublishedOntologyOutboxInput,
|
|
534
538
|
ReadMaterializationObjectExistenceInput,
|
|
535
|
-
ReclaimSourceMaterializationInput,
|
|
536
539
|
RescheduleOntologyOutboxLeaseInput,
|
|
537
540
|
SourceActivationWrite,
|
|
538
541
|
SourceReplacementLinkState,
|
|
@@ -595,6 +598,7 @@ export {
|
|
|
595
598
|
export type {
|
|
596
599
|
AdvanceProjectionTelemetryCheckpointInput,
|
|
597
600
|
FailProjectionRunEnqueueInput,
|
|
601
|
+
FindSupersedingProjectionRunInput,
|
|
598
602
|
FinishProjectionRunInput,
|
|
599
603
|
LinkProjectionRunRecord,
|
|
600
604
|
LinkProjectionTarget,
|
|
@@ -616,6 +620,7 @@ export type {
|
|
|
616
620
|
ProjectionTarget,
|
|
617
621
|
ProjectionTelemetryCheckpoint,
|
|
618
622
|
QueueProjectionRunInput,
|
|
623
|
+
RecordProjectionAttemptFailureInput,
|
|
619
624
|
RecordProjectionMissingTargetInput,
|
|
620
625
|
StartOrReclaimProjectionRunInput,
|
|
621
626
|
TelemetryProjectionRunRecord,
|
|
@@ -16,8 +16,11 @@ import {
|
|
|
16
16
|
sourceStageRoots,
|
|
17
17
|
} from "../provider"
|
|
18
18
|
import type {
|
|
19
|
+
AbandonRunSourceMaterializationInput,
|
|
19
20
|
AbandonSourceMaterializationCandidateInput,
|
|
20
21
|
AbandonSourceMaterializationInput,
|
|
22
|
+
AdoptedSourceMaterialization,
|
|
23
|
+
AdoptSourceMaterializationInput,
|
|
21
24
|
AssertSourceMaterializationExecution,
|
|
22
25
|
BeginSourceMaterializationInput,
|
|
23
26
|
CleanupTerminalSourceMaterializationsInput,
|
|
@@ -26,7 +29,7 @@ import type {
|
|
|
26
29
|
MarkSourceMaterializationReadyInput,
|
|
27
30
|
OntologySourceRecord,
|
|
28
31
|
OntologySourceStorage,
|
|
29
|
-
|
|
32
|
+
PurgeAbandonedSourceMaterializationsInput,
|
|
30
33
|
StageSourceAssertion,
|
|
31
34
|
StageSourceRowsInput,
|
|
32
35
|
StageSourceRowsResult,
|
|
@@ -38,6 +41,7 @@ import {
|
|
|
38
41
|
assertNonblank,
|
|
39
42
|
type InMemoryOntologyState,
|
|
40
43
|
type InMemorySourceMaterialization,
|
|
44
|
+
type InMemorySourceRoot,
|
|
41
45
|
insertBounded,
|
|
42
46
|
sourceMaterializationKey,
|
|
43
47
|
sourceMaterializationRecord,
|
|
@@ -78,7 +82,7 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
78
82
|
)
|
|
79
83
|
if (nonterminal.length > 0) {
|
|
80
84
|
throw sourceConflict(
|
|
81
|
-
`Projection run '${input.execution.projectionRunId}' already has a nonterminal source materialization;
|
|
85
|
+
`Projection run '${input.execution.projectionRunId}' already has a nonterminal source materialization; adopt it before beginning another.`
|
|
82
86
|
)
|
|
83
87
|
}
|
|
84
88
|
|
|
@@ -191,8 +195,42 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
191
195
|
})
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
async adopt(
|
|
199
|
+
input: AdoptSourceMaterializationInput
|
|
200
|
+
): Promise<AdoptedSourceMaterialization | null> {
|
|
201
|
+
return this.runRootOperation(async () => {
|
|
202
|
+
assertProjectAndSource(input)
|
|
203
|
+
assertSourceExecutionIdentity(input.execution.projectionRunId, input.execution.executionToken)
|
|
204
|
+
assertCanonicalTimestamp(input.adoptedAt, "Source adoptedAt")
|
|
205
|
+
await this.assertCurrentExecution(input)
|
|
206
|
+
const [candidate, ...others] = this.findRunCandidates(
|
|
207
|
+
input.projectId,
|
|
208
|
+
input.source.projectionId,
|
|
209
|
+
input.execution.projectionRunId
|
|
210
|
+
)
|
|
211
|
+
if (!candidate) return null
|
|
212
|
+
if (others.length > 0) throw multipleRunCandidates(input.execution.projectionRunId)
|
|
213
|
+
const [key, materialization] = candidate
|
|
214
|
+
const adopted: InMemorySourceMaterialization = {
|
|
215
|
+
...materialization,
|
|
216
|
+
executionToken: input.execution.executionToken,
|
|
217
|
+
// Workers' clocks differ; adoption never moves the record back in time.
|
|
218
|
+
updatedAt:
|
|
219
|
+
input.adoptedAt > materialization.updatedAt ? input.adoptedAt : materialization.updatedAt,
|
|
220
|
+
}
|
|
221
|
+
this.state.sourceMaterializations.set(key, adopted)
|
|
222
|
+
return {
|
|
223
|
+
record: sourceMaterializationRecord(adopted),
|
|
224
|
+
resumeStagingOrdinal:
|
|
225
|
+
adopted.status === "ready"
|
|
226
|
+
? (adopted.rootCount ?? 0)
|
|
227
|
+
: Math.max(0, ...adopted.ordinalRoots.keys()),
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
194
232
|
async abandon(input: AbandonSourceMaterializationCandidateInput): Promise<OntologySourceRecord>
|
|
195
|
-
async abandon(input:
|
|
233
|
+
async abandon(input: AbandonRunSourceMaterializationInput): Promise<OntologySourceRecord | null>
|
|
196
234
|
async abandon(input: AbandonSourceMaterializationInput): Promise<OntologySourceRecord | null> {
|
|
197
235
|
return this.runRootOperation(async () => {
|
|
198
236
|
assertProjectAndSource(input)
|
|
@@ -201,7 +239,7 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
201
239
|
await this.assertCurrentExecution(input)
|
|
202
240
|
return input.kind === "candidate"
|
|
203
241
|
? this.abandonCandidate(input)
|
|
204
|
-
: this.
|
|
242
|
+
: this.abandonRunCandidate(input)
|
|
205
243
|
})
|
|
206
244
|
}
|
|
207
245
|
|
|
@@ -211,18 +249,14 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
211
249
|
return this.runRootOperation(() => {
|
|
212
250
|
assertNonblank(input.projectId, "Terminal source cleanup project id")
|
|
213
251
|
assertCanonicalTimestamp(input.terminalBefore, "Terminal source cleanup cutoff")
|
|
214
|
-
|
|
215
|
-
throw new MaterializationValidationError(
|
|
216
|
-
"Terminal source cleanup limit must be a positive safe integer."
|
|
217
|
-
)
|
|
218
|
-
}
|
|
252
|
+
assertCleanupLimit(input.limit)
|
|
219
253
|
|
|
220
254
|
const candidates: [string, InMemorySourceMaterialization][] = []
|
|
221
255
|
for (const entry of this.state.sourceMaterializations.entries()) {
|
|
222
256
|
const materialization = entry[1]
|
|
223
257
|
if (
|
|
224
258
|
materialization.projectId !== input.projectId ||
|
|
225
|
-
|
|
259
|
+
materialization.status !== "superseded" ||
|
|
226
260
|
materialization.terminalAt === null ||
|
|
227
261
|
materialization.terminalAt >= input.terminalBefore
|
|
228
262
|
) {
|
|
@@ -239,42 +273,69 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
239
273
|
insertBounded(candidates, entry, input.limit, compareTerminalMaterializations)
|
|
240
274
|
}
|
|
241
275
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
276
|
+
return this.deleteRetiredRoots(
|
|
277
|
+
candidates,
|
|
278
|
+
input.limit,
|
|
279
|
+
(root) => !root.active && root.retiredAt !== null && root.retiredAt < input.terminalBefore
|
|
280
|
+
)
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async purgeAbandoned(
|
|
285
|
+
input: PurgeAbandonedSourceMaterializationsInput
|
|
286
|
+
): Promise<CleanupTerminalSourceMaterializationsResult> {
|
|
287
|
+
return this.runRootOperation(() => {
|
|
288
|
+
assertNonblank(input.projectId, "Abandoned source purge project id")
|
|
289
|
+
assertCleanupLimit(input.limit)
|
|
290
|
+
const abandoned = [...this.state.sourceMaterializations.entries()]
|
|
291
|
+
.filter(
|
|
292
|
+
([, materialization]) =>
|
|
293
|
+
materialization.projectId === input.projectId && materialization.status === "abandoned"
|
|
294
|
+
)
|
|
295
|
+
.sort(compareTerminalMaterializations)
|
|
296
|
+
return this.deleteRetiredRoots(abandoned, input.limit, () => true)
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private deleteRetiredRoots(
|
|
301
|
+
candidates: readonly [string, InMemorySourceMaterialization][],
|
|
302
|
+
limit: number,
|
|
303
|
+
retired: (root: InMemorySourceRoot) => boolean
|
|
304
|
+
): CleanupTerminalSourceMaterializationsResult {
|
|
305
|
+
let remaining = limit
|
|
306
|
+
let rowsDeleted = 0
|
|
307
|
+
let materializationsDeleted = 0
|
|
308
|
+
for (const [key, candidate] of candidates) {
|
|
309
|
+
if (remaining === 0) break
|
|
310
|
+
const current = this.state.sourceMaterializations.get(key)
|
|
311
|
+
if (current !== candidate) continue
|
|
312
|
+
current.rootOrdinals.clear()
|
|
313
|
+
current.ordinalRoots.clear()
|
|
314
|
+
for (const [rootKey, root] of current.roots) {
|
|
246
315
|
if (remaining === 0) break
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
current.rootOrdinals.clear()
|
|
250
|
-
current.ordinalRoots.clear()
|
|
251
|
-
for (const [rootKey, root] of current.roots) {
|
|
316
|
+
if (!retired(root)) continue
|
|
317
|
+
for (const entityKey of root.entityKeys) {
|
|
252
318
|
if (remaining === 0) break
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
current.rowsByEntity.delete(entityKey)
|
|
258
|
-
root.entityKeys.delete(entityKey)
|
|
259
|
-
rowsDeleted += 1
|
|
260
|
-
remaining -= 1
|
|
261
|
-
}
|
|
262
|
-
if (root.entityKeys.size === 0 && remaining > 0) {
|
|
263
|
-
current.roots.delete(rootKey)
|
|
264
|
-
current.rootOrdinals.delete(rootKey)
|
|
265
|
-
current.ordinalRoots.delete(root.stagingOrdinal)
|
|
266
|
-
rowsDeleted += 1
|
|
267
|
-
remaining -= 1
|
|
268
|
-
}
|
|
319
|
+
current.rowsByEntity.delete(entityKey)
|
|
320
|
+
root.entityKeys.delete(entityKey)
|
|
321
|
+
rowsDeleted += 1
|
|
322
|
+
remaining -= 1
|
|
269
323
|
}
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
324
|
+
if (root.entityKeys.size === 0 && remaining > 0) {
|
|
325
|
+
current.roots.delete(rootKey)
|
|
326
|
+
current.rootOrdinals.delete(rootKey)
|
|
327
|
+
current.ordinalRoots.delete(root.stagingOrdinal)
|
|
328
|
+
rowsDeleted += 1
|
|
273
329
|
remaining -= 1
|
|
274
330
|
}
|
|
275
331
|
}
|
|
276
|
-
|
|
277
|
-
|
|
332
|
+
if (current.roots.size === 0 && current.rowsByEntity.size === 0 && remaining > 0) {
|
|
333
|
+
this.state.sourceMaterializations.delete(key)
|
|
334
|
+
materializationsDeleted += 1
|
|
335
|
+
remaining -= 1
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return { rowsDeleted, materializationsDeleted }
|
|
278
339
|
}
|
|
279
340
|
|
|
280
341
|
async summarizeTerminal(
|
|
@@ -356,26 +417,15 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
356
417
|
return this.transitionToAbandoned(materialization, input.abandonedAt)
|
|
357
418
|
}
|
|
358
419
|
|
|
359
|
-
private
|
|
420
|
+
private abandonRunCandidate(
|
|
421
|
+
input: AbandonRunSourceMaterializationInput
|
|
422
|
+
): OntologySourceRecord | null {
|
|
360
423
|
const candidates = this.findRunCandidates(
|
|
361
424
|
input.projectId,
|
|
362
425
|
input.source.projectionId,
|
|
363
426
|
input.execution.projectionRunId
|
|
364
427
|
)
|
|
365
|
-
if (
|
|
366
|
-
candidates.some(
|
|
367
|
-
([, materialization]) => materialization.executionToken === input.execution.executionToken
|
|
368
|
-
)
|
|
369
|
-
) {
|
|
370
|
-
throw sourceConflict(
|
|
371
|
-
`Projection run '${input.execution.projectionRunId}' already has a source materialization owned by the current execution.`
|
|
372
|
-
)
|
|
373
|
-
}
|
|
374
|
-
if (candidates.length > 1) {
|
|
375
|
-
throw sourceConflict(
|
|
376
|
-
`Projection run '${input.execution.projectionRunId}' has multiple nonterminal source materializations.`
|
|
377
|
-
)
|
|
378
|
-
}
|
|
428
|
+
if (candidates.length > 1) throw multipleRunCandidates(input.execution.projectionRunId)
|
|
379
429
|
const candidate = candidates[0]?.[1]
|
|
380
430
|
return candidate ? this.transitionToAbandoned(candidate, input.abandonedAt) : null
|
|
381
431
|
}
|
|
@@ -609,6 +659,20 @@ function assertNotBefore(
|
|
|
609
659
|
}
|
|
610
660
|
}
|
|
611
661
|
|
|
662
|
+
function assertCleanupLimit(limit: number): void {
|
|
663
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
664
|
+
throw new MaterializationValidationError(
|
|
665
|
+
"Terminal source cleanup limit must be a positive safe integer."
|
|
666
|
+
)
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function multipleRunCandidates(projectionRunId: string) {
|
|
671
|
+
return sourceConflict(
|
|
672
|
+
`Projection run '${projectionRunId}' has multiple nonterminal source materializations.`
|
|
673
|
+
)
|
|
674
|
+
}
|
|
675
|
+
|
|
612
676
|
function compareTerminalMaterializations(
|
|
613
677
|
[, left]: readonly [string, InMemorySourceMaterialization],
|
|
614
678
|
[, right]: readonly [string, InMemorySourceMaterialization]
|
|
@@ -97,8 +97,11 @@ export type {
|
|
|
97
97
|
} from "./outbox"
|
|
98
98
|
export { ONTOLOGY_OUTBOX_FAILURE_CODES } from "./outbox"
|
|
99
99
|
export type {
|
|
100
|
+
AbandonRunSourceMaterializationInput,
|
|
100
101
|
AbandonSourceMaterializationCandidateInput,
|
|
101
102
|
AbandonSourceMaterializationInput,
|
|
103
|
+
AdoptedSourceMaterialization,
|
|
104
|
+
AdoptSourceMaterializationInput,
|
|
102
105
|
AssertSourceMaterializationExecution,
|
|
103
106
|
AssertSourceMaterializationExecutionInput,
|
|
104
107
|
BeginSourceMaterializationInput,
|
|
@@ -109,7 +112,7 @@ export type {
|
|
|
109
112
|
OntologySourceMaterializationStatus,
|
|
110
113
|
OntologySourceRecord,
|
|
111
114
|
OntologySourceStorage,
|
|
112
|
-
|
|
115
|
+
PurgeAbandonedSourceMaterializationsInput,
|
|
113
116
|
StageSourceAssertion,
|
|
114
117
|
StageSourceRoot,
|
|
115
118
|
StageSourceRowsInput,
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
ExpectedObjectRevision,
|
|
12
12
|
OntologyLinkRef,
|
|
13
13
|
OntologyObjectRef,
|
|
14
|
+
ProjectionEntityRef,
|
|
15
|
+
ProjectionSourceAssertion,
|
|
14
16
|
} from "../../materialization/model"
|
|
15
17
|
import { linkRefKey, objectRefKey, projectionEntityKey } from "../../materialization/refs"
|
|
16
18
|
import type { OntologyCommitOriginSelector, OntologyCommitWrite } from "./commits"
|
|
@@ -716,7 +718,6 @@ export function sourceStageRow(row: StageSourceAssertion): SourceStageRow {
|
|
|
716
718
|
export interface SourceStageRoot {
|
|
717
719
|
readonly root: StageSourceAssertion["root"]
|
|
718
720
|
readonly rootKey: string
|
|
719
|
-
readonly sortKey: string
|
|
720
721
|
readonly stagingOrdinal: number
|
|
721
722
|
readonly deleted: boolean
|
|
722
723
|
}
|
|
@@ -763,7 +764,6 @@ export function sourceStageRoots(
|
|
|
763
764
|
roots.set(key, {
|
|
764
765
|
root: row.root,
|
|
765
766
|
rootKey: key,
|
|
766
|
-
sortKey: utf8SortKey(key),
|
|
767
767
|
stagingOrdinal: row.stagingOrdinal,
|
|
768
768
|
deleted,
|
|
769
769
|
})
|
|
@@ -965,12 +965,69 @@ export function sourceEntityColumns(entity: StageSourceAssertion["root"]): {
|
|
|
965
965
|
}
|
|
966
966
|
}
|
|
967
967
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
968
|
+
/** The inverse of `projectionEntityKey`, for providers that store only the canonical key. */
|
|
969
|
+
export function sourceEntityFromKey(key: string): ProjectionEntityRef {
|
|
970
|
+
const parts: unknown = JSON.parse(key)
|
|
971
|
+
if (Array.isArray(parts) && parts.every((part) => typeof part === "string")) {
|
|
972
|
+
if (parts[0] === "object" && parts.length === 3) {
|
|
973
|
+
return { kind: "object", ref: { objectTypeId: parts[1]!, primaryId: parts[2]! } }
|
|
974
|
+
}
|
|
975
|
+
if (parts[0] === "link" && parts.length === 6) {
|
|
976
|
+
return {
|
|
977
|
+
kind: "link",
|
|
978
|
+
ref: {
|
|
979
|
+
source: { objectTypeId: parts[1]!, primaryId: parts[2]! },
|
|
980
|
+
linkId: parts[3]!,
|
|
981
|
+
target: { objectTypeId: parts[4]!, primaryId: parts[5]! },
|
|
982
|
+
},
|
|
983
|
+
}
|
|
984
|
+
}
|
|
972
985
|
}
|
|
973
|
-
|
|
986
|
+
throw new MaterializationConflictError(
|
|
987
|
+
"source-materialization",
|
|
988
|
+
`Stored source key ${key} is not a canonical entity key.`
|
|
989
|
+
)
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/** What an assertion carries beyond its identity, or null when that is nothing. */
|
|
993
|
+
export function sourceAssertionPayload(
|
|
994
|
+
assertion: ProjectionSourceAssertion
|
|
995
|
+
): Readonly<Record<string, unknown>> | null {
|
|
996
|
+
const { kind: _kind, ref: _ref, ...payload } = assertion
|
|
997
|
+
return Object.keys(payload).length === 0 ? null : payload
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/** Rebuilds an assertion from its typed identity columns and its payload. */
|
|
1001
|
+
export function sourceAssertionFromColumns(
|
|
1002
|
+
columns: {
|
|
1003
|
+
readonly entity_kind: "object" | "link"
|
|
1004
|
+
readonly object_type_id: string | null
|
|
1005
|
+
readonly primary_id: string | null
|
|
1006
|
+
readonly source_type_id: string | null
|
|
1007
|
+
readonly source_primary_id: string | null
|
|
1008
|
+
readonly link_id: string | null
|
|
1009
|
+
readonly target_type_id: string | null
|
|
1010
|
+
readonly target_primary_id: string | null
|
|
1011
|
+
},
|
|
1012
|
+
payload: unknown
|
|
1013
|
+
): ProjectionSourceAssertion {
|
|
1014
|
+
const rest = (payload ?? {}) as Record<string, unknown>
|
|
1015
|
+
if (columns.entity_kind === "object") {
|
|
1016
|
+
return {
|
|
1017
|
+
kind: "object",
|
|
1018
|
+
ref: { objectTypeId: columns.object_type_id!, primaryId: columns.primary_id! },
|
|
1019
|
+
...rest,
|
|
1020
|
+
} as ProjectionSourceAssertion
|
|
1021
|
+
}
|
|
1022
|
+
return {
|
|
1023
|
+
kind: "link",
|
|
1024
|
+
ref: {
|
|
1025
|
+
source: { objectTypeId: columns.source_type_id!, primaryId: columns.source_primary_id! },
|
|
1026
|
+
linkId: columns.link_id!,
|
|
1027
|
+
target: { objectTypeId: columns.target_type_id!, primaryId: columns.target_primary_id! },
|
|
1028
|
+
},
|
|
1029
|
+
...rest,
|
|
1030
|
+
} as ProjectionSourceAssertion
|
|
974
1031
|
}
|
|
975
1032
|
|
|
976
1033
|
export function isExactStagingManifest(
|