@fuzdev/fuz_app 0.57.2 → 0.58.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.
- package/dist/auth/CLAUDE.md +11 -6
- package/dist/auth/account_schema.d.ts +1 -1
- package/dist/auth/account_schema.js +1 -1
- package/dist/auth/audit_log_ddl.d.ts +24 -0
- package/dist/auth/audit_log_ddl.d.ts.map +1 -0
- package/dist/auth/audit_log_ddl.js +42 -0
- package/dist/auth/audit_log_schema.d.ts +3 -3
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +3 -34
- package/dist/auth/{ddl.d.ts → auth_ddl.d.ts} +7 -4
- package/dist/auth/auth_ddl.d.ts.map +1 -0
- package/dist/auth/{ddl.js → auth_ddl.js} +6 -3
- package/dist/auth/migrations.js +4 -4
- package/dist/auth/role_grant_offer_ddl.d.ts +43 -0
- package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_ddl.js +99 -0
- package/dist/auth/role_grant_offer_queries.d.ts +1 -1
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +1 -1
- package/dist/auth/role_grant_offer_schema.d.ts +3 -28
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_schema.js +3 -80
- package/dist/auth/role_grant_queries.d.ts +1 -1
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +1 -1
- package/package.json +1 -1
- package/dist/auth/ddl.d.ts.map +0 -1
package/dist/auth/CLAUDE.md
CHANGED
|
@@ -76,15 +76,20 @@ Design notes:
|
|
|
76
76
|
|
|
77
77
|
## Schemas, types, and DDL
|
|
78
78
|
|
|
79
|
+
Convention — `*_schema.ts` is Zod-only; `*_ddl.ts` holds DDL constants and
|
|
80
|
+
index strings. Mixed modules split into a `_schema` + `_ddl` pair.
|
|
81
|
+
|
|
79
82
|
| Module | What's inside |
|
|
80
83
|
| ----------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
81
84
|
| `account_schema.ts` | Runtime types + client-safe Zod schemas for identity entities |
|
|
82
85
|
| `role_schema.ts` | Role vocabulary and extensibility |
|
|
83
|
-
| `
|
|
86
|
+
| `auth_ddl.ts` | Raw `CREATE TABLE` / index / seed SQL strings for the core identity tables |
|
|
84
87
|
| `invite_schema.ts` | `Invite`, `InviteJson`, `InviteWithUsernamesJson`, `CreateInviteInput` |
|
|
85
88
|
| `app_settings_schema.ts` | `AppSettings`, `AppSettingsJson`, `AppSettingsWithUsernameJson`, `UpdateAppSettingsInput` |
|
|
86
|
-
| `audit_log_schema.ts` | Event-type enum, per-type metadata schemas,
|
|
87
|
-
| `
|
|
89
|
+
| `audit_log_schema.ts` | Event-type enum, per-type metadata schemas, client-safe Zod |
|
|
90
|
+
| `audit_log_ddl.ts` | `audit_log` table DDL + index strings |
|
|
91
|
+
| `role_grant_offer_schema.ts` | Role grant offer types and client-safe Zod |
|
|
92
|
+
| `role_grant_offer_ddl.ts` | `role_grant_offer` table DDL, indexes, and the index-side sentinel constants |
|
|
88
93
|
| `role_grant_offer_notifications.ts` | WS notification specs for the consentful-role-grant lifecycle |
|
|
89
94
|
|
|
90
95
|
### Identity entities (`account_schema.ts`)
|
|
@@ -237,7 +242,7 @@ against the corresponding open registries at construction time.
|
|
|
237
242
|
filter helpers used by `admin_actions` and
|
|
238
243
|
`self_service_role_actions` to derive their default eligibility.
|
|
239
244
|
|
|
240
|
-
### Raw DDL (`
|
|
245
|
+
### Raw DDL (`auth_ddl.ts`)
|
|
241
246
|
|
|
242
247
|
Separated from runtime types to isolate DDL concerns. Consumed by
|
|
243
248
|
`migrations.ts`:
|
|
@@ -262,7 +267,7 @@ Separated from runtime types to isolate DDL concerns. Consumed by
|
|
|
262
267
|
- `APP_SETTINGS_SCHEMA`, `APP_SETTINGS_SEED` — single-row via
|
|
263
268
|
`CHECK (id = 1)` constraint; seed is `ON CONFLICT DO NOTHING`.
|
|
264
269
|
|
|
265
|
-
### Audit log (`audit_log_schema.ts`)
|
|
270
|
+
### Audit log (`audit_log_schema.ts` + `audit_log_ddl.ts`)
|
|
266
271
|
|
|
267
272
|
#### Audit event types
|
|
268
273
|
|
|
@@ -396,7 +401,7 @@ Zod enum; `AuditOutcome` is `'success' | 'failure'`.
|
|
|
396
401
|
accidental mutation (bugs, test cross-contamination, cast escapes)
|
|
397
402
|
into loud TypeErrors — not a security boundary.
|
|
398
403
|
|
|
399
|
-
### Role grant offer (`role_grant_offer_schema.ts`)
|
|
404
|
+
### Role grant offer (`role_grant_offer_schema.ts` + `role_grant_offer_ddl.ts`)
|
|
400
405
|
|
|
401
406
|
The consentful-role-grants surface. Key constants:
|
|
402
407
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* `RouteAuth` (the two pair: `auth.actor !== 'none'` ⟺ input declares
|
|
12
12
|
* `acting?: ActingActor`).
|
|
13
13
|
*
|
|
14
|
-
* DDL lives in `auth/
|
|
14
|
+
* DDL lives in `auth/auth_ddl.ts`; role system in `auth/role_schema.ts`.
|
|
15
15
|
* See docs/identity.md for design rationale.
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* `RouteAuth` (the two pair: `auth.actor !== 'none'` ⟺ input declares
|
|
12
12
|
* `acting?: ActingActor`).
|
|
13
13
|
*
|
|
14
|
-
* DDL lives in `auth/
|
|
14
|
+
* DDL lives in `auth/auth_ddl.ts`; role system in `auth/role_schema.ts`.
|
|
15
15
|
* See docs/identity.md for design rationale.
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit log DDL — `CREATE TABLE` + index statements for the `audit_log` table.
|
|
3
|
+
*
|
|
4
|
+
* Consumed by `auth/migrations.ts`. Separated from `auth/audit_log_schema.ts`
|
|
5
|
+
* so the schema module stays Zod-only (paired with `auth/auth_ddl.ts` and
|
|
6
|
+
* `auth/role_grant_offer_ddl.ts`).
|
|
7
|
+
*
|
|
8
|
+
* Multi-actor invariants the envelope columns assume:
|
|
9
|
+
*
|
|
10
|
+
* - `actor_id` + `account_id`, when both populated, refer to the same
|
|
11
|
+
* account (derivable via `actor.account_id`). Denormalized for indexed
|
|
12
|
+
* audit queries; do not let them disagree.
|
|
13
|
+
* - `target_actor_id` + `target_account_id`, same rule when both populated.
|
|
14
|
+
* - `target_account_id` is the SSE/WS socket-close key — sessions stay
|
|
15
|
+
* account-grain after multi-actor lands, so this column carries the
|
|
16
|
+
* routing identity even on actor-bound events.
|
|
17
|
+
* - `target_actor_id` is populated iff the event subject is actor-bound
|
|
18
|
+
* (see `AuditLogEvent.target_actor_id` doc-comment for the rule).
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
export declare const AUDIT_LOG_SCHEMA = "\nCREATE TABLE IF NOT EXISTS audit_log (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n seq SERIAL NOT NULL,\n event_type TEXT NOT NULL,\n outcome TEXT NOT NULL DEFAULT 'success',\n actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,\n account_id UUID REFERENCES account(id) ON DELETE SET NULL,\n target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,\n target_actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,\n ip TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n metadata JSONB\n)";
|
|
23
|
+
export declare const AUDIT_LOG_INDEXES: string[];
|
|
24
|
+
//# sourceMappingURL=audit_log_ddl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit_log_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,eAAO,MAAM,gBAAgB,ihBAa3B,CAAC;AAEH,eAAO,MAAM,iBAAiB,UAM7B,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit log DDL — `CREATE TABLE` + index statements for the `audit_log` table.
|
|
3
|
+
*
|
|
4
|
+
* Consumed by `auth/migrations.ts`. Separated from `auth/audit_log_schema.ts`
|
|
5
|
+
* so the schema module stays Zod-only (paired with `auth/auth_ddl.ts` and
|
|
6
|
+
* `auth/role_grant_offer_ddl.ts`).
|
|
7
|
+
*
|
|
8
|
+
* Multi-actor invariants the envelope columns assume:
|
|
9
|
+
*
|
|
10
|
+
* - `actor_id` + `account_id`, when both populated, refer to the same
|
|
11
|
+
* account (derivable via `actor.account_id`). Denormalized for indexed
|
|
12
|
+
* audit queries; do not let them disagree.
|
|
13
|
+
* - `target_actor_id` + `target_account_id`, same rule when both populated.
|
|
14
|
+
* - `target_account_id` is the SSE/WS socket-close key — sessions stay
|
|
15
|
+
* account-grain after multi-actor lands, so this column carries the
|
|
16
|
+
* routing identity even on actor-bound events.
|
|
17
|
+
* - `target_actor_id` is populated iff the event subject is actor-bound
|
|
18
|
+
* (see `AuditLogEvent.target_actor_id` doc-comment for the rule).
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
export const AUDIT_LOG_SCHEMA = `
|
|
23
|
+
CREATE TABLE IF NOT EXISTS audit_log (
|
|
24
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
25
|
+
seq SERIAL NOT NULL,
|
|
26
|
+
event_type TEXT NOT NULL,
|
|
27
|
+
outcome TEXT NOT NULL DEFAULT 'success',
|
|
28
|
+
actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
|
|
29
|
+
account_id UUID REFERENCES account(id) ON DELETE SET NULL,
|
|
30
|
+
target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,
|
|
31
|
+
target_actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
|
|
32
|
+
ip TEXT,
|
|
33
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
34
|
+
metadata JSONB
|
|
35
|
+
)`;
|
|
36
|
+
export const AUDIT_LOG_INDEXES = [
|
|
37
|
+
`CREATE INDEX IF NOT EXISTS idx_audit_log_seq ON audit_log(seq DESC)`,
|
|
38
|
+
`CREATE INDEX IF NOT EXISTS idx_audit_log_account ON audit_log(account_id)`,
|
|
39
|
+
`CREATE INDEX IF NOT EXISTS idx_audit_log_event_type ON audit_log(event_type)`,
|
|
40
|
+
`CREATE INDEX IF NOT EXISTS idx_audit_log_target_account ON audit_log(target_account_id)`,
|
|
41
|
+
`CREATE INDEX IF NOT EXISTS idx_audit_log_target_actor ON audit_log(target_actor_id)`,
|
|
42
|
+
];
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Audit log
|
|
2
|
+
* Audit log types and client-safe Zod schemas.
|
|
3
3
|
*
|
|
4
4
|
* Records auth mutations (login, logout, grant, revoke, etc.) for
|
|
5
5
|
* security monitoring and operational visibility.
|
|
6
6
|
*
|
|
7
|
+
* Table DDL and indexes live in `auth/audit_log_ddl.ts`.
|
|
8
|
+
*
|
|
7
9
|
* @module
|
|
8
10
|
*/
|
|
9
11
|
import { z } from 'zod';
|
|
@@ -411,6 +413,4 @@ export declare const AdminSessionJson: z.ZodObject<{
|
|
|
411
413
|
username: z.ZodString;
|
|
412
414
|
}, z.core.$strict>;
|
|
413
415
|
export type AdminSessionJson = z.infer<typeof AdminSessionJson>;
|
|
414
|
-
export declare const AUDIT_LOG_SCHEMA = "\nCREATE TABLE IF NOT EXISTS audit_log (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n seq SERIAL NOT NULL,\n event_type TEXT NOT NULL,\n outcome TEXT NOT NULL DEFAULT 'success',\n actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,\n account_id UUID REFERENCES account(id) ON DELETE SET NULL,\n target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,\n target_actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,\n ip TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n metadata JSONB\n)";
|
|
415
|
-
export declare const AUDIT_LOG_INDEXES: string[];
|
|
416
416
|
//# sourceMappingURL=audit_log_schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_log_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_schema.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"audit_log_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAO5C;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,8aAsBnB,CAAC;AAEZ,wCAAwC;AACxC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;EAA4B,CAAC;AACxD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,QAA+B,CAAC;AAExE,0DAA0D;AAC1D,eAAO,MAAM,kBAAkB,aAE7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,2CAA2C;AAC3C,eAAO,MAAM,YAAY;;;EAAiC,CAAC;AAC3D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6MW,CAAC;AAE/C,+EAA+E;AAC/E,MAAM,MAAM,gBAAgB,GAAG;KAC7B,CAAC,IAAI,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;CAClE,CAAC;AAEF,oGAAoG;AACpG,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,IAAI,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,kBAAkB,CAAC;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB;;;;;;;;;;;;;OAaG;IACH,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACzC;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,cAAc,EAC1D,OAAO,aAAa,GAAG;IAAC,UAAU,EAAE,CAAC,CAAA;CAAC,KACpC,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAExB,CAAC;AAEF,6CAA6C;AAC7C,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,GAAG,cAAc;IAC/D,UAAU,EAAE,CAAC,CAAC;IACd,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAChC,eAAe,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,SAAS,cAAc,GAChC,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GACtD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,cAAc;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CAC/D;AAED,4FAA4F;AAC5F,eAAO,MAAM,wBAAwB,EAAE,cAGrC,CAAC;AAEH,6CAA6C;AAC7C,MAAM,WAAW,2BAA2B;IAC3C;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;CAC1D;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,GAAI,UAAU,2BAA2B,KAAG,cA2B/E,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;kBAY5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,+DAA+D;AAC/D,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;kBAGzC,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAE5F,wEAAwE;AACxE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;kBAGpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF,iEAAiE;AACjE,eAAO,MAAM,gBAAgB;;;;;;;kBAE3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Audit log
|
|
2
|
+
* Audit log types and client-safe Zod schemas.
|
|
3
3
|
*
|
|
4
4
|
* Records auth mutations (login, logout, grant, revoke, etc.) for
|
|
5
5
|
* security monitoring and operational visibility.
|
|
6
6
|
*
|
|
7
|
+
* Table DDL and indexes live in `auth/audit_log_ddl.ts`.
|
|
8
|
+
*
|
|
7
9
|
* @module
|
|
8
10
|
*/
|
|
9
11
|
import { z } from 'zod';
|
|
@@ -345,36 +347,3 @@ export const RoleGrantHistoryEventJson = AuditLogEventJson.extend({
|
|
|
345
347
|
export const AdminSessionJson = AuthSessionJson.extend({
|
|
346
348
|
username: z.string(),
|
|
347
349
|
});
|
|
348
|
-
// Schema DDL
|
|
349
|
-
//
|
|
350
|
-
// Multi-actor invariants the envelope columns assume:
|
|
351
|
-
// - `actor_id` + `account_id`, when both populated, refer to the same
|
|
352
|
-
// account (derivable via `actor.account_id`). Denormalized for
|
|
353
|
-
// indexed audit queries; do not let them disagree.
|
|
354
|
-
// - `target_actor_id` + `target_account_id`, same rule when both populated.
|
|
355
|
-
// - `target_account_id` is the SSE/WS socket-close key — sessions stay
|
|
356
|
-
// account-grain after multi-actor lands, so this column carries
|
|
357
|
-
// the routing identity even on actor-bound events.
|
|
358
|
-
// - `target_actor_id` is populated iff the event subject is actor-bound
|
|
359
|
-
// (see `AuditLogEvent.target_actor_id` doc-comment for the rule).
|
|
360
|
-
export const AUDIT_LOG_SCHEMA = `
|
|
361
|
-
CREATE TABLE IF NOT EXISTS audit_log (
|
|
362
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
363
|
-
seq SERIAL NOT NULL,
|
|
364
|
-
event_type TEXT NOT NULL,
|
|
365
|
-
outcome TEXT NOT NULL DEFAULT 'success',
|
|
366
|
-
actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
|
|
367
|
-
account_id UUID REFERENCES account(id) ON DELETE SET NULL,
|
|
368
|
-
target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,
|
|
369
|
-
target_actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
|
|
370
|
-
ip TEXT,
|
|
371
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
372
|
-
metadata JSONB
|
|
373
|
-
)`;
|
|
374
|
-
export const AUDIT_LOG_INDEXES = [
|
|
375
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_seq ON audit_log(seq DESC)`,
|
|
376
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_account ON audit_log(account_id)`,
|
|
377
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_event_type ON audit_log(event_type)`,
|
|
378
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_target_account ON audit_log(target_account_id)`,
|
|
379
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_target_actor ON audit_log(target_actor_id)`,
|
|
380
|
-
];
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Identity-table DDL — `CREATE TABLE`, index, and seed statements for the
|
|
3
|
+
* core auth tables (account, actor, role_grant, auth_session, api_token,
|
|
4
|
+
* bootstrap_lock, invite, app_settings).
|
|
3
5
|
*
|
|
4
|
-
* Consumed by `auth/migrations.ts`.
|
|
5
|
-
*
|
|
6
|
+
* Consumed by `auth/migrations.ts`. Paired with `auth/audit_log_ddl.ts`
|
|
7
|
+
* (audit table) and `auth/role_grant_offer_ddl.ts` (offer table) — DDL lives
|
|
8
|
+
* in `*_ddl.ts`, Zod schemas in `*_schema.ts`.
|
|
6
9
|
*
|
|
7
10
|
* @module
|
|
8
11
|
*/
|
|
@@ -24,4 +27,4 @@ export declare const INVITE_SCHEMA = "\nCREATE TABLE IF NOT EXISTS invite (\n i
|
|
|
24
27
|
export declare const INVITE_INDEXES: string[];
|
|
25
28
|
export declare const APP_SETTINGS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS app_settings (\n id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),\n open_signup BOOLEAN NOT NULL DEFAULT false,\n updated_at TIMESTAMPTZ,\n updated_by UUID\n)";
|
|
26
29
|
export declare const APP_SETTINGS_SEED = "\nINSERT INTO app_settings (id) VALUES (1) ON CONFLICT DO NOTHING";
|
|
27
|
-
//# sourceMappingURL=
|
|
30
|
+
//# sourceMappingURL=auth_ddl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/auth_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,cAAc,8WAWzB,CAAC;AAEH,eAAO,MAAM,YAAY,mUAQvB,CAAC;AAEH,eAAO,MAAM,WAAW,wEAC0C,CAAC;AAEnE,eAAO,MAAM,iBAAiB,2ZAU5B,CAAC;AAEH,eAAO,MAAM,kBAAkB,UAI9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,0RAO9B,CAAC;AAEH,eAAO,MAAM,oBAAoB,UAGhC,CAAC;AAEF,eAAO,MAAM,gBAAgB,iUAU3B,CAAC;AAEH,eAAO,MAAM,mBAAmB,4GACsE,CAAC;AAEvG,eAAO,MAAM,yBAAyB,6FACiD,CAAC;AAExF,eAAO,MAAM,eAAe,gFAC8C,CAAC;AAE3E,eAAO,MAAM,qBAAqB,wJAIhC,CAAC;AAEH,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,yHAGP,CAAC;AAE1B,eAAO,MAAM,aAAa,6ZAUxB,CAAC;AAEH,eAAO,MAAM,cAAc,UAI1B,CAAC;AAEF,eAAO,MAAM,mBAAmB,oMAM9B,CAAC;AAEH,eAAO,MAAM,iBAAiB,sEACkC,CAAC"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Identity-table DDL — `CREATE TABLE`, index, and seed statements for the
|
|
3
|
+
* core auth tables (account, actor, role_grant, auth_session, api_token,
|
|
4
|
+
* bootstrap_lock, invite, app_settings).
|
|
3
5
|
*
|
|
4
|
-
* Consumed by `auth/migrations.ts`.
|
|
5
|
-
*
|
|
6
|
+
* Consumed by `auth/migrations.ts`. Paired with `auth/audit_log_ddl.ts`
|
|
7
|
+
* (audit table) and `auth/role_grant_offer_ddl.ts` (offer table) — DDL lives
|
|
8
|
+
* in `*_ddl.ts`, Zod schemas in `*_schema.ts`.
|
|
6
9
|
*
|
|
7
10
|
* @module
|
|
8
11
|
*/
|
package/dist/auth/migrations.js
CHANGED
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
*
|
|
37
37
|
* @module
|
|
38
38
|
*/
|
|
39
|
-
import { ACCOUNT_SCHEMA, ACCOUNT_EMAIL_INDEX, ACCOUNT_USERNAME_CI_INDEX, ACTOR_SCHEMA, ACTOR_INDEX, ROLE_GRANT_SCHEMA, ROLE_GRANT_INDEXES, AUTH_SESSION_SCHEMA, AUTH_SESSION_INDEXES, API_TOKEN_SCHEMA, API_TOKEN_INDEX, BOOTSTRAP_LOCK_SCHEMA, BOOTSTRAP_LOCK_SEED, INVITE_SCHEMA, INVITE_INDEXES, APP_SETTINGS_SCHEMA, APP_SETTINGS_SEED, } from './
|
|
40
|
-
import { AUDIT_LOG_SCHEMA, AUDIT_LOG_INDEXES } from './
|
|
41
|
-
import { ROLE_GRANT_OFFER_SCHEMA, ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX, ROLE_GRANT_OFFER_INBOX_INDEX, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, } from './
|
|
39
|
+
import { ACCOUNT_SCHEMA, ACCOUNT_EMAIL_INDEX, ACCOUNT_USERNAME_CI_INDEX, ACTOR_SCHEMA, ACTOR_INDEX, ROLE_GRANT_SCHEMA, ROLE_GRANT_INDEXES, AUTH_SESSION_SCHEMA, AUTH_SESSION_INDEXES, API_TOKEN_SCHEMA, API_TOKEN_INDEX, BOOTSTRAP_LOCK_SCHEMA, BOOTSTRAP_LOCK_SEED, INVITE_SCHEMA, INVITE_INDEXES, APP_SETTINGS_SCHEMA, APP_SETTINGS_SEED, } from './auth_ddl.js';
|
|
40
|
+
import { AUDIT_LOG_SCHEMA, AUDIT_LOG_INDEXES } from './audit_log_ddl.js';
|
|
41
|
+
import { ROLE_GRANT_OFFER_SCHEMA, ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX, ROLE_GRANT_OFFER_INBOX_INDEX, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, } from './role_grant_offer_ddl.js';
|
|
42
42
|
/** Namespace identifier for fuz_app auth migrations. */
|
|
43
43
|
export const AUTH_MIGRATION_NAMESPACE = 'fuz_auth';
|
|
44
44
|
/**
|
|
@@ -105,7 +105,7 @@ export const AUTH_MIGRATIONS = [
|
|
|
105
105
|
},
|
|
106
106
|
// v1: consentful role_grants — role_grant_offer table + scoped role_grants
|
|
107
107
|
{
|
|
108
|
-
name: '
|
|
108
|
+
name: 'role_grant_offer_and_scoped_role_grants',
|
|
109
109
|
up: async (db) => {
|
|
110
110
|
await db.query(ROLE_GRANT_OFFER_SCHEMA);
|
|
111
111
|
await db.query(ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role grant offer DDL — `CREATE TABLE` + index statements and the index-side
|
|
3
|
+
* sentinel constants the queries / migrations interpolate.
|
|
4
|
+
*
|
|
5
|
+
* Separated from `auth/role_grant_offer_schema.ts` so the schema module stays
|
|
6
|
+
* Zod-only (paired with `auth/auth_ddl.ts` and `auth/audit_log_ddl.ts`).
|
|
7
|
+
*
|
|
8
|
+
* An offer is a pending grant awaiting recipient consent. Lifecycle states
|
|
9
|
+
* are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
|
|
10
|
+
* at most one of `accepted_at` / `declined_at` / `retracted_at` may be set.
|
|
11
|
+
* On accept, the offer's `resulting_role_grant_id` links to the role_grant row
|
|
12
|
+
* produced by `query_accept_offer`.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
|
|
17
|
+
export declare const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
18
|
+
/**
|
|
19
|
+
* Index-side token for the global case in the partial unique index. Uppercase
|
|
20
|
+
* so it cannot collide with consumer-declared `ScopeKindName` values (which
|
|
21
|
+
* are lowercase by regex). Never appears as a column value — column-level
|
|
22
|
+
* `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = "GLOBAL";
|
|
25
|
+
export declare const ROLE_GRANT_OFFER_SCHEMA = "\nCREATE TABLE IF NOT EXISTS role_grant_offer (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n scope_kind TEXT NULL,\n scope_id UUID NULL,\n message TEXT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ NOT NULL,\n accepted_at TIMESTAMPTZ NULL,\n declined_at TIMESTAMPTZ NULL,\n decline_reason TEXT NULL,\n retracted_at TIMESTAMPTZ NULL,\n superseded_at TIMESTAMPTZ NULL,\n resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,\n CONSTRAINT role_grant_offer_single_terminal CHECK (\n (accepted_at IS NOT NULL)::int\n + (declined_at IS NOT NULL)::int\n + (retracted_at IS NOT NULL)::int\n + (superseded_at IS NOT NULL)::int\n <= 1\n ),\n CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (\n (accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)\n ),\n CONSTRAINT role_grant_offer_reason_iff_declined CHECK (\n decline_reason IS NULL OR declined_at IS NOT NULL\n ),\n CONSTRAINT role_grant_offer_scope_kind_paired CHECK (\n (scope_kind IS NULL) = (scope_id IS NULL)\n )\n)";
|
|
26
|
+
/**
|
|
27
|
+
* At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
|
|
28
|
+
*
|
|
29
|
+
* Including `from_actor_id` in the tuple lets multiple grantors coexist —
|
|
30
|
+
* teacher A and teacher B can each have a pending `classroom_student` offer
|
|
31
|
+
* for the same student and scope. A same-grantor re-offer upserts the
|
|
32
|
+
* existing pending row. `COALESCE` collapses `NULL` scopes into the
|
|
33
|
+
* sentinel values so Postgres's NULL-in-unique-index quirk does not allow
|
|
34
|
+
* duplicate global pending offers; the `scope_kind` / `scope_id` pair is
|
|
35
|
+
* always either both null (global) or both non-null (scoped) per the
|
|
36
|
+
* `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
|
|
37
|
+
* always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
|
|
38
|
+
* match this expression literally.
|
|
39
|
+
*/
|
|
40
|
+
export declare const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = "\nCREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique\n ON role_grant_offer (\n to_account_id,\n role,\n COALESCE(scope_kind, 'GLOBAL'),\n COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),\n from_actor_id\n )\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
|
|
41
|
+
/** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
|
|
42
|
+
export declare const ROLE_GRANT_OFFER_INBOX_INDEX = "\nCREATE INDEX IF NOT EXISTS role_grant_offer_inbox\n ON role_grant_offer (to_account_id, expires_at)\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
|
|
43
|
+
//# sourceMappingURL=role_grant_offer_ddl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role_grant_offer_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,mHAAmH;AACnH,eAAO,MAAM,oCAAoC,yCAAyC,CAAC;AAE3F;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,WAAW,CAAC;AAEjE,eAAO,MAAM,uBAAuB,qzCAkClC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qCAAqC,2XAYpB,CAAC;AAE/B,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,kOAMX,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role grant offer DDL — `CREATE TABLE` + index statements and the index-side
|
|
3
|
+
* sentinel constants the queries / migrations interpolate.
|
|
4
|
+
*
|
|
5
|
+
* Separated from `auth/role_grant_offer_schema.ts` so the schema module stays
|
|
6
|
+
* Zod-only (paired with `auth/auth_ddl.ts` and `auth/audit_log_ddl.ts`).
|
|
7
|
+
*
|
|
8
|
+
* An offer is a pending grant awaiting recipient consent. Lifecycle states
|
|
9
|
+
* are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
|
|
10
|
+
* at most one of `accepted_at` / `declined_at` / `retracted_at` may be set.
|
|
11
|
+
* On accept, the offer's `resulting_role_grant_id` links to the role_grant row
|
|
12
|
+
* produced by `query_accept_offer`.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
|
|
17
|
+
export const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = '00000000-0000-0000-0000-000000000000';
|
|
18
|
+
/**
|
|
19
|
+
* Index-side token for the global case in the partial unique index. Uppercase
|
|
20
|
+
* so it cannot collide with consumer-declared `ScopeKindName` values (which
|
|
21
|
+
* are lowercase by regex). Never appears as a column value — column-level
|
|
22
|
+
* `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
|
|
23
|
+
*/
|
|
24
|
+
export const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = 'GLOBAL';
|
|
25
|
+
export const ROLE_GRANT_OFFER_SCHEMA = `
|
|
26
|
+
CREATE TABLE IF NOT EXISTS role_grant_offer (
|
|
27
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
28
|
+
from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,
|
|
29
|
+
to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,
|
|
30
|
+
to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,
|
|
31
|
+
role TEXT NOT NULL,
|
|
32
|
+
scope_kind TEXT NULL,
|
|
33
|
+
scope_id UUID NULL,
|
|
34
|
+
message TEXT NULL,
|
|
35
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
36
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
37
|
+
accepted_at TIMESTAMPTZ NULL,
|
|
38
|
+
declined_at TIMESTAMPTZ NULL,
|
|
39
|
+
decline_reason TEXT NULL,
|
|
40
|
+
retracted_at TIMESTAMPTZ NULL,
|
|
41
|
+
superseded_at TIMESTAMPTZ NULL,
|
|
42
|
+
resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,
|
|
43
|
+
CONSTRAINT role_grant_offer_single_terminal CHECK (
|
|
44
|
+
(accepted_at IS NOT NULL)::int
|
|
45
|
+
+ (declined_at IS NOT NULL)::int
|
|
46
|
+
+ (retracted_at IS NOT NULL)::int
|
|
47
|
+
+ (superseded_at IS NOT NULL)::int
|
|
48
|
+
<= 1
|
|
49
|
+
),
|
|
50
|
+
CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (
|
|
51
|
+
(accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)
|
|
52
|
+
),
|
|
53
|
+
CONSTRAINT role_grant_offer_reason_iff_declined CHECK (
|
|
54
|
+
decline_reason IS NULL OR declined_at IS NOT NULL
|
|
55
|
+
),
|
|
56
|
+
CONSTRAINT role_grant_offer_scope_kind_paired CHECK (
|
|
57
|
+
(scope_kind IS NULL) = (scope_id IS NULL)
|
|
58
|
+
)
|
|
59
|
+
)`;
|
|
60
|
+
/**
|
|
61
|
+
* At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
|
|
62
|
+
*
|
|
63
|
+
* Including `from_actor_id` in the tuple lets multiple grantors coexist —
|
|
64
|
+
* teacher A and teacher B can each have a pending `classroom_student` offer
|
|
65
|
+
* for the same student and scope. A same-grantor re-offer upserts the
|
|
66
|
+
* existing pending row. `COALESCE` collapses `NULL` scopes into the
|
|
67
|
+
* sentinel values so Postgres's NULL-in-unique-index quirk does not allow
|
|
68
|
+
* duplicate global pending offers; the `scope_kind` / `scope_id` pair is
|
|
69
|
+
* always either both null (global) or both non-null (scoped) per the
|
|
70
|
+
* `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
|
|
71
|
+
* always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
|
|
72
|
+
* match this expression literally.
|
|
73
|
+
*/
|
|
74
|
+
export const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = `
|
|
75
|
+
CREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique
|
|
76
|
+
ON role_grant_offer (
|
|
77
|
+
to_account_id,
|
|
78
|
+
role,
|
|
79
|
+
COALESCE(scope_kind, '${ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN}'),
|
|
80
|
+
COALESCE(scope_id, '${ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID}'::uuid),
|
|
81
|
+
from_actor_id
|
|
82
|
+
)
|
|
83
|
+
WHERE accepted_at IS NULL
|
|
84
|
+
AND declined_at IS NULL
|
|
85
|
+
AND retracted_at IS NULL
|
|
86
|
+
AND superseded_at IS NULL`;
|
|
87
|
+
/** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
|
|
88
|
+
export const ROLE_GRANT_OFFER_INBOX_INDEX = `
|
|
89
|
+
CREATE INDEX IF NOT EXISTS role_grant_offer_inbox
|
|
90
|
+
ON role_grant_offer (to_account_id, expires_at)
|
|
91
|
+
WHERE accepted_at IS NULL
|
|
92
|
+
AND declined_at IS NULL
|
|
93
|
+
AND retracted_at IS NULL
|
|
94
|
+
AND superseded_at IS NULL`;
|
|
95
|
+
// **Deferred**: a `role_grant_offer_to_actor` partial index belongs here once
|
|
96
|
+
// an actor-side inbox query (`query_role_grant_offer_list_for_actor`) lands —
|
|
97
|
+
// no current consumer filters on `to_actor_id`, and adding the index
|
|
98
|
+
// before the query is paying write-amp for nothing. Land the index in
|
|
99
|
+
// the same slice as the query.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import type { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
15
15
|
import type { QueryDeps } from '../db/query_deps.js';
|
|
16
16
|
import type { RoleGrant } from './account_schema.js';
|
|
17
|
-
import {
|
|
17
|
+
import type { CreateRoleGrantOfferInput, RoleGrantOffer, SupersededOffer } from './role_grant_offer_schema.js';
|
|
18
18
|
import type { AuditLogEvent } from './audit_log_schema.js';
|
|
19
19
|
/**
|
|
20
20
|
* Error thrown by offer-lifecycle queries when the offer is in a non-pending
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* @module
|
|
13
13
|
*/
|
|
14
14
|
import { assert_row } from '../db/assert_row.js';
|
|
15
|
-
import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './
|
|
15
|
+
import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './role_grant_offer_ddl.js';
|
|
16
16
|
import { query_audit_log } from './audit_log_queries.js';
|
|
17
17
|
/**
|
|
18
18
|
* Error thrown by offer-lifecycle queries when the offer is in a non-pending
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Role grant offer
|
|
2
|
+
* Role grant offer types and client-safe schemas.
|
|
3
3
|
*
|
|
4
4
|
* An offer is a pending grant awaiting recipient consent. Lifecycle states
|
|
5
5
|
* are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
|
|
@@ -7,41 +7,16 @@
|
|
|
7
7
|
* On accept, the offer's `resulting_role_grant_id` links to the role_grant row
|
|
8
8
|
* produced by `query_accept_offer`.
|
|
9
9
|
*
|
|
10
|
+
* Table DDL and index-side sentinel constants live in `auth/role_grant_offer_ddl.ts`.
|
|
11
|
+
*
|
|
10
12
|
* @module
|
|
11
13
|
*/
|
|
12
14
|
import { z } from 'zod';
|
|
13
15
|
import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
14
|
-
/** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
|
|
15
|
-
export declare const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
16
16
|
/** Maximum length of the optional message attached to an offer. */
|
|
17
17
|
export declare const ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX = 500;
|
|
18
18
|
/** Default TTL for a newly created offer — 30 days. Matches GitHub org-invite expiry. */
|
|
19
19
|
export declare const ROLE_GRANT_OFFER_DEFAULT_TTL_MS: number;
|
|
20
|
-
export declare const ROLE_GRANT_OFFER_SCHEMA = "\nCREATE TABLE IF NOT EXISTS role_grant_offer (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n scope_kind TEXT NULL,\n scope_id UUID NULL,\n message TEXT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ NOT NULL,\n accepted_at TIMESTAMPTZ NULL,\n declined_at TIMESTAMPTZ NULL,\n decline_reason TEXT NULL,\n retracted_at TIMESTAMPTZ NULL,\n superseded_at TIMESTAMPTZ NULL,\n resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,\n CONSTRAINT role_grant_offer_single_terminal CHECK (\n (accepted_at IS NOT NULL)::int\n + (declined_at IS NOT NULL)::int\n + (retracted_at IS NOT NULL)::int\n + (superseded_at IS NOT NULL)::int\n <= 1\n ),\n CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (\n (accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)\n ),\n CONSTRAINT role_grant_offer_reason_iff_declined CHECK (\n decline_reason IS NULL OR declined_at IS NOT NULL\n ),\n CONSTRAINT role_grant_offer_scope_kind_paired CHECK (\n (scope_kind IS NULL) = (scope_id IS NULL)\n )\n)";
|
|
21
|
-
/**
|
|
22
|
-
* Index-side token for the global case in the partial unique index. Uppercase
|
|
23
|
-
* so it cannot collide with consumer-declared `ScopeKindName` values (which
|
|
24
|
-
* are lowercase by regex). Never appears as a column value — column-level
|
|
25
|
-
* `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
|
|
26
|
-
*/
|
|
27
|
-
export declare const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = "GLOBAL";
|
|
28
|
-
/**
|
|
29
|
-
* At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
|
|
30
|
-
*
|
|
31
|
-
* Including `from_actor_id` in the tuple lets multiple grantors coexist —
|
|
32
|
-
* teacher A and teacher B can each have a pending `classroom_student` offer
|
|
33
|
-
* for the same student and scope. A same-grantor re-offer upserts the
|
|
34
|
-
* existing pending row. `COALESCE` collapses `NULL` scopes into the
|
|
35
|
-
* sentinel values so Postgres's NULL-in-unique-index quirk does not allow
|
|
36
|
-
* duplicate global pending offers; the `scope_kind` / `scope_id` pair is
|
|
37
|
-
* always either both null (global) or both non-null (scoped) per the
|
|
38
|
-
* `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
|
|
39
|
-
* always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
|
|
40
|
-
* match this expression literally.
|
|
41
|
-
*/
|
|
42
|
-
export declare const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = "\nCREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique\n ON role_grant_offer (\n to_account_id,\n role,\n COALESCE(scope_kind, 'GLOBAL'),\n COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),\n from_actor_id\n )\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
|
|
43
|
-
/** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
|
|
44
|
-
export declare const ROLE_GRANT_OFFER_INBOX_INDEX = "\nCREATE INDEX IF NOT EXISTS role_grant_offer_inbox\n ON role_grant_offer (to_account_id, expires_at)\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
|
|
45
20
|
/** Role grant offer row as returned by the database. */
|
|
46
21
|
export interface RoleGrantOffer {
|
|
47
22
|
id: Uuid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAI5C,mEAAmE;AACnE,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;kBAoD0D,CAAC;AAC1F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,kBAiB/D,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Role grant offer
|
|
2
|
+
* Role grant offer types and client-safe schemas.
|
|
3
3
|
*
|
|
4
4
|
* An offer is a pending grant awaiting recipient consent. Lifecycle states
|
|
5
5
|
* are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
|
|
@@ -7,94 +7,17 @@
|
|
|
7
7
|
* On accept, the offer's `resulting_role_grant_id` links to the role_grant row
|
|
8
8
|
* produced by `query_accept_offer`.
|
|
9
9
|
*
|
|
10
|
+
* Table DDL and index-side sentinel constants live in `auth/role_grant_offer_ddl.ts`.
|
|
11
|
+
*
|
|
10
12
|
* @module
|
|
11
13
|
*/
|
|
12
14
|
import { z } from 'zod';
|
|
13
15
|
import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
14
16
|
import { RoleName } from './role_schema.js';
|
|
15
|
-
/** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
|
|
16
|
-
export const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = '00000000-0000-0000-0000-000000000000';
|
|
17
17
|
/** Maximum length of the optional message attached to an offer. */
|
|
18
18
|
export const ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX = 500;
|
|
19
19
|
/** Default TTL for a newly created offer — 30 days. Matches GitHub org-invite expiry. */
|
|
20
20
|
export const ROLE_GRANT_OFFER_DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
21
|
-
export const ROLE_GRANT_OFFER_SCHEMA = `
|
|
22
|
-
CREATE TABLE IF NOT EXISTS role_grant_offer (
|
|
23
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
24
|
-
from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,
|
|
25
|
-
to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,
|
|
26
|
-
to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,
|
|
27
|
-
role TEXT NOT NULL,
|
|
28
|
-
scope_kind TEXT NULL,
|
|
29
|
-
scope_id UUID NULL,
|
|
30
|
-
message TEXT NULL,
|
|
31
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
32
|
-
expires_at TIMESTAMPTZ NOT NULL,
|
|
33
|
-
accepted_at TIMESTAMPTZ NULL,
|
|
34
|
-
declined_at TIMESTAMPTZ NULL,
|
|
35
|
-
decline_reason TEXT NULL,
|
|
36
|
-
retracted_at TIMESTAMPTZ NULL,
|
|
37
|
-
superseded_at TIMESTAMPTZ NULL,
|
|
38
|
-
resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,
|
|
39
|
-
CONSTRAINT role_grant_offer_single_terminal CHECK (
|
|
40
|
-
(accepted_at IS NOT NULL)::int
|
|
41
|
-
+ (declined_at IS NOT NULL)::int
|
|
42
|
-
+ (retracted_at IS NOT NULL)::int
|
|
43
|
-
+ (superseded_at IS NOT NULL)::int
|
|
44
|
-
<= 1
|
|
45
|
-
),
|
|
46
|
-
CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (
|
|
47
|
-
(accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)
|
|
48
|
-
),
|
|
49
|
-
CONSTRAINT role_grant_offer_reason_iff_declined CHECK (
|
|
50
|
-
decline_reason IS NULL OR declined_at IS NOT NULL
|
|
51
|
-
),
|
|
52
|
-
CONSTRAINT role_grant_offer_scope_kind_paired CHECK (
|
|
53
|
-
(scope_kind IS NULL) = (scope_id IS NULL)
|
|
54
|
-
)
|
|
55
|
-
)`;
|
|
56
|
-
/**
|
|
57
|
-
* Index-side token for the global case in the partial unique index. Uppercase
|
|
58
|
-
* so it cannot collide with consumer-declared `ScopeKindName` values (which
|
|
59
|
-
* are lowercase by regex). Never appears as a column value — column-level
|
|
60
|
-
* `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
|
|
61
|
-
*/
|
|
62
|
-
export const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = 'GLOBAL';
|
|
63
|
-
/**
|
|
64
|
-
* At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
|
|
65
|
-
*
|
|
66
|
-
* Including `from_actor_id` in the tuple lets multiple grantors coexist —
|
|
67
|
-
* teacher A and teacher B can each have a pending `classroom_student` offer
|
|
68
|
-
* for the same student and scope. A same-grantor re-offer upserts the
|
|
69
|
-
* existing pending row. `COALESCE` collapses `NULL` scopes into the
|
|
70
|
-
* sentinel values so Postgres's NULL-in-unique-index quirk does not allow
|
|
71
|
-
* duplicate global pending offers; the `scope_kind` / `scope_id` pair is
|
|
72
|
-
* always either both null (global) or both non-null (scoped) per the
|
|
73
|
-
* `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
|
|
74
|
-
* always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
|
|
75
|
-
* match this expression literally.
|
|
76
|
-
*/
|
|
77
|
-
export const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = `
|
|
78
|
-
CREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique
|
|
79
|
-
ON role_grant_offer (
|
|
80
|
-
to_account_id,
|
|
81
|
-
role,
|
|
82
|
-
COALESCE(scope_kind, '${ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN}'),
|
|
83
|
-
COALESCE(scope_id, '${ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID}'::uuid),
|
|
84
|
-
from_actor_id
|
|
85
|
-
)
|
|
86
|
-
WHERE accepted_at IS NULL
|
|
87
|
-
AND declined_at IS NULL
|
|
88
|
-
AND retracted_at IS NULL
|
|
89
|
-
AND superseded_at IS NULL`;
|
|
90
|
-
/** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
|
|
91
|
-
export const ROLE_GRANT_OFFER_INBOX_INDEX = `
|
|
92
|
-
CREATE INDEX IF NOT EXISTS role_grant_offer_inbox
|
|
93
|
-
ON role_grant_offer (to_account_id, expires_at)
|
|
94
|
-
WHERE accepted_at IS NULL
|
|
95
|
-
AND declined_at IS NULL
|
|
96
|
-
AND retracted_at IS NULL
|
|
97
|
-
AND superseded_at IS NULL`;
|
|
98
21
|
/** Zod schema for client-safe role_grant offer data. */
|
|
99
22
|
export const RoleGrantOfferJson = z
|
|
100
23
|
.strictObject({
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
10
10
|
import type { QueryDeps } from '../db/query_deps.js';
|
|
11
11
|
import type { RoleGrant, CreateRoleGrantInput } from './account_schema.js';
|
|
12
|
-
import {
|
|
12
|
+
import type { SupersededOffer } from './role_grant_offer_schema.js';
|
|
13
13
|
/**
|
|
14
14
|
* Grant a role_grant to an actor.
|
|
15
15
|
* Idempotent — if an active role_grant already exists for this actor, role, and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAC,SAAS,EAAE,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import { assert_row } from '../db/assert_row.js';
|
|
10
|
-
import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './
|
|
10
|
+
import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './role_grant_offer_ddl.js';
|
|
11
11
|
/**
|
|
12
12
|
* Grant a role_grant to an actor.
|
|
13
13
|
* Idempotent — if an active role_grant already exists for this actor, role, and
|
package/package.json
CHANGED
package/dist/auth/ddl.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,cAAc,8WAWzB,CAAC;AAEH,eAAO,MAAM,YAAY,mUAQvB,CAAC;AAEH,eAAO,MAAM,WAAW,wEAC0C,CAAC;AAEnE,eAAO,MAAM,iBAAiB,2ZAU5B,CAAC;AAEH,eAAO,MAAM,kBAAkB,UAI9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,0RAO9B,CAAC;AAEH,eAAO,MAAM,oBAAoB,UAGhC,CAAC;AAEF,eAAO,MAAM,gBAAgB,iUAU3B,CAAC;AAEH,eAAO,MAAM,mBAAmB,4GACsE,CAAC;AAEvG,eAAO,MAAM,yBAAyB,6FACiD,CAAC;AAExF,eAAO,MAAM,eAAe,gFAC8C,CAAC;AAE3E,eAAO,MAAM,qBAAqB,wJAIhC,CAAC;AAEH,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,yHAGP,CAAC;AAE1B,eAAO,MAAM,aAAa,6ZAUxB,CAAC;AAEH,eAAO,MAAM,cAAc,UAI1B,CAAC;AAEF,eAAO,MAAM,mBAAmB,oMAM9B,CAAC;AAEH,eAAO,MAAM,iBAAiB,sEACkC,CAAC"}
|