@stardeck-customer-apps/data-store-sdk 0.4.0 → 0.6.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.
- package/LICENSE +21 -0
- package/SKILL.md +8 -1
- package/dist/chunk-EWWQPGFI.mjs +662 -0
- package/dist/chunk-FEKROS4B.mjs +66 -0
- package/dist/cli/bin.d.mts +1 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +729 -0
- package/dist/cli/bin.mjs +11 -0
- package/dist/cli/generate-types.d.mts +75 -1
- package/dist/cli/generate-types.d.ts +75 -1
- package/dist/cli/generate-types.js +345 -45
- package/dist/cli/generate-types.mjs +24 -430
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/server/index.d.mts +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.mjs +8 -63
- package/dist/{types-D2D9kZNF.d.mts → types-P3VlojJ9.d.mts} +1 -1
- package/dist/{types-D2D9kZNF.d.ts → types-P3VlojJ9.d.ts} +1 -1
- package/package.json +6 -4
|
@@ -1,431 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
if (dataType === "numeric") {
|
|
27
|
-
return row.numeric_precision !== null && row.numeric_scale !== null ? `numeric(${row.numeric_precision},${row.numeric_scale})` : "numeric";
|
|
28
|
-
}
|
|
29
|
-
if (dataType === "timestamp with time zone") return "timestamptz";
|
|
30
|
-
if (dataType === "timestamp without time zone") return "timestamp";
|
|
31
|
-
if (dataType === "time with time zone") return "timetz";
|
|
32
|
-
if (dataType === "time without time zone") return "time";
|
|
33
|
-
return dataType;
|
|
34
|
-
}
|
|
35
|
-
async function introspectSchemaSnapshot(executor) {
|
|
36
|
-
const [extensions, enums, columns, constraints, fks, checks, indexes] = await Promise.all([
|
|
37
|
-
executor.query(`SELECT extname FROM pg_extension ORDER BY extname`),
|
|
38
|
-
executor.query(
|
|
39
|
-
`SELECT t.typname AS name, e.enumlabel AS value
|
|
40
|
-
FROM pg_type t
|
|
41
|
-
JOIN pg_enum e ON e.enumtypid = t.oid
|
|
42
|
-
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
43
|
-
WHERE n.nspname = 'public'
|
|
44
|
-
ORDER BY t.typname, e.enumsortorder`
|
|
45
|
-
),
|
|
46
|
-
executor.query(
|
|
47
|
-
`SELECT c.table_name, c.column_name, c.data_type, c.udt_name, c.is_nullable,
|
|
48
|
-
c.column_default, c.character_maximum_length, c.numeric_precision, c.numeric_scale,
|
|
49
|
-
c.is_generated, c.generation_expression
|
|
50
|
-
FROM information_schema.columns c
|
|
51
|
-
JOIN information_schema.tables t
|
|
52
|
-
ON c.table_name = t.table_name AND c.table_schema = t.table_schema
|
|
53
|
-
WHERE c.table_schema = 'public'
|
|
54
|
-
AND t.table_type = 'BASE TABLE'
|
|
55
|
-
AND t.table_name NOT LIKE '\\_deleted\\_%'
|
|
56
|
-
ORDER BY c.table_name, c.ordinal_position`
|
|
57
|
-
),
|
|
58
|
-
executor.query(
|
|
59
|
-
`SELECT tc.table_name, tc.constraint_name, tc.constraint_type, ku.column_name
|
|
60
|
-
FROM information_schema.table_constraints tc
|
|
61
|
-
JOIN information_schema.key_column_usage ku
|
|
62
|
-
ON tc.constraint_name = ku.constraint_name AND tc.table_schema = ku.table_schema
|
|
63
|
-
WHERE tc.table_schema = 'public'
|
|
64
|
-
AND tc.constraint_type IN ('PRIMARY KEY', 'UNIQUE')
|
|
65
|
-
ORDER BY tc.table_name, tc.constraint_name, ku.ordinal_position`
|
|
66
|
-
),
|
|
67
|
-
executor.query(
|
|
68
|
-
// Read column pairings from pg_catalog: information_schema's
|
|
69
|
-
// constraint_column_usage is uncorrelated with key_column_usage, so
|
|
70
|
-
// joining them for a composite FK produces a cross product. Unnesting
|
|
71
|
-
// conkey/confkey together WITH ORDINALITY keeps referencing↔referenced
|
|
72
|
-
// columns paired and ordered.
|
|
73
|
-
`SELECT con.conname AS constraint_name,
|
|
74
|
-
rel.relname AS table_name,
|
|
75
|
-
att.attname AS column_name,
|
|
76
|
-
frel.relname AS referenced_table,
|
|
77
|
-
fatt.attname AS referenced_column,
|
|
78
|
-
CASE con.confdeltype WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT'
|
|
79
|
-
WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END AS delete_rule,
|
|
80
|
-
CASE con.confupdtype WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT'
|
|
81
|
-
WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END AS update_rule
|
|
82
|
-
FROM pg_constraint con
|
|
83
|
-
JOIN pg_class rel ON rel.oid = con.conrelid
|
|
84
|
-
JOIN pg_namespace ns ON ns.oid = rel.relnamespace
|
|
85
|
-
JOIN pg_class frel ON frel.oid = con.confrelid
|
|
86
|
-
JOIN LATERAL unnest(con.conkey, con.confkey) WITH ORDINALITY AS cols(conkey, confkey, ord) ON true
|
|
87
|
-
JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = cols.conkey
|
|
88
|
-
JOIN pg_attribute fatt ON fatt.attrelid = con.confrelid AND fatt.attnum = cols.confkey
|
|
89
|
-
WHERE ns.nspname = 'public' AND con.contype = 'f'
|
|
90
|
-
ORDER BY rel.relname, con.conname, cols.ord`
|
|
91
|
-
),
|
|
92
|
-
executor.query(
|
|
93
|
-
`SELECT rel.relname AS table_name, con.conname AS name,
|
|
94
|
-
pg_get_constraintdef(con.oid) AS definition
|
|
95
|
-
FROM pg_constraint con
|
|
96
|
-
JOIN pg_class rel ON rel.oid = con.conrelid
|
|
97
|
-
JOIN pg_namespace ns ON ns.oid = rel.relnamespace
|
|
98
|
-
WHERE ns.nspname = 'public' AND con.contype = 'c'
|
|
99
|
-
AND rel.relname NOT LIKE '\\_deleted\\_%'
|
|
100
|
-
ORDER BY rel.relname, con.conname`
|
|
101
|
-
),
|
|
102
|
-
executor.query(
|
|
103
|
-
`SELECT i.indexdef
|
|
104
|
-
FROM pg_indexes i
|
|
105
|
-
WHERE i.schemaname = 'public'
|
|
106
|
-
AND i.tablename NOT LIKE '\\_deleted\\_%'
|
|
107
|
-
AND NOT EXISTS (
|
|
108
|
-
SELECT 1 FROM pg_constraint c
|
|
109
|
-
JOIN pg_class cls ON cls.oid = c.conindid
|
|
110
|
-
WHERE cls.relname = i.indexname
|
|
111
|
-
)
|
|
112
|
-
ORDER BY i.indexname`
|
|
113
|
-
)
|
|
114
|
-
]);
|
|
115
|
-
const enumMap = /* @__PURE__ */ new Map();
|
|
116
|
-
for (const row of enums.rows) {
|
|
117
|
-
const name = String(row.name);
|
|
118
|
-
if (!enumMap.has(name)) enumMap.set(name, []);
|
|
119
|
-
enumMap.get(name).push(String(row.value));
|
|
120
|
-
}
|
|
121
|
-
const tableMap = /* @__PURE__ */ new Map();
|
|
122
|
-
for (const row of columns.rows) {
|
|
123
|
-
const tableName = String(row.table_name);
|
|
124
|
-
if (!tableMap.has(tableName)) {
|
|
125
|
-
tableMap.set(tableName, { name: tableName, columns: [], primaryKey: [], uniques: [] });
|
|
126
|
-
}
|
|
127
|
-
const defaultExpr = row.column_default === null ? null : String(row.column_default);
|
|
128
|
-
const isSerial = defaultExpr !== null && /^nextval\(/.test(defaultExpr);
|
|
129
|
-
const isGenerated = row.is_generated === "ALWAYS";
|
|
130
|
-
tableMap.get(tableName).columns.push({
|
|
131
|
-
name: String(row.column_name),
|
|
132
|
-
sqlType: resolveSqlType({
|
|
133
|
-
data_type: String(row.data_type),
|
|
134
|
-
udt_name: String(row.udt_name),
|
|
135
|
-
character_maximum_length: row.character_maximum_length,
|
|
136
|
-
numeric_precision: row.numeric_precision,
|
|
137
|
-
numeric_scale: row.numeric_scale
|
|
138
|
-
}),
|
|
139
|
-
nullable: row.is_nullable === "YES",
|
|
140
|
-
defaultExpr: isGenerated ? null : defaultExpr,
|
|
141
|
-
isSerial: !isGenerated && isSerial,
|
|
142
|
-
generatedExpr: isGenerated ? String(row.generation_expression) : null
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
const constraintGroups = /* @__PURE__ */ new Map();
|
|
146
|
-
for (const row of constraints.rows) {
|
|
147
|
-
const key = String(row.constraint_name);
|
|
148
|
-
if (!constraintGroups.has(key)) {
|
|
149
|
-
constraintGroups.set(key, {
|
|
150
|
-
table: String(row.table_name),
|
|
151
|
-
type: String(row.constraint_type),
|
|
152
|
-
columns: []
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
constraintGroups.get(key).columns.push(String(row.column_name));
|
|
156
|
-
}
|
|
157
|
-
for (const group of constraintGroups.values()) {
|
|
158
|
-
const table = tableMap.get(group.table);
|
|
159
|
-
if (!table) continue;
|
|
160
|
-
if (group.type === "PRIMARY KEY") {
|
|
161
|
-
table.primaryKey = group.columns;
|
|
162
|
-
} else if (group.type === "UNIQUE") {
|
|
163
|
-
table.uniques.push(group.columns);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
const fkGroups = /* @__PURE__ */ new Map();
|
|
167
|
-
for (const row of fks.rows) {
|
|
168
|
-
const key = String(row.constraint_name);
|
|
169
|
-
if (!fkGroups.has(key)) {
|
|
170
|
-
fkGroups.set(key, {
|
|
171
|
-
constraintName: key,
|
|
172
|
-
table: String(row.table_name),
|
|
173
|
-
columns: [],
|
|
174
|
-
referencedTable: String(row.referenced_table),
|
|
175
|
-
referencedColumns: [],
|
|
176
|
-
onDelete: String(row.delete_rule),
|
|
177
|
-
onUpdate: String(row.update_rule)
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
const fk = fkGroups.get(key);
|
|
181
|
-
fk.columns.push(String(row.column_name));
|
|
182
|
-
fk.referencedColumns.push(String(row.referenced_column));
|
|
183
|
-
}
|
|
184
|
-
return {
|
|
185
|
-
extensions: extensions.rows.map((r) => String(r.extname)).filter((name) => TRACKED_EXTENSIONS.has(name)),
|
|
186
|
-
enums: Array.from(enumMap, ([name, values]) => ({ name, values })),
|
|
187
|
-
tables: Array.from(tableMap.values()),
|
|
188
|
-
foreignKeys: Array.from(fkGroups.values()),
|
|
189
|
-
checks: checks.rows.map((r) => ({
|
|
190
|
-
table: String(r.table_name),
|
|
191
|
-
name: String(r.name),
|
|
192
|
-
definition: String(r.definition)
|
|
193
|
-
})),
|
|
194
|
-
indexes: indexes.rows.map((r) => ({ definition: String(r.indexdef) }))
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
function columnDdl(column, isSinglePk) {
|
|
198
|
-
let sqlType = column.sqlType;
|
|
199
|
-
if (column.isSerial) {
|
|
200
|
-
sqlType = sqlType === "bigint" ? "bigserial" : sqlType === "smallint" ? "smallserial" : "serial";
|
|
201
|
-
}
|
|
202
|
-
let ddl = `${quoteIdent(column.name)} ${sqlType}`;
|
|
203
|
-
if (isSinglePk) ddl += " PRIMARY KEY";
|
|
204
|
-
if (!column.nullable && !isSinglePk) ddl += " NOT NULL";
|
|
205
|
-
if (column.generatedExpr) {
|
|
206
|
-
ddl += ` GENERATED ALWAYS AS (${column.generatedExpr}) STORED`;
|
|
207
|
-
} else if (column.defaultExpr && !column.isSerial) {
|
|
208
|
-
ddl += ` DEFAULT ${column.defaultExpr}`;
|
|
209
|
-
}
|
|
210
|
-
return ddl;
|
|
211
|
-
}
|
|
212
|
-
function generateSchemaSql(snapshot) {
|
|
213
|
-
const lines = [
|
|
214
|
-
"-- Auto-generated by @stardeck-customer-apps/data-store-sdk",
|
|
215
|
-
"-- Schema snapshot of the live data store, applied to PGlite by the test harness.",
|
|
216
|
-
"-- Do not edit manually \u2014 regenerate with: npx stardeck-data-store generate-types",
|
|
217
|
-
""
|
|
218
|
-
];
|
|
219
|
-
for (const ext of snapshot.extensions) {
|
|
220
|
-
lines.push(`CREATE EXTENSION IF NOT EXISTS ${ext === "uuid-ossp" ? '"uuid-ossp"' : ext};`);
|
|
221
|
-
}
|
|
222
|
-
if (snapshot.extensions.length > 0) lines.push("");
|
|
223
|
-
for (const enumType of snapshot.enums) {
|
|
224
|
-
const values = enumType.values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
225
|
-
lines.push(`CREATE TYPE ${quoteIdent(enumType.name)} AS ENUM (${values});`);
|
|
226
|
-
}
|
|
227
|
-
if (snapshot.enums.length > 0) lines.push("");
|
|
228
|
-
for (const table of snapshot.tables) {
|
|
229
|
-
const singlePk = table.primaryKey.length === 1 ? table.primaryKey[0] : null;
|
|
230
|
-
const parts = table.columns.map((col) => " " + columnDdl(col, col.name === singlePk));
|
|
231
|
-
if (table.primaryKey.length > 1) {
|
|
232
|
-
parts.push(` PRIMARY KEY (${table.primaryKey.map(quoteIdent).join(", ")})`);
|
|
233
|
-
}
|
|
234
|
-
for (const unique of table.uniques) {
|
|
235
|
-
parts.push(` UNIQUE (${unique.map(quoteIdent).join(", ")})`);
|
|
236
|
-
}
|
|
237
|
-
lines.push(`CREATE TABLE ${quoteIdent(table.name)} (`);
|
|
238
|
-
lines.push(parts.join(",\n"));
|
|
239
|
-
lines.push(`);`);
|
|
240
|
-
lines.push("");
|
|
241
|
-
}
|
|
242
|
-
for (const check of snapshot.checks) {
|
|
243
|
-
lines.push(
|
|
244
|
-
`ALTER TABLE ${quoteIdent(check.table)} ADD CONSTRAINT ${quoteIdent(check.name)} ${check.definition};`
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
if (snapshot.checks.length > 0) lines.push("");
|
|
248
|
-
for (const fk of snapshot.foreignKeys) {
|
|
249
|
-
let stmt = `ALTER TABLE ${quoteIdent(fk.table)} ADD CONSTRAINT ${quoteIdent(fk.constraintName)} FOREIGN KEY (${fk.columns.map(quoteIdent).join(", ")}) REFERENCES ${quoteIdent(fk.referencedTable)} (${fk.referencedColumns.map(quoteIdent).join(", ")})`;
|
|
250
|
-
if (fk.onDelete !== "NO ACTION") stmt += ` ON DELETE ${fk.onDelete}`;
|
|
251
|
-
if (fk.onUpdate !== "NO ACTION") stmt += ` ON UPDATE ${fk.onUpdate}`;
|
|
252
|
-
lines.push(`${stmt};`);
|
|
253
|
-
}
|
|
254
|
-
if (snapshot.foreignKeys.length > 0) lines.push("");
|
|
255
|
-
for (const index of snapshot.indexes) {
|
|
256
|
-
lines.push(`${index.definition};`);
|
|
257
|
-
}
|
|
258
|
-
if (snapshot.indexes.length > 0) lines.push("");
|
|
259
|
-
return lines.join("\n");
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// src/cli/generate-types.ts
|
|
263
|
-
var PG_TYPE_MAP = {
|
|
264
|
-
text: "string",
|
|
265
|
-
varchar: "string",
|
|
266
|
-
"character varying": "string",
|
|
267
|
-
char: "string",
|
|
268
|
-
character: "string",
|
|
269
|
-
uuid: "string",
|
|
270
|
-
integer: "number",
|
|
271
|
-
int: "number",
|
|
272
|
-
smallint: "number",
|
|
273
|
-
bigint: "string",
|
|
274
|
-
// bigint as string to avoid precision loss
|
|
275
|
-
numeric: "string",
|
|
276
|
-
decimal: "string",
|
|
277
|
-
real: "number",
|
|
278
|
-
"double precision": "number",
|
|
279
|
-
boolean: "boolean",
|
|
280
|
-
jsonb: "unknown",
|
|
281
|
-
json: "unknown",
|
|
282
|
-
"timestamp with time zone": "Date",
|
|
283
|
-
"timestamp without time zone": "Date",
|
|
284
|
-
timestamp: "Date",
|
|
285
|
-
date: "string",
|
|
286
|
-
time: "string",
|
|
287
|
-
"time with time zone": "string",
|
|
288
|
-
"time without time zone": "string",
|
|
289
|
-
bytea: "Buffer",
|
|
290
|
-
ARRAY: "unknown[]"
|
|
1
|
+
import {
|
|
2
|
+
PG_TYPE_MAP,
|
|
3
|
+
connectionStringFromManifest,
|
|
4
|
+
generateTypeScript,
|
|
5
|
+
introspectSchema,
|
|
6
|
+
loadEnvLocal,
|
|
7
|
+
main,
|
|
8
|
+
parseCliArgs,
|
|
9
|
+
pgTypeToTs,
|
|
10
|
+
runGenerateTypes,
|
|
11
|
+
toPascalCase
|
|
12
|
+
} from "../chunk-EWWQPGFI.mjs";
|
|
13
|
+
import "../chunk-FEKROS4B.mjs";
|
|
14
|
+
export {
|
|
15
|
+
PG_TYPE_MAP,
|
|
16
|
+
connectionStringFromManifest,
|
|
17
|
+
generateTypeScript,
|
|
18
|
+
introspectSchema,
|
|
19
|
+
loadEnvLocal,
|
|
20
|
+
main,
|
|
21
|
+
parseCliArgs,
|
|
22
|
+
pgTypeToTs,
|
|
23
|
+
runGenerateTypes,
|
|
24
|
+
toPascalCase
|
|
291
25
|
};
|
|
292
|
-
function pgTypeToTs(pgType) {
|
|
293
|
-
return PG_TYPE_MAP[pgType] ?? "unknown";
|
|
294
|
-
}
|
|
295
|
-
function toPascalCase(name) {
|
|
296
|
-
return name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
297
|
-
}
|
|
298
|
-
async function introspectSchema(connectionString) {
|
|
299
|
-
const pool = new Pool({ connectionString });
|
|
300
|
-
try {
|
|
301
|
-
const { rows } = await pool.query(`
|
|
302
|
-
SELECT
|
|
303
|
-
c.table_name,
|
|
304
|
-
c.column_name,
|
|
305
|
-
c.data_type,
|
|
306
|
-
c.is_nullable,
|
|
307
|
-
c.column_default
|
|
308
|
-
FROM information_schema.columns c
|
|
309
|
-
JOIN information_schema.tables t
|
|
310
|
-
ON c.table_name = t.table_name AND c.table_schema = t.table_schema
|
|
311
|
-
WHERE c.table_schema = 'public'
|
|
312
|
-
AND t.table_type = 'BASE TABLE'
|
|
313
|
-
AND t.table_name NOT LIKE '_deleted_%'
|
|
314
|
-
ORDER BY c.table_name, c.ordinal_position
|
|
315
|
-
`);
|
|
316
|
-
const tables = /* @__PURE__ */ new Map();
|
|
317
|
-
for (const row of rows) {
|
|
318
|
-
if (!tables.has(row.table_name)) {
|
|
319
|
-
tables.set(row.table_name, []);
|
|
320
|
-
}
|
|
321
|
-
tables.get(row.table_name).push(row);
|
|
322
|
-
}
|
|
323
|
-
return tables;
|
|
324
|
-
} finally {
|
|
325
|
-
await pool.end();
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
function generateTypeScript(tables) {
|
|
329
|
-
const lines = [
|
|
330
|
-
"// AUTO-GENERATED by @stardeck-customer-apps/data-store-sdk \u2014 DO NOT EDIT.",
|
|
331
|
-
"// This file is overwritten in full on every run of:",
|
|
332
|
-
"// npx stardeck-data-store generate-types",
|
|
333
|
-
"// Anything you add here (hand-written or derived types) WILL BE LOST on the next",
|
|
334
|
-
"// regeneration. Put those in a separate file that imports from this one, e.g.",
|
|
335
|
-
"// src/lib/<domain>-types.ts importing the table types or DB from this file.",
|
|
336
|
-
"",
|
|
337
|
-
'import type { Generated } from "kysely";',
|
|
338
|
-
""
|
|
339
|
-
];
|
|
340
|
-
for (const [tableName, columns] of tables) {
|
|
341
|
-
const interfaceName = toPascalCase(tableName) + "Table";
|
|
342
|
-
lines.push(`export interface ${interfaceName} {`);
|
|
343
|
-
for (const col of columns) {
|
|
344
|
-
const tsType = pgTypeToTs(col.data_type);
|
|
345
|
-
const isGenerated = col.column_default !== null;
|
|
346
|
-
const isNullable = col.is_nullable === "YES";
|
|
347
|
-
let type = tsType;
|
|
348
|
-
if (isNullable) {
|
|
349
|
-
type = `${tsType} | null`;
|
|
350
|
-
}
|
|
351
|
-
if (isGenerated) {
|
|
352
|
-
type = `Generated<${type}>`;
|
|
353
|
-
}
|
|
354
|
-
lines.push(` ${col.column_name}: ${type};`);
|
|
355
|
-
}
|
|
356
|
-
lines.push("}");
|
|
357
|
-
lines.push("");
|
|
358
|
-
}
|
|
359
|
-
lines.push("export interface DB {");
|
|
360
|
-
for (const tableName of tables.keys()) {
|
|
361
|
-
const interfaceName = toPascalCase(tableName) + "Table";
|
|
362
|
-
lines.push(` ${tableName}: ${interfaceName};`);
|
|
363
|
-
}
|
|
364
|
-
lines.push("}");
|
|
365
|
-
lines.push("");
|
|
366
|
-
return lines.join("\n");
|
|
367
|
-
}
|
|
368
|
-
async function main() {
|
|
369
|
-
const args = process.argv.slice(2);
|
|
370
|
-
let connectionString = process.env.DATA_STORE_URL;
|
|
371
|
-
let outputPath = "./src/generated/data-store-types.ts";
|
|
372
|
-
let schemaOutputPath;
|
|
373
|
-
let emitSchema = true;
|
|
374
|
-
for (let i = 0; i < args.length; i++) {
|
|
375
|
-
if (args[i] === "--connection-string" && args[i + 1]) {
|
|
376
|
-
connectionString = args[++i];
|
|
377
|
-
} else if (args[i] === "--output" && args[i + 1]) {
|
|
378
|
-
outputPath = args[++i];
|
|
379
|
-
} else if (args[i] === "--schema-output" && args[i + 1]) {
|
|
380
|
-
schemaOutputPath = args[++i];
|
|
381
|
-
} else if (args[i] === "--no-schema") {
|
|
382
|
-
emitSchema = false;
|
|
383
|
-
} else if (args[i] === "--help") {
|
|
384
|
-
console.log(`Usage: stardeck-data-store generate-types [options]
|
|
385
|
-
|
|
386
|
-
Options:
|
|
387
|
-
--connection-string <url> Postgres connection string (default: DATA_STORE_URL env var)
|
|
388
|
-
--output <path> Output file path (default: ./src/generated/data-store-types.ts)
|
|
389
|
-
--schema-output <path> DDL snapshot path (default: data-store-schema.sql next to --output)
|
|
390
|
-
--no-schema Skip the DDL snapshot used by the test harness
|
|
391
|
-
--help Show this help message`);
|
|
392
|
-
process.exit(0);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
if (!connectionString) {
|
|
396
|
-
console.error(
|
|
397
|
-
"Error: No connection string provided. Set DATA_STORE_URL or use --connection-string."
|
|
398
|
-
);
|
|
399
|
-
process.exit(1);
|
|
400
|
-
}
|
|
401
|
-
console.log("Introspecting database schema...");
|
|
402
|
-
const tables = await introspectSchema(connectionString);
|
|
403
|
-
if (tables.size === 0) {
|
|
404
|
-
console.log("No tables found in database.");
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
console.log(`Found ${tables.size} table(s): ${Array.from(tables.keys()).join(", ")}`);
|
|
408
|
-
const typeScript = generateTypeScript(tables);
|
|
409
|
-
const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
|
|
410
|
-
if (dir) {
|
|
411
|
-
const { mkdirSync } = await import("fs");
|
|
412
|
-
mkdirSync(dir, { recursive: true });
|
|
413
|
-
}
|
|
414
|
-
writeFileSync(outputPath, typeScript, "utf-8");
|
|
415
|
-
console.log(`Types written to ${outputPath}`);
|
|
416
|
-
if (emitSchema) {
|
|
417
|
-
const schemaPath = schemaOutputPath ?? `${dir ? `${dir}/` : ""}data-store-schema.sql`;
|
|
418
|
-
const pool = new Pool({ connectionString });
|
|
419
|
-
try {
|
|
420
|
-
const snapshot = await introspectSchemaSnapshot(pool);
|
|
421
|
-
writeFileSync(schemaPath, generateSchemaSql(snapshot), "utf-8");
|
|
422
|
-
console.log(`Schema snapshot written to ${schemaPath} (used by the test harness)`);
|
|
423
|
-
} finally {
|
|
424
|
-
await pool.end();
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
main().catch((error) => {
|
|
429
|
-
console.error("Failed to generate types:", error);
|
|
430
|
-
process.exit(1);
|
|
431
|
-
});
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { C as ColumnDefinition, D as DataStoreClientConfig, F as FilterOperator, L as ListObjectsResult, Q as QueryFilter,
|
|
1
|
+
export { C as ColumnDefinition, D as DataStoreClientConfig, F as FilterOperator, L as ListObjectsResult, Q as QueryFilter, a as QueryOptions, b as QueryResult, S as StorageObject, T as TableColumn, c as TableSchema } from './types-P3VlojJ9.mjs';
|
|
2
2
|
|
|
3
3
|
declare class DataStoreError extends Error {
|
|
4
4
|
code: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { C as ColumnDefinition, D as DataStoreClientConfig, F as FilterOperator, L as ListObjectsResult, Q as QueryFilter,
|
|
1
|
+
export { C as ColumnDefinition, D as DataStoreClientConfig, F as FilterOperator, L as ListObjectsResult, Q as QueryFilter, a as QueryOptions, b as QueryResult, S as StorageObject, T as TableColumn, c as TableSchema } from './types-P3VlojJ9.js';
|
|
2
2
|
|
|
3
3
|
declare class DataStoreError extends Error {
|
|
4
4
|
code: string;
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DataStoreClientConfig,
|
|
1
|
+
import { D as DataStoreClientConfig, c as TableSchema, C as ColumnDefinition, a as QueryOptions, b as QueryResult, L as ListObjectsResult } from '../types-P3VlojJ9.mjs';
|
|
2
2
|
import { KyselyConfig, Kysely } from 'kysely';
|
|
3
3
|
|
|
4
4
|
/**
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DataStoreClientConfig,
|
|
1
|
+
import { D as DataStoreClientConfig, c as TableSchema, C as ColumnDefinition, a as QueryOptions, b as QueryResult, L as ListObjectsResult } from '../types-P3VlojJ9.js';
|
|
2
2
|
import { KyselyConfig, Kysely } from 'kysely';
|
|
3
3
|
|
|
4
4
|
/**
|
package/dist/server/index.mjs
CHANGED
|
@@ -2,6 +2,11 @@ import {
|
|
|
2
2
|
AuthenticationError,
|
|
3
3
|
DataStoreError
|
|
4
4
|
} from "../chunk-7YN3WP4H.mjs";
|
|
5
|
+
import {
|
|
6
|
+
legacyDataStoreEnvName,
|
|
7
|
+
listDataStores,
|
|
8
|
+
resolveDataStore
|
|
9
|
+
} from "../chunk-FEKROS4B.mjs";
|
|
5
10
|
|
|
6
11
|
// src/server/hmac.ts
|
|
7
12
|
import crypto from "crypto";
|
|
@@ -20,66 +25,6 @@ function signDeploymentRequest(deploymentSecret, payload) {
|
|
|
20
25
|
return `${payloadB64}.${signature}`;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
// src/server/manifest.ts
|
|
24
|
-
var STARDECK_DATA_STORES_ENV = "STARDECK_DATA_STORES";
|
|
25
|
-
function readEnv(key) {
|
|
26
|
-
if (typeof process !== "undefined" && process.env?.[key]) {
|
|
27
|
-
return process.env[key];
|
|
28
|
-
}
|
|
29
|
-
return void 0;
|
|
30
|
-
}
|
|
31
|
-
function dataStoreSlug(name) {
|
|
32
|
-
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
33
|
-
return slug || "store";
|
|
34
|
-
}
|
|
35
|
-
function legacyDataStoreEnvName(name) {
|
|
36
|
-
return name.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
37
|
-
}
|
|
38
|
-
function readDataStoreManifest() {
|
|
39
|
-
const raw = readEnv(STARDECK_DATA_STORES_ENV);
|
|
40
|
-
if (!raw) return [];
|
|
41
|
-
try {
|
|
42
|
-
const parsed = JSON.parse(raw);
|
|
43
|
-
if (!Array.isArray(parsed)) return [];
|
|
44
|
-
return parsed.filter((e) => {
|
|
45
|
-
if (typeof e !== "object" || e === null) return false;
|
|
46
|
-
const entry = e;
|
|
47
|
-
if (typeof entry.id !== "string" || typeof entry.name !== "string" || typeof entry.slug !== "string") {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
return entry.storeType === "storage" || typeof entry.url === "string";
|
|
51
|
-
});
|
|
52
|
-
} catch {
|
|
53
|
-
return [];
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
function resolveDataStore(ref) {
|
|
57
|
-
return resolveManifestEntry(readDataStoreManifest(), ref);
|
|
58
|
-
}
|
|
59
|
-
function resolveManifestEntry(entries, ref) {
|
|
60
|
-
if (ref.storeId) {
|
|
61
|
-
const byId = entries.find((e) => e.id === ref.storeId);
|
|
62
|
-
if (byId) return byId;
|
|
63
|
-
}
|
|
64
|
-
if (ref.storeName) {
|
|
65
|
-
const target = ref.storeName;
|
|
66
|
-
const byBindingKey = entries.find((e) => e.bindingKey === target);
|
|
67
|
-
if (byBindingKey) return byBindingKey;
|
|
68
|
-
const exact = entries.find((e) => e.slug === target || e.id === target || e.name === target);
|
|
69
|
-
if (exact) return exact;
|
|
70
|
-
const norm = dataStoreSlug(target);
|
|
71
|
-
const byBindingKeyNorm = entries.find((e) => e.bindingKey === norm);
|
|
72
|
-
if (byBindingKeyNorm) return byBindingKeyNorm;
|
|
73
|
-
return entries.find(
|
|
74
|
-
(e) => e.slug === norm || typeof e.name === "string" && dataStoreSlug(e.name) === norm
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
return void 0;
|
|
78
|
-
}
|
|
79
|
-
function listDataStores() {
|
|
80
|
-
return readDataStoreManifest();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
28
|
// src/server/client.ts
|
|
84
29
|
var DataStoreClient = class {
|
|
85
30
|
baseUrl;
|
|
@@ -256,7 +201,7 @@ var DataStoreClient = class {
|
|
|
256
201
|
|
|
257
202
|
// src/server/kysely.ts
|
|
258
203
|
import "kysely";
|
|
259
|
-
function
|
|
204
|
+
function readEnv(key) {
|
|
260
205
|
if (typeof process !== "undefined" && process.env?.[key]) {
|
|
261
206
|
return process.env[key];
|
|
262
207
|
}
|
|
@@ -270,10 +215,10 @@ function resolveConnectionString(options) {
|
|
|
270
215
|
})?.url;
|
|
271
216
|
if (fromManifest) return fromManifest;
|
|
272
217
|
if (options?.storeName) {
|
|
273
|
-
const legacy =
|
|
218
|
+
const legacy = readEnv(`DATA_STORE_${legacyDataStoreEnvName(options.storeName)}_URL`);
|
|
274
219
|
if (legacy) return legacy;
|
|
275
220
|
}
|
|
276
|
-
return
|
|
221
|
+
return readEnv("DATA_STORE_URL");
|
|
277
222
|
}
|
|
278
223
|
async function createDataStore(options) {
|
|
279
224
|
const connectionString = resolveConnectionString(options);
|
|
@@ -82,4 +82,4 @@ interface ListObjectsResult {
|
|
|
82
82
|
hasMore: boolean;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
export type { ColumnDefinition as C, DataStoreClientConfig as D, FilterOperator as F, ListObjectsResult as L, QueryFilter as Q, StorageObject as S, TableColumn as T,
|
|
85
|
+
export type { ColumnDefinition as C, DataStoreClientConfig as D, FilterOperator as F, ListObjectsResult as L, QueryFilter as Q, StorageObject as S, TableColumn as T, QueryOptions as a, QueryResult as b, TableSchema as c };
|
|
@@ -82,4 +82,4 @@ interface ListObjectsResult {
|
|
|
82
82
|
hasMore: boolean;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
export type { ColumnDefinition as C, DataStoreClientConfig as D, FilterOperator as F, ListObjectsResult as L, QueryFilter as Q, StorageObject as S, TableColumn as T,
|
|
85
|
+
export type { ColumnDefinition as C, DataStoreClientConfig as D, FilterOperator as F, ListObjectsResult as L, QueryFilter as Q, StorageObject as S, TableColumn as T, QueryOptions as a, QueryResult as b, TableSchema as c };
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stardeck-customer-apps/data-store-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"license": "MIT",
|
|
4
5
|
"description": "SDK for accessing Stardeck data stores from deployed projects",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"module": "dist/index.mjs",
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
"access": "public"
|
|
11
12
|
},
|
|
12
13
|
"bin": {
|
|
13
|
-
"stardeck-data-store": "./dist/cli/
|
|
14
|
+
"stardeck-data-store": "./dist/cli/bin.js"
|
|
14
15
|
},
|
|
15
16
|
"files": [
|
|
16
17
|
"dist",
|
|
@@ -31,8 +32,8 @@
|
|
|
31
32
|
}
|
|
32
33
|
},
|
|
33
34
|
"scripts": {
|
|
34
|
-
"build": "tsup src/index.ts src/server/index.ts src/cli/generate-types.ts --format cjs,esm --dts",
|
|
35
|
-
"dev": "tsup src/index.ts src/server/index.ts src/cli/generate-types.ts --format cjs,esm --dts --watch",
|
|
35
|
+
"build": "tsup src/index.ts src/server/index.ts src/cli/generate-types.ts src/cli/bin.ts --format cjs,esm --dts",
|
|
36
|
+
"dev": "tsup src/index.ts src/server/index.ts src/cli/generate-types.ts src/cli/bin.ts --format cjs,esm --dts --watch",
|
|
36
37
|
"format": "prettier --write . && eslint . --fix",
|
|
37
38
|
"typecheck": "tsc --noEmit",
|
|
38
39
|
"lint": "eslint src/",
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"@neondatabase/serverless": "^1.1.0",
|
|
59
60
|
"@stardeck-customer-apps/tsconfig": "*",
|
|
60
61
|
"@types/node": "^24.10.1",
|
|
62
|
+
"globals": "^16.3.0",
|
|
61
63
|
"kysely-neon": "^2.0.0",
|
|
62
64
|
"tsup": "^8.0.0",
|
|
63
65
|
"typescript": "^5.0.0",
|