@secondlayer/shared 0.2.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 (76) hide show
  1. package/README.md +19 -0
  2. package/dist/src/crypto/hmac.d.ts +26 -0
  3. package/dist/src/crypto/hmac.js +75 -0
  4. package/dist/src/crypto/hmac.js.map +10 -0
  5. package/dist/src/db/index.d.ts +227 -0
  6. package/dist/src/db/index.js +75 -0
  7. package/dist/src/db/index.js.map +11 -0
  8. package/dist/src/db/jsonb.d.ts +13 -0
  9. package/dist/src/db/jsonb.js +35 -0
  10. package/dist/src/db/jsonb.js.map +10 -0
  11. package/dist/src/db/queries/accounts.d.ts +179 -0
  12. package/dist/src/db/queries/accounts.js +39 -0
  13. package/dist/src/db/queries/accounts.js.map +10 -0
  14. package/dist/src/db/queries/integrity.d.ts +178 -0
  15. package/dist/src/db/queries/integrity.js +68 -0
  16. package/dist/src/db/queries/integrity.js.map +10 -0
  17. package/dist/src/db/queries/metrics.d.ts +179 -0
  18. package/dist/src/db/queries/metrics.js +51 -0
  19. package/dist/src/db/queries/metrics.js.map +10 -0
  20. package/dist/src/db/queries/usage.d.ts +205 -0
  21. package/dist/src/db/queries/usage.js +117 -0
  22. package/dist/src/db/queries/usage.js.map +11 -0
  23. package/dist/src/db/queries/views.d.ts +191 -0
  24. package/dist/src/db/queries/views.js +111 -0
  25. package/dist/src/db/queries/views.js.map +11 -0
  26. package/dist/src/db/schema.d.ts +207 -0
  27. package/dist/src/db/schema.js +3 -0
  28. package/dist/src/db/schema.js.map +9 -0
  29. package/dist/src/env.d.ts +7 -0
  30. package/dist/src/env.js +60 -0
  31. package/dist/src/env.js.map +10 -0
  32. package/dist/src/errors.d.ts +51 -0
  33. package/dist/src/errors.js +103 -0
  34. package/dist/src/errors.js.map +10 -0
  35. package/dist/src/index.d.ts +464 -0
  36. package/dist/src/index.js +642 -0
  37. package/dist/src/index.js.map +19 -0
  38. package/dist/src/lib/plans.d.ts +10 -0
  39. package/dist/src/lib/plans.js +34 -0
  40. package/dist/src/lib/plans.js.map +10 -0
  41. package/dist/src/logger.d.ts +2 -0
  42. package/dist/src/logger.js +130 -0
  43. package/dist/src/logger.js.map +11 -0
  44. package/dist/src/node/client.d.ts +35 -0
  45. package/dist/src/node/client.js +56 -0
  46. package/dist/src/node/client.js.map +10 -0
  47. package/dist/src/node/hiro-client.d.ts +186 -0
  48. package/dist/src/node/hiro-client.js +410 -0
  49. package/dist/src/node/hiro-client.js.map +12 -0
  50. package/dist/src/queue/index.d.ts +50 -0
  51. package/dist/src/queue/index.js +176 -0
  52. package/dist/src/queue/index.js.map +12 -0
  53. package/dist/src/queue/listener.d.ts +20 -0
  54. package/dist/src/queue/listener.js +63 -0
  55. package/dist/src/queue/listener.js.map +10 -0
  56. package/dist/src/queue/recovery.d.ts +14 -0
  57. package/dist/src/queue/recovery.js +100 -0
  58. package/dist/src/queue/recovery.js.map +12 -0
  59. package/dist/src/schemas/filters.d.ts +30 -0
  60. package/dist/src/schemas/filters.js +133 -0
  61. package/dist/src/schemas/filters.js.map +10 -0
  62. package/dist/src/schemas/index.d.ts +109 -0
  63. package/dist/src/schemas/index.js +228 -0
  64. package/dist/src/schemas/index.js.map +12 -0
  65. package/dist/src/schemas/views.d.ts +51 -0
  66. package/dist/src/schemas/views.js +29 -0
  67. package/dist/src/schemas/views.js.map +10 -0
  68. package/dist/src/types.d.ts +102 -0
  69. package/dist/src/types.js +3 -0
  70. package/dist/src/types.js.map +9 -0
  71. package/migrations/0001_initial.ts +182 -0
  72. package/migrations/0002_api_keys.ts +38 -0
  73. package/migrations/0003_tenant_isolation.ts +114 -0
  74. package/migrations/0004_accounts_and_usage.ts +90 -0
  75. package/migrations/0005_sessions.ts +42 -0
  76. package/package.json +128 -0
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @secondlayer/shared
2
+
3
+ Foundational utilities for Second Layer services: DB layer (Kysely+Postgres), job queue, Zod schemas, HMAC signing, Stacks node clients.
4
+
5
+ ## Testing
6
+
7
+ ```bash
8
+ # Run tests (DB tests skip without DATABASE_URL)
9
+ bun test
10
+
11
+ # Run with database
12
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/streams_test bun test
13
+ ```
14
+
15
+ ## Migrations
16
+
17
+ ```bash
18
+ DATABASE_URL=... bun run migrate
19
+ ```
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Generate a random secret for webhook signing
3
+ * Returns 32 bytes as a 64-character hex string
4
+ */
5
+ declare function generateSecret(): string;
6
+ /**
7
+ * Sign a payload with HMAC-SHA256
8
+ * Returns the signature as a hex string
9
+ */
10
+ declare function signPayload(payload: string, secret: string): string;
11
+ /**
12
+ * Verify an HMAC signature
13
+ * Uses constant-time comparison to prevent timing attacks
14
+ */
15
+ declare function verifySignature(payload: string, signature: string, secret: string): boolean;
16
+ /**
17
+ * Create a Stripe-style signature header
18
+ * Format: t=timestamp,v1=signature
19
+ */
20
+ declare function createSignatureHeader(payload: string, secret: string, timestamp?: number): string;
21
+ /**
22
+ * Parse and verify a Stripe-style signature header
23
+ * Returns true if valid, false otherwise
24
+ */
25
+ declare function verifySignatureHeader(payload: string, header: string, secret: string, toleranceSeconds?: number): boolean;
26
+ export { verifySignatureHeader, verifySignature, signPayload, generateSecret, createSignatureHeader };
@@ -0,0 +1,75 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+
13
+ // src/crypto/hmac.ts
14
+ var exports_hmac = {};
15
+ __export(exports_hmac, {
16
+ verifySignatureHeader: () => verifySignatureHeader,
17
+ verifySignature: () => verifySignature,
18
+ signPayload: () => signPayload,
19
+ generateSecret: () => generateSecret,
20
+ createSignatureHeader: () => createSignatureHeader
21
+ });
22
+ import { createHmac, randomBytes } from "crypto";
23
+ function generateSecret() {
24
+ return randomBytes(32).toString("hex");
25
+ }
26
+ function signPayload(payload, secret) {
27
+ const hmac = createHmac("sha256", secret);
28
+ hmac.update(payload);
29
+ return hmac.digest("hex");
30
+ }
31
+ function verifySignature(payload, signature, secret) {
32
+ const expectedSignature = signPayload(payload, secret);
33
+ if (signature.length !== expectedSignature.length) {
34
+ return false;
35
+ }
36
+ let result = 0;
37
+ for (let i = 0;i < signature.length; i++) {
38
+ result |= signature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);
39
+ }
40
+ return result === 0;
41
+ }
42
+ function createSignatureHeader(payload, secret, timestamp) {
43
+ const ts = timestamp ?? Math.floor(Date.now() / 1000);
44
+ const signedPayload = `${ts}.${payload}`;
45
+ const signature = signPayload(signedPayload, secret);
46
+ return `t=${ts},v1=${signature}`;
47
+ }
48
+ function verifySignatureHeader(payload, header, secret, toleranceSeconds = 300) {
49
+ const parts = header.split(",");
50
+ const timestamp = parts.find((p) => p.startsWith("t="))?.slice(2);
51
+ const signature = parts.find((p) => p.startsWith("v1="))?.slice(3);
52
+ if (!timestamp || !signature) {
53
+ return false;
54
+ }
55
+ const ts = parseInt(timestamp, 10);
56
+ if (isNaN(ts)) {
57
+ return false;
58
+ }
59
+ const now = Math.floor(Date.now() / 1000);
60
+ if (Math.abs(now - ts) > toleranceSeconds) {
61
+ return false;
62
+ }
63
+ const signedPayload = `${ts}.${payload}`;
64
+ return verifySignature(signedPayload, signature, secret);
65
+ }
66
+ export {
67
+ verifySignatureHeader,
68
+ verifySignature,
69
+ signPayload,
70
+ generateSecret,
71
+ createSignatureHeader
72
+ };
73
+
74
+ //# debugId=C5FB6F078225904664756E2164756E21
75
+ //# sourceMappingURL=hmac.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/crypto/hmac.ts"],
4
+ "sourcesContent": [
5
+ "import { createHmac, randomBytes } from \"crypto\";\n\n/**\n * Generate a random secret for webhook signing\n * Returns 32 bytes as a 64-character hex string\n */\nexport function generateSecret(): string {\n return randomBytes(32).toString(\"hex\");\n}\n\n/**\n * Sign a payload with HMAC-SHA256\n * Returns the signature as a hex string\n */\nexport function signPayload(payload: string, secret: string): string {\n const hmac = createHmac(\"sha256\", secret);\n hmac.update(payload);\n return hmac.digest(\"hex\");\n}\n\n/**\n * Verify an HMAC signature\n * Uses constant-time comparison to prevent timing attacks\n */\nexport function verifySignature(\n payload: string,\n signature: string,\n secret: string\n): boolean {\n const expectedSignature = signPayload(payload, secret);\n\n // Constant-time comparison\n if (signature.length !== expectedSignature.length) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < signature.length; i++) {\n result |= signature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);\n }\n\n return result === 0;\n}\n\n/**\n * Create a Stripe-style signature header\n * Format: t=timestamp,v1=signature\n */\nexport function createSignatureHeader(\n payload: string,\n secret: string,\n timestamp?: number\n): string {\n const ts = timestamp ?? Math.floor(Date.now() / 1000);\n const signedPayload = `${ts}.${payload}`;\n const signature = signPayload(signedPayload, secret);\n\n return `t=${ts},v1=${signature}`;\n}\n\n/**\n * Parse and verify a Stripe-style signature header\n * Returns true if valid, false otherwise\n */\nexport function verifySignatureHeader(\n payload: string,\n header: string,\n secret: string,\n toleranceSeconds = 300 // 5 minutes\n): boolean {\n // Parse header\n const parts = header.split(\",\");\n const timestamp = parts\n .find((p) => p.startsWith(\"t=\"))\n ?.slice(2);\n const signature = parts\n .find((p) => p.startsWith(\"v1=\"))\n ?.slice(3);\n\n if (!timestamp || !signature) {\n return false;\n }\n\n const ts = parseInt(timestamp, 10);\n if (isNaN(ts)) {\n return false;\n }\n\n // Check timestamp is within tolerance\n const now = Math.floor(Date.now() / 1000);\n if (Math.abs(now - ts) > toleranceSeconds) {\n return false;\n }\n\n // Verify signature\n const signedPayload = `${ts}.${payload}`;\n return verifySignature(signedPayload, signature, secret);\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAMO,SAAS,cAAc,GAAW;AAAA,EACvC,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAAA;AAOhC,SAAS,WAAW,CAAC,SAAiB,QAAwB;AAAA,EACnE,MAAM,OAAO,WAAW,UAAU,MAAM;AAAA,EACxC,KAAK,OAAO,OAAO;AAAA,EACnB,OAAO,KAAK,OAAO,KAAK;AAAA;AAOnB,SAAS,eAAe,CAC7B,SACA,WACA,QACS;AAAA,EACT,MAAM,oBAAoB,YAAY,SAAS,MAAM;AAAA,EAGrD,IAAI,UAAU,WAAW,kBAAkB,QAAQ;AAAA,IACjD,OAAO;AAAA,EACT;AAAA,EAEA,IAAI,SAAS;AAAA,EACb,SAAS,IAAI,EAAG,IAAI,UAAU,QAAQ,KAAK;AAAA,IACzC,UAAU,UAAU,WAAW,CAAC,IAAI,kBAAkB,WAAW,CAAC;AAAA,EACpE;AAAA,EAEA,OAAO,WAAW;AAAA;AAOb,SAAS,qBAAqB,CACnC,SACA,QACA,WACQ;AAAA,EACR,MAAM,KAAK,aAAa,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI;AAAA,EACpD,MAAM,gBAAgB,GAAG,MAAM;AAAA,EAC/B,MAAM,YAAY,YAAY,eAAe,MAAM;AAAA,EAEnD,OAAO,KAAK,SAAS;AAAA;AAOhB,SAAS,qBAAqB,CACnC,SACA,QACA,QACA,mBAAmB,KACV;AAAA,EAET,MAAM,QAAQ,OAAO,MAAM,GAAG;AAAA,EAC9B,MAAM,YAAY,MACf,KAAK,CAAC,MAAM,EAAE,WAAW,IAAI,CAAC,GAC7B,MAAM,CAAC;AAAA,EACX,MAAM,YAAY,MACf,KAAK,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,GAC9B,MAAM,CAAC;AAAA,EAEX,IAAI,CAAC,aAAa,CAAC,WAAW;AAAA,IAC5B,OAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,SAAS,WAAW,EAAE;AAAA,EACjC,IAAI,MAAM,EAAE,GAAG;AAAA,IACb,OAAO;AAAA,EACT;AAAA,EAGA,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI;AAAA,EACxC,IAAI,KAAK,IAAI,MAAM,EAAE,IAAI,kBAAkB;AAAA,IACzC,OAAO;AAAA,EACT;AAAA,EAGA,MAAM,gBAAgB,GAAG,MAAM;AAAA,EAC/B,OAAO,gBAAgB,eAAe,WAAW,MAAM;AAAA;",
8
+ "debugId": "C5FB6F078225904664756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.
3
+ * Kysely + postgres.js double-encodes JSON when using parameterized queries
4
+ * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.
5
+ */
6
+ declare function jsonb(value: unknown);
7
+ /**
8
+ * Safely parse a JSONB value from the database.
9
+ * Handles double-encoded strings where postgres.js returns a JSON string
10
+ * instead of a parsed object.
11
+ */
12
+ declare function parseJsonb<T = unknown>(value: unknown): T;
13
+ import { Kysely } from "kysely";
14
+ import postgres from "postgres";
15
+ import { Generated, Insertable, Selectable, Updateable } from "kysely";
16
+ interface BlocksTable {
17
+ height: number;
18
+ hash: string;
19
+ parent_hash: string;
20
+ burn_block_height: number;
21
+ timestamp: number;
22
+ canonical: Generated<boolean>;
23
+ created_at: Generated<Date>;
24
+ }
25
+ interface TransactionsTable {
26
+ tx_id: string;
27
+ block_height: number;
28
+ type: string;
29
+ sender: string;
30
+ status: string;
31
+ contract_id: string | null;
32
+ function_name: string | null;
33
+ raw_tx: string;
34
+ created_at: Generated<Date>;
35
+ }
36
+ interface EventsTable {
37
+ id: Generated<string>;
38
+ tx_id: string;
39
+ block_height: number;
40
+ event_index: number;
41
+ type: string;
42
+ data: unknown;
43
+ created_at: Generated<Date>;
44
+ }
45
+ interface StreamsTable {
46
+ id: Generated<string>;
47
+ name: string;
48
+ status: Generated<string>;
49
+ filters: unknown;
50
+ options: Generated<unknown>;
51
+ webhook_url: string;
52
+ webhook_secret: string | null;
53
+ api_key_id: string | null;
54
+ created_at: Generated<Date>;
55
+ updated_at: Generated<Date>;
56
+ }
57
+ interface StreamMetricsTable {
58
+ stream_id: string;
59
+ last_triggered_at: Date | null;
60
+ last_triggered_block: number | null;
61
+ total_deliveries: Generated<number>;
62
+ failed_deliveries: Generated<number>;
63
+ error_message: string | null;
64
+ }
65
+ interface JobsTable {
66
+ id: Generated<string>;
67
+ stream_id: string;
68
+ block_height: number;
69
+ status: Generated<string>;
70
+ attempts: Generated<number>;
71
+ locked_at: Date | null;
72
+ locked_by: string | null;
73
+ error: string | null;
74
+ backfill: Generated<boolean>;
75
+ created_at: Generated<Date>;
76
+ completed_at: Date | null;
77
+ }
78
+ interface IndexProgressTable {
79
+ network: string;
80
+ last_indexed_block: Generated<number>;
81
+ last_contiguous_block: Generated<number>;
82
+ highest_seen_block: Generated<number>;
83
+ updated_at: Generated<Date>;
84
+ }
85
+ interface DeliveriesTable {
86
+ id: Generated<string>;
87
+ stream_id: string;
88
+ job_id: string | null;
89
+ block_height: number;
90
+ status: string;
91
+ status_code: number | null;
92
+ response_time_ms: number | null;
93
+ attempts: Generated<number>;
94
+ error: string | null;
95
+ payload: unknown;
96
+ created_at: Generated<Date>;
97
+ }
98
+ interface ViewsTable {
99
+ id: Generated<string>;
100
+ name: string;
101
+ version: Generated<string>;
102
+ status: Generated<string>;
103
+ definition: unknown;
104
+ schema_hash: string;
105
+ handler_path: string;
106
+ schema_name: string | null;
107
+ last_processed_block: Generated<number>;
108
+ last_error: string | null;
109
+ last_error_at: Date | null;
110
+ total_processed: Generated<number>;
111
+ total_errors: Generated<number>;
112
+ api_key_id: string | null;
113
+ created_at: Generated<Date>;
114
+ updated_at: Generated<Date>;
115
+ }
116
+ interface ApiKeysTable {
117
+ id: Generated<string>;
118
+ key_hash: string;
119
+ key_prefix: string;
120
+ name: string | null;
121
+ status: Generated<string>;
122
+ rate_limit: Generated<number>;
123
+ ip_address: string;
124
+ account_id: string;
125
+ last_used_at: Date | null;
126
+ revoked_at: Date | null;
127
+ created_at: Generated<Date>;
128
+ }
129
+ interface AccountsTable {
130
+ id: Generated<string>;
131
+ email: string;
132
+ plan: Generated<string>;
133
+ created_at: Generated<Date>;
134
+ }
135
+ interface SessionsTable {
136
+ id: Generated<string>;
137
+ token_hash: string;
138
+ token_prefix: string;
139
+ account_id: string;
140
+ ip_address: string;
141
+ expires_at: Generated<Date>;
142
+ revoked_at: Date | null;
143
+ last_used_at: Date | null;
144
+ created_at: Generated<Date>;
145
+ }
146
+ interface MagicLinksTable {
147
+ id: Generated<string>;
148
+ email: string;
149
+ token: string;
150
+ expires_at: Date;
151
+ used_at: Date | null;
152
+ created_at: Generated<Date>;
153
+ }
154
+ interface UsageDailyTable {
155
+ account_id: string;
156
+ date: string;
157
+ api_requests: Generated<number>;
158
+ deliveries: Generated<number>;
159
+ }
160
+ interface UsageSnapshotsTable {
161
+ id: Generated<string>;
162
+ account_id: string;
163
+ measured_at: Generated<Date>;
164
+ storage_bytes: Generated<number>;
165
+ }
166
+ interface Database {
167
+ blocks: BlocksTable;
168
+ transactions: TransactionsTable;
169
+ events: EventsTable;
170
+ streams: StreamsTable;
171
+ stream_metrics: StreamMetricsTable;
172
+ jobs: JobsTable;
173
+ index_progress: IndexProgressTable;
174
+ deliveries: DeliveriesTable;
175
+ views: ViewsTable;
176
+ api_keys: ApiKeysTable;
177
+ accounts: AccountsTable;
178
+ sessions: SessionsTable;
179
+ magic_links: MagicLinksTable;
180
+ usage_daily: UsageDailyTable;
181
+ usage_snapshots: UsageSnapshotsTable;
182
+ }
183
+ type Block = Selectable<BlocksTable>;
184
+ type InsertBlock = Insertable<BlocksTable>;
185
+ type UpdateBlock = Updateable<BlocksTable>;
186
+ type Transaction = Selectable<TransactionsTable>;
187
+ type InsertTransaction = Insertable<TransactionsTable>;
188
+ type UpdateTransaction = Updateable<TransactionsTable>;
189
+ type Event = Selectable<EventsTable>;
190
+ type InsertEvent = Insertable<EventsTable>;
191
+ type UpdateEvent = Updateable<EventsTable>;
192
+ type Stream = Selectable<StreamsTable>;
193
+ type InsertStream = Insertable<StreamsTable>;
194
+ type UpdateStreamRow = Updateable<StreamsTable>;
195
+ type StreamMetrics = Selectable<StreamMetricsTable>;
196
+ type InsertStreamMetrics = Insertable<StreamMetricsTable>;
197
+ type UpdateStreamMetrics = Updateable<StreamMetricsTable>;
198
+ type Job = Selectable<JobsTable>;
199
+ type InsertJob = Insertable<JobsTable>;
200
+ type UpdateJob = Updateable<JobsTable>;
201
+ type IndexProgress = Selectable<IndexProgressTable>;
202
+ type InsertIndexProgress = Insertable<IndexProgressTable>;
203
+ type UpdateIndexProgress = Updateable<IndexProgressTable>;
204
+ type Delivery = Selectable<DeliveriesTable>;
205
+ type InsertDelivery = Insertable<DeliveriesTable>;
206
+ type UpdateDelivery = Updateable<DeliveriesTable>;
207
+ type View = Selectable<ViewsTable>;
208
+ type InsertView = Insertable<ViewsTable>;
209
+ type UpdateView = Updateable<ViewsTable>;
210
+ type ApiKey = Selectable<ApiKeysTable>;
211
+ type InsertApiKey = Insertable<ApiKeysTable>;
212
+ type UpdateApiKey = Updateable<ApiKeysTable>;
213
+ type Account = Selectable<AccountsTable>;
214
+ type InsertAccount = Insertable<AccountsTable>;
215
+ type MagicLink = Selectable<MagicLinksTable>;
216
+ type InsertMagicLink = Insertable<MagicLinksTable>;
217
+ type Session = Selectable<SessionsTable>;
218
+ type InsertSession = Insertable<SessionsTable>;
219
+ type UsageDaily = Selectable<UsageDailyTable>;
220
+ type UsageSnapshot = Selectable<UsageSnapshotsTable>;
221
+ import { sql } from "kysely";
222
+ declare function getDb(connectionString?: string): Kysely<Database>;
223
+ /** Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.) */
224
+ declare function getRawClient(): ReturnType<typeof postgres>;
225
+ /** Close the DB connection pool. Call in CLI commands to allow process exit. */
226
+ declare function closeDb(): Promise<void>;
227
+ export { sql, parseJsonb, jsonb, getRawClient, getDb, closeDb, ViewsTable, View, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateView, UpdateTransaction, UpdateStreamRow, UpdateStreamMetrics, UpdateJob, UpdateIndexProgress, UpdateEvent, UpdateDelivery, UpdateBlock, UpdateApiKey, TransactionsTable, Transaction, StreamsTable, StreamMetricsTable, StreamMetrics, Stream, SessionsTable, Session, MagicLinksTable, MagicLink, JobsTable, Job, InsertView, InsertTransaction, InsertStreamMetrics, InsertStream, InsertSession, InsertMagicLink, InsertJob, InsertIndexProgress, InsertEvent, InsertDelivery, InsertBlock, InsertApiKey, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Delivery, DeliveriesTable, Database, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, Account };
@@ -0,0 +1,75 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+
13
+ // src/db/jsonb.ts
14
+ import { sql } from "kysely";
15
+ function jsonb(value) {
16
+ const escaped = JSON.stringify(value).replace(/'/g, "''");
17
+ return sql`${sql.raw(`'${escaped}'::jsonb`)}`;
18
+ }
19
+ function parseJsonb(value) {
20
+ if (typeof value === "string") {
21
+ try {
22
+ return JSON.parse(value);
23
+ } catch {
24
+ return value;
25
+ }
26
+ }
27
+ return value ?? {};
28
+ }
29
+
30
+ // src/db/index.ts
31
+ import { Kysely } from "kysely";
32
+ import { PostgresJSDialect } from "kysely-postgres-js";
33
+ import postgres from "postgres";
34
+ import { sql as sql2 } from "kysely";
35
+ var db = null;
36
+ var rawClient = null;
37
+ function getDb(connectionString) {
38
+ if (!db) {
39
+ const url = connectionString || process.env.DATABASE_URL || "postgres://postgres:postgres@localhost:5432/streams_dev";
40
+ const isLocal = url.includes("localhost") || url.includes("127.0.0.1") || url.includes("@postgres:");
41
+ rawClient = postgres(url, {
42
+ ssl: isLocal ? undefined : { rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0" }
43
+ });
44
+ db = new Kysely({
45
+ dialect: new PostgresJSDialect({ postgres: rawClient })
46
+ });
47
+ }
48
+ return db;
49
+ }
50
+ function getRawClient() {
51
+ if (!rawClient)
52
+ getDb();
53
+ return rawClient;
54
+ }
55
+ async function closeDb() {
56
+ if (db) {
57
+ await db.destroy();
58
+ db = null;
59
+ }
60
+ if (rawClient) {
61
+ await rawClient.end();
62
+ rawClient = null;
63
+ }
64
+ }
65
+ export {
66
+ sql2 as sql,
67
+ parseJsonb,
68
+ jsonb,
69
+ getRawClient,
70
+ getDb,
71
+ closeDb
72
+ };
73
+
74
+ //# debugId=F675FEF083D960AD64756E2164756E21
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/db/jsonb.ts", "../src/db/index.ts"],
4
+ "sourcesContent": [
5
+ "import { sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n */\nexport function jsonb(value: unknown) {\n const escaped = JSON.stringify(value).replace(/'/g, \"''\");\n return sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n if (typeof value === \"string\") {\n try {\n return JSON.parse(value) as T;\n } catch {\n return value as T;\n }\n }\n return (value ?? {}) as T;\n}\n",
6
+ "import { Kysely } from \"kysely\";\nimport { PostgresJSDialect } from \"kysely-postgres-js\";\nimport postgres from \"postgres\";\nimport type { Database } from \"./types.ts\";\n\nlet db: Kysely<Database> | null = null;\nlet rawClient: ReturnType<typeof postgres> | null = null;\n\nexport function getDb(connectionString?: string): Kysely<Database> {\n if (!db) {\n const url = connectionString || process.env.DATABASE_URL || \"postgres://postgres:postgres@localhost:5432/streams_dev\";\n\n // Always use SSL for remote databases, just disable cert verification if needed\n const isLocal = url.includes(\"localhost\") || url.includes(\"127.0.0.1\") || url.includes(\"@postgres:\");\n rawClient = postgres(url, {\n ssl: isLocal ? undefined : { rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== \"0\" },\n });\n db = new Kysely<Database>({\n dialect: new PostgresJSDialect({ postgres: rawClient }),\n });\n }\n return db;\n}\n\n/** Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.) */\nexport function getRawClient(): ReturnType<typeof postgres> {\n if (!rawClient) getDb();\n return rawClient!;\n}\n\n/** Close the DB connection pool. Call in CLI commands to allow process exit. */\nexport async function closeDb(): Promise<void> {\n if (db) {\n await db.destroy();\n db = null;\n }\n if (rawClient) {\n await rawClient.end();\n rawClient = null;\n }\n}\n\nimport { sql } from \"kysely\";\nexport { sql };\nexport * from \"./types.ts\";\nexport { jsonb, parseJsonb } from \"./jsonb.ts\";\n"
7
+ ],
8
+ "mappings": ";;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAgB;AAAA,EACpC,MAAM,UAAU,KAAK,UAAU,KAAK,EAAE,QAAQ,MAAM,IAAI;AAAA,EACxD,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQrC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EACzD,IAAI,OAAO,UAAU,UAAU;AAAA,IAC7B,IAAI;AAAA,MACF,OAAO,KAAK,MAAM,KAAK;AAAA,MACvB,MAAM;AAAA,MACN,OAAO;AAAA;AAAA,EAEX;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;;;ACzBpB;AACA;AACA;AAwCA,gBAAS;AArCT,IAAI,KAA8B;AAClC,IAAI,YAAgD;AAE7C,SAAS,KAAK,CAAC,kBAA6C;AAAA,EACjE,IAAI,CAAC,IAAI;AAAA,IACP,MAAM,MAAM,oBAAoB,QAAQ,IAAI,gBAAgB;AAAA,IAG5D,MAAM,UAAU,IAAI,SAAS,WAAW,KAAK,IAAI,SAAS,WAAW,KAAK,IAAI,SAAS,YAAY;AAAA,IACnG,YAAY,SAAS,KAAK;AAAA,MACxB,KAAK,UAAU,YAAY,EAAE,oBAAoB,QAAQ,IAAI,iCAAiC,IAAI;AAAA,IACpG,CAAC;AAAA,IACD,KAAK,IAAI,OAAiB;AAAA,MACxB,SAAS,IAAI,kBAAkB,EAAE,UAAU,UAAU,CAAC;AAAA,IACxD,CAAC;AAAA,EACH;AAAA,EACA,OAAO;AAAA;AAIF,SAAS,YAAY,GAAgC;AAAA,EAC1D,IAAI,CAAC;AAAA,IAAW,MAAM;AAAA,EACtB,OAAO;AAAA;AAIT,eAAsB,OAAO,GAAkB;AAAA,EAC7C,IAAI,IAAI;AAAA,IACN,MAAM,GAAG,QAAQ;AAAA,IACjB,KAAK;AAAA,EACP;AAAA,EACA,IAAI,WAAW;AAAA,IACb,MAAM,UAAU,IAAI;AAAA,IACpB,YAAY;AAAA,EACd;AAAA;",
9
+ "debugId": "F675FEF083D960AD64756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.
3
+ * Kysely + postgres.js double-encodes JSON when using parameterized queries
4
+ * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.
5
+ */
6
+ declare function jsonb(value: unknown);
7
+ /**
8
+ * Safely parse a JSONB value from the database.
9
+ * Handles double-encoded strings where postgres.js returns a JSON string
10
+ * instead of a parsed object.
11
+ */
12
+ declare function parseJsonb<T = unknown>(value: unknown): T;
13
+ export { parseJsonb, jsonb };
@@ -0,0 +1,35 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+
13
+ // src/db/jsonb.ts
14
+ import { sql } from "kysely";
15
+ function jsonb(value) {
16
+ const escaped = JSON.stringify(value).replace(/'/g, "''");
17
+ return sql`${sql.raw(`'${escaped}'::jsonb`)}`;
18
+ }
19
+ function parseJsonb(value) {
20
+ if (typeof value === "string") {
21
+ try {
22
+ return JSON.parse(value);
23
+ } catch {
24
+ return value;
25
+ }
26
+ }
27
+ return value ?? {};
28
+ }
29
+ export {
30
+ parseJsonb,
31
+ jsonb
32
+ };
33
+
34
+ //# debugId=FD7B2A33BAF805D564756E2164756E21
35
+ //# sourceMappingURL=jsonb.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/db/jsonb.ts"],
4
+ "sourcesContent": [
5
+ "import { sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n */\nexport function jsonb(value: unknown) {\n const escaped = JSON.stringify(value).replace(/'/g, \"''\");\n return sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n if (typeof value === \"string\") {\n try {\n return JSON.parse(value) as T;\n } catch {\n return value as T;\n }\n }\n return (value ?? {}) as T;\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAgB;AAAA,EACpC,MAAM,UAAU,KAAK,UAAU,KAAK,EAAE,QAAQ,MAAM,IAAI;AAAA,EACxD,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQrC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EACzD,IAAI,OAAO,UAAU,UAAU;AAAA,IAC7B,IAAI;AAAA,MACF,OAAO,KAAK,MAAM,KAAK;AAAA,MACvB,MAAM;AAAA,MACN,OAAO;AAAA;AAAA,EAEX;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;",
8
+ "debugId": "FD7B2A33BAF805D564756E2164756E21",
9
+ "names": []
10
+ }