@opengeni/db 3.4.0-canary.2 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/canonical-human-identities.d.ts +20 -0
  2. package/dist/canonical-human-identities.js +7 -3
  3. package/dist/{chunk-ZWRIPCTR.js → chunk-2NT476HD.js} +2 -2
  4. package/dist/{chunk-PYMY3AJW.js → chunk-744DFC65.js} +202 -7
  5. package/dist/chunk-744DFC65.js.map +1 -0
  6. package/dist/{chunk-O6FTELKH.js → chunk-CDMMSUWJ.js} +3 -3
  7. package/dist/chunk-G3732M3T.js +299 -0
  8. package/dist/chunk-G3732M3T.js.map +1 -0
  9. package/dist/{chunk-TRBOWUG3.js → chunk-IW6O6G7R.js} +2 -2
  10. package/dist/{chunk-HY3F5Q4E.js → chunk-Q5YUGQVK.js} +3 -3
  11. package/dist/{chunk-V3TRUXFJ.js → chunk-UGDVSQ6U.js} +72 -19
  12. package/dist/chunk-UGDVSQ6U.js.map +1 -0
  13. package/dist/{chunk-C2AFP7SB.js → chunk-UJ2EW3QI.js} +6664 -6051
  14. package/dist/chunk-UJ2EW3QI.js.map +1 -0
  15. package/dist/{chunk-H7MXYG7R.js → chunk-WW7AJ5IC.js} +2 -2
  16. package/dist/editable-artifact-durable-export.js +2 -2
  17. package/dist/editable-artifacts.js +3 -3
  18. package/dist/index.d.ts +44 -6
  19. package/dist/index.js +2914 -1535
  20. package/dist/index.js.map +1 -1
  21. package/dist/managed-auth-session-set-schema.d.ts +1399 -0
  22. package/dist/managed-auth-session-sets.d.ts +145 -0
  23. package/dist/managed-auth-session-sets.js +50 -0
  24. package/dist/managed-auth-session-sets.js.map +1 -0
  25. package/dist/organization-recovery-schema.d.ts +2144 -0
  26. package/dist/organization-recovery.d.ts +186 -0
  27. package/dist/provision-roles.js +1 -1
  28. package/dist/runtime-posture.d.ts +7 -4
  29. package/dist/schema.d.ts +9 -6
  30. package/dist/schema.js +43 -1
  31. package/dist/session-background-commands.js +3 -3
  32. package/dist/session-control.d.ts +5 -3
  33. package/dist/session-queue-commands.d.ts +2 -0
  34. package/dist/session-tenancy.js +3 -3
  35. package/dist/video-generation.js +3 -3
  36. package/dist/work-claims-schema.d.ts +975 -0
  37. package/dist/work-claims.d.ts +83 -0
  38. package/drizzle/0362_managed_auth_session_sets.sql +2347 -0
  39. package/drizzle/0363_organization_recovery_custody.sql +2418 -0
  40. package/drizzle/0364_workspace_learning_policy_snapshot_lock_order.sql +221 -0
  41. package/drizzle/0365_permission_scoped_work_claims.sql +1306 -0
  42. package/drizzle/0366_session_discovery_title_search_index.sql +5 -0
  43. package/drizzle/0367_session_discovery_goal_search_index.sql +5 -0
  44. package/drizzle/0368_session_discovery_claim_search_index.sql +6 -0
  45. package/package.json +10 -6
  46. package/src/canonical-human-identities.ts +105 -27
  47. package/src/index.ts +1471 -433
  48. package/src/managed-auth-session-set-schema.ts +176 -0
  49. package/src/managed-auth-session-sets.ts +430 -0
  50. package/src/organization-recovery-schema.ts +277 -0
  51. package/src/organization-recovery.ts +424 -0
  52. package/src/provision-roles.ts +67 -0
  53. package/src/runtime-posture.ts +144 -6
  54. package/src/schema.ts +3 -0
  55. package/src/session-control.ts +255 -16
  56. package/src/session-queue-commands.ts +10 -1
  57. package/src/session-tool-call-settlement.ts +6 -1
  58. package/src/work-claims-schema.ts +148 -0
  59. package/src/work-claims.ts +200 -0
  60. package/dist/chunk-C2AFP7SB.js.map +0 -1
  61. package/dist/chunk-PYMY3AJW.js.map +0 -1
  62. package/dist/chunk-V3TRUXFJ.js.map +0 -1
  63. /package/dist/{chunk-ZWRIPCTR.js.map → chunk-2NT476HD.js.map} +0 -0
  64. /package/dist/{chunk-O6FTELKH.js.map → chunk-CDMMSUWJ.js.map} +0 -0
  65. /package/dist/{chunk-TRBOWUG3.js.map → chunk-IW6O6G7R.js.map} +0 -0
  66. /package/dist/{chunk-HY3F5Q4E.js.map → chunk-Q5YUGQVK.js.map} +0 -0
  67. /package/dist/{chunk-H7MXYG7R.js.map → chunk-WW7AJ5IC.js.map} +0 -0
