@heychunky/core 0.14.0 → 0.15.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 (64) hide show
  1. package/dist/admin/apps.d.ts +3 -0
  2. package/dist/admin/apps.d.ts.map +1 -1
  3. package/dist/admin/apps.js +16 -2
  4. package/dist/admin/apps.js.map +1 -1
  5. package/dist/admin/index.d.ts +2 -0
  6. package/dist/admin/index.d.ts.map +1 -1
  7. package/dist/admin/index.js +2 -0
  8. package/dist/admin/index.js.map +1 -1
  9. package/dist/admin/migrations.d.ts +19 -0
  10. package/dist/admin/migrations.d.ts.map +1 -1
  11. package/dist/admin/migrations.js +27 -0
  12. package/dist/admin/migrations.js.map +1 -1
  13. package/dist/admin/orgs.d.ts +142 -0
  14. package/dist/admin/orgs.d.ts.map +1 -0
  15. package/dist/admin/orgs.js +166 -0
  16. package/dist/admin/orgs.js.map +1 -0
  17. package/dist/admin/register.d.ts +98 -0
  18. package/dist/admin/register.d.ts.map +1 -0
  19. package/dist/admin/register.js +191 -0
  20. package/dist/admin/register.js.map +1 -0
  21. package/dist/services/db.d.ts +16 -0
  22. package/dist/services/db.d.ts.map +1 -1
  23. package/dist/services/db.js +27 -22
  24. package/dist/services/db.js.map +1 -1
  25. package/package.json +3 -2
  26. package/register/0001-baseline/01-invite-requests.sql +11 -0
  27. package/register/0001-baseline/02-invite-requests-email-key.sql +1 -0
  28. package/register/0001-baseline/03-login-codes.sql +12 -0
  29. package/register/0001-baseline/04-login-codes-email-idx.sql +1 -0
  30. package/register/0001-baseline/05-notifications.sql +11 -0
  31. package/register/0001-baseline/06-notifications-unread-idx.sql +1 -0
  32. package/register/0001-baseline/07-users.sql +13 -0
  33. package/register/0001-baseline/08-users-email-key.sql +1 -0
  34. package/register/0001-baseline/09-users-one-superadmin.sql +1 -0
  35. package/register/0001-baseline/10-users-username-unique.sql +1 -0
  36. package/register/0001-baseline/11-api-tokens.sql +13 -0
  37. package/register/0001-baseline/12-apps.sql +14 -0
  38. package/register/0001-baseline/13-apps-name-key.sql +1 -0
  39. package/register/0001-baseline/14-apps-owner-idx.sql +1 -0
  40. package/register/0001-baseline/15-contexts.sql +9 -0
  41. package/register/0001-baseline/16-credit-ledger.sql +10 -0
  42. package/register/0001-baseline/17-credit-ledger-user.sql +1 -0
  43. package/register/0001-baseline/18-runs.sql +16 -0
  44. package/register/0001-baseline/19-sessions.sql +10 -0
  45. package/register/0001-baseline/20-sessions-user-idx.sql +1 -0
  46. package/register/0001-baseline/21-app-chunks.sql +7 -0
  47. package/register/0001-baseline/22-context-members.sql +10 -0
  48. package/register/0002-orgs/01-orgs.sql +14 -0
  49. package/register/0002-orgs/02-orgs-name-key.sql +1 -0
  50. package/register/0002-orgs/03-org-members.sql +10 -0
  51. package/register/0002-orgs/04-org-members-user-idx.sql +1 -0
  52. package/register/0002-orgs/05-apps-org-id.sql +1 -0
  53. package/register/0002-orgs/06-apps-org-idx.sql +1 -0
  54. package/register/0002-orgs/07-apps-org-name-key.sql +1 -0
  55. package/register/0002-orgs/08-credit-ledger-org-id.sql +1 -0
  56. package/register/0002-orgs/09-credit-ledger-org-idx.sql +1 -0
  57. package/register/0002-orgs/10-api-tokens-org-id.sql +1 -0
  58. package/register/0002-orgs/11-api-tokens-created-by.sql +1 -0
  59. package/register/0002-orgs/12-backfill-personal-orgs.sql +15 -0
  60. package/register/0002-orgs/13-backfill-org-members.sql +12 -0
  61. package/register/0002-orgs/14-backfill-apps-org.sql +14 -0
  62. package/register/0002-orgs/15-backfill-credit-ledger-org.sql +11 -0
  63. package/register/0002-orgs/16-backfill-api-tokens-org.sql +12 -0
  64. package/register/README.md +73 -0
