@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
|
@@ -81,6 +81,34 @@ async function createUser(
|
|
|
81
81
|
})
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
async function completeOidcSignInForAva(
|
|
85
|
+
storage: AuthStorage,
|
|
86
|
+
directoryGroupIds: readonly string[] | undefined
|
|
87
|
+
): Promise<void> {
|
|
88
|
+
await storage.oidcAuthorizationAttempts.create({
|
|
89
|
+
id: "oidc_directory",
|
|
90
|
+
projectId,
|
|
91
|
+
strategyId: "entra",
|
|
92
|
+
audience: "atlas",
|
|
93
|
+
stateHash: "directory-state",
|
|
94
|
+
nonceHash: "nonce-hash",
|
|
95
|
+
codeVerifier: "verifier",
|
|
96
|
+
createdAt: at("2026-05-14T10:00:00.000Z"),
|
|
97
|
+
expiresAt: at("2026-05-14T10:10:00.000Z"),
|
|
98
|
+
})
|
|
99
|
+
await storage.completeOidcSignIn({
|
|
100
|
+
projectId,
|
|
101
|
+
oidcAuthorizationAttemptId: "oidc_directory",
|
|
102
|
+
stateHash: "directory-state",
|
|
103
|
+
completedAt: at("2026-05-14T10:01:00.000Z"),
|
|
104
|
+
subject: "entra-ava",
|
|
105
|
+
email: "ava@acme.com",
|
|
106
|
+
directoryGroupIds,
|
|
107
|
+
newUserId: "usr_unused",
|
|
108
|
+
session: sessionInput("ses_directory"),
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
84
112
|
/**
|
|
85
113
|
* Runs the shared `AuthStorage` contract against any storage implementation.
|
|
86
114
|
*
|
|
@@ -1721,91 +1749,6 @@ export function runAuthStorageContractSuite<TStorage extends AuthStorage>(
|
|
|
1721
1749
|
})
|
|
1722
1750
|
})
|
|
1723
1751
|
|
|
1724
|
-
test("closes bootstrap user creation when an active user already exists", async () => {
|
|
1725
|
-
await withStorage(async (storage) => {
|
|
1726
|
-
await createUser(storage, {
|
|
1727
|
-
id: "usr_existing",
|
|
1728
|
-
email: "existing@acme.com",
|
|
1729
|
-
})
|
|
1730
|
-
await storage.magicLinks.create({
|
|
1731
|
-
id: "ml_1",
|
|
1732
|
-
projectId,
|
|
1733
|
-
strategyId: "magic-link",
|
|
1734
|
-
audience: "atlas",
|
|
1735
|
-
email: "founder@acme.com",
|
|
1736
|
-
tokenHash: "link-hash",
|
|
1737
|
-
createdAt: at("2026-05-14T10:01:00.000Z"),
|
|
1738
|
-
expiresAt: at("2026-05-14T10:16:00.000Z"),
|
|
1739
|
-
})
|
|
1740
|
-
|
|
1741
|
-
await expectAuthError(
|
|
1742
|
-
storage.completeMagicLinkSignIn({
|
|
1743
|
-
projectId,
|
|
1744
|
-
magicLinkId: "ml_1",
|
|
1745
|
-
tokenHash: "link-hash",
|
|
1746
|
-
completedAt: at("2026-05-14T10:03:00.000Z"),
|
|
1747
|
-
newUserId: "usr_founder",
|
|
1748
|
-
allowUserCreationWithoutInvitation: true,
|
|
1749
|
-
requireNoActiveUsersForUserCreation: true,
|
|
1750
|
-
session: sessionInput("ses_founder"),
|
|
1751
|
-
}),
|
|
1752
|
-
"user_creation_not_allowed"
|
|
1753
|
-
)
|
|
1754
|
-
await expect(
|
|
1755
|
-
storage.users.getByEmail({ projectId, email: "founder@acme.com" })
|
|
1756
|
-
).resolves.toBeNull()
|
|
1757
|
-
await expect(storage.magicLinks.getById({ projectId, id: "ml_1" })).resolves.toMatchObject({
|
|
1758
|
-
consumedAt: at("2026-05-14T10:03:00.000Z"),
|
|
1759
|
-
})
|
|
1760
|
-
})
|
|
1761
|
-
})
|
|
1762
|
-
|
|
1763
|
-
test("does not apply the bootstrap closure guard to invited user creation", async () => {
|
|
1764
|
-
await withStorage(async (storage) => {
|
|
1765
|
-
await createUser(storage, {
|
|
1766
|
-
id: "usr_existing",
|
|
1767
|
-
email: "existing@acme.com",
|
|
1768
|
-
})
|
|
1769
|
-
await storage.invitations.createOrUpdateActive({
|
|
1770
|
-
id: "inv_1",
|
|
1771
|
-
projectId,
|
|
1772
|
-
email: "invited@acme.com",
|
|
1773
|
-
groupIds: ["commercial"],
|
|
1774
|
-
createdAt: at("2026-05-14T10:00:00.000Z"),
|
|
1775
|
-
expiresAt: at("2026-05-21T10:00:00.000Z"),
|
|
1776
|
-
})
|
|
1777
|
-
await storage.magicLinks.create({
|
|
1778
|
-
id: "ml_1",
|
|
1779
|
-
projectId,
|
|
1780
|
-
strategyId: "magic-link",
|
|
1781
|
-
audience: "atlas",
|
|
1782
|
-
email: "invited@acme.com",
|
|
1783
|
-
tokenHash: "link-hash",
|
|
1784
|
-
createdAt: at("2026-05-14T10:01:00.000Z"),
|
|
1785
|
-
expiresAt: at("2026-05-14T10:16:00.000Z"),
|
|
1786
|
-
})
|
|
1787
|
-
|
|
1788
|
-
const result = await storage.completeMagicLinkSignIn({
|
|
1789
|
-
projectId,
|
|
1790
|
-
magicLinkId: "ml_1",
|
|
1791
|
-
tokenHash: "link-hash",
|
|
1792
|
-
completedAt: at("2026-05-14T10:03:00.000Z"),
|
|
1793
|
-
newUserId: "usr_invited",
|
|
1794
|
-
requireNoActiveUsersForUserCreation: true,
|
|
1795
|
-
session: sessionInput("ses_invited"),
|
|
1796
|
-
})
|
|
1797
|
-
|
|
1798
|
-
expect(result.user).toMatchObject({
|
|
1799
|
-
id: "usr_invited",
|
|
1800
|
-
email: "invited@acme.com",
|
|
1801
|
-
})
|
|
1802
|
-
expect(result.invitation).toMatchObject({
|
|
1803
|
-
id: "inv_1",
|
|
1804
|
-
status: "accepted",
|
|
1805
|
-
})
|
|
1806
|
-
})
|
|
1807
|
-
})
|
|
1808
|
-
|
|
1809
1752
|
test("consumes valid magic links when an existing user is suspended", async () => {
|
|
1810
1753
|
await withStorage(async (storage) => {
|
|
1811
1754
|
await createUser(storage, { status: "suspended" })
|
|
@@ -1867,7 +1810,6 @@ export function runAuthStorageContractSuite<TStorage extends AuthStorage>(
|
|
|
1867
1810
|
completedAt: at("2026-05-14T10:02:00.000Z"),
|
|
1868
1811
|
subject: "00u1",
|
|
1869
1812
|
email: "renamed@acme.com",
|
|
1870
|
-
emailVerified: true,
|
|
1871
1813
|
claims: { email: "renamed@acme.com" },
|
|
1872
1814
|
newUserId: "usr_unused",
|
|
1873
1815
|
session: sessionInput("ses_oidc"),
|
|
@@ -1885,65 +1827,35 @@ export function runAuthStorageContractSuite<TStorage extends AuthStorage>(
|
|
|
1885
1827
|
})
|
|
1886
1828
|
})
|
|
1887
1829
|
|
|
1888
|
-
test("
|
|
1830
|
+
test("links a new OIDC identity to the existing user with its email", async () => {
|
|
1889
1831
|
await withStorage(async (storage) => {
|
|
1890
1832
|
await createUser(storage, { id: "usr_existing", email: "ava@acme.com" })
|
|
1891
1833
|
await storage.oidcAuthorizationAttempts.create({
|
|
1892
|
-
id: "
|
|
1834
|
+
id: "oidc_1",
|
|
1893
1835
|
projectId,
|
|
1894
1836
|
strategyId: "okta",
|
|
1895
1837
|
audience: "atlas",
|
|
1896
|
-
stateHash: "
|
|
1838
|
+
stateHash: "state-hash",
|
|
1897
1839
|
nonceHash: "nonce-hash",
|
|
1898
1840
|
codeVerifier: "verifier",
|
|
1899
1841
|
createdAt: at("2026-05-14T10:00:00.000Z"),
|
|
1900
1842
|
expiresAt: at("2026-05-14T10:10:00.000Z"),
|
|
1901
1843
|
})
|
|
1902
1844
|
|
|
1903
|
-
await expectAuthError(
|
|
1904
|
-
storage.completeOidcSignIn({
|
|
1905
|
-
projectId,
|
|
1906
|
-
oidcAuthorizationAttemptId: "oidc_blocked",
|
|
1907
|
-
stateHash: "blocked-state",
|
|
1908
|
-
completedAt: at("2026-05-14T10:01:00.000Z"),
|
|
1909
|
-
subject: "00u1",
|
|
1910
|
-
email: "ava@acme.com",
|
|
1911
|
-
emailVerified: false,
|
|
1912
|
-
autoLinkByVerifiedEmail: true,
|
|
1913
|
-
newUserId: "usr_unused",
|
|
1914
|
-
session: sessionInput("ses_blocked"),
|
|
1915
|
-
}),
|
|
1916
|
-
"email_link_not_allowed"
|
|
1917
|
-
)
|
|
1918
|
-
|
|
1919
|
-
await storage.oidcAuthorizationAttempts.create({
|
|
1920
|
-
id: "oidc_allowed",
|
|
1921
|
-
projectId,
|
|
1922
|
-
strategyId: "okta",
|
|
1923
|
-
audience: "atlas",
|
|
1924
|
-
stateHash: "allowed-state",
|
|
1925
|
-
nonceHash: "nonce-hash",
|
|
1926
|
-
codeVerifier: "verifier",
|
|
1927
|
-
createdAt: at("2026-05-14T10:02:00.000Z"),
|
|
1928
|
-
expiresAt: at("2026-05-14T10:12:00.000Z"),
|
|
1929
|
-
})
|
|
1930
1845
|
const result = await storage.completeOidcSignIn({
|
|
1931
1846
|
projectId,
|
|
1932
|
-
oidcAuthorizationAttemptId: "
|
|
1933
|
-
stateHash: "
|
|
1934
|
-
completedAt: at("2026-05-14T10:
|
|
1847
|
+
oidcAuthorizationAttemptId: "oidc_1",
|
|
1848
|
+
stateHash: "state-hash",
|
|
1849
|
+
completedAt: at("2026-05-14T10:01:00.000Z"),
|
|
1935
1850
|
subject: "00u1",
|
|
1936
1851
|
email: "ava@acme.com",
|
|
1937
|
-
emailVerified: true,
|
|
1938
|
-
autoLinkByVerifiedEmail: true,
|
|
1939
1852
|
newUserId: "usr_unused",
|
|
1940
|
-
session: sessionInput("
|
|
1853
|
+
session: sessionInput("ses_oidc"),
|
|
1941
1854
|
})
|
|
1942
1855
|
|
|
1943
1856
|
expect(result.user.id).toBe("usr_existing")
|
|
1944
|
-
expect(result.identity).toMatchObject({
|
|
1945
|
-
|
|
1946
|
-
})
|
|
1857
|
+
expect(result.identity).toMatchObject({ subject: "00u1", userId: "usr_existing" })
|
|
1858
|
+
await expect(storage.users.getById({ projectId, id: "usr_unused" })).resolves.toBeNull()
|
|
1947
1859
|
})
|
|
1948
1860
|
})
|
|
1949
1861
|
|
|
@@ -1977,7 +1889,6 @@ export function runAuthStorageContractSuite<TStorage extends AuthStorage>(
|
|
|
1977
1889
|
subject: "00u1",
|
|
1978
1890
|
email: "ava@acme.com",
|
|
1979
1891
|
displayName: "Ava Chen",
|
|
1980
|
-
emailVerified: true,
|
|
1981
1892
|
newUserId: "usr_oidc",
|
|
1982
1893
|
session: sessionInput("ses_oidc"),
|
|
1983
1894
|
})
|
|
@@ -2024,7 +1935,6 @@ export function runAuthStorageContractSuite<TStorage extends AuthStorage>(
|
|
|
2024
1935
|
completedAt: at("2026-05-14T10:01:00.000Z"),
|
|
2025
1936
|
subject: "00u1",
|
|
2026
1937
|
email: "ava@acme.com",
|
|
2027
|
-
emailVerified: true,
|
|
2028
1938
|
newUserId: "usr_oidc",
|
|
2029
1939
|
session: sessionInput("ses_oidc"),
|
|
2030
1940
|
}),
|
|
@@ -2036,7 +1946,7 @@ export function runAuthStorageContractSuite<TStorage extends AuthStorage>(
|
|
|
2036
1946
|
})
|
|
2037
1947
|
})
|
|
2038
1948
|
|
|
2039
|
-
test("allows
|
|
1949
|
+
test("allows OIDC bootstrap user creation with manual groups", async () => {
|
|
2040
1950
|
await withStorage(async (storage) => {
|
|
2041
1951
|
await storage.oidcAuthorizationAttempts.create({
|
|
2042
1952
|
id: "oidc_bootstrap",
|
|
@@ -2057,9 +1967,7 @@ export function runAuthStorageContractSuite<TStorage extends AuthStorage>(
|
|
|
2057
1967
|
completedAt: at("2026-05-14T10:01:00.000Z"),
|
|
2058
1968
|
subject: "00u1",
|
|
2059
1969
|
email: "founder@acme.com",
|
|
2060
|
-
emailVerified: true,
|
|
2061
1970
|
allowUserCreationWithoutInvitation: true,
|
|
2062
|
-
requireNoActiveUsersForUserCreation: true,
|
|
2063
1971
|
manualGroupIds: ["security-admins"],
|
|
2064
1972
|
newUserId: "usr_founder",
|
|
2065
1973
|
session: sessionInput("ses_oidc_bootstrap"),
|
|
@@ -2075,6 +1983,72 @@ export function runAuthStorageContractSuite<TStorage extends AuthStorage>(
|
|
|
2075
1983
|
})
|
|
2076
1984
|
})
|
|
2077
1985
|
|
|
1986
|
+
test("syncs directory memberships to exactly the groups the provider grants", async () => {
|
|
1987
|
+
await withStorage(async (storage) => {
|
|
1988
|
+
await createUser(storage)
|
|
1989
|
+
for (const [groupId, source] of [
|
|
1990
|
+
["security-admins", "manual"],
|
|
1991
|
+
["commercial", "invitation"],
|
|
1992
|
+
["legacy-team", "directory"],
|
|
1993
|
+
] as const) {
|
|
1994
|
+
await storage.groupMemberships.upsert({ projectId, userId: "usr_1", groupId, source })
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
await completeOidcSignInForAva(storage, ["security-admins", "field-ops"])
|
|
1998
|
+
|
|
1999
|
+
// legacy-team is no longer granted; security-admins keeps its manual source.
|
|
2000
|
+
await expect(
|
|
2001
|
+
storage.groupMemberships.listForUser({ projectId, userId: "usr_1" })
|
|
2002
|
+
).resolves.toMatchObject([
|
|
2003
|
+
{ groupId: "commercial", source: "invitation" },
|
|
2004
|
+
{ groupId: "field-ops", source: "directory" },
|
|
2005
|
+
{ groupId: "security-admins", source: "manual" },
|
|
2006
|
+
])
|
|
2007
|
+
})
|
|
2008
|
+
})
|
|
2009
|
+
|
|
2010
|
+
test("removes every directory membership when the provider grants no groups", async () => {
|
|
2011
|
+
await withStorage(async (storage) => {
|
|
2012
|
+
await createUser(storage)
|
|
2013
|
+
await storage.groupMemberships.upsert({
|
|
2014
|
+
projectId,
|
|
2015
|
+
userId: "usr_1",
|
|
2016
|
+
groupId: "security-admins",
|
|
2017
|
+
source: "manual",
|
|
2018
|
+
})
|
|
2019
|
+
await storage.groupMemberships.upsert({
|
|
2020
|
+
projectId,
|
|
2021
|
+
userId: "usr_1",
|
|
2022
|
+
groupId: "field-ops",
|
|
2023
|
+
source: "directory",
|
|
2024
|
+
})
|
|
2025
|
+
|
|
2026
|
+
await completeOidcSignInForAva(storage, [])
|
|
2027
|
+
|
|
2028
|
+
await expect(
|
|
2029
|
+
storage.groupMemberships.listForUser({ projectId, userId: "usr_1" })
|
|
2030
|
+
).resolves.toMatchObject([{ groupId: "security-admins", source: "manual" }])
|
|
2031
|
+
})
|
|
2032
|
+
})
|
|
2033
|
+
|
|
2034
|
+
test("leaves directory memberships alone when sign-in syncs no groups", async () => {
|
|
2035
|
+
await withStorage(async (storage) => {
|
|
2036
|
+
await createUser(storage)
|
|
2037
|
+
await storage.groupMemberships.upsert({
|
|
2038
|
+
projectId,
|
|
2039
|
+
userId: "usr_1",
|
|
2040
|
+
groupId: "field-ops",
|
|
2041
|
+
source: "directory",
|
|
2042
|
+
})
|
|
2043
|
+
|
|
2044
|
+
await completeOidcSignInForAva(storage, undefined)
|
|
2045
|
+
|
|
2046
|
+
await expect(
|
|
2047
|
+
storage.groupMemberships.listForUser({ projectId, userId: "usr_1" })
|
|
2048
|
+
).resolves.toMatchObject([{ groupId: "field-ops", source: "directory" }])
|
|
2049
|
+
})
|
|
2050
|
+
})
|
|
2051
|
+
|
|
2078
2052
|
test("suspends users and revokes active sessions immediately", async () => {
|
|
2079
2053
|
await withStorage(async (storage) => {
|
|
2080
2054
|
await createUser(storage)
|
|
@@ -410,38 +410,76 @@ export function runOntologyStorageContractSuite<TStorage extends OntologyStorage
|
|
|
410
410
|
abandonedAt: "2026-01-01T00:03:00.000Z",
|
|
411
411
|
})
|
|
412
412
|
).rejects.toMatchObject({ kind: "execution-lost" })
|
|
413
|
+
const adopted = await storage.ontology.sources.adopt({
|
|
414
|
+
projectId: "contract-project",
|
|
415
|
+
source,
|
|
416
|
+
execution: reclaimed.execution,
|
|
417
|
+
adoptedAt: "2026-01-01T00:01:30.000Z",
|
|
418
|
+
})
|
|
419
|
+
expect(adopted).toMatchObject({
|
|
420
|
+
record: {
|
|
421
|
+
materializationId: "staging-candidate",
|
|
422
|
+
status: "ready",
|
|
423
|
+
executionToken: reclaimed.execution.executionToken,
|
|
424
|
+
updatedAt: "2026-01-01T00:01:30.000Z",
|
|
425
|
+
},
|
|
426
|
+
resumeStagingOrdinal: 1,
|
|
427
|
+
})
|
|
428
|
+
// Adopting twice from the same execution is a no-op, not a second owner change.
|
|
429
|
+
expect(
|
|
430
|
+
await storage.ontology.sources.adopt({
|
|
431
|
+
projectId: "contract-project",
|
|
432
|
+
source,
|
|
433
|
+
execution: reclaimed.execution,
|
|
434
|
+
adoptedAt: "2026-01-01T00:01:40.000Z",
|
|
435
|
+
})
|
|
436
|
+
).toMatchObject({ record: { executionToken: reclaimed.execution.executionToken } })
|
|
437
|
+
|
|
413
438
|
const abandoned = await storage.ontology.sources.abandon({
|
|
414
|
-
kind: "
|
|
439
|
+
kind: "run",
|
|
415
440
|
projectId: "contract-project",
|
|
416
441
|
source,
|
|
417
442
|
execution: reclaimed.execution,
|
|
418
443
|
abandonedAt: "2026-01-01T00:02:00.000Z",
|
|
419
444
|
})
|
|
420
445
|
expect(abandoned).toMatchObject({ status: "abandoned", executionToken: null })
|
|
446
|
+
expect(
|
|
447
|
+
await storage.ontology.sources.abandon({
|
|
448
|
+
kind: "run",
|
|
449
|
+
projectId: "contract-project",
|
|
450
|
+
source,
|
|
451
|
+
execution: reclaimed.execution,
|
|
452
|
+
abandonedAt: "2026-01-01T00:02:00.000Z",
|
|
453
|
+
})
|
|
454
|
+
).toBeNull()
|
|
455
|
+
expect(
|
|
456
|
+
await storage.ontology.sources.adopt({
|
|
457
|
+
projectId: "contract-project",
|
|
458
|
+
source,
|
|
459
|
+
execution: reclaimed.execution,
|
|
460
|
+
adoptedAt: "2026-01-01T00:02:10.000Z",
|
|
461
|
+
})
|
|
462
|
+
).toBeNull()
|
|
421
463
|
expect(
|
|
422
464
|
await storage.ontology.sources.summarizeTerminal({ projectId: "contract-project" })
|
|
423
465
|
).toEqual({ count: 1, oldestTerminalAt: "2026-01-01T00:02:00.000Z" })
|
|
424
466
|
|
|
467
|
+
// Superseded retention does not hold an abandoned candidate back.
|
|
425
468
|
expect(
|
|
426
469
|
await storage.ontology.sources.cleanupTerminal({
|
|
427
470
|
projectId: "contract-project",
|
|
428
471
|
terminalBefore: "2026-02-01T00:00:00.000Z",
|
|
429
|
-
limit:
|
|
472
|
+
limit: 10,
|
|
430
473
|
})
|
|
474
|
+
).toEqual({ rowsDeleted: 0, materializationsDeleted: 0 })
|
|
475
|
+
expect(
|
|
476
|
+
await storage.ontology.sources.purgeAbandoned({ projectId: "contract-project", limit: 1 })
|
|
431
477
|
).toEqual({ rowsDeleted: 1, materializationsDeleted: 0 })
|
|
432
478
|
expect(
|
|
433
|
-
await storage.ontology.sources.
|
|
434
|
-
projectId: "contract-project",
|
|
435
|
-
terminalBefore: "2026-02-01T00:00:00.000Z",
|
|
436
|
-
limit: 1,
|
|
437
|
-
})
|
|
479
|
+
await storage.ontology.sources.purgeAbandoned({ projectId: "contract-project", limit: 1 })
|
|
438
480
|
).toEqual({ rowsDeleted: 1, materializationsDeleted: 0 })
|
|
439
481
|
expect(
|
|
440
|
-
await storage.ontology.sources.
|
|
441
|
-
projectId: "contract-project",
|
|
442
|
-
terminalBefore: "2026-02-01T00:00:00.000Z",
|
|
443
|
-
limit: 1,
|
|
444
|
-
})
|
|
482
|
+
await storage.ontology.sources.purgeAbandoned({ projectId: "contract-project", limit: 1 })
|
|
445
483
|
).toEqual({ rowsDeleted: 0, materializationsDeleted: 1 })
|
|
446
484
|
expect(
|
|
447
485
|
await storage.ontology.sources.summarizeTerminal({ projectId: "contract-project" })
|
|
@@ -449,6 +487,95 @@ export function runOntologyStorageContractSuite<TStorage extends OntologyStorage
|
|
|
449
487
|
})
|
|
450
488
|
})
|
|
451
489
|
|
|
490
|
+
test("adopts a staging candidate at its last staged root", async () => {
|
|
491
|
+
await withStorage(async (storage) => {
|
|
492
|
+
const identity = replacementIdentity("01")
|
|
493
|
+
const claimed = await startTestProjectionRun(storage, {
|
|
494
|
+
id: "resumed-run",
|
|
495
|
+
projectId: "contract-project",
|
|
496
|
+
identity,
|
|
497
|
+
target: { objectTypeId: "ContractDevice" },
|
|
498
|
+
})
|
|
499
|
+
const source = { projectionId: identity.projectionId }
|
|
500
|
+
await storage.ontology.sources.beginMaterialization({
|
|
501
|
+
projectId: "contract-project",
|
|
502
|
+
source,
|
|
503
|
+
materializationId: "resumed-candidate",
|
|
504
|
+
execution: claimed.execution,
|
|
505
|
+
projectionKind: "object",
|
|
506
|
+
protocol: "replacement",
|
|
507
|
+
datasetVersion: identity.datasetVersion,
|
|
508
|
+
projectionRevision: identity.projectionRevision,
|
|
509
|
+
ownershipHash: identity.ownershipHash,
|
|
510
|
+
ontologyRevision: identity.ontologyRevision,
|
|
511
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
512
|
+
})
|
|
513
|
+
const row = (primaryId: string, stagingOrdinal: number) => ({
|
|
514
|
+
root: { kind: "object" as const, ref: { objectTypeId: "ContractDevice", primaryId } },
|
|
515
|
+
assertion: {
|
|
516
|
+
kind: "object" as const,
|
|
517
|
+
ref: { objectTypeId: "ContractDevice", primaryId },
|
|
518
|
+
properties: { name: primaryId },
|
|
519
|
+
},
|
|
520
|
+
stagingOrdinal,
|
|
521
|
+
})
|
|
522
|
+
await storage.ontology.sources.stageRows({
|
|
523
|
+
projectId: "contract-project",
|
|
524
|
+
source,
|
|
525
|
+
materializationId: "resumed-candidate",
|
|
526
|
+
execution: claimed.execution,
|
|
527
|
+
rows: [row("zero", 0), row("one", 1)],
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
const reclaimed = await storage.projectionRuns.startOrReclaim({
|
|
531
|
+
id: claimed.run.id,
|
|
532
|
+
projectId: claimed.run.projectId,
|
|
533
|
+
identity,
|
|
534
|
+
target: { objectTypeId: "ContractDevice" },
|
|
535
|
+
})
|
|
536
|
+
const adopted = await storage.ontology.sources.adopt({
|
|
537
|
+
projectId: "contract-project",
|
|
538
|
+
source,
|
|
539
|
+
execution: reclaimed.execution,
|
|
540
|
+
adoptedAt: "2026-01-01T00:01:00.000Z",
|
|
541
|
+
})
|
|
542
|
+
expect(adopted).toMatchObject({
|
|
543
|
+
record: { status: "staging", executionToken: reclaimed.execution.executionToken },
|
|
544
|
+
resumeStagingOrdinal: 1,
|
|
545
|
+
})
|
|
546
|
+
// The previous execution lost the candidate with the run.
|
|
547
|
+
await expect(
|
|
548
|
+
storage.ontology.sources.stageRows({
|
|
549
|
+
projectId: "contract-project",
|
|
550
|
+
source,
|
|
551
|
+
materializationId: "resumed-candidate",
|
|
552
|
+
execution: claimed.execution,
|
|
553
|
+
rows: [row("two", 2)],
|
|
554
|
+
})
|
|
555
|
+
).rejects.toMatchObject({ kind: "execution-lost" })
|
|
556
|
+
expect(
|
|
557
|
+
await storage.ontology.sources.stageRows({
|
|
558
|
+
projectId: "contract-project",
|
|
559
|
+
source,
|
|
560
|
+
materializationId: "resumed-candidate",
|
|
561
|
+
execution: reclaimed.execution,
|
|
562
|
+
rows: [row("one", 1), row("two", 2)],
|
|
563
|
+
})
|
|
564
|
+
).toEqual({ inserted: 1, unchanged: 1 })
|
|
565
|
+
expect(
|
|
566
|
+
await storage.ontology.sources.markReady({
|
|
567
|
+
projectId: "contract-project",
|
|
568
|
+
source,
|
|
569
|
+
materializationId: "resumed-candidate",
|
|
570
|
+
execution: reclaimed.execution,
|
|
571
|
+
rootCount: 3,
|
|
572
|
+
assertionCount: 3,
|
|
573
|
+
readyAt: "2026-01-01T00:02:00.000Z",
|
|
574
|
+
})
|
|
575
|
+
).toMatchObject({ status: "ready", rootCount: 3 })
|
|
576
|
+
})
|
|
577
|
+
})
|
|
578
|
+
|
|
452
579
|
test("atomically activates explicit empty candidates and rejects competing stale heads", async () => {
|
|
453
580
|
await withStorage(async (storage) => {
|
|
454
581
|
const first = await createReadyEmptyCandidate(storage, "run-one", "candidate-one", "01")
|
|
@@ -532,16 +659,15 @@ export function runOntologyStorageContractSuite<TStorage extends OntologyStorage
|
|
|
532
659
|
)
|
|
533
660
|
).rejects.toThrow("execution token is stale")
|
|
534
661
|
await storage.ontology.sources.abandon({
|
|
535
|
-
kind: "
|
|
662
|
+
kind: "run",
|
|
536
663
|
projectId: "contract-project",
|
|
537
664
|
source: stale.source,
|
|
538
665
|
execution: reclaimed.execution,
|
|
539
666
|
abandonedAt: "2026-01-06T00:01:00.000Z",
|
|
540
667
|
})
|
|
541
668
|
|
|
542
|
-
const cleanup = await storage.ontology.sources.
|
|
669
|
+
const cleanup = await storage.ontology.sources.purgeAbandoned({
|
|
543
670
|
projectId: "contract-project",
|
|
544
|
-
terminalBefore: "2026-02-01T00:00:00.000Z",
|
|
545
671
|
limit: 10,
|
|
546
672
|
})
|
|
547
673
|
expect(cleanup.materializationsDeleted).toBeGreaterThanOrEqual(1)
|