@@ -0,0 +1,5 @@
1
+ -- deployment-mode: rolling
2
+ -- opengeni:concurrent-index lock-timeout=5s
3
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS sessions_discovery_title_fts_idx
4
+ ON sessions USING gin (to_tsvector('simple', title))
5
+ WHERE title IS NOT NULL;
@@ -0,0 +1,5 @@
1
+ -- deployment-mode: rolling
2
+ -- opengeni:concurrent-index lock-timeout=5s
3
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS session_goals_discovery_active_text_fts_idx
4
+ ON session_goals USING gin (to_tsvector('simple', text))
5
+ WHERE status = 'active';
@@ -0,0 +1,6 @@
1
+ -- deployment-mode: rolling
2
+ -- opengeni:concurrent-index lock-timeout=5s
3
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS session_work_claims_discovery_text_fts_idx
4
+ ON session_work_claims USING gin (
5
+ to_tsvector('simple', canonical_key || ' ' || coalesce(display_label, ''))
6
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "3.4.0-canary.2",
3
+ "version": "3.6.0",
4
4
  "description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -51,6 +51,10 @@
51
51
  "types": "./dist/canonical-human-identities.d.ts",
52
52
  "import": "./dist/canonical-human-identities.js"
53
53
  },
54
+ "./managed-auth-session-sets": {
55
+ "types": "./dist/managed-auth-session-sets.d.ts",
56
+ "import": "./dist/managed-auth-session-sets.js"
57
+ },
54
58
  "./session-tenancy": {
55
59
  "types": "./dist/session-tenancy.d.ts",
56
60
  "import": "./dist/session-tenancy.js"
@@ -74,11 +78,11 @@
74
78
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
75
79
  },
76
80
  "dependencies": {
77
- "@opengeni/codemode": "^0.4.15-canary.2",
78
- "@opengeni/codex": "^0.2.19-canary.3",
79
- "@opengeni/config": "^0.20.1-canary.2",
80
- "@opengeni/contracts": "^2.5.0-canary.2",
81
- "@opengeni/network": "^0.2.3-canary.3",
81
+ "@opengeni/codemode": "^0.4.17",
82
+ "@opengeni/codex": "^0.2.19",
83
+ "@opengeni/config": "^0.22.0",
84
+ "@opengeni/contracts": "^2.7.0",
85
+ "@opengeni/network": "^0.2.3",
82
86
  "drizzle-orm": "^0.45.2",
83
87
  "postgres": "^3.4.7"
84
88
  },
@@ -18,6 +18,37 @@ export type CanonicalHumanSessionAuthority = {
18
18
  identityStatus: "active" | "recovery_required" | "disputed" | "disabled";
19
19
  };
20
20
 