@@ -19,6 +19,9 @@ import type { User } from "./users.ts";
19
19
  export interface App {
20
20
  id: string;
21
21
  name: string;
22
+ /** The tenancy boundary. Null only for an owner who has claimed no username. */
23
+ org_id: string | null;
24
+ /** Who created it. Provenance, not tenancy — see orgs.ts. */
22
25
  owner_id: string;
23
26
  org: string | null;
24
27
  repo: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/admin/apps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/admin/apps.ts"],"names":[],"mappings":"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"}
@@ -66,9 +66,23 @@ export async function assertCanCreate(owner, register) {
66
66
  */
67
67
  export async function recordApp(app, register) {
68
68
  const conn = register;
69
- const { rows } = await conn.write(`insert into apps (name, owner_id, org, repo, vercel_project, neon_project, provisioned)
70
- values ($1, $2, $3, $4, $5, $6, $7)
69
+ const { rows } = await conn.write(
70
+ // `org_id` is derived rather than passed, and derived the same way the
71
+ // backfill derived it: through the owner's username to their personal
72
+ // organisation. A caller that had to supply it is a caller that can supply
73
+ // the wrong one, and the invariant `heychunky register verify` asserts —
74
+ // every app is in an organisation — would then hold only until the next
75
+ // app was made.
76
+ //
77
+ // Kept on conflict, never overwritten. Re-running provisioning fills in
78
+ // what a partial run could not; it must not move an app between tenants.
79
+ `insert into apps (name, owner_id, org_id, org, repo, vercel_project, neon_project, provisioned)
80
+ values ($1, $2,
81
+ (select o.id from orgs o join users u on lower(o.name) = lower(u.username)
82
+ where u.id = $2 and o.kind = 'personal'),
83
+ $3, $4, $5, $6, $7)
71
84
  on conflict (lower(name)) do update set
85
+ org_id = coalesce(apps.org_id, excluded.org_id),
72
86
  org = coalesce(excluded.org, apps.org),
73
87
  repo = coalesce(excluded.repo, apps.repo),
74
88
  vercel_project = coalesce(excluded.vercel_project, apps.vercel_project),
@@ -1 +1 @@
1
- {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../src/admin/apps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../src/admin/apps.ts"],"names":[],"mappings":"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"}
@@ -8,11 +8,13 @@ export * from "./secrets/infisical.ts";
8
8
  export * from "./syncenv.ts";
9
9
  export * from "./master.ts";
10
10
  export * from "./apps.ts";
11
+ export * from "./orgs.ts";
11
12
  export * from "./users.ts";
12
13
  export * from "./run.ts";
13
14
  export * from "./provision.ts";
14
15
  export * from "./decommission.ts";
15
16
  export * from "./migrations.ts";
17
+ export * from "./register.ts";
16
18
  export * from "./promote.ts";
17
19
  export * from "./runs.ts";
18
20
  export * from "./entrypoints.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAG5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAG5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC"}
@@ -8,11 +8,13 @@ export * from "./secrets/infisical.js";
8
8
  export * from "./syncenv.js";
9
9
  export * from "./master.js";
10
10
  export * from "./apps.js";
11
+ export * from "./orgs.js";
11
12
  export * from "./users.js";
12
13
  export * from "./run.js";
13
14
  export * from "./provision.js";
14
15
  export * from "./decommission.js";
15
16
  export * from "./migrations.js";
17
+ export * from "./register.js";
16
18
  export * from "./promote.js";
17
19
  export * from "./runs.js";
18
20
  export * from "./entrypoints.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAE5B,+CAA+C;AAC/C,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAE5B,+CAA+C;AAC/C,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC"}
@@ -95,4 +95,23 @@ export declare function pending(all: Migration[], have: Set<string>): Migration[
95
95
  * failed, and running them anyway turns one clear error into several.
96
96
  */
97
97
  export declare function apply(db: Ledger, migrations: Migration[], watch?: (m: Migration) => void): Promise<number>;
98
+ /**
99
+ * Record statements as applied without running them.
100
+ *
101
+ * For a database that predates its ledger. The register is the case this was
102
+ * written for — `apps` and `users` were typed into psql long before anything
103
+ * wrote them down, so the first migration file describing them is a record of
104
+ * what is already there rather than work to do. Running it would fail on every
105
+ * `create table`, and failing is not the answer: the database is *correct*, it
106
+ * simply has no receipt.
107
+ *
108
+ * Deliberately separate from `apply`, and deliberately not a flag on it. One
109
+ * function that either runs SQL or does not, depending on an argument, is one
110
+ * a caller can get backwards — and getting this one backwards means a schema
111
+ * change silently skipped, discovered later as a missing column in production.
112
+ *
113
+ * Only ever right when somebody has looked at the database and confirmed the
114
+ * statements are already true of it. There is no way to check that from here.
115
+ */
116
+ export declare function adopt(db: Ledger, migrations: Migration[]): Promise<string[]>;
98
117
  //# sourceMappingURL=migrations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/admin/migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,kEAAkE;AAClE,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,oEAAoE;AACpE,eAAO,MAAM,MAAM,0BAA0B,CAAC;AAE9C,wEAAwE;AACxE,MAAM,WAAW,SAAS;IACxB,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb;AAED,8DAA8D;AAC9D,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI3E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIlD;AAED,oEAAoE;AACpE,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5D;AAED;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAS9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,EAAE,CAExE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,KAAK,CACzB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,SAAS,EAAE,EACvB,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,GAC7B,OAAO,CAAC,MAAM,CAAC,CAUjB"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/admin/migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,kEAAkE;AAClE,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,oEAAoE;AACpE,eAAO,MAAM,MAAM,0BAA0B,CAAC;AAE9C,wEAAwE;AACxE,MAAM,WAAW,SAAS;IACxB,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb;AAED,8DAA8D;AAC9D,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI3E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIlD;AAED,oEAAoE;AACpE,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5D;AAED;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAS9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,EAAE,CAExE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,KAAK,CACzB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,SAAS,EAAE,EACvB,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,GAC7B,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQlF"}
@@ -90,4 +90,31 @@ export async function apply(db, migrations, watch) {
90
90
  }
91
91
  return ran;
92
92
  }
93
+ /**
94
+ * Record statements as applied without running them.
95
+ *
96
+ * For a database that predates its ledger. The register is the case this was
97
+ * written for — `apps` and `users` were typed into psql long before anything
98
+ * wrote them down, so the first migration file describing them is a record of
99
+ * what is already there rather than work to do. Running it would fail on every
100
+ * `create table`, and failing is not the answer: the database is *correct*, it
101
+ * simply has no receipt.
102
+ *
103
+ * Deliberately separate from `apply`, and deliberately not a flag on it. One
104
+ * function that either runs SQL or does not, depending on an argument, is one
105
+ * a caller can get backwards — and getting this one backwards means a schema
106
+ * change silently skipped, discovered later as a missing column in production.
107
+ *
108
+ * Only ever right when somebody has looked at the database and confirmed the
109
+ * statements are already true of it. There is no way to check that from here.
110
+ */
111
+ export async function adopt(db, migrations) {
112
+ await ensureLedger(db);
113
+ const recorded = [];
114
+ for (const m of migrations) {
115
+ await db.write(`insert into ${LEDGER} (id) values ($1) on conflict (id) do nothing`, [m.id]);
116
+ recorded.push(m.id);
117
+ }
118
+ return recorded;
119
+ }
93
120
  //# sourceMappingURL=migrations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/admin/migrations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/admin/migrations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * The tenancy boundary.
3
+ *
4
+ * An **organisation** is what owns things. A **user** is a person who builds.
5
+ * Not the users of anybody's app — Carmel's citizens are a Carmel City concept
6
+ * and stay in Carmel's database; these are the people who make apps here.
7
+ *
8
+ * `owner_id` on `apps` was the boundary until now, and a person is the wrong
9
+ * shape for one: it cannot be shared, it cannot be handed over, and it cannot
10
+ * hold a plan two people are both paying against. `org_id` is the boundary and
11
+ * `owner_id` survives beside it as provenance — who created this.
12
+ *
13
+ * **An organisation's name is the publish scope.** The curated shelf keeps bare
14
+ * names because somebody chose them; everyone else publishes under a scope, and
15
+ * `@carmelcity/auth` needs no second entity to explain what `carmelcity` is. It
16
+ * is also the URL segment a signed-in session is scoped by, which is why the
17
+ * database insists it is a slug rather than a title: one string, three jobs,
18
+ * and nothing anywhere has to remember which spelling it is holding.
19
+ *
20
+ * **The organisation is not the GitHub organisation — it has one.** Carmel's
21
+ * organisation is `carmelcity` here and `carmelcity` on GitHub today, and
22
+ * nothing should be built on those staying equal; `github_org`, `vercel_team`
23
+ * and `vault_folder` are where a provider's own name for the same tenant goes.
24
+ *
25
+ * **One level.** No spaces or workspaces inside an organisation until something
26
+ * needs one.
27
+ *
28
+ * Two kinds of role, and they live in different places on purpose. `superadmin`
29
+ * is a factory role and stays on the user, where a partial unique index permits
30
+ * exactly one of them. `admin` and `publisher` are organisation roles and live
31
+ * on the membership. One column for both would let an organisation's own admin
32
+ * inherit the factory.
33
+ */
34
+ import type { Register } from "./master.ts";
35
+ import type { PlanKey } from "./users.ts";
36
+ /** What somebody may do inside one organisation. Never a factory role. */
37
+ export declare const ORG_ROLES: readonly ["admin", "publisher"];
38
+ export type OrgRole = (typeof ORG_ROLES)[number];
39
+ /**
40
+ * Whether an organisation is a person or a group.
41
+ *
42
+ * `personal` is made for you when you claim a username and is never made any
43
+ * other way, which is what keeps a name meaning one thing across the People
44
+ * page, the shelf and the URL.
45
+ */
46
+ export declare const ORG_KINDS: readonly ["personal", "team"];
47
+ export type OrgKind = (typeof ORG_KINDS)[number];
48
+ /**
49
+ * The shape a name may take.
50
+ *
51
+ * The same rule as a chunk's scope and a username, because they are the same
52
+ * string. Enforced by a check constraint as well as here — a rule that lives
53
+ * only in TypeScript is a rule the next caller writes around.
54
+ */
55
+ export declare const ORG_NAME: RegExp;
56
+ export interface Org {
57
+ id: string;
58
+ /** The publish scope and the URL segment. Unique without case. */
59
+ name: string;
60
+ kind: OrgKind;
61
+ /** What this tenant may spend. The plan moved here from the user. */
62
+ plan: PlanKey;
63
+ /** A provider's own name for the same tenant. Null until one is linked. */
64
+ github_org: string | null;
65
+ vercel_team: string | null;
66
+ vault_folder: string | null;
67
+ created_at: string;
68
+ }
69
+ /** An organisation with what the person asking may do in it. */
70
+ export interface Membership extends Org {
71
+ role: OrgRole;
72
+ }
73
+ /** Refused because the name belongs to an organisation that is not personal. */
74
+ export declare class OrgNameTakenError extends Error {
75
+ constructor(name: string);
76
+ }
77
+ /**
78
+ * Give a user the organisation that matches their username.
79
+ *
80
+ * Runs in the same moment as claiming the name, because a name that is yours on
81
+ * the People page and somebody else's on the shelf would be worse than having
82
+ * no names at all. Safe to run again: if the membership insert fails the name is
83
+ * still theirs and the organisation is still there, which is the right way round
84
+ * — a person without their scope yet is inconvenienced, a person whose scope
85
+ * belongs to somebody else is a bug you cannot undo.
86
+ *
87
+ * Three statements rather than one because the client sends one statement per
88
+ * request. The insert is the check for the same reason `claimUsername`'s is: two
89
+ * people reaching for a name in the same second is a race only the unique index
90
+ * can settle.
91
+ *
92
+ * The read between the two writes is not ceremony. `on conflict do nothing` is
93
+ * silent about *why* it did nothing, and the one case that matters is a team
94
+ * organisation already holding this name — adding the caller to that as an admin
95
+ * would hand them somebody else's tenant. So it is looked at, and refused.
96
+ */
97
+ export declare function claimPersonalOrg(userId: string, username: string, register: Register): Promise<Org>;
98
+ /** The organisations this person belongs to, personal one first. */
99
+ export declare function orgsFor(userId: string, register: Register): Promise<Membership[]>;
100
+ /**
101
+ * Whether this person may publish under this name.
102
+ *
103
+ * Membership is the whole answer — both roles publish, which is what makes
104
+ * `publisher` worth having as the one that cannot also invite people.
105
+ */
106
+ export declare function mayPublish(userId: string, org: string, register: Register): Promise<boolean>;
107
+ /**
108
+ * What must be true of the register once organisations are backfilled.
109
+ *
110
+ * Written as data, and as violations rather than assertions, for one reason:
111
+ * **a wrong backfill does not fail loudly.** It sits looking fine until the day
112
+ * reads move onto `org_id`, and then it is one customer seeing another's apps.
113
+ * Nothing about that shows up in a test — the schema is right either way — so
114
+ * the proof has to be a question asked of the live rows.
115
+ *
116
+ * Every check counts rows that should not exist. Zero is the only passing
117
+ * answer, which means a check that is wrong tends to fail rather than pass, and
118
+ * a table that is empty passes honestly rather than vacuously hiding a bug.
119
+ *
120
+ * Exported so the CLI can print the SQL beside the count. A verification
121
+ * somebody has to take on trust is not one.
122
+ */
123
+ export declare const ORG_CHECKS: readonly {
124
+ check: string;
125
+ sql: string;
126
+ }[];
127
+ export interface CheckResult {
128
+ check: string;
129
+ sql: string;
130
+ /** Rows that should not exist. Anything but zero is a finding. */
131
+ found: number;
132
+ ok: boolean;
133
+ }
134
+ /**
135
+ * Ask the register all of them.
136
+ *
137
+ * Read-only, and safe to run before the migration as well as after — a check
138
+ * against a table that does not exist yet is a failure worth seeing rather than
139
+ * one to swallow, so nothing here catches.
140
+ */
141
+ export declare function verifyOrgs(register: Register): Promise<CheckResult[]>;
142
+ //# sourceMappingURL=orgs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orgs.d.ts","sourceRoot":"","sources":["../../src/admin/orgs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,0EAA0E;AAC1E,eAAO,MAAM,SAAS,iCAAkC,CAAC;AACzD,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,+BAAgC,CAAC;AACvD,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,QAAyB,CAAC;AAE/C,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,qEAAqE;IACrE,IAAI,EAAE,OAAO,CAAC;IACd,2EAA2E;IAC3E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,gEAAgE;AAChE,MAAM,WAAW,UAAW,SAAQ,GAAG;IACrC,IAAI,EAAE,OAAO,CAAC;CACf;AAED,gFAAgF;AAChF,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,IAAI,EAAE,MAAM;CAIzB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,GAAG,CAAC,CAwBd;AAED,oEAAoE;AACpE,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAQvF;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,EAAE,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAqD/D,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,OAAO,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAQ3E"}
@@ -0,0 +1,166 @@
1
+ /** What somebody may do inside one organisation. Never a factory role. */
2
+ export const ORG_ROLES = ["admin", "publisher"];
3
+ /**
4
+ * Whether an organisation is a person or a group.
5
+ *
6
+ * `personal` is made for you when you claim a username and is never made any
7
+ * other way, which is what keeps a name meaning one thing across the People
8
+ * page, the shelf and the URL.
9
+ */
10
+ export const ORG_KINDS = ["personal", "team"];
11
+ /**
12
+ * The shape a name may take.
13
+ *
14
+ * The same rule as a chunk's scope and a username, because they are the same
15
+ * string. Enforced by a check constraint as well as here — a rule that lives
16
+ * only in TypeScript is a rule the next caller writes around.
17
+ */
18
+ export const ORG_NAME = /^[a-z0-9][a-z0-9-]*$/;
19
+ /** Refused because the name belongs to an organisation that is not personal. */
20
+ export class OrgNameTakenError extends Error {
21
+ constructor(name) {
22
+ super(`"${name}" is a team organisation, so it cannot also be somebody's personal one.`);
23
+ this.name = "OrgNameTakenError";
24
+ }
25
+ }
26
+ /**
27
+ * Give a user the organisation that matches their username.
28
+ *
29
+ * Runs in the same moment as claiming the name, because a name that is yours on
30
+ * the People page and somebody else's on the shelf would be worse than having
31
+ * no names at all. Safe to run again: if the membership insert fails the name is
32
+ * still theirs and the organisation is still there, which is the right way round
33
+ * — a person without their scope yet is inconvenienced, a person whose scope
34
+ * belongs to somebody else is a bug you cannot undo.
35
+ *
36
+ * Three statements rather than one because the client sends one statement per
37
+ * request. The insert is the check for the same reason `claimUsername`'s is: two
38
+ * people reaching for a name in the same second is a race only the unique index
39
+ * can settle.
40
+ *
41
+ * The read between the two writes is not ceremony. `on conflict do nothing` is
42
+ * silent about *why* it did nothing, and the one case that matters is a team
43
+ * organisation already holding this name — adding the caller to that as an admin
44
+ * would hand them somebody else's tenant. So it is looked at, and refused.
45
+ */
46
+ export async function claimPersonalOrg(userId, username, register) {
47
+ if (!ORG_NAME.test(username))
48
+ throw new Error(`"${username}" is not a name an organisation can have.`);
49
+ await register.write(`insert into orgs (name, kind, plan)
50
+ select $1, 'personal', coalesce(u.plan, 'free') from users u where u.id = $2
51
+ on conflict (lower(name)) do nothing`, [username, userId]);
52
+ const { rows } = await register.query("select * from orgs where lower(name) = lower($1)", [username]);
53
+ const org = rows[0];
54
+ if (!org)
55
+ throw new Error(`No user ${userId}, so "${username}" belongs to nobody.`);
56
+ if (org.kind !== "personal")
57
+ throw new OrgNameTakenError(username);
58
+ await register.write(`insert into org_members (org_id, user_id, role) values ($1, $2, 'admin')
59
+ on conflict (org_id, user_id) do nothing`, [org.id, userId]);
60
+ return org;
61
+ }
62
+ /** The organisations this person belongs to, personal one first. */
63
+ export async function orgsFor(userId, register) {
64
+ const { rows } = await register.query(`select o.*, m.role from orgs o join org_members m on m.org_id = o.id
65
+ where m.user_id = $1
66
+ order by (o.kind = 'personal') desc, o.name`, [userId]);
67
+ return rows;
68
+ }
69
+ /**
70
+ * Whether this person may publish under this name.
71
+ *
72
+ * Membership is the whole answer — both roles publish, which is what makes
73
+ * `publisher` worth having as the one that cannot also invite people.
74
+ */
75
+ export async function mayPublish(userId, org, register) {
76
+ const { rows } = await register.query(`select 1 from org_members m join orgs o on o.id = m.org_id
77
+ where m.user_id = $1 and lower(o.name) = lower($2)`, [userId, org]);
78
+ return rows.length > 0;
79
+ }
80
+ /**
81
+ * What must be true of the register once organisations are backfilled.
82
+ *
83
+ * Written as data, and as violations rather than assertions, for one reason:
84
+ * **a wrong backfill does not fail loudly.** It sits looking fine until the day
85
+ * reads move onto `org_id`, and then it is one customer seeing another's apps.
86
+ * Nothing about that shows up in a test — the schema is right either way — so
87
+ * the proof has to be a question asked of the live rows.
88
+ *
89
+ * Every check counts rows that should not exist. Zero is the only passing
90
+ * answer, which means a check that is wrong tends to fail rather than pass, and
91
+ * a table that is empty passes honestly rather than vacuously hiding a bug.
92
+ *
93
+ * Exported so the CLI can print the SQL beside the count. A verification
94
+ * somebody has to take on trust is not one.
95
+ */
96
+ export const ORG_CHECKS = [
97
+ {
98
+ check: "users with a username and no personal organisation",
99
+ sql: `select count(*) as n from users u where u.username is not null
100
+ and not exists (select 1 from orgs o
101
+ where lower(o.name) = lower(u.username) and o.kind = 'personal')`,
102
+ },
103
+ {
104
+ check: "personal organisations whose name is nobody's username",
105
+ sql: `select count(*) as n from orgs o where o.kind = 'personal'
106
+ and not exists (select 1 from users u where lower(u.username) = lower(o.name))`,
107
+ },
108
+ {
109
+ check: "personal organisations without exactly one admin member",
110
+ sql: `select count(*) as n from orgs o where o.kind = 'personal'
111
+ and (select count(*) from org_members m
112
+ where m.org_id = o.id and m.role = 'admin') <> 1`,
113
+ },
114
+ {
115
+ check: "members of an organisation they are not in the register of",
116
+ sql: `select count(*) as n from org_members m
117
+ where not exists (select 1 from users u where u.id = m.user_id)`,
118
+ },
119
+ {
120
+ check: "apps in no organisation",
121
+ sql: "select count(*) as n from apps where org_id is null",
122
+ },
123
+ {
124
+ check: "apps whose organisation is not their owner's personal one",
125
+ sql: `select count(*) as n from apps a
126
+ join users u on u.id = a.owner_id
127
+ where a.org_id is not null
128
+ and a.org_id is distinct from
129
+ (select o.id from orgs o
130
+ where lower(o.name) = lower(u.username) and o.kind = 'personal')`,
131
+ },
132
+ {
133
+ check: "apps whose owner has claimed no username, so can have no organisation",
134
+ sql: `select count(*) as n from apps a join users u on u.id = a.owner_id
135
+ where u.username is null`,
136
+ },
137
+ {
138
+ check: "credit entries in no organisation",
139
+ sql: "select count(*) as n from credit_ledger where org_id is null",
140
+ },
141
+ {
142
+ check: "api tokens in no organisation",
143
+ sql: "select count(*) as n from api_tokens where org_id is null",
144
+ },
145
+ {
146
+ check: "api tokens with no recorded creator",
147
+ sql: "select count(*) as n from api_tokens where created_by is null",
148
+ },
149
+ ];
150
+ /**
151
+ * Ask the register all of them.
152
+ *
153
+ * Read-only, and safe to run before the migration as well as after — a check
154
+ * against a table that does not exist yet is a failure worth seeing rather than
155
+ * one to swallow, so nothing here catches.
156
+ */
157
+ export async function verifyOrgs(register) {
158
+ const out = [];
159
+ for (const { check, sql } of ORG_CHECKS) {
160
+ const { rows } = await register.query(sql);
161
+ const found = Number(rows[0]?.["n"] ?? 0);
162
+ out.push({ check, sql, found, ok: found === 0 });
163
+ }
164
+ return out;
165
+ }
166
+ //# sourceMappingURL=orgs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orgs.js","sourceRoot":"","sources":["../../src/admin/orgs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import { type Migration } from "./migrations.ts";
2
+ import type { Register } from "./master.ts";
3
+ /** The name of the change a statement belongs to — its directory. */
4
+ export declare const changeOf: (id: string) => string;
5
+ /**
6
+ * Every statement the register's schema is made of, in order.
7
+ *
8
+ * Read rather than declared, so the SQL lives in `.sql` files somebody can
9
+ * review as SQL. A directory with no `.sql` in it is not an error — it is a
10
+ * change whose files have not been written yet, and an empty answer is the
11
+ * honest one.
12
+ */
13
+ export declare function registerMigrations(): Promise<Migration[]>;
14
+ export interface RegisterState {
15
+ /** Every statement in this Core, in order. */
16
+ all: Migration[];
17
+ /** What this register has already had. */
18
+ had: Set<string>;
19
+ /** What it owes, in the order it owes them. */
20
+ owed: Migration[];
21
+ /** Statements among the owed that alter or discard what is already there. */
22
+ destructive: Migration[];
23
+ }
24
+ /**
25
+ * What this register has had, and what it still owes.
26
+ *
27
+ * Asks without creating: a register that has never been migrated has no ledger
28
+ * table, and `applied` reads that as "none" rather than making one. This is the
29
+ * call an unconfirmed plan makes, and a plan that wrote to the database would
30
+ * not be a plan.
31
+ */
32
+ export declare function registerState(register: Register): Promise<RegisterState>;
33
+ /** A migration that would alter or discard, without the separate opt-in. */
34
+ export declare class DestructiveMigrationError extends Error {
35
+ readonly migrations: Migration[];
36
+ constructor(migrations: Migration[]);
37
+ }
38
+ /**
39
+ * Bring the register up to what this Core describes.
40
+ *
41
+ * The destructive guard is the same distinction `promote` draws and for the
42
+ * same reason: confirming says "yes, run these"; accepting destruction says
43
+ * "yes, to a dropped column on the database that knows who everybody is". One
44
+ * yes covering both hides the one that cannot be undone by redeploying.
45
+ */
46
+ export declare function migrateRegister(register: Register, opts?: {
47
+ acceptDestructive?: boolean;
48
+ }, watch?: (m: Migration) => void): Promise<{
49
+ ran: number;
50
+ migrations: Migration[];
51
+ }>;
52
+ /**
53
+ * The relation a `create` statement makes.
54
+ *
55
+ * Only the two forms the register's schema is written in, and undefined for
56
+ * anything else — which is the point. A statement whose effect cannot be named
57
+ * cannot be checked, and `adoptChange` refuses rather than guessing.
58
+ */
59
+ export declare function relationOf(sql: string): {
60
+ kind: "table" | "index";
61
+ name: string;
62
+ } | undefined;
63
+ /** Refused because the register does not hold what it was asked to vouch for. */
64
+ export declare class MissingFromRegisterError extends Error {
65
+ readonly missing: {
66
+ id: string;
67
+ relation: string;
68
+ }[];
69
+ constructor(change: string, missing: {
70
+ id: string;
71
+ relation: string;
72
+ }[]);
73
+ }
74
+ /**
75
+ * Record one change as applied, having run none of it.
76
+ *
77
+ * The register predates its own ledger, so `0001-baseline` describes tables
78
+ * that have been there for months. Running it would fail on the first `create
79
+ * table`; skipping it would leave every later change looking like it depends on
80
+ * something that never happened. Recording it is the truth: those statements
81
+ * are already true of this database.
82
+ *
83
+ * **Checked rather than trusted.** The failure this command can cause is
84
+ * invisible — a false receipt, and then a schema change silently skipped
85
+ * forever after — so every statement's relation is looked up in the catalog
86
+ * first, and a change that is not entirely true of this register is refused
87
+ * with the missing pieces named. That matters most where databases have
88
+ * drifted: the factory's production register has a `runs` table and its dev one
89
+ * does not, and the honest answer there is "apply that one, adopt the rest"
90
+ * rather than a receipt covering both.
91
+ *
92
+ * Named rather than inferred. "Mark everything pending as done" would be a
93
+ * command that quietly swallows a real migration somebody meant to run — so a
94
+ * caller has to say which change it means, and mean it. A full statement id is
95
+ * accepted as well as a change name, which is how the operator resolves drift.
96
+ */
97
+ export declare function adoptChange(register: Register, change: string): Promise<string[]>;
98
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/admin/register.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAChG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAY5C,qEAAqE;AACrE,eAAO,MAAM,QAAQ,GAAI,IAAI,MAAM,KAAG,MAAgC,CAAC;AAEvE;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAgB/D;AAED,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,GAAG,EAAE,SAAS,EAAE,CAAC;IACjB,0CAA0C;IAC1C,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjB,+CAA+C;IAC/C,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,6EAA6E;IAC7E,WAAW,EAAE,SAAS,EAAE,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CAK9E;AAED,4EAA4E;AAC5E,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,UAAU,EAAE,SAAS,EAAE,CAAC;gBACrB,UAAU,EAAE,SAAS,EAAE;CASpC;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,IAAI,GAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,EAC1C,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,GAC7B,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC,CAOnD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAU7F;AAED,iFAAiF;AACjF,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;gBACzC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE;CASxE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA6BvF"}