@noodleseed/one 0.38.0 → 0.39.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 (175) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +3 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/billing-apply-ops.d.ts +2 -0
  5. package/dist/commands/billing-apply-ops.d.ts.map +1 -0
  6. package/dist/commands/billing-apply-ops.js +224 -0
  7. package/dist/commands/billing-apply-ops.js.map +1 -0
  8. package/dist/commands/billing-migration-files.d.ts +13 -0
  9. package/dist/commands/billing-migration-files.d.ts.map +1 -0
  10. package/dist/commands/billing-migration-files.js +84 -0
  11. package/dist/commands/billing-migration-files.js.map +1 -0
  12. package/dist/commands/billing-ops.d.ts +3 -0
  13. package/dist/commands/billing-ops.d.ts.map +1 -0
  14. package/dist/commands/billing-ops.js +121 -0
  15. package/dist/commands/billing-ops.js.map +1 -0
  16. package/dist/commands/catalog-data-resources.d.ts.map +1 -1
  17. package/dist/commands/catalog-data-resources.js +19 -0
  18. package/dist/commands/catalog-data-resources.js.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/widget-scaffold-template.d.ts.map +1 -1
  23. package/dist/widget-scaffold-template.js +9 -0
  24. package/dist/widget-scaffold-template.js.map +1 -1
  25. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.d.ts.map +1 -1
  26. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +4 -0
  27. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js.map +1 -1
  28. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +3 -3
  29. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js.map +1 -1
  30. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.d.ts.map +1 -1
  31. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +1 -0
  32. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js.map +1 -1
  33. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.d.ts.map +1 -1
  34. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +28 -0
  35. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js.map +1 -1
  36. package/node_modules/@noodle-borg/agent-kit/dist/skill-operations-refs.d.ts.map +1 -1
  37. package/node_modules/@noodle-borg/agent-kit/dist/skill-operations-refs.js +5 -0
  38. package/node_modules/@noodle-borg/agent-kit/dist/skill-operations-refs.js.map +1 -1
  39. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  40. package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +44 -0
  41. package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts.map +1 -1
  42. package/node_modules/@noodle-borg/authoring/dist/assistant.js.map +1 -1
  43. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -1
  44. package/node_modules/@noodle-borg/authoring/dist/index.d.ts.map +1 -1
  45. package/node_modules/@noodle-borg/authoring/dist/index.js.map +1 -1
  46. package/node_modules/@noodle-borg/authoring/dist/server.js +6 -1
  47. package/node_modules/@noodle-borg/authoring/dist/server.js.map +1 -1
  48. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +83 -0
  49. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts.map +1 -1
  50. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +82 -14
  51. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js.map +1 -1
  52. package/node_modules/@noodle-borg/module-audit/dist/index.d.ts +2 -1
  53. package/node_modules/@noodle-borg/module-audit/dist/index.d.ts.map +1 -1
  54. package/node_modules/@noodle-borg/module-audit/dist/index.js +1 -1
  55. package/node_modules/@noodle-borg/module-audit/dist/index.js.map +1 -1
  56. package/node_modules/@noodle-borg/module-audit/dist/postgres-audit.d.ts +12 -3
  57. package/node_modules/@noodle-borg/module-audit/dist/postgres-audit.d.ts.map +1 -1
  58. package/node_modules/@noodle-borg/module-audit/dist/postgres-audit.js +30 -20
  59. package/node_modules/@noodle-borg/module-audit/dist/postgres-audit.js.map +1 -1
  60. package/node_modules/@noodle-borg/service/dist/billing/in-memory-serialization.d.ts +6 -0
  61. package/node_modules/@noodle-borg/service/dist/billing/in-memory-serialization.d.ts.map +1 -0
  62. package/node_modules/@noodle-borg/service/dist/billing/in-memory-serialization.js +23 -0
  63. package/node_modules/@noodle-borg/service/dist/billing/in-memory-serialization.js.map +1 -0
  64. package/node_modules/@noodle-borg/service/dist/billing/in-memory.d.ts +32 -0
  65. package/node_modules/@noodle-borg/service/dist/billing/in-memory.d.ts.map +1 -0
  66. package/node_modules/@noodle-borg/service/dist/billing/in-memory.js +185 -0
  67. package/node_modules/@noodle-borg/service/dist/billing/in-memory.js.map +1 -0
  68. package/node_modules/@noodle-borg/service/dist/billing/migration-apply.d.ts +98 -0
  69. package/node_modules/@noodle-borg/service/dist/billing/migration-apply.d.ts.map +1 -0
  70. package/node_modules/@noodle-borg/service/dist/billing/migration-apply.js +194 -0
  71. package/node_modules/@noodle-borg/service/dist/billing/migration-apply.js.map +1 -0
  72. package/node_modules/@noodle-borg/service/dist/billing/migration-contract.d.ts +79 -0
  73. package/node_modules/@noodle-borg/service/dist/billing/migration-contract.d.ts.map +1 -0
  74. package/node_modules/@noodle-borg/service/dist/billing/migration-contract.js +86 -0
  75. package/node_modules/@noodle-borg/service/dist/billing/migration-contract.js.map +1 -0
  76. package/node_modules/@noodle-borg/service/dist/billing/migration.d.ts +13 -0
  77. package/node_modules/@noodle-borg/service/dist/billing/migration.d.ts.map +1 -0
  78. package/node_modules/@noodle-borg/service/dist/billing/migration.js +246 -0
  79. package/node_modules/@noodle-borg/service/dist/billing/migration.js.map +1 -0
  80. package/node_modules/@noodle-borg/service/dist/billing/model.d.ts +85 -0
  81. package/node_modules/@noodle-borg/service/dist/billing/model.d.ts.map +1 -0
  82. package/node_modules/@noodle-borg/service/dist/billing/model.js +134 -0
  83. package/node_modules/@noodle-borg/service/dist/billing/model.js.map +1 -0
  84. package/node_modules/@noodle-borg/service/dist/billing/postgres-migration-support.d.ts +36 -0
  85. package/node_modules/@noodle-borg/service/dist/billing/postgres-migration-support.d.ts.map +1 -0
  86. package/node_modules/@noodle-borg/service/dist/billing/postgres-migration-support.js +122 -0
  87. package/node_modules/@noodle-borg/service/dist/billing/postgres-migration-support.js.map +1 -0
  88. package/node_modules/@noodle-borg/service/dist/billing/postgres-migration.d.ts +9 -0
  89. package/node_modules/@noodle-borg/service/dist/billing/postgres-migration.d.ts.map +1 -0
  90. package/node_modules/@noodle-borg/service/dist/billing/postgres-migration.js +265 -0
  91. package/node_modules/@noodle-borg/service/dist/billing/postgres-migration.js.map +1 -0
  92. package/node_modules/@noodle-borg/service/dist/billing/postgres-records.d.ts +66 -0
  93. package/node_modules/@noodle-borg/service/dist/billing/postgres-records.d.ts.map +1 -0
  94. package/node_modules/@noodle-borg/service/dist/billing/postgres-records.js +163 -0
  95. package/node_modules/@noodle-borg/service/dist/billing/postgres-records.js.map +1 -0
  96. package/node_modules/@noodle-borg/service/dist/billing/postgres-schema.d.ts +4 -0
  97. package/node_modules/@noodle-borg/service/dist/billing/postgres-schema.d.ts.map +1 -0
  98. package/node_modules/@noodle-borg/service/dist/billing/postgres-schema.js +211 -0
  99. package/node_modules/@noodle-borg/service/dist/billing/postgres-schema.js.map +1 -0
  100. package/node_modules/@noodle-borg/service/dist/billing/postgres.d.ts +33 -0
  101. package/node_modules/@noodle-borg/service/dist/billing/postgres.d.ts.map +1 -0
  102. package/node_modules/@noodle-borg/service/dist/billing/postgres.js +285 -0
  103. package/node_modules/@noodle-borg/service/dist/billing/postgres.js.map +1 -0
  104. package/node_modules/@noodle-borg/service/dist/billing/store.d.ts +27 -0
  105. package/node_modules/@noodle-borg/service/dist/billing/store.d.ts.map +1 -0
  106. package/node_modules/@noodle-borg/service/dist/billing/store.js +2 -0
  107. package/node_modules/@noodle-borg/service/dist/billing/store.js.map +1 -0
  108. package/node_modules/@noodle-borg/service/dist/index.d.ts +4 -0
  109. package/node_modules/@noodle-borg/service/dist/index.d.ts.map +1 -1
  110. package/node_modules/@noodle-borg/service/dist/index.js +3 -0
  111. package/node_modules/@noodle-borg/service/dist/index.js.map +1 -1
  112. package/node_modules/@noodle-borg/service/dist/modules/host.js +3 -0
  113. package/node_modules/@noodle-borg/service/dist/modules/host.js.map +1 -1
  114. package/node_modules/@noodle-borg/service/dist/options.d.ts +17 -5
  115. package/node_modules/@noodle-borg/service/dist/options.d.ts.map +1 -1
  116. package/node_modules/@noodle-borg/service/dist/routes/billing.d.ts +23 -0
  117. package/node_modules/@noodle-borg/service/dist/routes/billing.d.ts.map +1 -0
  118. package/node_modules/@noodle-borg/service/dist/routes/billing.js +165 -0
  119. package/node_modules/@noodle-borg/service/dist/routes/billing.js.map +1 -0
  120. package/node_modules/@noodle-borg/service/dist/routes/commercial-dispatch.d.ts +28 -0
  121. package/node_modules/@noodle-borg/service/dist/routes/commercial-dispatch.d.ts.map +1 -0
  122. package/node_modules/@noodle-borg/service/dist/routes/commercial-dispatch.js +77 -0
  123. package/node_modules/@noodle-borg/service/dist/routes/commercial-dispatch.js.map +1 -0
  124. package/node_modules/@noodle-borg/service/dist/routes/control-plane.d.ts.map +1 -1
  125. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +17 -0
  126. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js.map +1 -1
  127. package/node_modules/@noodle-borg/service/dist/serve.d.ts.map +1 -1
  128. package/node_modules/@noodle-borg/service/dist/serve.js +28 -6
  129. package/node_modules/@noodle-borg/service/dist/serve.js.map +1 -1
  130. package/node_modules/@noodle-borg/service/dist/service.d.ts.map +1 -1
  131. package/node_modules/@noodle-borg/service/dist/service.js +23 -30
  132. package/node_modules/@noodle-borg/service/dist/service.js.map +1 -1
  133. package/node_modules/@noodle-borg/service/dist/store/audit.d.ts +8 -0
  134. package/node_modules/@noodle-borg/service/dist/store/audit.d.ts.map +1 -1
  135. package/node_modules/@noodle-borg/service/dist/store/audit.js +15 -0
  136. package/node_modules/@noodle-borg/service/dist/store/audit.js.map +1 -1
  137. package/node_modules/@noodle-borg/service/dist/store/control-plane.d.ts +2 -1
  138. package/node_modules/@noodle-borg/service/dist/store/control-plane.d.ts.map +1 -1
  139. package/node_modules/@noodle-borg/service/dist/store/control-plane.js +81 -48
  140. package/node_modules/@noodle-borg/service/dist/store/control-plane.js.map +1 -1
  141. package/node_modules/@noodle-borg/service/dist/store/postgres-apps.d.ts +4 -2
  142. package/node_modules/@noodle-borg/service/dist/store/postgres-apps.d.ts.map +1 -1
  143. package/node_modules/@noodle-borg/service/dist/store/postgres-apps.js.map +1 -1
  144. package/node_modules/@noodle-borg/service/dist/store/postgres-billing.d.ts +11 -0
  145. package/node_modules/@noodle-borg/service/dist/store/postgres-billing.d.ts.map +1 -0
  146. package/node_modules/@noodle-borg/service/dist/store/postgres-billing.js +17 -0
  147. package/node_modules/@noodle-borg/service/dist/store/postgres-billing.js.map +1 -0
  148. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.d.ts +1 -1
  149. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.d.ts.map +1 -1
  150. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.js +18 -6
  151. package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.js.map +1 -1
  152. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.d.ts.map +1 -1
  153. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js +4 -0
  154. package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js.map +1 -1
  155. package/node_modules/@noodle-borg/service/dist/store/postgres-signup.d.ts +14 -1
  156. package/node_modules/@noodle-borg/service/dist/store/postgres-signup.d.ts.map +1 -1
  157. package/node_modules/@noodle-borg/service/dist/store/postgres-signup.js +28 -0
  158. package/node_modules/@noodle-borg/service/dist/store/postgres-signup.js.map +1 -1
  159. package/node_modules/@noodle-borg/service/dist/store/postgres.d.ts +2 -2
  160. package/node_modules/@noodle-borg/service/dist/store/postgres.d.ts.map +1 -1
  161. package/node_modules/@noodle-borg/service/dist/store/postgres.js +9 -5
  162. package/node_modules/@noodle-borg/service/dist/store/postgres.js.map +1 -1
  163. package/node_modules/@noodle-borg/service/dist/store/validate.d.ts +4 -0
  164. package/node_modules/@noodle-borg/service/dist/store/validate.d.ts.map +1 -1
  165. package/node_modules/@noodle-borg/service/dist/store/validate.js +13 -0
  166. package/node_modules/@noodle-borg/service/dist/store/validate.js.map +1 -1
  167. package/node_modules/@noodle-borg/service/dist/store.d.ts +5 -2
  168. package/node_modules/@noodle-borg/service/dist/store.d.ts.map +1 -1
  169. package/node_modules/@noodle-borg/service/dist/store.js +1 -1
  170. package/node_modules/@noodle-borg/service/dist/store.js.map +1 -1
  171. package/node_modules/@noodle-borg/wire-contracts/dist/assistant.d.ts +3 -2
  172. package/node_modules/@noodle-borg/wire-contracts/dist/assistant.d.ts.map +1 -1
  173. package/node_modules/@noodle-borg/wire-contracts/dist/assistant.js +3 -2
  174. package/node_modules/@noodle-borg/wire-contracts/dist/assistant.js.map +1 -1
  175. package/package.json +1 -1