21
+ export type CanonicalHumanExactLoginBinding = {
22
+ id: string;
23
+ identityId: string;
24
+ revision: number;
25
+ status: "active" | "recovery_pending" | "stale" | "disputed" | "revoked";
26
+ };
27
+
28
+ export async function getCanonicalHumanExactLoginBindingForAuthUser(
29
+ db: Database,
30
+ input: { authUserId: string; providerId: string },
31
+ ): Promise<CanonicalHumanExactLoginBinding> {
32
+ const [row] = await rawRows<{
33
+ result:
34
+ | (Omit<CanonicalHumanExactLoginBinding, "revision"> & { revision: number | string })
35
+ | null;
36
+ }>(
37
+ db,
38
+ sql`
39
+ select get_canonical_human_exact_login_binding(
40
+ ${input.authUserId}, ${input.providerId}
41
+ ) as result
42
+ `,
43
+ );
44
+ if (!row?.result) {
45
+ throw new CanonicalHumanIdentityAuthorityError(
46
+ "Authentication did not prove exactly one canonical login binding",
47
+ );
48
+ }
49
+ return { ...row.result, revision: Number(row.result.revision) };
50
+ }
51
+
21
52
  export class CanonicalHumanIdentityConflictError extends Error {
22
53
  readonly name = "CanonicalHumanIdentityConflictError";
23
54
  readonly code = "CANONICAL_HUMAN_IDENTITY_CONFLICT";
@@ -36,6 +67,15 @@ export class CanonicalHumanIdentityOperationReuseError extends Error {
36
67
  }
37
68
  }
38
69
 
