@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.
- package/README.md +2 -2
- package/dist/src/crypto/secrets.js +47 -3
- package/dist/src/crypto/secrets.js.map +5 -4
- package/dist/src/db/index.d.ts +112 -137
- package/dist/src/db/index.js.map +2 -2
- package/dist/src/db/jsonb.d.ts +5 -1
- package/dist/src/db/jsonb.js.map +2 -2
- package/dist/src/db/queries/account-spend-caps.d.ts +444 -0
- package/dist/src/db/queries/account-spend-caps.js +60 -0
- package/dist/src/db/queries/account-spend-caps.js.map +10 -0
- package/dist/src/db/queries/account-usage.d.ts +468 -0
- package/dist/src/db/queries/account-usage.js +222 -0
- package/dist/src/db/queries/account-usage.js.map +11 -0
- package/dist/src/db/queries/accounts.d.ts +100 -109
- package/dist/src/db/queries/accounts.js +15 -1
- package/dist/src/db/queries/accounts.js.map +3 -3
- package/dist/src/db/queries/integrity.d.ts +85 -107
- package/dist/src/db/queries/projects.d.ts +87 -109
- package/dist/src/db/queries/provisioning-audit.d.ts +85 -107
- package/dist/src/db/queries/subgraph-gaps.d.ts +85 -107
- package/dist/src/db/queries/subgraphs.d.ts +86 -109
- package/dist/src/db/queries/subgraphs.js +2 -3
- package/dist/src/db/queries/subgraphs.js.map +4 -4
- package/dist/src/db/queries/{workflows.d.ts → tenant-compute-addons.d.ts} +108 -142
- package/dist/src/db/queries/tenant-compute-addons.js +47 -0
- package/dist/src/db/queries/tenant-compute-addons.js.map +10 -0
- package/dist/src/db/queries/tenants.d.ts +98 -110
- package/dist/src/db/queries/tenants.js +55 -8
- package/dist/src/db/queries/tenants.js.map +6 -5
- package/dist/src/db/queries/usage.d.ts +86 -132
- package/dist/src/db/queries/usage.js +5 -64
- package/dist/src/db/queries/usage.js.map +4 -5
- package/dist/src/db/schema.d.ts +107 -136
- package/dist/src/errors.d.ts +8 -7
- package/dist/src/errors.js +11 -12
- package/dist/src/errors.js.map +3 -3
- package/dist/src/index.d.ts +119 -143
- package/dist/src/index.js +11 -12
- package/dist/src/index.js.map +4 -4
- package/dist/src/node/local-client.d.ts +85 -107
- package/dist/src/pricing.d.ts +20 -1
- package/dist/src/pricing.js +58 -1
- package/dist/src/pricing.js.map +3 -3
- package/migrations/0045_drop_marketplace_columns.ts +47 -0
- package/migrations/0046_tenant_activity_signal.ts +47 -0
- package/migrations/0047_usage_daily_tenant_id.ts +73 -0
- package/migrations/0048_tenant_compute_addons.ts +49 -0
- package/migrations/0049_accounts_stripe_customer_id.ts +30 -0
- package/migrations/0050_account_spend_caps.ts +45 -0
- package/migrations/0051_workflow_ai_usage_daily.ts +40 -0
- package/migrations/0052_sentries.ts +61 -0
- package/migrations/0053_workflow_runtime.ts +88 -0
- package/migrations/0054_accounts_plan_hobby.ts +32 -0
- package/migrations/0055_ai_usage_account_scope.ts +108 -0
- package/migrations/0056_drop_workflow_sentry_residuals.ts +23 -0
- package/migrations/0057_subscriptions.ts +137 -0
- package/package.json +26 -14
- package/dist/src/db/queries/workflows.js +0 -260
- package/dist/src/db/queries/workflows.js.map +0 -12
- package/dist/src/lib/plans.d.ts +0 -9
- package/dist/src/lib/plans.js +0 -37
- package/dist/src/lib/plans.js.map +0 -10
- package/dist/src/schemas/workflows.d.ts +0 -70
- package/dist/src/schemas/workflows.js +0 -43
- package/dist/src/schemas/workflows.js.map +0 -10
package/README.md
CHANGED
|
@@ -24,10 +24,11 @@ DATABASE_URL=... bun run migrate
|
|
|
24
24
|
|------|-------------|
|
|
25
25
|
| `@secondlayer/shared` | Core utilities |
|
|
26
26
|
| `@secondlayer/shared/db` | Kysely database layer |
|
|
27
|
-
| `@secondlayer/shared/db/queries/*` | Query helpers (integrity, accounts, usage, subgraphs,
|
|
27
|
+
| `@secondlayer/shared/db/queries/*` | Query helpers (integrity, accounts, usage, subgraphs, projects, subgraph-gaps, tenants, provisioning-audit) |
|
|
28
28
|
| `@secondlayer/shared/db/schema` | Database schema |
|
|
29
29
|
| `@secondlayer/shared/db/jsonb` | JSONB helpers |
|
|
30
30
|
| `@secondlayer/shared/schemas` | Zod schemas |
|
|
31
|
+
| `@secondlayer/shared/schemas/accounts` | Account profile schemas |
|
|
31
32
|
| `@secondlayer/shared/schemas/filters` | Event filter schemas |
|
|
32
33
|
| `@secondlayer/shared/schemas/subgraphs` | Subgraph schemas |
|
|
33
34
|
| `@secondlayer/shared/types` | Shared TypeScript types |
|
|
@@ -38,4 +39,3 @@ DATABASE_URL=... bun run migrate
|
|
|
38
39
|
| `@secondlayer/shared/crypto` | HMAC signing |
|
|
39
40
|
| `@secondlayer/shared/node` | Stacks node client |
|
|
40
41
|
| `@secondlayer/shared/node/hiro-pg-client` | Direct PG queries against Hiro DB |
|
|
41
|
-
| `@secondlayer/shared/lib/plans` | Plan definitions |
|
|
@@ -14,15 +14,59 @@ var __export = (target, all) => {
|
|
|
14
14
|
});
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// src/mode.ts
|
|
18
|
+
var VALID_MODES = ["oss", "dedicated", "platform"];
|
|
19
|
+
function getInstanceMode() {
|
|
20
|
+
const raw = process.env.INSTANCE_MODE?.trim().toLowerCase();
|
|
21
|
+
if (raw && VALID_MODES.includes(raw)) {
|
|
22
|
+
return raw;
|
|
23
|
+
}
|
|
24
|
+
return "oss";
|
|
25
|
+
}
|
|
26
|
+
function isPlatformMode() {
|
|
27
|
+
return getInstanceMode() === "platform";
|
|
28
|
+
}
|
|
29
|
+
function isOssMode() {
|
|
30
|
+
return getInstanceMode() === "oss";
|
|
31
|
+
}
|
|
32
|
+
function isDedicatedMode() {
|
|
33
|
+
return getInstanceMode() === "dedicated";
|
|
34
|
+
}
|
|
35
|
+
|
|
17
36
|
// src/crypto/secrets.ts
|
|
18
37
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
38
|
+
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
39
|
+
import { resolve } from "node:path";
|
|
19
40
|
var KEY_ENV = "SECONDLAYER_SECRETS_KEY";
|
|
20
41
|
var IV_LEN = 12;
|
|
21
42
|
var TAG_LEN = 16;
|
|
43
|
+
function bootstrapOssKey() {
|
|
44
|
+
const envPath = resolve(process.cwd(), ".env.local");
|
|
45
|
+
if (existsSync(envPath)) {
|
|
46
|
+
const contents = readFileSync(envPath, "utf8");
|
|
47
|
+
const match = contents.match(/^SECONDLAYER_SECRETS_KEY=([a-fA-F0-9]{64})/m);
|
|
48
|
+
if (match) {
|
|
49
|
+
process.env[KEY_ENV] = match[1];
|
|
50
|
+
return match[1];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const hex = randomBytes(32).toString("hex");
|
|
54
|
+
const line = `${existsSync(envPath) ? `
|
|
55
|
+
` : ""}${KEY_ENV}=${hex}
|
|
56
|
+
`;
|
|
57
|
+
appendFileSync(envPath, line, { mode: 384 });
|
|
58
|
+
process.env[KEY_ENV] = hex;
|
|
59
|
+
console.log(`[secondlayer] generated ${KEY_ENV}; saved to ${envPath} (mode 0600)`);
|
|
60
|
+
return hex;
|
|
61
|
+
}
|
|
22
62
|
function loadKey() {
|
|
23
|
-
|
|
63
|
+
let hex = process.env[KEY_ENV];
|
|
24
64
|
if (!hex) {
|
|
25
|
-
|
|
65
|
+
if (getInstanceMode() === "oss") {
|
|
66
|
+
hex = bootstrapOssKey();
|
|
67
|
+
} else {
|
|
68
|
+
throw new Error(`${KEY_ENV} not set. Generate one with: openssl rand -hex 32`);
|
|
69
|
+
}
|
|
26
70
|
}
|
|
27
71
|
const key = Buffer.from(hex, "hex");
|
|
28
72
|
if (key.length !== 32) {
|
|
@@ -65,5 +109,5 @@ export {
|
|
|
65
109
|
decryptSecret
|
|
66
110
|
};
|
|
67
111
|
|
|
68
|
-
//# debugId=
|
|
112
|
+
//# debugId=982B15A94DFBA98464756E2164756E21
|
|
69
113
|
//# sourceMappingURL=secrets.js.map
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/crypto/secrets.ts"],
|
|
3
|
+
"sources": ["../src/mode.ts", "../src/crypto/secrets.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"
|
|
5
|
+
"/**\n * Instance modes for the Secondlayer platform.\n *\n * - `oss`: self-hosted, single-tenant. No auth middleware, no platform routes\n * (projects, admin, workflows). Everything runs against a single\n * `DATABASE_URL`. Intended for `docker compose up`.\n *\n * - `dedicated`: per-customer managed instance. JWT-based auth (anon =\n * read-only, service = full). Dual-DB mode — shared source indexer DB for\n * block reads, per-tenant target DB for subgraph data. No platform-wide\n * routes mounted (no cross-tenant accounts).\n *\n * - `platform`: control-plane mode. Magic-link auth, API keys, projects,\n * tenants, admin. Serves the dashboard + CLI against a single shared DB.\n */\n\nexport type InstanceMode = \"oss\" | \"dedicated\" | \"platform\";\n\nconst VALID_MODES: readonly InstanceMode[] = [\"oss\", \"dedicated\", \"platform\"];\n\n/**\n * Resolve the active instance mode from `process.env.INSTANCE_MODE`.\n * Defaults to `\"oss\"` — the safest default for self-hosters who deploy\n * without setting the variable.\n */\nexport function getInstanceMode(): InstanceMode {\n\tconst raw = process.env.INSTANCE_MODE?.trim().toLowerCase();\n\tif (raw && (VALID_MODES as readonly string[]).includes(raw)) {\n\t\treturn raw as InstanceMode;\n\t}\n\treturn \"oss\";\n}\n\n/** True when the active mode is `\"platform\"` (shared multi-tenant). */\nexport function isPlatformMode(): boolean {\n\treturn getInstanceMode() === \"platform\";\n}\n\n/** True when the active mode is `\"oss\"` (self-hosted). */\nexport function isOssMode(): boolean {\n\treturn getInstanceMode() === \"oss\";\n}\n\n/** True when the active mode is `\"dedicated\"` (per-tenant managed). */\nexport function isDedicatedMode(): boolean {\n\treturn getInstanceMode() === \"dedicated\";\n}\n",
|
|
6
|
+
"import { createCipheriv, createDecipheriv, randomBytes } from \"node:crypto\";\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { getInstanceMode } from \"../mode.ts\";\n\n/**\n * AES-256-GCM symmetric envelope for encrypted secrets at rest (tenant keys,\n * subscription signing secrets, etc.).\n *\n * Ciphertext layout: `iv (12 bytes) || authTag (16 bytes) || ciphertext`\n *\n * The key comes from `SECONDLAYER_SECRETS_KEY` — 32 bytes hex. In OSS mode,\n * if the env var is unset on first use we autogenerate a key and persist it\n * to `.env.local` in the current working directory so subsequent restarts\n * pick it up without user intervention. Dedicated/platform modes throw —\n * those runtimes must provision the key explicitly.\n *\n * Rotation strategy: re-encrypt all rows with the new key and swap the env\n * var. Not zero-downtime, but acceptable at v2 scale. For real KMS (AWS\n * KMS, Vault, GCP KMS), wrap the same byte layout behind an\n * `EncryptSecret`/`DecryptSecret` interface and swap at startup.\n */\n\nconst KEY_ENV = \"SECONDLAYER_SECRETS_KEY\";\nconst IV_LEN = 12;\nconst TAG_LEN = 16;\n\nfunction bootstrapOssKey(): string {\n\tconst envPath = resolve(process.cwd(), \".env.local\");\n\n\t// Check existing .env.local first — prior run may have written it.\n\tif (existsSync(envPath)) {\n\t\tconst contents = readFileSync(envPath, \"utf8\");\n\t\tconst match = contents.match(/^SECONDLAYER_SECRETS_KEY=([a-fA-F0-9]{64})/m);\n\t\tif (match) {\n\t\t\tprocess.env[KEY_ENV] = match[1];\n\t\t\treturn match[1];\n\t\t}\n\t}\n\n\tconst hex = randomBytes(32).toString(\"hex\");\n\tconst line = `${existsSync(envPath) ? \"\\n\" : \"\"}${KEY_ENV}=${hex}\\n`;\n\tappendFileSync(envPath, line, { mode: 0o600 });\n\tprocess.env[KEY_ENV] = hex;\n\tconsole.log(\n\t\t`[secondlayer] generated ${KEY_ENV}; saved to ${envPath} (mode 0600)`,\n\t);\n\treturn hex;\n}\n\nfunction loadKey(): Buffer {\n\tlet hex = process.env[KEY_ENV];\n\tif (!hex) {\n\t\tif (getInstanceMode() === \"oss\") {\n\t\t\thex = bootstrapOssKey();\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`${KEY_ENV} not set. Generate one with: openssl rand -hex 32`,\n\t\t\t);\n\t\t}\n\t}\n\tconst key = Buffer.from(hex, \"hex\");\n\tif (key.length !== 32) {\n\t\tthrow new Error(`${KEY_ENV} must be 32 bytes hex (got ${key.length})`);\n\t}\n\treturn key;\n}\n\nlet _cachedKey: Buffer | null = null;\nfunction getKey(): Buffer {\n\tif (!_cachedKey) _cachedKey = loadKey();\n\treturn _cachedKey;\n}\n\nexport function encryptSecret(plaintext: string): Buffer {\n\tconst key = getKey();\n\tconst iv = randomBytes(IV_LEN);\n\tconst cipher = createCipheriv(\"aes-256-gcm\", key, iv);\n\tconst ciphertext = Buffer.concat([\n\t\tcipher.update(plaintext, \"utf8\"),\n\t\tcipher.final(),\n\t]);\n\tconst tag = cipher.getAuthTag();\n\treturn Buffer.concat([iv, tag, ciphertext]);\n}\n\nexport function decryptSecret(envelope: Buffer): string {\n\tconst key = getKey();\n\tconst iv = envelope.subarray(0, IV_LEN);\n\tconst tag = envelope.subarray(IV_LEN, IV_LEN + TAG_LEN);\n\tconst ciphertext = envelope.subarray(IV_LEN + TAG_LEN);\n\tconst decipher = createDecipheriv(\"aes-256-gcm\", key, iv);\n\tdecipher.setAuthTag(tag);\n\treturn decipher.update(ciphertext).toString(\"utf8\") + decipher.final(\"utf8\");\n}\n\n/** Generate a fresh 32-byte hex key suitable for `SECONDLAYER_SECRETS_KEY`. */\nexport function generateSecretsKey(): string {\n\treturn randomBytes(32).toString(\"hex\");\n}\n"
|
|
6
7
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;
|
|
8
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "982B15A94DFBA98464756E2164756E21",
|
|
9
10
|
"names": []
|
|
10
11
|
}
|
package/dist/src/db/index.d.ts
CHANGED
|
@@ -3,8 +3,12 @@ import { RawBuilder } from "kysely";
|
|
|
3
3
|
* Safely encode a JS value as a JSONB literal for Kysely inserts/updates.
|
|
4
4
|
* Kysely + postgres.js double-encodes JSON when using parameterized queries
|
|
5
5
|
* with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.
|
|
6
|
+
*
|
|
7
|
+
* Generic parameter lets callers set the RawBuilder's output type so they
|
|
8
|
+
* don't need to cast at the insert site. Default is `unknown` — widen at
|
|
9
|
+
* the call site when the column type is narrower, e.g. `jsonb<MyShape>(...)`.
|
|
6
10
|
*/
|
|
7
|
-
declare function jsonb(value:
|
|
11
|
+
declare function jsonb<T = unknown>(value: T): RawBuilder<T>;
|
|
8
12
|
/**
|
|
9
13
|
* Safely parse a JSONB value from the database.
|
|
10
14
|
* Handles double-encoded strings where postgres.js returns a JSON string
|
|
@@ -74,10 +78,6 @@ interface SubgraphsTable {
|
|
|
74
78
|
handler_code: string | null;
|
|
75
79
|
source_code: string | null;
|
|
76
80
|
project_id: string | null;
|
|
77
|
-
is_public: Generated<boolean>;
|
|
78
|
-
tags: Generated<string[]>;
|
|
79
|
-
description: string | null;
|
|
80
|
-
forked_from_id: string | null;
|
|
81
81
|
created_at: Generated<Date>;
|
|
82
82
|
updated_at: Generated<Date>;
|
|
83
83
|
}
|
|
@@ -112,6 +112,7 @@ interface AccountsTable {
|
|
|
112
112
|
bio: string | null;
|
|
113
113
|
avatar_url: string | null;
|
|
114
114
|
slug: string | null;
|
|
115
|
+
stripe_customer_id: string | null;
|
|
115
116
|
created_at: Generated<Date>;
|
|
116
117
|
}
|
|
117
118
|
interface SessionsTable {
|
|
@@ -137,6 +138,7 @@ interface MagicLinksTable {
|
|
|
137
138
|
}
|
|
138
139
|
interface UsageDailyTable {
|
|
139
140
|
account_id: string;
|
|
141
|
+
tenant_id: string | null;
|
|
140
142
|
date: string;
|
|
141
143
|
api_requests: Generated<number>;
|
|
142
144
|
deliveries: Generated<number>;
|
|
@@ -263,83 +265,6 @@ interface ChatMessagesTable {
|
|
|
263
265
|
metadata: unknown | null;
|
|
264
266
|
created_at: Generated<Date>;
|
|
265
267
|
}
|
|
266
|
-
interface WorkflowDefinitionsTable {
|
|
267
|
-
id: Generated<string>;
|
|
268
|
-
name: string;
|
|
269
|
-
version: Generated<string>;
|
|
270
|
-
status: Generated<string>;
|
|
271
|
-
trigger_type: string;
|
|
272
|
-
trigger_config: unknown;
|
|
273
|
-
handler_path: string;
|
|
274
|
-
source_code: string | null;
|
|
275
|
-
retries_config: unknown | null;
|
|
276
|
-
timeout_ms: number | null;
|
|
277
|
-
api_key_id: string;
|
|
278
|
-
project_id: string | null;
|
|
279
|
-
created_at: Generated<Date>;
|
|
280
|
-
updated_at: Generated<Date>;
|
|
281
|
-
}
|
|
282
|
-
interface WorkflowRunsTable {
|
|
283
|
-
id: Generated<string>;
|
|
284
|
-
definition_id: string;
|
|
285
|
-
status: Generated<string>;
|
|
286
|
-
trigger_type: string;
|
|
287
|
-
trigger_data: unknown | null;
|
|
288
|
-
dedup_key: string | null;
|
|
289
|
-
error: string | null;
|
|
290
|
-
started_at: Date | null;
|
|
291
|
-
completed_at: Date | null;
|
|
292
|
-
duration_ms: number | null;
|
|
293
|
-
total_ai_tokens: Generated<number>;
|
|
294
|
-
created_at: Generated<Date>;
|
|
295
|
-
}
|
|
296
|
-
interface WorkflowStepsTable {
|
|
297
|
-
id: Generated<string>;
|
|
298
|
-
run_id: string;
|
|
299
|
-
step_index: number;
|
|
300
|
-
step_id: string;
|
|
301
|
-
step_type: string;
|
|
302
|
-
status: Generated<string>;
|
|
303
|
-
input: unknown | null;
|
|
304
|
-
output: unknown | null;
|
|
305
|
-
error: string | null;
|
|
306
|
-
retry_count: Generated<number>;
|
|
307
|
-
ai_tokens_used: Generated<number>;
|
|
308
|
-
started_at: Date | null;
|
|
309
|
-
completed_at: Date | null;
|
|
310
|
-
duration_ms: number | null;
|
|
311
|
-
memo_key: string | null;
|
|
312
|
-
parent_step_id: string | null;
|
|
313
|
-
created_at: Generated<Date>;
|
|
314
|
-
}
|
|
315
|
-
interface WorkflowQueueTable {
|
|
316
|
-
id: Generated<string>;
|
|
317
|
-
run_id: string;
|
|
318
|
-
status: Generated<string>;
|
|
319
|
-
attempts: Generated<number>;
|
|
320
|
-
max_attempts: Generated<number>;
|
|
321
|
-
scheduled_for: Generated<Date>;
|
|
322
|
-
locked_at: Date | null;
|
|
323
|
-
locked_by: string | null;
|
|
324
|
-
error: string | null;
|
|
325
|
-
created_at: Generated<Date>;
|
|
326
|
-
completed_at: Date | null;
|
|
327
|
-
}
|
|
328
|
-
interface WorkflowSchedulesTable {
|
|
329
|
-
id: Generated<string>;
|
|
330
|
-
definition_id: string;
|
|
331
|
-
cron_expr: string;
|
|
332
|
-
timezone: Generated<string>;
|
|
333
|
-
next_run_at: Date;
|
|
334
|
-
last_run_at: Date | null;
|
|
335
|
-
enabled: Generated<boolean>;
|
|
336
|
-
created_at: Generated<Date>;
|
|
337
|
-
}
|
|
338
|
-
interface WorkflowCursorsTable {
|
|
339
|
-
name: string;
|
|
340
|
-
block_height: Generated<number>;
|
|
341
|
-
updated_at: Generated<Date>;
|
|
342
|
-
}
|
|
343
268
|
interface Database {
|
|
344
269
|
blocks: BlocksTable;
|
|
345
270
|
transactions: TransactionsTable;
|
|
@@ -365,17 +290,14 @@ interface Database {
|
|
|
365
290
|
team_invitations: TeamInvitationsTable;
|
|
366
291
|
chat_sessions: ChatSessionsTable;
|
|
367
292
|
chat_messages: ChatMessagesTable;
|
|
368
|
-
workflow_definitions: WorkflowDefinitionsTable;
|
|
369
|
-
workflow_runs: WorkflowRunsTable;
|
|
370
|
-
workflow_steps: WorkflowStepsTable;
|
|
371
|
-
workflow_queue: WorkflowQueueTable;
|
|
372
|
-
workflow_schedules: WorkflowSchedulesTable;
|
|
373
|
-
workflow_cursors: WorkflowCursorsTable;
|
|
374
|
-
workflow_signer_secrets: WorkflowSignerSecretsTable;
|
|
375
|
-
workflow_budgets: WorkflowBudgetsTable;
|
|
376
293
|
tenants: TenantsTable;
|
|
377
294
|
tenant_usage_monthly: TenantUsageMonthlyTable;
|
|
295
|
+
tenant_compute_addons: TenantComputeAddonsTable;
|
|
296
|
+
account_spend_caps: AccountSpendCapsTable;
|
|
378
297
|
provisioning_audit_log: ProvisioningAuditLogTable;
|
|
298
|
+
subscriptions: SubscriptionsTable;
|
|
299
|
+
subscription_outbox: SubscriptionOutboxTable;
|
|
300
|
+
subscription_deliveries: SubscriptionDeliveriesTable;
|
|
379
301
|
}
|
|
380
302
|
type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
|
|
381
303
|
interface TenantsTable {
|
|
@@ -397,9 +319,9 @@ interface TenantsTable {
|
|
|
397
319
|
service_key_enc: Buffer;
|
|
398
320
|
api_url_internal: string;
|
|
399
321
|
api_url_public: string;
|
|
400
|
-
trial_ends_at: Date;
|
|
401
322
|
suspended_at: Date | null;
|
|
402
323
|
last_health_check_at: Date | null;
|
|
324
|
+
last_active_at: Generated<Date>;
|
|
403
325
|
service_gen: Generated<number>;
|
|
404
326
|
anon_gen: Generated<number>;
|
|
405
327
|
project_id: string | null;
|
|
@@ -423,6 +345,34 @@ interface TenantUsageMonthlyTable {
|
|
|
423
345
|
type TenantUsageMonthly = Selectable<TenantUsageMonthlyTable>;
|
|
424
346
|
type InsertTenantUsageMonthly = Insertable<TenantUsageMonthlyTable>;
|
|
425
347
|
type UpdateTenantUsageMonthly = Updateable<TenantUsageMonthlyTable>;
|
|
348
|
+
interface TenantComputeAddonsTable {
|
|
349
|
+
id: Generated<string>;
|
|
350
|
+
tenant_id: string;
|
|
351
|
+
memory_mb_delta: Generated<number>;
|
|
352
|
+
cpu_delta: Generated<number | string>;
|
|
353
|
+
storage_mb_delta: Generated<number>;
|
|
354
|
+
effective_from: Generated<Date>;
|
|
355
|
+
effective_until: Date | null;
|
|
356
|
+
stripe_subscription_item_id: string | null;
|
|
357
|
+
created_at: Generated<Date>;
|
|
358
|
+
}
|
|
359
|
+
type TenantComputeAddon = Selectable<TenantComputeAddonsTable>;
|
|
360
|
+
type InsertTenantComputeAddon = Insertable<TenantComputeAddonsTable>;
|
|
361
|
+
type UpdateTenantComputeAddon = Updateable<TenantComputeAddonsTable>;
|
|
362
|
+
interface AccountSpendCapsTable {
|
|
363
|
+
account_id: string;
|
|
364
|
+
monthly_cap_cents: number | null;
|
|
365
|
+
compute_cap_cents: number | null;
|
|
366
|
+
storage_cap_cents: number | null;
|
|
367
|
+
ai_cap_cents: number | null;
|
|
368
|
+
alert_threshold_pct: Generated<number>;
|
|
369
|
+
alert_sent_at: Date | null;
|
|
370
|
+
frozen_at: Date | null;
|
|
371
|
+
updated_at: Generated<Date>;
|
|
372
|
+
}
|
|
373
|
+
type AccountSpendCap = Selectable<AccountSpendCapsTable>;
|
|
374
|
+
type InsertAccountSpendCap = Insertable<AccountSpendCapsTable>;
|
|
375
|
+
type UpdateAccountSpendCap = Updateable<AccountSpendCapsTable>;
|
|
426
376
|
type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
|
|
427
377
|
type ProvisioningAuditStatus = "ok" | "error";
|
|
428
378
|
interface ProvisioningAuditLogTable {
|
|
@@ -439,30 +389,6 @@ interface ProvisioningAuditLogTable {
|
|
|
439
389
|
}
|
|
440
390
|
type ProvisioningAuditLog = Selectable<ProvisioningAuditLogTable>;
|
|
441
391
|
type InsertProvisioningAuditLog = Insertable<ProvisioningAuditLogTable>;
|
|
442
|
-
interface WorkflowBudgetsTable {
|
|
443
|
-
id: Generated<string>;
|
|
444
|
-
workflow_definition_id: string;
|
|
445
|
-
/** Period key: "daily:YYYY-MM-DD" | "weekly:YYYY-Www" | "per-run:<uuid>". */
|
|
446
|
-
period: string;
|
|
447
|
-
ai_usd_used: Generated<string>;
|
|
448
|
-
ai_tokens_used: Generated<string>;
|
|
449
|
-
chain_microstx_used: Generated<string>;
|
|
450
|
-
chain_tx_count: Generated<number>;
|
|
451
|
-
run_count: Generated<number>;
|
|
452
|
-
step_count: Generated<number>;
|
|
453
|
-
reset_at: Date;
|
|
454
|
-
created_at: Generated<Date>;
|
|
455
|
-
updated_at: Generated<Date>;
|
|
456
|
-
}
|
|
457
|
-
interface WorkflowSignerSecretsTable {
|
|
458
|
-
id: Generated<string>;
|
|
459
|
-
account_id: string;
|
|
460
|
-
name: string;
|
|
461
|
-
/** AES-GCM ciphertext bytes produced by the runner's KMS on write. */
|
|
462
|
-
encrypted_value: Buffer;
|
|
463
|
-
created_at: Generated<Date>;
|
|
464
|
-
updated_at: Generated<Date>;
|
|
465
|
-
}
|
|
466
392
|
type Block = Selectable<BlocksTable>;
|
|
467
393
|
type InsertBlock = Insertable<BlocksTable>;
|
|
468
394
|
type UpdateBlock = Updateable<BlocksTable>;
|
|
@@ -499,27 +425,6 @@ type SubgraphGap = Selectable<SubgraphGapsTable>;
|
|
|
499
425
|
type InsertSubgraphGap = Insertable<SubgraphGapsTable>;
|
|
500
426
|
type SubgraphUsageDaily = Selectable<SubgraphUsageDailyTable>;
|
|
501
427
|
type InsertSubgraphUsageDaily = Insertable<SubgraphUsageDailyTable>;
|
|
502
|
-
type WorkflowDefinition = Selectable<WorkflowDefinitionsTable>;
|
|
503
|
-
type InsertWorkflowDefinition = Insertable<WorkflowDefinitionsTable>;
|
|
504
|
-
type UpdateWorkflowDefinition = Updateable<WorkflowDefinitionsTable>;
|
|
505
|
-
type WorkflowRun = Selectable<WorkflowRunsTable>;
|
|
506
|
-
type InsertWorkflowRun = Insertable<WorkflowRunsTable>;
|
|
507
|
-
type UpdateWorkflowRun = Updateable<WorkflowRunsTable>;
|
|
508
|
-
type WorkflowStep = Selectable<WorkflowStepsTable>;
|
|
509
|
-
type InsertWorkflowStep = Insertable<WorkflowStepsTable>;
|
|
510
|
-
type UpdateWorkflowStep = Updateable<WorkflowStepsTable>;
|
|
511
|
-
type WorkflowQueueItem = Selectable<WorkflowQueueTable>;
|
|
512
|
-
type InsertWorkflowQueueItem = Insertable<WorkflowQueueTable>;
|
|
513
|
-
type WorkflowSchedule = Selectable<WorkflowSchedulesTable>;
|
|
514
|
-
type InsertWorkflowSchedule = Insertable<WorkflowSchedulesTable>;
|
|
515
|
-
type UpdateWorkflowSchedule = Updateable<WorkflowSchedulesTable>;
|
|
516
|
-
type WorkflowCursor = Selectable<WorkflowCursorsTable>;
|
|
517
|
-
type WorkflowSignerSecret = Selectable<WorkflowSignerSecretsTable>;
|
|
518
|
-
type InsertWorkflowSignerSecret = Insertable<WorkflowSignerSecretsTable>;
|
|
519
|
-
type UpdateWorkflowSignerSecret = Updateable<WorkflowSignerSecretsTable>;
|
|
520
|
-
type WorkflowBudget = Selectable<WorkflowBudgetsTable>;
|
|
521
|
-
type InsertWorkflowBudget = Insertable<WorkflowBudgetsTable>;
|
|
522
|
-
type UpdateWorkflowBudget = Updateable<WorkflowBudgetsTable>;
|
|
523
428
|
type Project = Selectable<ProjectsTable>;
|
|
524
429
|
type InsertProject = Insertable<ProjectsTable>;
|
|
525
430
|
type UpdateProject = Updateable<ProjectsTable>;
|
|
@@ -532,6 +437,76 @@ type InsertChatSession = Insertable<ChatSessionsTable>;
|
|
|
532
437
|
type UpdateChatSession = Updateable<ChatSessionsTable>;
|
|
533
438
|
type ChatMessage = Selectable<ChatMessagesTable>;
|
|
534
439
|
type InsertChatMessage = Insertable<ChatMessagesTable>;
|
|
440
|
+
type SubscriptionStatus = "active" | "paused" | "error";
|
|
441
|
+
type SubscriptionFormat = "standard-webhooks" | "inngest" | "trigger" | "cloudflare" | "cloudevents" | "raw";
|
|
442
|
+
type SubscriptionRuntime = "inngest" | "trigger" | "cloudflare" | "node";
|
|
443
|
+
interface SubscriptionsTable {
|
|
444
|
+
id: Generated<string>;
|
|
445
|
+
account_id: string;
|
|
446
|
+
project_id: string | null;
|
|
447
|
+
name: string;
|
|
448
|
+
status: ColumnType<SubscriptionStatus, SubscriptionStatus | undefined, SubscriptionStatus>;
|
|
449
|
+
subgraph_name: string;
|
|
450
|
+
table_name: string;
|
|
451
|
+
filter: Generated<unknown>;
|
|
452
|
+
format: ColumnType<SubscriptionFormat, SubscriptionFormat | undefined, SubscriptionFormat>;
|
|
453
|
+
runtime: SubscriptionRuntime | null;
|
|
454
|
+
url: string;
|
|
455
|
+
signing_secret_enc: Buffer;
|
|
456
|
+
auth_config: Generated<unknown>;
|
|
457
|
+
max_retries: Generated<number>;
|
|
458
|
+
timeout_ms: Generated<number>;
|
|
459
|
+
concurrency: Generated<number>;
|
|
460
|
+
circuit_failures: Generated<number>;
|
|
461
|
+
circuit_opened_at: Date | null;
|
|
462
|
+
last_delivery_at: Date | null;
|
|
463
|
+
last_success_at: Date | null;
|
|
464
|
+
last_error: string | null;
|
|
465
|
+
created_at: Generated<Date>;
|
|
466
|
+
updated_at: Generated<Date>;
|
|
467
|
+
}
|
|
468
|
+
type Subscription = Selectable<SubscriptionsTable>;
|
|
469
|
+
type InsertSubscription = Insertable<SubscriptionsTable>;
|
|
470
|
+
type UpdateSubscription = Updateable<SubscriptionsTable>;
|
|
471
|
+
type OutboxStatus = "pending" | "delivered" | "dead";
|
|
472
|
+
interface SubscriptionOutboxTable {
|
|
473
|
+
id: Generated<string>;
|
|
474
|
+
subscription_id: string;
|
|
475
|
+
subgraph_name: string;
|
|
476
|
+
table_name: string;
|
|
477
|
+
block_height: number | bigint;
|
|
478
|
+
tx_id: string | null;
|
|
479
|
+
row_pk: unknown;
|
|
480
|
+
event_type: string;
|
|
481
|
+
payload: unknown;
|
|
482
|
+
dedup_key: string;
|
|
483
|
+
attempt: Generated<number>;
|
|
484
|
+
next_attempt_at: Generated<Date>;
|
|
485
|
+
status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
|
|
486
|
+
is_replay: Generated<boolean>;
|
|
487
|
+
delivered_at: Date | null;
|
|
488
|
+
failed_at: Date | null;
|
|
489
|
+
locked_by: string | null;
|
|
490
|
+
locked_until: Date | null;
|
|
491
|
+
created_at: Generated<Date>;
|
|
492
|
+
}
|
|
493
|
+
type SubscriptionOutbox = Selectable<SubscriptionOutboxTable>;
|
|
494
|
+
type InsertSubscriptionOutbox = Insertable<SubscriptionOutboxTable>;
|
|
495
|
+
type UpdateSubscriptionOutbox = Updateable<SubscriptionOutboxTable>;
|
|
496
|
+
interface SubscriptionDeliveriesTable {
|
|
497
|
+
id: Generated<string>;
|
|
498
|
+
outbox_id: string;
|
|
499
|
+
subscription_id: string;
|
|
500
|
+
attempt: number;
|
|
501
|
+
status_code: number | null;
|
|
502
|
+
response_headers: unknown | null;
|
|
503
|
+
response_body: string | null;
|
|
504
|
+
error_message: string | null;
|
|
505
|
+
duration_ms: number | null;
|
|
506
|
+
dispatched_at: Generated<Date>;
|
|
507
|
+
}
|
|
508
|
+
type SubscriptionDelivery = Selectable<SubscriptionDeliveriesTable>;
|
|
509
|
+
type InsertSubscriptionDelivery = Insertable<SubscriptionDeliveriesTable>;
|
|
535
510
|
import { sql } from "kysely";
|
|
536
511
|
/**
|
|
537
512
|
* Kysely instance for the SOURCE DB (block/tx/event reads from the shared
|
|
@@ -557,4 +532,4 @@ declare function getDb(connectionString?: string): Kysely<Database>;
|
|
|
557
532
|
declare function getRawClient(role?: "source" | "target"): ReturnType<typeof postgres>;
|
|
558
533
|
/** Close all DB connection pools. Call in CLI commands to allow process exit. */
|
|
559
534
|
declare function closeDb(): Promise<void>;
|
|
560
|
-
export { sql, parseJsonb, jsonb, getTargetDb, getSourceDb, getRawClient, getDb, closeDb,
|
|
535
|
+
export { sql, parseJsonb, jsonb, getTargetDb, getSourceDb, getRawClient, getDb, closeDb, WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateTenantUsageMonthly, UpdateTenantComputeAddon, UpdateTenant, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraph, UpdateProject, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, UpdateAccountSpendCap, TransactionsTable, Transaction, TenantsTable, TenantUsageMonthlyTable, TenantUsageMonthly, TenantStatus, TenantComputeAddonsTable, TenantComputeAddon, Tenant, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubscriptionsTable, SubscriptionStatus, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionFormat, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, SessionsTable, Session, ProvisioningAuditStatus, ProvisioningAuditLogTable, ProvisioningAuditLog, ProvisioningAuditEvent, ProjectsTable, Project, OutboxStatus, MagicLinksTable, MagicLink, InsertTransaction, InsertTenantUsageMonthly, InsertTenantComputeAddon, InsertTenant, InsertTeamMember, InsertTeamInvitation, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphUsageDaily, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProvisioningAuditLog, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountSpendCap, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Database, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
|
package/dist/src/db/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/jsonb.ts", "../src/db/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { type RawBuilder, 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:
|
|
5
|
+
"import { type RawBuilder, 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 *\n * Generic parameter lets callers set the RawBuilder's output type so they\n * don't need to cast at the insert site. Default is `unknown` — widen at\n * the call site when the column type is narrower, e.g. `jsonb<MyShape>(...)`.\n */\nexport function jsonb<T = unknown>(value: T): RawBuilder<T> {\n\tconst escaped = JSON.stringify(value, (_k, v) =>\n\t\ttypeof v === \"bigint\" ? v.toString() : v,\n\t).replace(/'/g, \"''\");\n\treturn 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\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n",
|
|
6
6
|
"import { Kysely } from \"kysely\";\nimport { PostgresJSDialect } from \"kysely-postgres-js\";\nimport postgres from \"postgres\";\nimport type { Database } from \"./types.ts\";\n\nconst DEFAULT_URL =\n\t\"postgres://postgres:postgres@localhost:5432/secondlayer_dev\";\n\ninterface PoolEntry {\n\tdb: Kysely<Database>;\n\trawClient: ReturnType<typeof postgres>;\n}\n\n/**\n * Cache of Kysely + raw postgres.js pools keyed by resolved URL.\n * Two getters resolving to the same URL share one entry (single pool) —\n * this is the single-DB backward-compat contract: when only `DATABASE_URL`\n * is set, `getSourceDb() === getTargetDb()` (zero regression vs. pre-dual-DB).\n */\nconst pools = new Map<string, PoolEntry>();\n\nfunction resolveSourceUrl(): string {\n\treturn (\n\t\tprocess.env.SOURCE_DATABASE_URL || process.env.DATABASE_URL || DEFAULT_URL\n\t);\n}\n\nfunction resolveTargetUrl(): string {\n\treturn (\n\t\tprocess.env.TARGET_DATABASE_URL || process.env.DATABASE_URL || DEFAULT_URL\n\t);\n}\n\nfunction getOrCreatePool(url: string): PoolEntry {\n\tconst existing = pools.get(url);\n\tif (existing) return existing;\n\n\t// \"Local\" = we skip TLS. Any Docker service alias (single-label hostname\n\t// with no dots) is on an internal network and won't serve TLS.\n\tconst host = (() => {\n\t\ttry {\n\t\t\treturn new URL(url).hostname;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t})();\n\tconst isLocal =\n\t\thost === \"localhost\" || host === \"127.0.0.1\" || !host.includes(\".\");\n\tconst poolMax = Number.parseInt(process.env.DATABASE_POOL_MAX ?? \"20\", 10);\n\tconst rawClient = postgres(url, {\n\t\tmax: poolMax,\n\t\tssl: isLocal\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\trejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== \"0\",\n\t\t\t\t},\n\t});\n\tconst db = new Kysely<Database>({\n\t\tdialect: new PostgresJSDialect({ postgres: rawClient }),\n\t});\n\tconst entry: PoolEntry = { db, rawClient };\n\tpools.set(url, entry);\n\treturn entry;\n}\n\n/**\n * Kysely instance for the SOURCE DB (block/tx/event reads from the shared\n * indexer). Resolution: `SOURCE_DATABASE_URL || DATABASE_URL`.\n */\nexport function getSourceDb(): Kysely<Database> {\n\treturn getOrCreatePool(resolveSourceUrl()).db;\n}\n\n/**\n * Kysely instance for the TARGET DB (subgraph schemas, subgraphs table,\n * account-scoped data — tenant-side writes). Resolution:\n * `TARGET_DATABASE_URL || DATABASE_URL`.\n */\nexport function getTargetDb(): Kysely<Database> {\n\treturn getOrCreatePool(resolveTargetUrl()).db;\n}\n\n/**\n * Backward-compat alias for `getTargetDb()`. Accepts an optional\n * `connectionString` override used by seed/test helpers — when supplied,\n * bypasses env resolution and uses the provided URL directly (still cached).\n */\nexport function getDb(connectionString?: string): Kysely<Database> {\n\tif (connectionString) return getOrCreatePool(connectionString).db;\n\treturn getTargetDb();\n}\n\n/**\n * Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.).\n * Defaults to the target role (tenant schemas live in the target DB).\n */\nexport function getRawClient(\n\trole: \"source\" | \"target\" = \"target\",\n): ReturnType<typeof postgres> {\n\tconst url = role === \"source\" ? resolveSourceUrl() : resolveTargetUrl();\n\treturn getOrCreatePool(url).rawClient;\n}\n\n/** Close all DB connection pools. Call in CLI commands to allow process exit. */\nexport async function closeDb(): Promise<void> {\n\tfor (const entry of pools.values()) {\n\t\tawait entry.db.destroy();\n\t\tawait entry.rawClient.end();\n\t}\n\tpools.clear();\n}\n\nimport { sql } from \"kysely\";\nexport { sql };\nexport * from \"./types.ts\";\nexport { jsonb, parseJsonb } from \"./jsonb.ts\";\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;
|
|
8
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAWO,SAAS,KAAkB,CAAC,OAAyB;AAAA,EAC3D,MAAM,UAAU,KAAK,UAAU,OAAO,CAAC,IAAI,MAC1C,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CACxC,EAAE,QAAQ,MAAM,IAAI;AAAA,EACpB,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQpC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,KAAK;AAAA,MACtB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,EAET;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;;;AC/BnB;AACA;AACA;AA8GA,gBAAS;AA3GT,IAAM,cACL;AAaD,IAAM,QAAQ,IAAI;AAElB,SAAS,gBAAgB,GAAW;AAAA,EACnC,OACC,QAAQ,IAAI,uBAAuB,QAAQ,IAAI,gBAAgB;AAAA;AAIjE,SAAS,gBAAgB,GAAW;AAAA,EACnC,OACC,QAAQ,IAAI,uBAAuB,QAAQ,IAAI,gBAAgB;AAAA;AAIjE,SAAS,eAAe,CAAC,KAAwB;AAAA,EAChD,MAAM,WAAW,MAAM,IAAI,GAAG;AAAA,EAC9B,IAAI;AAAA,IAAU,OAAO;AAAA,EAIrB,MAAM,QAAQ,MAAM;AAAA,IACnB,IAAI;AAAA,MACH,OAAO,IAAI,IAAI,GAAG,EAAE;AAAA,MACnB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,KAEN;AAAA,EACH,MAAM,UACL,SAAS,eAAe,SAAS,eAAe,CAAC,KAAK,SAAS,GAAG;AAAA,EACnE,MAAM,UAAU,OAAO,SAAS,QAAQ,IAAI,qBAAqB,MAAM,EAAE;AAAA,EACzE,MAAM,YAAY,SAAS,KAAK;AAAA,IAC/B,KAAK;AAAA,IACL,KAAK,UACF,YACA;AAAA,MACA,oBAAoB,QAAQ,IAAI,iCAAiC;AAAA,IAClE;AAAA,EACH,CAAC;AAAA,EACD,MAAM,KAAK,IAAI,OAAiB;AAAA,IAC/B,SAAS,IAAI,kBAAkB,EAAE,UAAU,UAAU,CAAC;AAAA,EACvD,CAAC;AAAA,EACD,MAAM,QAAmB,EAAE,IAAI,UAAU;AAAA,EACzC,MAAM,IAAI,KAAK,KAAK;AAAA,EACpB,OAAO;AAAA;AAOD,SAAS,WAAW,GAAqB;AAAA,EAC/C,OAAO,gBAAgB,iBAAiB,CAAC,EAAE;AAAA;AAQrC,SAAS,WAAW,GAAqB;AAAA,EAC/C,OAAO,gBAAgB,iBAAiB,CAAC,EAAE;AAAA;AAQrC,SAAS,KAAK,CAAC,kBAA6C;AAAA,EAClE,IAAI;AAAA,IAAkB,OAAO,gBAAgB,gBAAgB,EAAE;AAAA,EAC/D,OAAO,YAAY;AAAA;AAOb,SAAS,YAAY,CAC3B,OAA4B,UACE;AAAA,EAC9B,MAAM,MAAM,SAAS,WAAW,iBAAiB,IAAI,iBAAiB;AAAA,EACtE,OAAO,gBAAgB,GAAG,EAAE;AAAA;AAI7B,eAAsB,OAAO,GAAkB;AAAA,EAC9C,WAAW,SAAS,MAAM,OAAO,GAAG;AAAA,IACnC,MAAM,MAAM,GAAG,QAAQ;AAAA,IACvB,MAAM,MAAM,UAAU,IAAI;AAAA,EAC3B;AAAA,EACA,MAAM,MAAM;AAAA;",
|
|
9
9
|
"debugId": "C0DD7408E000897364756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/dist/src/db/jsonb.d.ts
CHANGED
|
@@ -3,8 +3,12 @@ import { RawBuilder } from "kysely";
|
|
|
3
3
|
* Safely encode a JS value as a JSONB literal for Kysely inserts/updates.
|
|
4
4
|
* Kysely + postgres.js double-encodes JSON when using parameterized queries
|
|
5
5
|
* with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.
|
|
6
|
+
*
|
|
7
|
+
* Generic parameter lets callers set the RawBuilder's output type so they
|
|
8
|
+
* don't need to cast at the insert site. Default is `unknown` — widen at
|
|
9
|
+
* the call site when the column type is narrower, e.g. `jsonb<MyShape>(...)`.
|
|
6
10
|
*/
|
|
7
|
-
declare function jsonb(value:
|
|
11
|
+
declare function jsonb<T = unknown>(value: T): RawBuilder<T>;
|
|
8
12
|
/**
|
|
9
13
|
* Safely parse a JSONB value from the database.
|
|
10
14
|
* Handles double-encoded strings where postgres.js returns a JSON string
|
package/dist/src/db/jsonb.js.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/jsonb.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { type RawBuilder, 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:
|
|
5
|
+
"import { type RawBuilder, 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 *\n * Generic parameter lets callers set the RawBuilder's output type so they\n * don't need to cast at the insert site. Default is `unknown` — widen at\n * the call site when the column type is narrower, e.g. `jsonb<MyShape>(...)`.\n */\nexport function jsonb<T = unknown>(value: T): RawBuilder<T> {\n\tconst escaped = JSON.stringify(value, (_k, v) =>\n\t\ttypeof v === \"bigint\" ? v.toString() : v,\n\t).replace(/'/g, \"''\");\n\treturn 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\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAWO,SAAS,KAAkB,CAAC,OAAyB;AAAA,EAC3D,MAAM,UAAU,KAAK,UAAU,OAAO,CAAC,IAAI,MAC1C,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CACxC,EAAE,QAAQ,MAAM,IAAI;AAAA,EACpB,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQpC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,KAAK;AAAA,MACtB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,EAET;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;",
|
|
8
8
|
"debugId": "DA6ABA81E2ABDC7864756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|