@secondlayer/shared 2.1.0 → 3.0.0-beta.1

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 (65) hide show
  1. package/README.md +2 -2
  2. package/dist/src/crypto/secrets.js +47 -3
  3. package/dist/src/crypto/secrets.js.map +5 -4
  4. package/dist/src/db/index.d.ts +112 -137
  5. package/dist/src/db/index.js.map +2 -2
  6. package/dist/src/db/jsonb.d.ts +5 -1
  7. package/dist/src/db/jsonb.js.map +2 -2
  8. package/dist/src/db/queries/account-spend-caps.d.ts +444 -0
  9. package/dist/src/db/queries/account-spend-caps.js +60 -0
  10. package/dist/src/db/queries/account-spend-caps.js.map +10 -0
  11. package/dist/src/db/queries/account-usage.d.ts +468 -0
  12. package/dist/src/db/queries/account-usage.js +222 -0
  13. package/dist/src/db/queries/account-usage.js.map +11 -0
  14. package/dist/src/db/queries/accounts.d.ts +100 -109
  15. package/dist/src/db/queries/accounts.js +15 -1
  16. package/dist/src/db/queries/accounts.js.map +3 -3
  17. package/dist/src/db/queries/integrity.d.ts +85 -107
  18. package/dist/src/db/queries/projects.d.ts +87 -109
  19. package/dist/src/db/queries/provisioning-audit.d.ts +85 -107
  20. package/dist/src/db/queries/subgraph-gaps.d.ts +85 -107
  21. package/dist/src/db/queries/subgraphs.d.ts +86 -109
  22. package/dist/src/db/queries/subgraphs.js +2 -3
  23. package/dist/src/db/queries/subgraphs.js.map +4 -4
  24. package/dist/src/db/queries/{workflows.d.ts → tenant-compute-addons.d.ts} +108 -142
  25. package/dist/src/db/queries/tenant-compute-addons.js +47 -0
  26. package/dist/src/db/queries/tenant-compute-addons.js.map +10 -0
  27. package/dist/src/db/queries/tenants.d.ts +98 -110
  28. package/dist/src/db/queries/tenants.js +55 -8
  29. package/dist/src/db/queries/tenants.js.map +6 -5
  30. package/dist/src/db/queries/usage.d.ts +86 -132
  31. package/dist/src/db/queries/usage.js +5 -64
  32. package/dist/src/db/queries/usage.js.map +4 -5
  33. package/dist/src/db/schema.d.ts +107 -136
  34. package/dist/src/errors.d.ts +8 -7
  35. package/dist/src/errors.js +11 -12
  36. package/dist/src/errors.js.map +3 -3
  37. package/dist/src/index.d.ts +119 -143
  38. package/dist/src/index.js +11 -12
  39. package/dist/src/index.js.map +4 -4
  40. package/dist/src/node/local-client.d.ts +85 -107
  41. package/dist/src/pricing.d.ts +20 -1
  42. package/dist/src/pricing.js +58 -1
  43. package/dist/src/pricing.js.map +3 -3
  44. package/migrations/0045_drop_marketplace_columns.ts +47 -0
  45. package/migrations/0046_tenant_activity_signal.ts +47 -0
  46. package/migrations/0047_usage_daily_tenant_id.ts +73 -0
  47. package/migrations/0048_tenant_compute_addons.ts +49 -0
  48. package/migrations/0049_accounts_stripe_customer_id.ts +30 -0
  49. package/migrations/0050_account_spend_caps.ts +45 -0
  50. package/migrations/0051_workflow_ai_usage_daily.ts +40 -0
  51. package/migrations/0052_sentries.ts +61 -0
  52. package/migrations/0053_workflow_runtime.ts +88 -0
  53. package/migrations/0054_accounts_plan_hobby.ts +32 -0
  54. package/migrations/0055_ai_usage_account_scope.ts +108 -0
  55. package/migrations/0056_drop_workflow_sentry_residuals.ts +23 -0
  56. package/migrations/0057_subscriptions.ts +137 -0
  57. package/package.json +26 -14
  58. package/dist/src/db/queries/workflows.js +0 -260
  59. package/dist/src/db/queries/workflows.js.map +0 -12
  60. package/dist/src/lib/plans.d.ts +0 -9
  61. package/dist/src/lib/plans.js +0 -37
  62. package/dist/src/lib/plans.js.map +0 -10
  63. package/dist/src/schemas/workflows.d.ts +0 -70
  64. package/dist/src/schemas/workflows.js +0 -43
  65. package/dist/src/schemas/workflows.js.map +0 -10