70
+ export class CanonicalHumanIdentityMutationInFlightError extends Error {
71
+ readonly name = "CanonicalHumanIdentityMutationInFlightError";
72
+ readonly code = "CANONICAL_HUMAN_IDENTITY_MUTATION_IN_FLIGHT";
73
+
74
+ constructor() {
75
+ super("Another request is still using this browser actor");
76
+ }
77
+ }
78
+
39
79
  export class CanonicalHumanIdentityNotFoundError extends Error {
40
80
  readonly name = "CanonicalHumanIdentityNotFoundError";
41
81
 
@@ -65,6 +105,7 @@ function mapSqlError(error: unknown): never {
65
105
  const state = nestedPostgresSqlState(error);
66
106
  if (state === "40001") throw new CanonicalHumanIdentityConflictError();
67
107
  if (state === "23505") throw new CanonicalHumanIdentityOperationReuseError();
108
+ if (state === "55P03") throw new CanonicalHumanIdentityMutationInFlightError();
68
109
  if (state === "P0002") throw new CanonicalHumanIdentityNotFoundError();
69
110
  if (state === "42501") {
70
111
  throw new CanonicalHumanIdentityAuthorityError(
@@ -111,6 +152,17 @@ export async function ensureCanonicalHumanIdentityForAuthUser(
111
152
  db: Database,
112
153
  authUserId: string,
113
154
  ): Promise<CanonicalHumanSessionAuthority> {
155
+ try {
156
+ const existing = await getCanonicalHumanIdentityProjection(db, authUserId);
157
+ return {
158
+ identityId: existing.activeIdentity.id,
159
+ identityRevision: existing.activeIdentity.identityRevision,
160
+ authRevision: existing.activeIdentity.authRevision,
161
+ identityStatus: existing.activeIdentity.status,
162
+ };
163
+ } catch (error) {
164
+ if (!(error instanceof CanonicalHumanIdentityNotFoundError)) throw error;
165
+ }
114
166
  const [user] = await rawRows<{ displayName: string }>(
115
167
  db,
116
168
  sql`
@@ -264,6 +316,11 @@ export type ApplyCanonicalHumanIdentityOperationInput = {
264
316
  providerId?: string | null;
265
317
  providerAccountId?: string | null;
266
318
  reason: string;
319
+ actorFence?: {
320
+ authorityHash: string;
321
+ actorEpoch: string;
322
+ requestId: string;
323
+ };
267
324
  };
268
325
 
269
326
  export async function applyCanonicalHumanIdentityOperation(
@@ -271,14 +328,46 @@ export async function applyCanonicalHumanIdentityOperation(
271
328
  input: ApplyCanonicalHumanIdentityOperationInput,
272
329
  ): Promise<CanonicalHumanIdentityMutationResponseType> {
273
330
  try {
274
- const [row] = await rawRows<{
275
- outcome: CanonicalHumanIdentityMutationOutcome;
276
- identityId: string;
277
- identityRevision: number | string;
278
- authRevision: number | string;
279
- }>(
280
- db,
281
- sql`
331
+ if (input.actorFence) {
332
+ return await db.transaction(async (tx) => {
333
+ const txDb = tx as unknown as Database;
334
+ await rawRows(
335
+ txDb,
336
+ sql`select managed_auth_actor_mutation_fence(
337
+ ${input.actorFence!.authorityHash}, ${input.actorFence!.actorEpoch}::bigint,
338
+ ${input.actorFence!.requestId}::uuid
339
+ )`,
340
+ );
341
+ return await applyCanonicalHumanIdentityOperationInner(txDb, input);
342
+ });
343
+ }
344
+ return await applyCanonicalHumanIdentityOperationInner(db, input);
345
+ } catch (error) {
346
+ if (
347
+ error instanceof CanonicalHumanIdentityConflictError ||
348
+ error instanceof CanonicalHumanIdentityOperationReuseError ||
349
+ error instanceof CanonicalHumanIdentityMutationInFlightError ||
350
+ error instanceof CanonicalHumanIdentityNotFoundError ||
351
+ error instanceof CanonicalHumanIdentityAuthorityError
352
+ ) {
353
+ throw error;
354
+ }
355
+ mapSqlError(error);
356
+ }
357
+ }
358
+
359
+ async function applyCanonicalHumanIdentityOperationInner(
360
+ db: Database,
361
+ input: ApplyCanonicalHumanIdentityOperationInput,
362
+ ): Promise<CanonicalHumanIdentityMutationResponseType> {
363
+ const [row] = await rawRows<{
364
+ outcome: CanonicalHumanIdentityMutationOutcome;
365
+ identityId: string;
366
+ identityRevision: number | string;
367
+ authRevision: number | string;
368
+ }>(
369
+ db,
370
+ sql`
282
371
  select
283
372
  outcome,
284
373
  identity_id as "identityId",
@@ -295,23 +384,12 @@ export async function applyCanonicalHumanIdentityOperation(
295
384
  ${input.reason}
296
385
  )
297
386
  `,
298
- );
299
- if (!row) throw new CanonicalHumanIdentityNotFoundError();
300
- const identity = await getCanonicalHumanIdentityProjection(db, input.authUserId);
301
- return CanonicalHumanIdentityMutationResponse.parse({
302
- outcome: row.outcome,
303
- operationId: input.operationId,
304
- identity,
305
- });
306
- } catch (error) {
307
- if (
308
- error instanceof CanonicalHumanIdentityConflictError ||
309
- error instanceof CanonicalHumanIdentityOperationReuseError ||
310
- error instanceof CanonicalHumanIdentityNotFoundError ||
311
- error instanceof CanonicalHumanIdentityAuthorityError
312
- ) {
313
- throw error;
314
- }
315
- mapSqlError(error);
316
- }
387
+ );
388
+ if (!row) throw new CanonicalHumanIdentityNotFoundError();
389
+ const identity = await getCanonicalHumanIdentityProjection(db, input.authUserId);
390
+ return CanonicalHumanIdentityMutationResponse.parse({
391
+ outcome: row.outcome,
392
+ operationId: input.operationId,
393
+ identity,
394
+ });
317
395
  }