@@ -0,0 +1,211 @@
1
+ import { FREE_PLAN_VERSION } from './model.js';
2
+ import { planVersionRowToRecord } from './postgres-records.js';
3
+ /** Additive BA-1 schema. No existing organization is linked or otherwise migrated here. */
4
+ export async function ensureBillingSchema(pool) {
5
+ await pool.query(`
6
+ CREATE TABLE IF NOT EXISTS billing_plan_versions (
7
+ id text PRIMARY KEY,
8
+ plan_code text NOT NULL,
9
+ version integer NOT NULL CHECK (version > 0),
10
+ effective_from timestamptz NOT NULL,
11
+ entitlements jsonb NOT NULL,
12
+ content_checksum text NOT NULL,
13
+ created_at timestamptz NOT NULL,
14
+ UNIQUE (plan_code, version)
15
+ )
16
+ `);
17
+ await pool.query(`
18
+ CREATE TABLE IF NOT EXISTS billing_accounts (
19
+ id text PRIMARY KEY,
20
+ display_name text NOT NULL,
21
+ state text NOT NULL CHECK (state IN ('active', 'draft', 'inactive')),
22
+ created_at timestamptz NOT NULL
23
+ )
24
+ `);
25
+ await pool.query(`
26
+ CREATE TABLE IF NOT EXISTS billing_account_defaults (
27
+ identity_issuer text NOT NULL,
28
+ subject text NOT NULL,
29
+ billing_account_id text NOT NULL UNIQUE REFERENCES billing_accounts(id) ON DELETE RESTRICT,
30
+ PRIMARY KEY (identity_issuer, subject)
31
+ )
32
+ `);
33
+ await pool.query(`
34
+ CREATE TABLE IF NOT EXISTS billing_account_members (
35
+ billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
36
+ identity_issuer text NOT NULL,
37
+ subject text NOT NULL,
38
+ email text NOT NULL,
39
+ role text NOT NULL CHECK (role IN ('owner', 'admin', 'viewer')),
40
+ created_at timestamptz NOT NULL,
41
+ PRIMARY KEY (billing_account_id, identity_issuer, subject)
42
+ )
43
+ `);
44
+ await pool.query(`
45
+ CREATE UNIQUE INDEX IF NOT EXISTS billing_account_members_default_owner_key
46
+ ON billing_account_members (identity_issuer, subject, billing_account_id)
47
+ `);
48
+ await pool.query(`
49
+ CREATE UNIQUE INDEX IF NOT EXISTS billing_account_defaults_owner_account_key
50
+ ON billing_account_defaults (identity_issuer, subject, billing_account_id)
51
+ `);
52
+ await pool.query(`
53
+ DO $billing_default_owner$
54
+ BEGIN
55
+ IF NOT EXISTS (
56
+ SELECT 1
57
+ FROM pg_constraint
58
+ WHERE conname = 'billing_account_defaults_owner_member_fk'
59
+ AND conrelid = 'billing_account_defaults'::regclass
60
+ ) THEN
61
+ BEGIN
62
+ ALTER TABLE billing_account_defaults
63
+ ADD CONSTRAINT billing_account_defaults_owner_member_fk
64
+ FOREIGN KEY (identity_issuer, subject, billing_account_id)
65
+ REFERENCES billing_account_members (identity_issuer, subject, billing_account_id)
66
+ ON DELETE RESTRICT;
67
+ EXCEPTION WHEN duplicate_object THEN
68
+ NULL;
69
+ END;
70
+ END IF;
71
+ END
72
+ $billing_default_owner$
73
+ `);
74
+ await pool.query(`
75
+ CREATE TABLE IF NOT EXISTS billing_entitlement_snapshots (
76
+ id text PRIMARY KEY,
77
+ billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
78
+ version integer NOT NULL CHECK (version > 0),
79
+ plan_version_id text NOT NULL REFERENCES billing_plan_versions(id) ON DELETE RESTRICT,
80
+ source_kind text NOT NULL CHECK (source_kind IN ('system-free')),
81
+ generation_reason text NOT NULL CHECK (generation_reason IN ('default-account-created')),
82
+ verification_status text NOT NULL CHECK (verification_status IN ('verified')),
83
+ effective_from timestamptz NOT NULL,
84
+ monthly_usage_anchor timestamptz NOT NULL,
85
+ entitlements jsonb NOT NULL,
86
+ content_checksum text NOT NULL,
87
+ verified_at timestamptz NOT NULL,
88
+ created_at timestamptz NOT NULL,
89
+ UNIQUE (billing_account_id, version),
90
+ UNIQUE (billing_account_id, id)
91
+ )
92
+ `);
93
+ await pool.query(`
94
+ CREATE TABLE IF NOT EXISTS billing_account_active_snapshots (
95
+ billing_account_id text PRIMARY KEY REFERENCES billing_accounts(id) ON DELETE RESTRICT,
96
+ snapshot_id text NOT NULL UNIQUE,
97
+ FOREIGN KEY (billing_account_id, snapshot_id)
98
+ REFERENCES billing_entitlement_snapshots(billing_account_id, id) ON DELETE RESTRICT
99
+ )
100
+ `);
101
+ await pool.query(`
102
+ CREATE TABLE IF NOT EXISTS org_billing_home_accounts (
103
+ org_slug text PRIMARY KEY REFERENCES orgs(slug) ON DELETE RESTRICT,
104
+ billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
105
+ home_owner_identity_issuer text NOT NULL,
106
+ home_owner_subject text NOT NULL,
107
+ UNIQUE (org_slug, billing_account_id),
108
+ FOREIGN KEY (home_owner_identity_issuer, home_owner_subject, billing_account_id)
109
+ REFERENCES billing_account_defaults(identity_issuer, subject, billing_account_id)
110
+ ON DELETE RESTRICT
111
+ )
112
+ `);
113
+ await pool.query(`
114
+ CREATE TABLE IF NOT EXISTS org_billing_links (
115
+ org_slug text PRIMARY KEY REFERENCES orgs(slug) ON DELETE RESTRICT,
116
+ billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
117
+ home_free_billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
118
+ fallback_billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
119
+ version integer NOT NULL CHECK (version > 0),
120
+ active boolean NOT NULL DEFAULT true CHECK (active),
121
+ linked_at timestamptz NOT NULL,
122
+ linked_by_identity_issuer text NOT NULL,
123
+ linked_by_subject text NOT NULL,
124
+ reason text NOT NULL,
125
+ FOREIGN KEY (org_slug, home_free_billing_account_id)
126
+ REFERENCES org_billing_home_accounts(org_slug, billing_account_id) ON DELETE RESTRICT
127
+ )
128
+ `);
129
+ await pool.query(`
130
+ CREATE TABLE IF NOT EXISTS billing_legacy_migration_preparations (
131
+ id text PRIMARY KEY,
132
+ migration_kind text NOT NULL UNIQUE CHECK (migration_kind = 'hosted-default-free-v1'),
133
+ schema_version integer NOT NULL CHECK (schema_version = 1),
134
+ mode text NOT NULL CHECK (mode = 'shadow'),
135
+ state text NOT NULL CHECK (state = 'prepared'),
136
+ idempotency_key text NOT NULL UNIQUE,
137
+ request_fingerprint text NOT NULL,
138
+ mapping_checksum text NOT NULL,
139
+ preview_checksum text NOT NULL,
140
+ plan_evidence_checksum text NOT NULL,
141
+ prepared_at timestamptz NOT NULL,
142
+ prepared_by_subject text NOT NULL,
143
+ prepared_by_email text NOT NULL,
144
+ reason text NOT NULL
145
+ )
146
+ `);
147
+ await pool.query(`
148
+ CREATE TABLE IF NOT EXISTS billing_legacy_migration_accounts (
149
+ migration_id text NOT NULL REFERENCES billing_legacy_migration_preparations(id),
150
+ billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
151
+ account_disposition text NOT NULL CHECK (account_disposition IN ('created', 'existing')),
152
+ snapshot_id text NOT NULL REFERENCES billing_entitlement_snapshots(id) ON DELETE RESTRICT,
153
+ organization_count integer NOT NULL CHECK (organization_count >= 0),
154
+ production_app_count integer NOT NULL CHECK (production_app_count >= 0),
155
+ production_capacity integer NOT NULL CHECK (production_capacity >= 0),
156
+ grant_candidate_at_preparation boolean NOT NULL,
157
+ PRIMARY KEY (migration_id, billing_account_id)
158
+ )
159
+ `);
160
+ await pool.query(`
161
+ CREATE TABLE IF NOT EXISTS billing_legacy_migration_organizations (
162
+ migration_id text NOT NULL REFERENCES billing_legacy_migration_preparations(id),
163
+ org_slug text NOT NULL,
164
+ billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
165
+ link_disposition text NOT NULL CHECK (link_disposition IN ('created', 'preserved')),
166
+ link_version integer NOT NULL CHECK (link_version > 0),
167
+ home_owner_identity_issuer text NOT NULL,
168
+ home_owner_subject text NOT NULL,
169
+ PRIMARY KEY (migration_id, org_slug)
170
+ )
171
+ `);
172
+ await pool.query(`
173
+ CREATE TABLE IF NOT EXISTS billing_legacy_migration_apps (
174
+ migration_id text NOT NULL REFERENCES billing_legacy_migration_preparations(id),
175
+ billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
176
+ org_slug text NOT NULL,
177
+ app_slug text NOT NULL,
178
+ app_created_at timestamptz NOT NULL,
179
+ PRIMARY KEY (migration_id, org_slug, app_slug, app_created_at)
180
+ )
181
+ `);
182
+ await pool.query(`
183
+ CREATE TABLE IF NOT EXISTS billing_legacy_migration_plan_evidence (
184
+ migration_id text NOT NULL REFERENCES billing_legacy_migration_preparations(id),
185
+ org_slug text NOT NULL,
186
+ source text NOT NULL CHECK (source = 'default'),
187
+ plan_code text NOT NULL CHECK (plan_code = 'free'),
188
+ plan_state text NOT NULL CHECK (plan_state = 'active'),
189
+ PRIMARY KEY (migration_id, org_slug)
190
+ )
191
+ `);
192
+ await pool.query(`INSERT INTO billing_plan_versions
193
+ (id, plan_code, version, effective_from, entitlements, content_checksum, created_at)
194
+ VALUES ($1, $2, $3, $4, $5::jsonb, $6, $7)
195
+ ON CONFLICT (id) DO NOTHING`, [
196
+ FREE_PLAN_VERSION.id,
197
+ FREE_PLAN_VERSION.planCode,
198
+ FREE_PLAN_VERSION.version,
199
+ FREE_PLAN_VERSION.effectiveFrom,
200
+ JSON.stringify(FREE_PLAN_VERSION.entitlements),
201
+ FREE_PLAN_VERSION.contentChecksum,
202
+ FREE_PLAN_VERSION.createdAt,
203
+ ]);
204
+ const { rows } = await pool.query('SELECT * FROM billing_plan_versions WHERE id = $1', [FREE_PLAN_VERSION.id]);
205
+ const persistedFree = rows[0] ? planVersionRowToRecord(rows[0]) : undefined;
206
+ if (persistedFree === undefined ||
207
+ persistedFree.contentChecksum !== FREE_PLAN_VERSION.contentChecksum) {
208
+ throw new Error('persisted Free plan version does not match the immutable platform definition');
209
+ }
210
+ }
211
+ //# sourceMappingURL=postgres-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-schema.js","sourceRoot":"","sources":["../../src/billing/postgres-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAuB,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEpF,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAU;IAClD,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;GAWhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;GAOhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;GAOhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;GAUhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;GAGhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;GAGhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;GAqBhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;GAkBhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;GAOhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;GAWhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;GAehB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;GAiBhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;GAYhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;GAWhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;GAShB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;GAShB,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,KAAK,CACd;;;iCAG6B,EAC7B;QACE,iBAAiB,CAAC,EAAE;QACpB,iBAAiB,CAAC,QAAQ;QAC1B,iBAAiB,CAAC,OAAO;QACzB,iBAAiB,CAAC,aAAa;QAC/B,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,YAAY,CAAC;QAC9C,iBAAiB,CAAC,eAAe;QACjC,iBAAiB,CAAC,SAAS;KAC5B,CACF,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAC/B,mDAAmD,EACnD,CAAC,iBAAiB,CAAC,EAAE,CAAC,CACvB,CAAC;IACF,MAAM,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,IACE,aAAa,KAAK,SAAS;QAC3B,aAAa,CAAC,eAAe,KAAK,iBAAiB,CAAC,eAAe,EACnE,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;IAClG,CAAC;AACH,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { Pool, PoolClient } from 'pg';
2
+ import { type BillingAccountMemberRecord, type BillingAccountRecord, type BillingIdentityRef, type EntitlementSnapshotRecord, type OrgBillingLinkRecord, type PlanVersionRecord } from './model.js';
3
+ import type { EnsureDefaultBillingAccountInput, EnsureOrgBillingLinkInput } from './store.js';
4
+ export declare function ensureDefaultBillingAccountRow(pool: Pool, input: EnsureDefaultBillingAccountInput, now: () => Date): Promise<BillingAccountRecord>;
5
+ export declare function ensureDefaultBillingAccountTx(client: PoolClient, input: EnsureDefaultBillingAccountInput, createdAt: Date): Promise<BillingAccountRecord>;
6
+ /** Legacy-migration only: the caller must hold the complete EXCLUSIVE billing/source table freeze. */
7
+ export declare function ensureDefaultBillingAccountForLegacyMigrationTx(client: PoolClient, input: EnsureDefaultBillingAccountInput, createdAt: Date): Promise<BillingAccountRecord>;
8
+ export declare function lockDefaultBillingIdentityTx(client: PoolClient, input: BillingIdentityRef): Promise<void>;
9
+ export declare function ensureDefaultOrgBillingTx(client: PoolClient, input: {
10
+ readonly org: string;
11
+ readonly owner: {
12
+ readonly identityIssuer?: string;
13
+ readonly subject: string;
14
+ readonly email: string;
15
+ };
16
+ readonly reason: string;
17
+ }, createdAt: Date): Promise<void>;
18
+ export declare function getBillingAccountRow(pool: Pool, id: string): Promise<BillingAccountRecord | undefined>;
19
+ export declare function getDefaultBillingAccountRow(pool: Pool, identity: BillingIdentityRef): Promise<BillingAccountRecord | undefined>;
20
+ export declare function listBillingAccountMemberRows(pool: Pool, id: string): Promise<readonly BillingAccountMemberRecord[]>;
21
+ export declare function removeBillingAccountMemberRow(pool: Pool, input: BillingIdentityRef & {
22
+ readonly billingAccountId: string;
23
+ }): Promise<boolean>;
24
+ export declare function getPlanVersionRow(pool: Pool, id: string): Promise<PlanVersionRecord | undefined>;
25
+ export declare function getActiveEntitlementSnapshotRow(pool: Pool, billingAccountId: string): Promise<EntitlementSnapshotRecord | undefined>;
26
+ export declare function ensureOrgBillingLinkRow(pool: Pool, input: EnsureOrgBillingLinkInput, now: () => Date): Promise<OrgBillingLinkRecord>;
27
+ export declare function ensureOrgBillingLinkTx(client: PoolClient, input: EnsureOrgBillingLinkInput, linkedAt: Date): Promise<OrgBillingLinkRecord>;
28
+ export declare function getOrgBillingLinkRow(pool: Pool, org: string): Promise<OrgBillingLinkRecord | undefined>;
29
+ export declare function getBillingAccountQuery(queryable: Pool | PoolClient, id: string): Promise<BillingAccountRecord | undefined>;
30
+ export declare function getDefaultBillingAccountQuery(queryable: Pool | PoolClient, identity: BillingIdentityRef, forUpdate?: boolean): Promise<BillingAccountRecord | undefined>;
31
+ export declare function getActiveEntitlementSnapshotQuery(queryable: Pool | PoolClient, billingAccountId: string): Promise<EntitlementSnapshotRecord | undefined>;
32
+ export declare function getOrgBillingLinkQuery(queryable: Pool | PoolClient, org: string): Promise<OrgBillingLinkRecord | undefined>;
33
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/billing/postgres.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,285 @@
1
+ import { validateSlug } from '../store/validate.js';
2
+ import { createDefaultFreeBillingRecords, GOOGLE_CONTROL_PLANE_ISSUER, normalizeBillingEmail, normalizeBillingIdentity, validateBillingAccountId, validateBillingLinkReason, } from './model.js';
3
+ import { accountRowToRecord, entitlementSnapshotRowToRecord, memberRowToRecord, orgBillingLinkRowToRecord, planVersionRowToRecord, validateScalarId, } from './postgres-records.js';
4
+ export function ensureDefaultBillingAccountRow(pool, input, now) {
5
+ return withBillingTransaction(pool, (client) => ensureDefaultBillingAccountTx(client, input, now()));
6
+ }
7
+ export async function ensureDefaultBillingAccountTx(client, input, createdAt) {
8
+ const identity = normalizeBillingIdentity(input);
9
+ await lockDefaultBillingIdentityTx(client, identity);
10
+ return ensureDefaultBillingAccountAfterSerializationTx(client, input, createdAt);
11
+ }
12
+ /** Legacy-migration only: the caller must hold the complete EXCLUSIVE billing/source table freeze. */
13
+ export function ensureDefaultBillingAccountForLegacyMigrationTx(client, input, createdAt) {
14
+ return ensureDefaultBillingAccountAfterSerializationTx(client, input, createdAt);
15
+ }
16
+ async function ensureDefaultBillingAccountAfterSerializationTx(client, input, createdAt) {
17
+ const identity = normalizeBillingIdentity(input);
18
+ const email = normalizeBillingEmail(input.email);
19
+ const existing = await getDefaultBillingAccountQuery(client, identity, true);
20
+ if (existing !== undefined) {
21
+ const updated = await client.query(`UPDATE billing_account_members
22
+ SET email = $4
23
+ WHERE billing_account_id = $1 AND identity_issuer = $2 AND subject = $3
24
+ RETURNING *`, [existing.id, identity.identityIssuer, identity.subject, email]);
25
+ const activeSnapshot = await getActiveEntitlementSnapshotQuery(client, existing.id);
26
+ if (updated.rows[0]?.role !== 'owner' || activeSnapshot === undefined) {
27
+ throw new Error('default billing account is incomplete');
28
+ }
29
+ return existing;
30
+ }
31
+ const created = createDefaultFreeBillingRecords({
32
+ identity,
33
+ email,
34
+ ...(input.displayName !== undefined ? { displayName: input.displayName } : {}),
35
+ createdAt: createdAt.toISOString(),
36
+ });
37
+ const plan = await getPlanVersionQuery(client, created.snapshot.planVersionId);
38
+ if (plan === undefined)
39
+ throw new Error('immutable Free plan version is missing');
40
+ await client.query(`INSERT INTO billing_accounts (id, display_name, state, created_at)
41
+ VALUES ($1, $2, $3, $4)`, [
42
+ created.account.id,
43
+ created.account.displayName,
44
+ created.account.state,
45
+ created.account.createdAt,
46
+ ]);
47
+ await client.query(`INSERT INTO billing_account_members
48
+ (billing_account_id, identity_issuer, subject, email, role, created_at)
49
+ VALUES ($1, $2, $3, $4, $5, $6)`, [
50
+ created.member.billingAccountId,
51
+ created.member.identityIssuer,
52
+ created.member.subject,
53
+ created.member.email,
54
+ created.member.role,
55
+ created.member.createdAt,
56
+ ]);
57
+ await client.query(`INSERT INTO billing_account_defaults (identity_issuer, subject, billing_account_id)
58
+ VALUES ($1, $2, $3)`, [identity.identityIssuer, identity.subject, created.account.id]);
59
+ await client.query(`INSERT INTO billing_entitlement_snapshots
60
+ (id, billing_account_id, version, plan_version_id, source_kind, generation_reason,
61
+ verification_status, effective_from, monthly_usage_anchor, entitlements,
62
+ content_checksum, verified_at, created_at)
63
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, $11, $12, $13)`, [
64
+ created.snapshot.id,
65
+ created.snapshot.billingAccountId,
66
+ created.snapshot.version,
67
+ created.snapshot.planVersionId,
68
+ created.snapshot.sourceKind,
69
+ created.snapshot.generationReason,
70
+ created.snapshot.verificationStatus,
71
+ created.snapshot.effectiveFrom,
72
+ created.snapshot.monthlyUsageAnchor,
73
+ JSON.stringify(created.snapshot.entitlements),
74
+ created.snapshot.contentChecksum,
75
+ created.snapshot.verifiedAt,
76
+ created.snapshot.createdAt,
77
+ ]);
78
+ await client.query(`INSERT INTO billing_account_active_snapshots (billing_account_id, snapshot_id)
79
+ VALUES ($1, $2)`, [created.account.id, created.snapshot.id]);
80
+ return created.account;
81
+ }
82
+ export async function lockDefaultBillingIdentityTx(client, input) {
83
+ const identity = normalizeBillingIdentity(input);
84
+ // JSON keeps the tuple unambiguous without the NUL separator used by the in-memory key; PostgreSQL
85
+ // rejects NUL bytes in text parameters. The composite defaults primary key remains authoritative.
86
+ await client.query('SELECT pg_advisory_xact_lock(hashtextextended($1, 0))', [
87
+ `billing-default:${JSON.stringify([identity.identityIssuer, identity.subject])}`,
88
+ ]);
89
+ }
90
+ export async function ensureDefaultOrgBillingTx(client, input, createdAt) {
91
+ const identity = {
92
+ identityIssuer: input.owner.identityIssuer ?? GOOGLE_CONTROL_PLANE_ISSUER,
93
+ subject: input.owner.subject,
94
+ };
95
+ const billingAccount = await ensureDefaultBillingAccountTx(client, { ...identity, email: input.owner.email }, createdAt);
96
+ await ensureOrgBillingLinkTx(client, {
97
+ org: input.org,
98
+ billingAccountId: billingAccount.id,
99
+ homeFreeBillingAccountId: billingAccount.id,
100
+ homeOwner: identity,
101
+ linkedBy: identity,
102
+ reason: input.reason,
103
+ }, createdAt);
104
+ }
105
+ export function getBillingAccountRow(pool, id) {
106
+ return getBillingAccountQuery(pool, validateBillingAccountId(id));
107
+ }
108
+ export function getDefaultBillingAccountRow(pool, identity) {
109
+ return getDefaultBillingAccountQuery(pool, normalizeBillingIdentity(identity));
110
+ }
111
+ export async function listBillingAccountMemberRows(pool, id) {
112
+ const { rows } = await pool.query(`SELECT * FROM billing_account_members
113
+ WHERE billing_account_id = $1
114
+ ORDER BY created_at, identity_issuer, subject`, [validateBillingAccountId(id)]);
115
+ return rows.map(memberRowToRecord);
116
+ }
117
+ export function removeBillingAccountMemberRow(pool, input) {
118
+ const billingAccountId = validateBillingAccountId(input.billingAccountId);
119
+ const identity = normalizeBillingIdentity(input);
120
+ return withBillingTransaction(pool, async (client) => {
121
+ const { rows } = await client.query(`SELECT * FROM billing_account_members
122
+ WHERE billing_account_id = $1
123
+ ORDER BY identity_issuer, subject
124
+ FOR UPDATE`, [billingAccountId]);
125
+ const member = rows.find((candidate) => candidate.identity_issuer === identity.identityIssuer &&
126
+ candidate.subject === identity.subject);
127
+ if (member === undefined)
128
+ return false;
129
+ if (member.role === 'owner' &&
130
+ rows.filter((candidate) => candidate.role === 'owner').length === 1) {
131
+ throw new Error('cannot remove the last billing owner');
132
+ }
133
+ const defaultMapping = await client.query(`SELECT 1 FROM billing_account_defaults
134
+ WHERE identity_issuer = $1 AND subject = $2 AND billing_account_id = $3`, [identity.identityIssuer, identity.subject, billingAccountId]);
135
+ if (defaultMapping.rows[0] !== undefined) {
136
+ throw new Error('cannot remove the default billing owner without an atomic default transfer');
137
+ }
138
+ const result = await client.query(`DELETE FROM billing_account_members
139
+ WHERE billing_account_id = $1 AND identity_issuer = $2 AND subject = $3`, [billingAccountId, identity.identityIssuer, identity.subject]);
140
+ return (result.rowCount ?? 0) > 0;
141
+ });
142
+ }
143
+ export function getPlanVersionRow(pool, id) {
144
+ return getPlanVersionQuery(pool, validateScalarId('plan version id', id));
145
+ }
146
+ export function getActiveEntitlementSnapshotRow(pool, billingAccountId) {
147
+ return getActiveEntitlementSnapshotQuery(pool, validateBillingAccountId(billingAccountId));
148
+ }
149
+ export function ensureOrgBillingLinkRow(pool, input, now) {
150
+ return withBillingTransaction(pool, (client) => ensureOrgBillingLinkTx(client, input, now()));
151
+ }
152
+ export async function ensureOrgBillingLinkTx(client, input, linkedAt) {
153
+ const org = validateSlug('org', input.org);
154
+ const billingAccountId = validateBillingAccountId(input.billingAccountId);
155
+ const homeFreeBillingAccountId = validateBillingAccountId(input.homeFreeBillingAccountId);
156
+ if (billingAccountId !== homeFreeBillingAccountId) {
157
+ throw new Error('initial billing-account link must use the organization home Free account');
158
+ }
159
+ const orgResult = await client.query('SELECT slug FROM orgs WHERE slug = $1 FOR UPDATE', [org]);
160
+ if (orgResult.rows[0] === undefined)
161
+ throw new Error('organization not found');
162
+ const reason = validateBillingLinkReason(input.reason);
163
+ if ((await getBillingAccountQuery(client, billingAccountId)) === undefined) {
164
+ throw new Error('billing account not found');
165
+ }
166
+ const homeOwner = normalizeBillingIdentity(input.homeOwner);
167
+ const ownerDefault = await getDefaultBillingAccountQuery(client, homeOwner, true);
168
+ const ownerMember = await client.query(`SELECT role FROM billing_account_members
169
+ WHERE billing_account_id = $1 AND identity_issuer = $2 AND subject = $3
170
+ FOR UPDATE`, [homeFreeBillingAccountId, homeOwner.identityIssuer, homeOwner.subject]);
171
+ if (ownerDefault?.id !== homeFreeBillingAccountId || ownerMember.rows[0]?.role !== 'owner') {
172
+ throw new Error('organization home Free account must be the home owner default account');
173
+ }
174
+ const orgOwner = await client.query(`SELECT role FROM org_members
175
+ WHERE org_slug = $1 AND subject = $2
176
+ FOR UPDATE`, [org, homeOwner.subject]);
177
+ if (orgOwner.rows[0]?.role !== 'owner') {
178
+ throw new Error('organization home Free owner must be a current organization owner');
179
+ }
180
+ const linkedBy = normalizeBillingIdentity(input.linkedBy);
181
+ const existing = await getOrgBillingLinkQuery(client, org);
182
+ if (existing !== undefined) {
183
+ if (existing.billingAccountId !== billingAccountId ||
184
+ existing.homeFreeBillingAccountId !== homeFreeBillingAccountId) {
185
+ throw new Error('organization already has an active billing-account link');
186
+ }
187
+ return existing;
188
+ }
189
+ await client.query(`INSERT INTO org_billing_home_accounts
190
+ (org_slug, billing_account_id, home_owner_identity_issuer, home_owner_subject)
191
+ VALUES ($1, $2, $3, $4)
192
+ ON CONFLICT (org_slug) DO NOTHING`, [org, homeFreeBillingAccountId, homeOwner.identityIssuer, homeOwner.subject]);
193
+ const home = await client.query(`SELECT billing_account_id, home_owner_identity_issuer, home_owner_subject
194
+ FROM org_billing_home_accounts WHERE org_slug = $1`, [org]);
195
+ if (home.rows[0]?.billing_account_id !== homeFreeBillingAccountId ||
196
+ home.rows[0]?.home_owner_identity_issuer !== homeOwner.identityIssuer ||
197
+ home.rows[0]?.home_owner_subject !== homeOwner.subject) {
198
+ throw new Error('organization home Free billing account and owner are immutable');
199
+ }
200
+ const { rows } = await client.query(`WITH inserted AS (
201
+ INSERT INTO org_billing_links
202
+ (org_slug, billing_account_id, home_free_billing_account_id,
203
+ fallback_billing_account_id, version, active, linked_at,
204
+ linked_by_identity_issuer, linked_by_subject, reason)
205
+ VALUES ($1, $2, $3, $3, 1, true, $4, $5, $6, $7)
206
+ RETURNING *
207
+ )
208
+ SELECT inserted.*,
209
+ home.home_owner_identity_issuer,
210
+ home.home_owner_subject
211
+ FROM inserted
212
+ JOIN org_billing_home_accounts AS home ON home.org_slug = inserted.org_slug`, [
213
+ org,
214
+ billingAccountId,
215
+ homeFreeBillingAccountId,
216
+ linkedAt,
217
+ linkedBy.identityIssuer,
218
+ linkedBy.subject,
219
+ reason,
220
+ ]);
221
+ return orgBillingLinkRowToRecord(rows[0]);
222
+ }
223
+ export function getOrgBillingLinkRow(pool, org) {
224
+ return getOrgBillingLinkQuery(pool, validateSlug('org', org));
225
+ }
226
+ export async function getBillingAccountQuery(queryable, id) {
227
+ const { rows } = await queryable.query('SELECT * FROM billing_accounts WHERE id = $1', [id]);
228
+ return rows[0] ? accountRowToRecord(rows[0]) : undefined;
229
+ }
230
+ export async function getDefaultBillingAccountQuery(queryable, identity, forUpdate = false) {
231
+ const { rows } = await queryable.query(`SELECT account.*
232
+ FROM billing_account_defaults AS defaults
233
+ JOIN billing_accounts AS account ON account.id = defaults.billing_account_id
234
+ WHERE defaults.identity_issuer = $1 AND defaults.subject = $2
235
+ ${forUpdate ? 'FOR UPDATE OF defaults, account' : ''}`, [identity.identityIssuer, identity.subject]);
236
+ return rows[0] ? accountRowToRecord(rows[0]) : undefined;
237
+ }
238
+ async function getPlanVersionQuery(queryable, id) {
239
+ const { rows } = await queryable.query('SELECT * FROM billing_plan_versions WHERE id = $1', [id]);
240
+ return rows[0] ? planVersionRowToRecord(rows[0]) : undefined;
241
+ }
242
+ export async function getActiveEntitlementSnapshotQuery(queryable, billingAccountId) {
243
+ const { rows } = await queryable.query(`SELECT snapshot.*,
244
+ plan.plan_code,
245
+ plan.version AS plan_version,
246
+ plan.effective_from AS plan_effective_from,
247
+ plan.entitlements AS plan_entitlements,
248
+ plan.content_checksum AS plan_content_checksum,
249
+ plan.created_at AS plan_created_at
250
+ FROM billing_account_active_snapshots AS active
251
+ JOIN billing_entitlement_snapshots AS snapshot
252
+ ON snapshot.billing_account_id = active.billing_account_id
253
+ AND snapshot.id = active.snapshot_id
254
+ JOIN billing_plan_versions AS plan ON plan.id = snapshot.plan_version_id
255
+ WHERE active.billing_account_id = $1`, [billingAccountId]);
256
+ return rows[0] ? entitlementSnapshotRowToRecord(rows[0]) : undefined;
257
+ }
258
+ export async function getOrgBillingLinkQuery(queryable, org) {
259
+ const { rows } = await queryable.query(`SELECT links.*,
260
+ home.home_owner_identity_issuer,
261
+ home.home_owner_subject
262
+ FROM org_billing_links AS links
263
+ JOIN org_billing_home_accounts AS home ON home.org_slug = links.org_slug
264
+ WHERE links.org_slug = $1`, [org]);
265
+ return rows[0] ? orgBillingLinkRowToRecord(rows[0]) : undefined;
266
+ }
267
+ async function withBillingTransaction(pool, operation) {
268
+ const client = await pool.connect();
269
+ try {
270
+ await client.query('BEGIN');
271
+ try {
272
+ const result = await operation(client);
273
+ await client.query('COMMIT');
274
+ return result;
275
+ }
276
+ catch (error) {
277
+ await client.query('ROLLBACK');
278
+ throw error;
279
+ }
280
+ }
281
+ finally {
282
+ client.release();
283
+ }
284
+ }
285
+ //# sourceMappingURL=postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/billing/postgres.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { BillingAccountMemberRecord, BillingAccountRecord, BillingIdentityRef, EntitlementSnapshotRecord, OrgBillingLinkRecord, PlanVersionRecord } from './model.js';
2
+ export interface EnsureDefaultBillingAccountInput extends BillingIdentityRef {
3
+ readonly email: string;
4
+ readonly displayName?: string;
5
+ }
6
+ export interface EnsureOrgBillingLinkInput {
7
+ readonly org: string;
8
+ readonly billingAccountId: string;
9
+ readonly homeFreeBillingAccountId: string;
10
+ readonly homeOwner: BillingIdentityRef;
11
+ readonly linkedBy: BillingIdentityRef;
12
+ readonly reason: string;
13
+ }
14
+ export interface BillingAccountStore {
15
+ ensureDefaultBillingAccount(input: EnsureDefaultBillingAccountInput): Promise<BillingAccountRecord>;
16
+ getBillingAccount(id: string): Promise<BillingAccountRecord | undefined>;
17
+ getDefaultBillingAccount(identity: BillingIdentityRef): Promise<BillingAccountRecord | undefined>;
18
+ listBillingAccountMembers(id: string): Promise<readonly BillingAccountMemberRecord[]>;
19
+ removeBillingAccountMember(input: BillingIdentityRef & {
20
+ readonly billingAccountId: string;
21
+ }): Promise<boolean>;
22
+ getPlanVersion(id: string): Promise<PlanVersionRecord | undefined>;
23
+ getActiveEntitlementSnapshot(billingAccountId: string): Promise<EntitlementSnapshotRecord | undefined>;
24
+ ensureOrgBillingLink(input: EnsureOrgBillingLinkInput): Promise<OrgBillingLinkRecord>;
25
+ getOrgBillingLink(org: string): Promise<OrgBillingLinkRecord | undefined>;
26
+ }
27
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/billing/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,gCAAiC,SAAQ,kBAAkB;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,2BAA2B,CACzB,KAAK,EAAE,gCAAgC,GACtC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACjC,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAC;IACzE,wBAAwB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAC;IAClG,yBAAyB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,0BAA0B,EAAE,CAAC,CAAC;IACtF,0BAA0B,CACxB,KAAK,EAAE,kBAAkB,GAAG;QAAE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,GAChE,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;IACnE,4BAA4B,CAC1B,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,yBAAyB,GAAG,SAAS,CAAC,CAAC;IAClD,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACtF,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAC;CAC3E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/billing/store.ts"],"names":[],"mappings":""}
@@ -6,6 +6,10 @@ export { type AssetAdapterKind, type AssetConfigInput, type AssetConfigResolutio
6
6
  export { type AssetStore, type AssetUploadPlan, type AssetUploadTarget, InMemoryAssetStore, type InMemoryAssetStoreOptions, } from './assets.js';
7
7
  export { ASSISTANT_INTERACTION_OUTCOME_RETENTION_MS, type AssistantClientRecord, type AssistantConfirmationInteractionRecord, type AssistantHistoryMessage, AssistantInteractionCapacityError, type AssistantInteractionClaimResult, type AssistantInteractionCompletion, type AssistantInteractionCompletionResult, type AssistantInteractionCreateInput, type AssistantInteractionKind, type AssistantInteractionPublicOutcome, type AssistantInteractionRecord, type AssistantInteractionScope, type AssistantInteractionStatus, type AssistantInteractionTransitionInput, type AssistantInteractionTransitionNext, type AssistantInteractionTransitionResult, type AssistantSessionRecord, type AssistantStore, DEFAULT_MAX_PENDING_INTERACTIONS_PER_SESSION, InMemoryAssistantStore, } from './assistant-store.js';
8
8
  export { allowAllGate, bearerToken, type ControlPlaneAuthResult, type ControlPlaneIdentity, constantTimeEqual, type DeployAuthGate, GoogleControlPlaneGate, type GoogleIdTokenVerifier, NoodleOAuthControlPlaneGate, } from './auth/deploy-gate.js';
9
+ export { type LegacyBillingMigrationBlocker, type LegacyBillingMigrationBlockerCode, type LegacyBillingMigrationMapping, type LegacyBillingMigrationPreview, type LegacyBillingMigrationRequest, type LegacyProductionAppIdentity, legacyBillingMigrationMappingChecksum, legacyBillingMigrationPreviewChecksum, parseLegacyBillingMigrationRequest, previewLegacyBillingMigration, } from './billing/migration.js';
10
+ export { type LegacyBillingMigrationApplyRequest, type LegacyBillingMigrationApplyResult, type LegacyBillingMigrationConflictCode, LegacyBillingMigrationConflictError, type LegacyBillingMigrationStore, type LegacyBillingPlanEvidence, legacyBillingMigrationPlanEvidenceChecksum, normalizeBillingMigrationServiceUrl, parseLegacyBillingMigrationApplyRequest, parseLegacyBillingMigrationPlanEvidence, } from './billing/migration-apply.js';
11
+ export { type BillingAccountMemberRecord, type BillingAccountMemberRole, type BillingAccountRecord, type BillingAccountState, type BillingEntitlements, type BillingIdentityRef, type EntitlementSnapshotRecord, FREE_ENTITLEMENTS, FREE_PLAN_VERSION, GOOGLE_CONTROL_PLANE_ISSUER, type OrgBillingLinkRecord, type PlanVersionRecord, } from './billing/model.js';
12
+ export type { BillingAccountStore, EnsureDefaultBillingAccountInput, EnsureOrgBillingLinkInput, } from './billing/store.js';
9
13
  export { type BuildInfo, resolveBuildInfo } from './build-info.js';
10
14
  export { COMMERCIAL_PLAN_NAMES, type CommercialPlanCatalog, type CommercialPlanName, commercialPlanPolicyId, DEFAULT_COMMERCIAL_PLAN_CATALOG, isCommercialPlanName, isCommercialPlanPolicyId, planNameForPolicy, resolveCommercialPlanCatalog, } from './commercial-plans.js';
11
15
  export { type CustomerVerifierFactoryOptions, createCustomerVerifierFactory, createFirebaseIdTokenVerifier, } from './customer-verifier.js';