@@ -0,0 +1,444 @@
1
+ import { Kysely } from "kysely";
2
+ import { ColumnType, Generated, Selectable, Updateable } from "kysely";
3
+ interface BlocksTable {
4
+ height: number;
5
+ hash: string;
6
+ parent_hash: string;
7
+ burn_block_height: number;
8
+ timestamp: number;
9
+ canonical: Generated<boolean>;
10
+ created_at: Generated<Date>;
11
+ }
12
+ interface TransactionsTable {
13
+ tx_id: string;
14
+ block_height: number;
15
+ tx_index: Generated<number>;
16
+ type: string;
17
+ sender: string;
18
+ status: string;
19
+ contract_id: string | null;
20
+ function_name: string | null;
21
+ function_args: Generated<unknown | null>;
22
+ raw_result: Generated<string | null>;
23
+ raw_tx: string;
24
+ created_at: Generated<Date>;
25
+ }
26
+ interface EventsTable {
27
+ id: Generated<string>;
28
+ tx_id: string;
29
+ block_height: number;
30
+ event_index: number;
31
+ type: string;
32
+ data: unknown;
33
+ created_at: Generated<Date>;
34
+ }
35
+ interface IndexProgressTable {
36
+ network: string;
37
+ last_indexed_block: Generated<number>;
38
+ last_contiguous_block: Generated<number>;
39
+ highest_seen_block: Generated<number>;
40
+ updated_at: Generated<Date>;
41
+ }
42
+ interface SubgraphsTable {
43
+ id: Generated<string>;
44
+ name: string;
45
+ version: Generated<string>;
46
+ status: Generated<string>;
47
+ definition: Record<string, unknown>;
48
+ schema_hash: string;
49
+ handler_path: string;
50
+ schema_name: string | null;
51
+ start_block: Generated<number>;
52
+ last_processed_block: Generated<number>;
53
+ reindex_from_block: number | null;
54
+ reindex_to_block: number | null;
55
+ last_error: string | null;
56
+ last_error_at: Date | null;
57
+ total_processed: Generated<number>;
58
+ total_errors: Generated<number>;
59
+ account_id: string;
60
+ handler_code: string | null;
61
+ source_code: string | null;
62
+ project_id: string | null;
63
+ created_at: Generated<Date>;
64
+ updated_at: Generated<Date>;
65
+ }
66
+ interface SubgraphGapsTable {
67
+ id: Generated<string>;
68
+ subgraph_id: string;
69
+ subgraph_name: string;
70
+ gap_start: number;
71
+ gap_end: number;
72
+ reason: string;
73
+ detected_at: Generated<Date>;
74
+ resolved_at: Date | null;
75
+ }
76
+ interface ApiKeysTable {
77
+ id: Generated<string>;
78
+ key_hash: string;
79
+ key_prefix: string;
80
+ name: string | null;
81
+ status: Generated<string>;
82
+ rate_limit: Generated<number>;
83
+ ip_address: string;
84
+ account_id: string;
85
+ last_used_at: Date | null;
86
+ revoked_at: Date | null;
87
+ created_at: Generated<Date>;
88
+ }
89
+ interface AccountsTable {
90
+ id: Generated<string>;
91
+ email: string;
92
+ plan: Generated<string>;
93
+ display_name: string | null;
94
+ bio: string | null;
95
+ avatar_url: string | null;
96
+ slug: string | null;
97
+ stripe_customer_id: string | null;
98
+ created_at: Generated<Date>;
99
+ }
100
+ interface SessionsTable {
101
+ id: Generated<string>;
102
+ token_hash: string;
103
+ token_prefix: string;
104
+ account_id: string;
105
+ ip_address: string;
106
+ expires_at: Generated<Date>;
107
+ revoked_at: Date | null;
108
+ last_used_at: Date | null;
109
+ created_at: Generated<Date>;
110
+ }
111
+ interface MagicLinksTable {
112
+ id: Generated<string>;
113
+ email: string;
114
+ token: string;
115
+ code: string | null;
116
+ expires_at: Date;
117
+ used_at: Date | null;
118
+ failed_attempts: Generated<number>;
119
+ created_at: Generated<Date>;
120
+ }
121
+ interface UsageDailyTable {
122
+ account_id: string;
123
+ tenant_id: string | null;
124
+ date: string;
125
+ api_requests: Generated<number>;
126
+ deliveries: Generated<number>;
127
+ }
128
+ interface UsageSnapshotsTable {
129
+ id: Generated<string>;
130
+ account_id: string;
131
+ measured_at: Generated<Date>;
132
+ storage_bytes: Generated<number>;
133
+ }
134
+ interface WaitlistTable {
135
+ id: Generated<string>;
136
+ email: string;
137
+ source: Generated<string>;
138
+ status: Generated<string>;
139
+ created_at: Generated<Date>;
140
+ }
141
+ interface AccountInsightsTable {
142
+ id: Generated<string>;
143
+ account_id: string;
144
+ category: string;
145
+ insight_type: string;
146
+ resource_id: string | null;
147
+ severity: string;
148
+ title: string;
149
+ body: string;
150
+ data: unknown;
151
+ dismissed_at: Date | null;
152
+ expires_at: Date | null;
153
+ created_at: Generated<Date>;
154
+ }
155
+ interface AccountAgentRunsTable {
156
+ id: Generated<string>;
157
+ account_id: string;
158
+ started_at: Generated<Date>;
159
+ completed_at: Date | null;
160
+ status: Generated<string>;
161
+ input_tokens: Generated<number>;
162
+ output_tokens: Generated<number>;
163
+ cost_usd: Generated<number>;
164
+ insights_created: Generated<number>;
165
+ error: string | null;
166
+ }
167
+ interface SubgraphProcessingStatsTable {
168
+ id: Generated<string>;
169
+ subgraph_name: string;
170
+ api_key_id: string | null;
171
+ bucket_start: Date | null;
172
+ bucket_end: Date | null;
173
+ blocks_processed: number | null;
174
+ total_time_ms: number | null;
175
+ handler_time_ms: number | null;
176
+ flush_time_ms: number | null;
177
+ max_block_time_ms: number | null;
178
+ max_handler_time_ms: number | null;
179
+ avg_ops_per_block: number | null;
180
+ is_catchup: Generated<boolean>;
181
+ created_at: Generated<Date>;
182
+ }
183
+ interface SubgraphTableSnapshotsTable {
184
+ id: Generated<string>;
185
+ subgraph_name: string;
186
+ api_key_id: string | null;
187
+ table_name: string;
188
+ row_count: number | null;
189
+ created_at: Generated<Date>;
190
+ }
191
+ interface SubgraphHealthSnapshotsTable {
192
+ id: Generated<string>;
193
+ subgraph_id: string;
194
+ total_processed: number;
195
+ total_errors: number;
196
+ last_processed_block: number | null;
197
+ captured_at: Generated<Date>;
198
+ }
199
+ interface SubgraphUsageDailyTable {
200
+ subgraph_id: string;
201
+ date: string;
202
+ query_count: Generated<number>;
203
+ }
204
+ interface ProjectsTable {
205
+ id: Generated<string>;
206
+ name: string;
207
+ slug: string;
208
+ account_id: string;
209
+ settings: Generated<Record<string, unknown>>;
210
+ network: Generated<string>;
211
+ node_rpc: string | null;
212
+ created_at: Generated<Date>;
213
+ updated_at: Generated<Date>;
214
+ }
215
+ interface TeamMembersTable {
216
+ id: Generated<string>;
217
+ project_id: string;
218
+ account_id: string;
219
+ role: Generated<string>;
220
+ invited_by: string | null;
221
+ created_at: Generated<Date>;
222
+ }
223
+ interface TeamInvitationsTable {
224
+ id: Generated<string>;
225
+ project_id: string;
226
+ email: string;
227
+ role: Generated<string>;
228
+ token: string;
229
+ invited_by: string | null;
230
+ expires_at: Date;
231
+ accepted_at: Date | null;
232
+ created_at: Generated<Date>;
233
+ }
234
+ interface ChatSessionsTable {
235
+ id: Generated<string>;
236
+ account_id: string;
237
+ title: string | null;
238
+ summary: unknown | null;
239
+ created_at: Generated<Date>;
240
+ updated_at: Generated<Date>;
241
+ }
242
+ interface ChatMessagesTable {
243
+ id: Generated<string>;
244
+ chat_session_id: string;
245
+ role: string;
246
+ parts: unknown;
247
+ metadata: unknown | null;
248
+ created_at: Generated<Date>;
249
+ }
250
+ interface Database {
251
+ blocks: BlocksTable;
252
+ transactions: TransactionsTable;
253
+ events: EventsTable;
254
+ index_progress: IndexProgressTable;
255
+ subgraphs: SubgraphsTable;
256
+ api_keys: ApiKeysTable;
257
+ accounts: AccountsTable;
258
+ sessions: SessionsTable;
259
+ magic_links: MagicLinksTable;
260
+ usage_daily: UsageDailyTable;
261
+ usage_snapshots: UsageSnapshotsTable;
262
+ waitlist: WaitlistTable;
263
+ account_insights: AccountInsightsTable;
264
+ account_agent_runs: AccountAgentRunsTable;
265
+ subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
266
+ subgraph_processing_stats: SubgraphProcessingStatsTable;
267
+ subgraph_table_snapshots: SubgraphTableSnapshotsTable;
268
+ subgraph_gaps: SubgraphGapsTable;
269
+ subgraph_usage_daily: SubgraphUsageDailyTable;
270
+ projects: ProjectsTable;
271
+ team_members: TeamMembersTable;
272
+ team_invitations: TeamInvitationsTable;
273
+ chat_sessions: ChatSessionsTable;
274
+ chat_messages: ChatMessagesTable;
275
+ tenants: TenantsTable;
276
+ tenant_usage_monthly: TenantUsageMonthlyTable;
277
+ tenant_compute_addons: TenantComputeAddonsTable;
278
+ account_spend_caps: AccountSpendCapsTable;
279
+ provisioning_audit_log: ProvisioningAuditLogTable;
280
+ subscriptions: SubscriptionsTable;
281
+ subscription_outbox: SubscriptionOutboxTable;
282
+ subscription_deliveries: SubscriptionDeliveriesTable;
283
+ }
284
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
285
+ interface TenantsTable {
286
+ id: Generated<string>;
287
+ account_id: string;
288
+ slug: string;
289
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
290
+ plan: string;
291
+ cpus: ColumnType<number, number | string, number | string>;
292
+ memory_mb: number;
293
+ storage_limit_mb: number;
294
+ storage_used_mb: number | null;
295
+ pg_container_id: string | null;
296
+ api_container_id: string | null;
297
+ processor_container_id: string | null;
298
+ target_database_url_enc: Buffer;
299
+ tenant_jwt_secret_enc: Buffer;
300
+ anon_key_enc: Buffer;
301
+ service_key_enc: Buffer;
302
+ api_url_internal: string;
303
+ api_url_public: string;
304
+ suspended_at: Date | null;
305
+ last_health_check_at: Date | null;
306
+ last_active_at: Generated<Date>;
307
+ service_gen: Generated<number>;
308
+ anon_gen: Generated<number>;
309
+ project_id: string | null;
310
+ created_at: Generated<Date>;
311
+ updated_at: Generated<Date>;
312
+ }
313
+ interface TenantUsageMonthlyTable {
314
+ id: Generated<string>;
315
+ tenant_id: string;
316
+ period_month: Date;
317
+ storage_peak_mb: Generated<number>;
318
+ storage_avg_mb: Generated<number>;
319
+ storage_last_mb: Generated<number>;
320
+ measurements: Generated<number>;
321
+ first_at: Generated<Date>;
322
+ last_at: Generated<Date>;
323
+ }
324
+ interface TenantComputeAddonsTable {
325
+ id: Generated<string>;
326
+ tenant_id: string;
327
+ memory_mb_delta: Generated<number>;
328
+ cpu_delta: Generated<number | string>;
329
+ storage_mb_delta: Generated<number>;
330
+ effective_from: Generated<Date>;
331
+ effective_until: Date | null;
332
+ stripe_subscription_item_id: string | null;
333
+ created_at: Generated<Date>;
334
+ }
335
+ interface AccountSpendCapsTable {
336
+ account_id: string;
337
+ monthly_cap_cents: number | null;
338
+ compute_cap_cents: number | null;
339
+ storage_cap_cents: number | null;
340
+ ai_cap_cents: number | null;
341
+ alert_threshold_pct: Generated<number>;
342
+ alert_sent_at: Date | null;
343
+ frozen_at: Date | null;
344
+ updated_at: Generated<Date>;
345
+ }
346
+ type AccountSpendCap = Selectable<AccountSpendCapsTable>;
347
+ type UpdateAccountSpendCap = Updateable<AccountSpendCapsTable>;
348
+ type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
349
+ type ProvisioningAuditStatus = "ok" | "error";
350
+ interface ProvisioningAuditLogTable {
351
+ id: Generated<string>;
352
+ tenant_id: string | null;
353
+ tenant_slug: string | null;
354
+ account_id: string | null;
355
+ actor: string;
356
+ event: ProvisioningAuditEvent;
357
+ status: ProvisioningAuditStatus;
358
+ detail: unknown | null;
359
+ error: string | null;
360
+ created_at: Generated<Date>;
361
+ }
362
+ type SubscriptionStatus = "active" | "paused" | "error";
363
+ type SubscriptionFormat = "standard-webhooks" | "inngest" | "trigger" | "cloudflare" | "cloudevents" | "raw";
364
+ type SubscriptionRuntime = "inngest" | "trigger" | "cloudflare" | "node";
365
+ interface SubscriptionsTable {
366
+ id: Generated<string>;
367
+ account_id: string;
368
+ project_id: string | null;
369
+ name: string;
370
+ status: ColumnType<SubscriptionStatus, SubscriptionStatus | undefined, SubscriptionStatus>;
371
+ subgraph_name: string;
372
+ table_name: string;
373
+ filter: Generated<unknown>;
374
+ format: ColumnType<SubscriptionFormat, SubscriptionFormat | undefined, SubscriptionFormat>;
375
+ runtime: SubscriptionRuntime | null;
376
+ url: string;
377
+ signing_secret_enc: Buffer;
378
+ auth_config: Generated<unknown>;
379
+ max_retries: Generated<number>;
380
+ timeout_ms: Generated<number>;
381
+ concurrency: Generated<number>;
382
+ circuit_failures: Generated<number>;
383
+ circuit_opened_at: Date | null;
384
+ last_delivery_at: Date | null;
385
+ last_success_at: Date | null;
386
+ last_error: string | null;
387
+ created_at: Generated<Date>;
388
+ updated_at: Generated<Date>;
389
+ }
390
+ type OutboxStatus = "pending" | "delivered" | "dead";
391
+ interface SubscriptionOutboxTable {
392
+ id: Generated<string>;
393
+ subscription_id: string;
394
+ subgraph_name: string;
395
+ table_name: string;
396
+ block_height: number | bigint;
397
+ tx_id: string | null;
398
+ row_pk: unknown;
399
+ event_type: string;
400
+ payload: unknown;
401
+ dedup_key: string;
402
+ attempt: Generated<number>;
403
+ next_attempt_at: Generated<Date>;
404
+ status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
405
+ is_replay: Generated<boolean>;
406
+ delivered_at: Date | null;
407
+ failed_at: Date | null;
408
+ locked_by: string | null;
409
+ locked_until: Date | null;
410
+ created_at: Generated<Date>;
411
+ }
412
+ interface SubscriptionDeliveriesTable {
413
+ id: Generated<string>;
414
+ outbox_id: string;
415
+ subscription_id: string;
416
+ attempt: number;
417
+ status_code: number | null;
418
+ response_headers: unknown | null;
419
+ response_body: string | null;
420
+ error_message: string | null;
421
+ duration_ms: number | null;
422
+ dispatched_at: Generated<Date>;
423
+ }
424
+ /**
425
+ * Spend-cap state for an account. Both the metering crons (check + set
426
+ * frozen_at) and the dashboard (read + update caps) call through here.
427
+ */
428
+ declare function getCaps(db: Kysely<Database>, accountId: string): Promise<AccountSpendCap | null>;
429
+ /**
430
+ * Upsert semantics: row is created on first write (default threshold
431
+ * 80%), subsequent writes PATCH. `updated_at` is always bumped.
432
+ */
433
+ declare function upsertCaps(db: Kysely<Database>, accountId: string, patch: Omit<UpdateAccountSpendCap, "account_id" | "updated_at">): Promise<AccountSpendCap>;
434
+ /** Mark an account frozen at the current time (cap just tripped). */
435
+ declare function freezeAccount(db: Kysely<Database>, accountId: string): Promise<void>;
436
+ /**
437
+ * Clear the frozen + alert state — called on `invoice.paid` webhook at
438
+ * cycle rollover (new billing period starts fresh) OR when the user
439
+ * explicitly raises their cap above current usage.
440
+ */
441
+ declare function clearFreeze(db: Kysely<Database>, accountId: string): Promise<void>;
442
+ /** Is this account currently cap-frozen? Bulk-checked by metering crons. */
443
+ declare function listFrozenAccountIds(db: Kysely<Database>): Promise<Set<string>>;
444
+ export { upsertCaps, listFrozenAccountIds, getCaps, freezeAccount, clearFreeze };
@@ -0,0 +1,60 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+
17
+ // src/db/queries/account-spend-caps.ts
18
+ async function getCaps(db, accountId) {
19
+ const row = await db.selectFrom("account_spend_caps").selectAll().where("account_id", "=", accountId).executeTakeFirst();
20
+ return row ?? null;
21
+ }
22
+ async function upsertCaps(db, accountId, patch) {
23
+ const insert = {
24
+ account_id: accountId,
25
+ monthly_cap_cents: patch.monthly_cap_cents ?? null,
26
+ compute_cap_cents: patch.compute_cap_cents ?? null,
27
+ storage_cap_cents: patch.storage_cap_cents ?? null,
28
+ ai_cap_cents: patch.ai_cap_cents ?? null,
29
+ alert_threshold_pct: patch.alert_threshold_pct ?? 80,
30
+ alert_sent_at: patch.alert_sent_at ?? null,
31
+ frozen_at: patch.frozen_at ?? null
32
+ };
33
+ return db.insertInto("account_spend_caps").values(insert).onConflict((oc) => oc.column("account_id").doUpdateSet({
34
+ ...patch,
35
+ updated_at: new Date
36
+ })).returningAll().executeTakeFirstOrThrow();
37
+ }
38
+ async function freezeAccount(db, accountId) {
39
+ await upsertCaps(db, accountId, { frozen_at: new Date });
40
+ }
41
+ async function clearFreeze(db, accountId) {
42
+ await upsertCaps(db, accountId, {
43
+ frozen_at: null,
44
+ alert_sent_at: null
45
+ });
46
+ }
47
+ async function listFrozenAccountIds(db) {
48
+ const rows = await db.selectFrom("account_spend_caps").select("account_id").where("frozen_at", "is not", null).execute();
49
+ return new Set(rows.map((r) => r.account_id));
50
+ }
51
+ export {
52
+ upsertCaps,
53
+ listFrozenAccountIds,
54
+ getCaps,
55
+ freezeAccount,
56
+ clearFreeze
57
+ };
58
+
59
+ //# debugId=ECBEFE5FC18A7EFB64756E2164756E21
60
+ //# sourceMappingURL=account-spend-caps.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/db/queries/account-spend-caps.ts"],
4
+ "sourcesContent": [
5
+ "import type { Kysely } from \"kysely\";\nimport type {\n\tAccountSpendCap,\n\tDatabase,\n\tInsertAccountSpendCap,\n\tUpdateAccountSpendCap,\n} from \"../types.ts\";\n\n/**\n * Spend-cap state for an account. Both the metering crons (check + set\n * frozen_at) and the dashboard (read + update caps) call through here.\n */\n\nexport async function getCaps(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<AccountSpendCap | null> {\n\tconst row = await db\n\t\t.selectFrom(\"account_spend_caps\")\n\t\t.selectAll()\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\n/**\n * Upsert semantics: row is created on first write (default threshold\n * 80%), subsequent writes PATCH. `updated_at` is always bumped.\n */\nexport async function upsertCaps(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tpatch: Omit<UpdateAccountSpendCap, \"account_id\" | \"updated_at\">,\n): Promise<AccountSpendCap> {\n\tconst insert: InsertAccountSpendCap = {\n\t\taccount_id: accountId,\n\t\tmonthly_cap_cents: patch.monthly_cap_cents ?? null,\n\t\tcompute_cap_cents: patch.compute_cap_cents ?? null,\n\t\tstorage_cap_cents: patch.storage_cap_cents ?? null,\n\t\tai_cap_cents: patch.ai_cap_cents ?? null,\n\t\talert_threshold_pct: patch.alert_threshold_pct ?? 80,\n\t\talert_sent_at: patch.alert_sent_at ?? null,\n\t\tfrozen_at: patch.frozen_at ?? null,\n\t};\n\n\treturn db\n\t\t.insertInto(\"account_spend_caps\")\n\t\t.values(insert)\n\t\t.onConflict((oc) =>\n\t\t\toc.column(\"account_id\").doUpdateSet({\n\t\t\t\t...patch,\n\t\t\t\tupdated_at: new Date(),\n\t\t\t}),\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\n/** Mark an account frozen at the current time (cap just tripped). */\nexport async function freezeAccount(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<void> {\n\tawait upsertCaps(db, accountId, { frozen_at: new Date() });\n}\n\n/**\n * Clear the frozen + alert state — called on `invoice.paid` webhook at\n * cycle rollover (new billing period starts fresh) OR when the user\n * explicitly raises their cap above current usage.\n */\nexport async function clearFreeze(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<void> {\n\tawait upsertCaps(db, accountId, {\n\t\tfrozen_at: null,\n\t\talert_sent_at: null,\n\t});\n}\n\n/** Is this account currently cap-frozen? Bulk-checked by metering crons. */\nexport async function listFrozenAccountIds(\n\tdb: Kysely<Database>,\n): Promise<Set<string>> {\n\tconst rows = await db\n\t\t.selectFrom(\"account_spend_caps\")\n\t\t.select(\"account_id\")\n\t\t.where(\"frozen_at\", \"is not\", null)\n\t\t.execute();\n\treturn new Set(rows.map((r) => r.account_id));\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;AAaA,eAAsB,OAAO,CAC5B,IACA,WACkC;AAAA,EAClC,MAAM,MAAM,MAAM,GAChB,WAAW,oBAAoB,EAC/B,UAAU,EACV,MAAM,cAAc,KAAK,SAAS,EAClC,iBAAiB;AAAA,EACnB,OAAO,OAAO;AAAA;AAOf,eAAsB,UAAU,CAC/B,IACA,WACA,OAC2B;AAAA,EAC3B,MAAM,SAAgC;AAAA,IACrC,YAAY;AAAA,IACZ,mBAAmB,MAAM,qBAAqB;AAAA,IAC9C,mBAAmB,MAAM,qBAAqB;AAAA,IAC9C,mBAAmB,MAAM,qBAAqB;AAAA,IAC9C,cAAc,MAAM,gBAAgB;AAAA,IACpC,qBAAqB,MAAM,uBAAuB;AAAA,IAClD,eAAe,MAAM,iBAAiB;AAAA,IACtC,WAAW,MAAM,aAAa;AAAA,EAC/B;AAAA,EAEA,OAAO,GACL,WAAW,oBAAoB,EAC/B,OAAO,MAAM,EACb,WAAW,CAAC,OACZ,GAAG,OAAO,YAAY,EAAE,YAAY;AAAA,OAChC;AAAA,IACH,YAAY,IAAI;AAAA,EACjB,CAAC,CACF,EACC,aAAa,EACb,wBAAwB;AAAA;AAI3B,eAAsB,aAAa,CAClC,IACA,WACgB;AAAA,EAChB,MAAM,WAAW,IAAI,WAAW,EAAE,WAAW,IAAI,KAAO,CAAC;AAAA;AAQ1D,eAAsB,WAAW,CAChC,IACA,WACgB;AAAA,EAChB,MAAM,WAAW,IAAI,WAAW;AAAA,IAC/B,WAAW;AAAA,IACX,eAAe;AAAA,EAChB,CAAC;AAAA;AAIF,eAAsB,oBAAoB,CACzC,IACuB;AAAA,EACvB,MAAM,OAAO,MAAM,GACjB,WAAW,oBAAoB,EAC/B,OAAO,YAAY,EACnB,MAAM,aAAa,UAAU,IAAI,EACjC,QAAQ;AAAA,EACV,OAAO,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC;AAAA;",
8
+ "debugId": "ECBEFE5FC18A7EFB64756E2164756E21",
9
+ "names": []
10
+ }