@stardeck-customer-apps/data-store-sdk 0.3.3 → 0.5.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/dist/cli/generate-types.d.mts +49 -0
- package/dist/cli/generate-types.d.ts +49 -0
- package/dist/cli/generate-types.js +250 -44
- package/dist/cli/generate-types.mjs +229 -42
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/server/index.d.mts +14 -2
- package/dist/server/index.d.ts +14 -2
- package/dist/server/index.js +18 -3
- package/dist/server/index.mjs +18 -3
- 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 +1 -1
|
@@ -1 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
declare function toPascalCase(name: string): string;
|
|
3
|
+
|
|
4
|
+
interface ColumnInfo {
|
|
5
|
+
table_name: string;
|
|
6
|
+
column_name: string;
|
|
7
|
+
data_type: string;
|
|
8
|
+
is_nullable: string;
|
|
9
|
+
column_default: string | null;
|
|
10
|
+
}
|
|
11
|
+
declare const PG_TYPE_MAP: Record<string, string>;
|
|
12
|
+
declare function pgTypeToTs(pgType: string): string;
|
|
13
|
+
|
|
14
|
+
type ParsedCliArgs = {
|
|
15
|
+
connectionString: string | undefined;
|
|
16
|
+
outputPath: string;
|
|
17
|
+
schemaOutputPath: string | undefined;
|
|
18
|
+
emitSchema: boolean;
|
|
19
|
+
modulesDir: string;
|
|
20
|
+
noModules: boolean;
|
|
21
|
+
help: boolean;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Pure argv parser for generate-types flags. Exported for unit tests.
|
|
25
|
+
*/
|
|
26
|
+
declare function parseCliArgs(argv: string[], options?: {
|
|
27
|
+
cwd?: string;
|
|
28
|
+
env?: NodeJS.ProcessEnv;
|
|
29
|
+
}): ParsedCliArgs;
|
|
30
|
+
declare function introspectSchema(connectionString: string): Promise<Map<string, ColumnInfo[]>>;
|
|
31
|
+
declare function generateTypeScript(tables: Map<string, ColumnInfo[]>, moduleSlices?: string): string;
|
|
32
|
+
type RunGenerateTypesOptions = {
|
|
33
|
+
connectionString: string;
|
|
34
|
+
outputPath: string;
|
|
35
|
+
schemaOutputPath?: string;
|
|
36
|
+
emitSchema?: boolean;
|
|
37
|
+
modulesDir: string;
|
|
38
|
+
noModules: boolean;
|
|
39
|
+
/** Injectable for tests — defaults to live Postgres introspection. */
|
|
40
|
+
introspect?: (connectionString: string) => Promise<Map<string, ColumnInfo[]>>;
|
|
41
|
+
warn?: (message: string) => void;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Flag → discovery → emitter orchestration. Accepts an injectable introspect
|
|
45
|
+
* so tests can stub the pool without a live database.
|
|
46
|
+
*/
|
|
47
|
+
declare function runGenerateTypes(options: RunGenerateTypesOptions): Promise<void>;
|
|
48
|
+
declare function main(argv?: string[]): Promise<void>;
|
|
49
|
+
|
|
50
|
+
export { type ColumnInfo, PG_TYPE_MAP, type ParsedCliArgs, type RunGenerateTypesOptions, generateTypeScript, introspectSchema, main, parseCliArgs, pgTypeToTs, runGenerateTypes, toPascalCase };
|
|
@@ -1 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
declare function toPascalCase(name: string): string;
|
|
3
|
+
|
|
4
|
+
interface ColumnInfo {
|
|
5
|
+
table_name: string;
|
|
6
|
+
column_name: string;
|
|
7
|
+
data_type: string;
|
|
8
|
+
is_nullable: string;
|
|
9
|
+
column_default: string | null;
|
|
10
|
+
}
|
|
11
|
+
declare const PG_TYPE_MAP: Record<string, string>;
|
|
12
|
+
declare function pgTypeToTs(pgType: string): string;
|
|
13
|
+
|
|
14
|
+
type ParsedCliArgs = {
|
|
15
|
+
connectionString: string | undefined;
|
|
16
|
+
outputPath: string;
|
|
17
|
+
schemaOutputPath: string | undefined;
|
|
18
|
+
emitSchema: boolean;
|
|
19
|
+
modulesDir: string;
|
|
20
|
+
noModules: boolean;
|
|
21
|
+
help: boolean;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Pure argv parser for generate-types flags. Exported for unit tests.
|
|
25
|
+
*/
|
|
26
|
+
declare function parseCliArgs(argv: string[], options?: {
|
|
27
|
+
cwd?: string;
|
|
28
|
+
env?: NodeJS.ProcessEnv;
|
|
29
|
+
}): ParsedCliArgs;
|
|
30
|
+
declare function introspectSchema(connectionString: string): Promise<Map<string, ColumnInfo[]>>;
|
|
31
|
+
declare function generateTypeScript(tables: Map<string, ColumnInfo[]>, moduleSlices?: string): string;
|
|
32
|
+
type RunGenerateTypesOptions = {
|
|
33
|
+
connectionString: string;
|
|
34
|
+
outputPath: string;
|
|
35
|
+
schemaOutputPath?: string;
|
|
36
|
+
emitSchema?: boolean;
|
|
37
|
+
modulesDir: string;
|
|
38
|
+
noModules: boolean;
|
|
39
|
+
/** Injectable for tests — defaults to live Postgres introspection. */
|
|
40
|
+
introspect?: (connectionString: string) => Promise<Map<string, ColumnInfo[]>>;
|
|
41
|
+
warn?: (message: string) => void;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Flag → discovery → emitter orchestration. Accepts an injectable introspect
|
|
45
|
+
* so tests can stub the pool without a live database.
|
|
46
|
+
*/
|
|
47
|
+
declare function runGenerateTypes(options: RunGenerateTypesOptions): Promise<void>;
|
|
48
|
+
declare function main(argv?: string[]): Promise<void>;
|
|
49
|
+
|
|
50
|
+
export { type ColumnInfo, PG_TYPE_MAP, type ParsedCliArgs, type RunGenerateTypesOptions, generateTypeScript, introspectSchema, main, parseCliArgs, pgTypeToTs, runGenerateTypes, toPascalCase };
|
|
@@ -6,6 +6,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
9
13
|
var __copyProps = (to, from, except, desc) => {
|
|
10
14
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
15
|
for (let key of __getOwnPropNames(from))
|
|
@@ -22,11 +26,132 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
26
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
27
|
mod
|
|
24
28
|
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
30
|
|
|
26
31
|
// src/cli/generate-types.ts
|
|
27
|
-
var
|
|
32
|
+
var generate_types_exports = {};
|
|
33
|
+
__export(generate_types_exports, {
|
|
34
|
+
PG_TYPE_MAP: () => PG_TYPE_MAP,
|
|
35
|
+
generateTypeScript: () => generateTypeScript,
|
|
36
|
+
introspectSchema: () => introspectSchema,
|
|
37
|
+
main: () => main,
|
|
38
|
+
parseCliArgs: () => parseCliArgs,
|
|
39
|
+
pgTypeToTs: () => pgTypeToTs,
|
|
40
|
+
runGenerateTypes: () => runGenerateTypes,
|
|
41
|
+
toPascalCase: () => toPascalCase
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(generate_types_exports);
|
|
44
|
+
var import_fs2 = require("fs");
|
|
45
|
+
var import_path2 = require("path");
|
|
46
|
+
var import_url = require("url");
|
|
28
47
|
var import_serverless = require("@neondatabase/serverless");
|
|
29
48
|
|
|
49
|
+
// src/cli/module-type-slices.ts
|
|
50
|
+
var import_fs = require("fs");
|
|
51
|
+
var import_path = require("path");
|
|
52
|
+
function toPascalCase(name) {
|
|
53
|
+
return name.split(/[_-]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
54
|
+
}
|
|
55
|
+
function parseModuleManifestSlice(content, sourceLabel, warn) {
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = JSON.parse(content);
|
|
59
|
+
} catch {
|
|
60
|
+
warn(`Skipping malformed module manifest at ${sourceLabel}`);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
64
|
+
warn(`Skipping malformed module manifest at ${sourceLabel}`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const record = parsed;
|
|
68
|
+
const name = record.name;
|
|
69
|
+
const tables = record.tables;
|
|
70
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
71
|
+
warn(`Skipping module manifest at ${sourceLabel}: missing or invalid "name"`);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (!Array.isArray(tables) || tables.length === 0) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const tableNames = [];
|
|
78
|
+
for (const entry of tables) {
|
|
79
|
+
if (typeof entry !== "string" || entry.length === 0) {
|
|
80
|
+
warn(`Skipping module manifest at ${sourceLabel}: invalid "tables" entry`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
tableNames.push(entry);
|
|
84
|
+
}
|
|
85
|
+
return { name, tables: tableNames };
|
|
86
|
+
}
|
|
87
|
+
function discoverModuleManifests(modulesDir, warn) {
|
|
88
|
+
if (!(0, import_fs.existsSync)(modulesDir)) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
let entries;
|
|
92
|
+
try {
|
|
93
|
+
entries = (0, import_fs.readdirSync)(modulesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
94
|
+
} catch {
|
|
95
|
+
warn(`Could not read modules directory: ${modulesDir}`);
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
const manifests = [];
|
|
99
|
+
for (const dirName of entries) {
|
|
100
|
+
const manifestPath = (0, import_path.join)(modulesDir, dirName, "module.json");
|
|
101
|
+
if (!(0, import_fs.existsSync)(manifestPath)) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
let content;
|
|
105
|
+
try {
|
|
106
|
+
content = (0, import_fs.readFileSync)(manifestPath, "utf-8");
|
|
107
|
+
} catch {
|
|
108
|
+
warn(`Skipping unreadable module manifest at ${manifestPath}`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const slice = parseModuleManifestSlice(content, manifestPath, warn);
|
|
112
|
+
if (slice) {
|
|
113
|
+
manifests.push(slice);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return manifests.sort((a, b) => a.name.localeCompare(b.name));
|
|
117
|
+
}
|
|
118
|
+
function shouldLoadModuleManifests(noModules) {
|
|
119
|
+
return !noModules;
|
|
120
|
+
}
|
|
121
|
+
function emitModuleDbSlices(schemaTableNames, manifests, warn) {
|
|
122
|
+
const lines = [];
|
|
123
|
+
const moduleMapEntries = [];
|
|
124
|
+
for (const manifest of manifests) {
|
|
125
|
+
const presentTables = manifest.tables.filter((t) => schemaTableNames.has(t));
|
|
126
|
+
const absentTables = manifest.tables.filter((t) => !schemaTableNames.has(t));
|
|
127
|
+
if (absentTables.length > 0) {
|
|
128
|
+
warn(`Module "${manifest.name}": table(s) not in database: ${absentTables.join(", ")}`);
|
|
129
|
+
}
|
|
130
|
+
if (presentTables.length === 0) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const interfaceName = `${toPascalCase(manifest.name)}ModuleDB`;
|
|
134
|
+
lines.push(`export interface ${interfaceName} {`);
|
|
135
|
+
for (const tableName of presentTables) {
|
|
136
|
+
const tableTypeName = `${toPascalCase(tableName)}Table`;
|
|
137
|
+
lines.push(` ${tableName}: ${tableTypeName};`);
|
|
138
|
+
}
|
|
139
|
+
lines.push("}");
|
|
140
|
+
lines.push("");
|
|
141
|
+
moduleMapEntries.push({ name: manifest.name, interfaceName });
|
|
142
|
+
}
|
|
143
|
+
if (moduleMapEntries.length === 0) {
|
|
144
|
+
return "";
|
|
145
|
+
}
|
|
146
|
+
lines.push("export interface ModuleDBs {");
|
|
147
|
+
for (const { name, interfaceName } of moduleMapEntries) {
|
|
148
|
+
lines.push(` "${name}": ${interfaceName};`);
|
|
149
|
+
}
|
|
150
|
+
lines.push("}");
|
|
151
|
+
lines.push("");
|
|
152
|
+
return lines.join("\n");
|
|
153
|
+
}
|
|
154
|
+
|
|
30
155
|
// src/cli/schema-ddl.ts
|
|
31
156
|
function quoteIdent(name) {
|
|
32
157
|
return `"${name.replace(/"/g, '""')}"`;
|
|
@@ -283,6 +408,7 @@ function generateSchemaSql(snapshot) {
|
|
|
283
408
|
}
|
|
284
409
|
|
|
285
410
|
// src/cli/generate-types.ts
|
|
411
|
+
var import_meta = {};
|
|
286
412
|
var PG_TYPE_MAP = {
|
|
287
413
|
text: "string",
|
|
288
414
|
varchar: "string",
|
|
@@ -315,8 +441,42 @@ var PG_TYPE_MAP = {
|
|
|
315
441
|
function pgTypeToTs(pgType) {
|
|
316
442
|
return PG_TYPE_MAP[pgType] ?? "unknown";
|
|
317
443
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
444
|
+
function parseCliArgs(argv, options = {}) {
|
|
445
|
+
const cwd = options.cwd ?? process.cwd();
|
|
446
|
+
const env = options.env ?? process.env;
|
|
447
|
+
let connectionString = env.DATA_STORE_URL;
|
|
448
|
+
let outputPath = "./src/generated/data-store-types.ts";
|
|
449
|
+
let schemaOutputPath;
|
|
450
|
+
let emitSchema = true;
|
|
451
|
+
let modulesDir = (0, import_path2.resolve)(cwd, "./src/modules");
|
|
452
|
+
let noModules = false;
|
|
453
|
+
let help = false;
|
|
454
|
+
for (let i = 0; i < argv.length; i++) {
|
|
455
|
+
if (argv[i] === "--connection-string" && argv[i + 1]) {
|
|
456
|
+
connectionString = argv[++i];
|
|
457
|
+
} else if (argv[i] === "--output" && argv[i + 1]) {
|
|
458
|
+
outputPath = argv[++i];
|
|
459
|
+
} else if (argv[i] === "--schema-output" && argv[i + 1]) {
|
|
460
|
+
schemaOutputPath = argv[++i];
|
|
461
|
+
} else if (argv[i] === "--no-schema") {
|
|
462
|
+
emitSchema = false;
|
|
463
|
+
} else if (argv[i] === "--modules-dir" && argv[i + 1]) {
|
|
464
|
+
modulesDir = (0, import_path2.resolve)(cwd, argv[++i]);
|
|
465
|
+
} else if (argv[i] === "--no-modules") {
|
|
466
|
+
noModules = true;
|
|
467
|
+
} else if (argv[i] === "--help") {
|
|
468
|
+
help = true;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
connectionString,
|
|
473
|
+
outputPath,
|
|
474
|
+
schemaOutputPath,
|
|
475
|
+
emitSchema,
|
|
476
|
+
modulesDir,
|
|
477
|
+
noModules,
|
|
478
|
+
help
|
|
479
|
+
};
|
|
320
480
|
}
|
|
321
481
|
async function introspectSchema(connectionString) {
|
|
322
482
|
const pool = new import_serverless.Pool({ connectionString });
|
|
@@ -348,7 +508,7 @@ async function introspectSchema(connectionString) {
|
|
|
348
508
|
await pool.end();
|
|
349
509
|
}
|
|
350
510
|
}
|
|
351
|
-
function generateTypeScript(tables) {
|
|
511
|
+
function generateTypeScript(tables, moduleSlices) {
|
|
352
512
|
const lines = [
|
|
353
513
|
"// AUTO-GENERATED by @stardeck-customer-apps/data-store-sdk \u2014 DO NOT EDIT.",
|
|
354
514
|
"// This file is overwritten in full on every run of:",
|
|
@@ -356,6 +516,8 @@ function generateTypeScript(tables) {
|
|
|
356
516
|
"// Anything you add here (hand-written or derived types) WILL BE LOST on the next",
|
|
357
517
|
"// regeneration. Put those in a separate file that imports from this one, e.g.",
|
|
358
518
|
"// src/lib/<domain>-types.ts importing the table types or DB from this file.",
|
|
519
|
+
"// When module manifests are discoverable, per-module DB slices and ModuleDBs are",
|
|
520
|
+
"// appended below the flat DB interface.",
|
|
359
521
|
"",
|
|
360
522
|
'import type { Generated } from "kysely";',
|
|
361
523
|
""
|
|
@@ -386,69 +548,113 @@ function generateTypeScript(tables) {
|
|
|
386
548
|
}
|
|
387
549
|
lines.push("}");
|
|
388
550
|
lines.push("");
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
let outputPath = "./src/generated/data-store-types.ts";
|
|
395
|
-
let schemaOutputPath;
|
|
396
|
-
let emitSchema = true;
|
|
397
|
-
for (let i = 0; i < args.length; i++) {
|
|
398
|
-
if (args[i] === "--connection-string" && args[i + 1]) {
|
|
399
|
-
connectionString = args[++i];
|
|
400
|
-
} else if (args[i] === "--output" && args[i + 1]) {
|
|
401
|
-
outputPath = args[++i];
|
|
402
|
-
} else if (args[i] === "--schema-output" && args[i + 1]) {
|
|
403
|
-
schemaOutputPath = args[++i];
|
|
404
|
-
} else if (args[i] === "--no-schema") {
|
|
405
|
-
emitSchema = false;
|
|
406
|
-
} else if (args[i] === "--help") {
|
|
407
|
-
console.log(`Usage: stardeck-data-store generate-types [options]
|
|
408
|
-
|
|
409
|
-
Options:
|
|
410
|
-
--connection-string <url> Postgres connection string (default: DATA_STORE_URL env var)
|
|
411
|
-
--output <path> Output file path (default: ./src/generated/data-store-types.ts)
|
|
412
|
-
--schema-output <path> DDL snapshot path (default: data-store-schema.sql next to --output)
|
|
413
|
-
--no-schema Skip the DDL snapshot used by the test harness
|
|
414
|
-
--help Show this help message`);
|
|
415
|
-
process.exit(0);
|
|
551
|
+
if (moduleSlices) {
|
|
552
|
+
const trimmed = moduleSlices.trimEnd();
|
|
553
|
+
if (trimmed.length > 0) {
|
|
554
|
+
lines.push(trimmed);
|
|
555
|
+
lines.push("");
|
|
416
556
|
}
|
|
417
557
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
558
|
+
return lines.join("\n");
|
|
559
|
+
}
|
|
560
|
+
async function runGenerateTypes(options) {
|
|
561
|
+
const {
|
|
562
|
+
connectionString,
|
|
563
|
+
outputPath,
|
|
564
|
+
schemaOutputPath,
|
|
565
|
+
emitSchema = true,
|
|
566
|
+
modulesDir,
|
|
567
|
+
noModules,
|
|
568
|
+
introspect = introspectSchema,
|
|
569
|
+
warn = (message) => console.warn(message)
|
|
570
|
+
} = options;
|
|
424
571
|
console.log("Introspecting database schema...");
|
|
425
|
-
const tables = await
|
|
572
|
+
const tables = await introspect(connectionString);
|
|
426
573
|
if (tables.size === 0) {
|
|
427
574
|
console.log("No tables found in database.");
|
|
428
575
|
return;
|
|
429
576
|
}
|
|
430
577
|
console.log(`Found ${tables.size} table(s): ${Array.from(tables.keys()).join(", ")}`);
|
|
431
|
-
|
|
578
|
+
let moduleSlices = "";
|
|
579
|
+
if (shouldLoadModuleManifests(noModules)) {
|
|
580
|
+
const manifests = discoverModuleManifests(modulesDir, warn);
|
|
581
|
+
if (manifests.length > 0) {
|
|
582
|
+
moduleSlices = emitModuleDbSlices(new Set(tables.keys()), manifests, warn);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
const typeScript = generateTypeScript(tables, moduleSlices || void 0);
|
|
432
586
|
const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
|
|
433
587
|
if (dir) {
|
|
434
588
|
const { mkdirSync } = await import("fs");
|
|
435
589
|
mkdirSync(dir, { recursive: true });
|
|
436
590
|
}
|
|
437
|
-
(0,
|
|
591
|
+
(0, import_fs2.writeFileSync)(outputPath, typeScript, "utf-8");
|
|
438
592
|
console.log(`Types written to ${outputPath}`);
|
|
439
593
|
if (emitSchema) {
|
|
440
594
|
const schemaPath = schemaOutputPath ?? `${dir ? `${dir}/` : ""}data-store-schema.sql`;
|
|
441
595
|
const pool = new import_serverless.Pool({ connectionString });
|
|
442
596
|
try {
|
|
443
597
|
const snapshot = await introspectSchemaSnapshot(pool);
|
|
444
|
-
(0,
|
|
598
|
+
(0, import_fs2.writeFileSync)(schemaPath, generateSchemaSql(snapshot), "utf-8");
|
|
445
599
|
console.log(`Schema snapshot written to ${schemaPath} (used by the test harness)`);
|
|
446
600
|
} finally {
|
|
447
601
|
await pool.end();
|
|
448
602
|
}
|
|
449
603
|
}
|
|
450
604
|
}
|
|
451
|
-
main(
|
|
452
|
-
|
|
453
|
-
|
|
605
|
+
async function main(argv = process.argv.slice(2)) {
|
|
606
|
+
const parsed = parseCliArgs(argv);
|
|
607
|
+
if (parsed.help) {
|
|
608
|
+
console.log(`Usage: stardeck-data-store generate-types [options]
|
|
609
|
+
|
|
610
|
+
Options:
|
|
611
|
+
--connection-string <url> Postgres connection string (default: DATA_STORE_URL env var)
|
|
612
|
+
--output <path> Output file path (default: ./src/generated/data-store-types.ts)
|
|
613
|
+
--schema-output <path> DDL snapshot path (default: data-store-schema.sql next to --output)
|
|
614
|
+
--no-schema Skip the DDL snapshot used by the test harness
|
|
615
|
+
--modules-dir <path> Directory containing module folders (default: ./src/modules)
|
|
616
|
+
--no-modules Skip per-module DB slice generation
|
|
617
|
+
--help Show this help message`);
|
|
618
|
+
process.exit(0);
|
|
619
|
+
}
|
|
620
|
+
if (!parsed.connectionString) {
|
|
621
|
+
console.error(
|
|
622
|
+
"Error: No connection string provided. Set DATA_STORE_URL or use --connection-string."
|
|
623
|
+
);
|
|
624
|
+
process.exit(1);
|
|
625
|
+
}
|
|
626
|
+
await runGenerateTypes({
|
|
627
|
+
connectionString: parsed.connectionString,
|
|
628
|
+
outputPath: parsed.outputPath,
|
|
629
|
+
schemaOutputPath: parsed.schemaOutputPath,
|
|
630
|
+
emitSchema: parsed.emitSchema,
|
|
631
|
+
modulesDir: parsed.modulesDir,
|
|
632
|
+
noModules: parsed.noModules
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
function isDirectCliRun() {
|
|
636
|
+
const entry = process.argv[1];
|
|
637
|
+
if (!entry) return false;
|
|
638
|
+
try {
|
|
639
|
+
return import_meta.url === (0, import_url.pathToFileURL)((0, import_path2.resolve)(entry)).href;
|
|
640
|
+
} catch {
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if (isDirectCliRun()) {
|
|
645
|
+
main().catch((error) => {
|
|
646
|
+
console.error("Failed to generate types:", error);
|
|
647
|
+
process.exit(1);
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
651
|
+
0 && (module.exports = {
|
|
652
|
+
PG_TYPE_MAP,
|
|
653
|
+
generateTypeScript,
|
|
654
|
+
introspectSchema,
|
|
655
|
+
main,
|
|
656
|
+
parseCliArgs,
|
|
657
|
+
pgTypeToTs,
|
|
658
|
+
runGenerateTypes,
|
|
659
|
+
toPascalCase
|
|
454
660
|
});
|
|
@@ -2,8 +2,116 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli/generate-types.ts
|
|
4
4
|
import { writeFileSync } from "fs";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
5
7
|
import { Pool } from "@neondatabase/serverless";
|
|
6
8
|
|
|
9
|
+
// src/cli/module-type-slices.ts
|
|
10
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
function toPascalCase(name) {
|
|
13
|
+
return name.split(/[_-]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
14
|
+
}
|
|
15
|
+
function parseModuleManifestSlice(content, sourceLabel, warn) {
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = JSON.parse(content);
|
|
19
|
+
} catch {
|
|
20
|
+
warn(`Skipping malformed module manifest at ${sourceLabel}`);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
24
|
+
warn(`Skipping malformed module manifest at ${sourceLabel}`);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const record = parsed;
|
|
28
|
+
const name = record.name;
|
|
29
|
+
const tables = record.tables;
|
|
30
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
31
|
+
warn(`Skipping module manifest at ${sourceLabel}: missing or invalid "name"`);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (!Array.isArray(tables) || tables.length === 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const tableNames = [];
|
|
38
|
+
for (const entry of tables) {
|
|
39
|
+
if (typeof entry !== "string" || entry.length === 0) {
|
|
40
|
+
warn(`Skipping module manifest at ${sourceLabel}: invalid "tables" entry`);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
tableNames.push(entry);
|
|
44
|
+
}
|
|
45
|
+
return { name, tables: tableNames };
|
|
46
|
+
}
|
|
47
|
+
function discoverModuleManifests(modulesDir, warn) {
|
|
48
|
+
if (!existsSync(modulesDir)) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
let entries;
|
|
52
|
+
try {
|
|
53
|
+
entries = readdirSync(modulesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
54
|
+
} catch {
|
|
55
|
+
warn(`Could not read modules directory: ${modulesDir}`);
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
const manifests = [];
|
|
59
|
+
for (const dirName of entries) {
|
|
60
|
+
const manifestPath = join(modulesDir, dirName, "module.json");
|
|
61
|
+
if (!existsSync(manifestPath)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
let content;
|
|
65
|
+
try {
|
|
66
|
+
content = readFileSync(manifestPath, "utf-8");
|
|
67
|
+
} catch {
|
|
68
|
+
warn(`Skipping unreadable module manifest at ${manifestPath}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const slice = parseModuleManifestSlice(content, manifestPath, warn);
|
|
72
|
+
if (slice) {
|
|
73
|
+
manifests.push(slice);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return manifests.sort((a, b) => a.name.localeCompare(b.name));
|
|
77
|
+
}
|
|
78
|
+
function shouldLoadModuleManifests(noModules) {
|
|
79
|
+
return !noModules;
|
|
80
|
+
}
|
|
81
|
+
function emitModuleDbSlices(schemaTableNames, manifests, warn) {
|
|
82
|
+
const lines = [];
|
|
83
|
+
const moduleMapEntries = [];
|
|
84
|
+
for (const manifest of manifests) {
|
|
85
|
+
const presentTables = manifest.tables.filter((t) => schemaTableNames.has(t));
|
|
86
|
+
const absentTables = manifest.tables.filter((t) => !schemaTableNames.has(t));
|
|
87
|
+
if (absentTables.length > 0) {
|
|
88
|
+
warn(`Module "${manifest.name}": table(s) not in database: ${absentTables.join(", ")}`);
|
|
89
|
+
}
|
|
90
|
+
if (presentTables.length === 0) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const interfaceName = `${toPascalCase(manifest.name)}ModuleDB`;
|
|
94
|
+
lines.push(`export interface ${interfaceName} {`);
|
|
95
|
+
for (const tableName of presentTables) {
|
|
96
|
+
const tableTypeName = `${toPascalCase(tableName)}Table`;
|
|
97
|
+
lines.push(` ${tableName}: ${tableTypeName};`);
|
|
98
|
+
}
|
|
99
|
+
lines.push("}");
|
|
100
|
+
lines.push("");
|
|
101
|
+
moduleMapEntries.push({ name: manifest.name, interfaceName });
|
|
102
|
+
}
|
|
103
|
+
if (moduleMapEntries.length === 0) {
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
lines.push("export interface ModuleDBs {");
|
|
107
|
+
for (const { name, interfaceName } of moduleMapEntries) {
|
|
108
|
+
lines.push(` "${name}": ${interfaceName};`);
|
|
109
|
+
}
|
|
110
|
+
lines.push("}");
|
|
111
|
+
lines.push("");
|
|
112
|
+
return lines.join("\n");
|
|
113
|
+
}
|
|
114
|
+
|
|
7
115
|
// src/cli/schema-ddl.ts
|
|
8
116
|
function quoteIdent(name) {
|
|
9
117
|
return `"${name.replace(/"/g, '""')}"`;
|
|
@@ -292,8 +400,42 @@ var PG_TYPE_MAP = {
|
|
|
292
400
|
function pgTypeToTs(pgType) {
|
|
293
401
|
return PG_TYPE_MAP[pgType] ?? "unknown";
|
|
294
402
|
}
|
|
295
|
-
function
|
|
296
|
-
|
|
403
|
+
function parseCliArgs(argv, options = {}) {
|
|
404
|
+
const cwd = options.cwd ?? process.cwd();
|
|
405
|
+
const env = options.env ?? process.env;
|
|
406
|
+
let connectionString = env.DATA_STORE_URL;
|
|
407
|
+
let outputPath = "./src/generated/data-store-types.ts";
|
|
408
|
+
let schemaOutputPath;
|
|
409
|
+
let emitSchema = true;
|
|
410
|
+
let modulesDir = resolve(cwd, "./src/modules");
|
|
411
|
+
let noModules = false;
|
|
412
|
+
let help = false;
|
|
413
|
+
for (let i = 0; i < argv.length; i++) {
|
|
414
|
+
if (argv[i] === "--connection-string" && argv[i + 1]) {
|
|
415
|
+
connectionString = argv[++i];
|
|
416
|
+
} else if (argv[i] === "--output" && argv[i + 1]) {
|
|
417
|
+
outputPath = argv[++i];
|
|
418
|
+
} else if (argv[i] === "--schema-output" && argv[i + 1]) {
|
|
419
|
+
schemaOutputPath = argv[++i];
|
|
420
|
+
} else if (argv[i] === "--no-schema") {
|
|
421
|
+
emitSchema = false;
|
|
422
|
+
} else if (argv[i] === "--modules-dir" && argv[i + 1]) {
|
|
423
|
+
modulesDir = resolve(cwd, argv[++i]);
|
|
424
|
+
} else if (argv[i] === "--no-modules") {
|
|
425
|
+
noModules = true;
|
|
426
|
+
} else if (argv[i] === "--help") {
|
|
427
|
+
help = true;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
connectionString,
|
|
432
|
+
outputPath,
|
|
433
|
+
schemaOutputPath,
|
|
434
|
+
emitSchema,
|
|
435
|
+
modulesDir,
|
|
436
|
+
noModules,
|
|
437
|
+
help
|
|
438
|
+
};
|
|
297
439
|
}
|
|
298
440
|
async function introspectSchema(connectionString) {
|
|
299
441
|
const pool = new Pool({ connectionString });
|
|
@@ -325,7 +467,7 @@ async function introspectSchema(connectionString) {
|
|
|
325
467
|
await pool.end();
|
|
326
468
|
}
|
|
327
469
|
}
|
|
328
|
-
function generateTypeScript(tables) {
|
|
470
|
+
function generateTypeScript(tables, moduleSlices) {
|
|
329
471
|
const lines = [
|
|
330
472
|
"// AUTO-GENERATED by @stardeck-customer-apps/data-store-sdk \u2014 DO NOT EDIT.",
|
|
331
473
|
"// This file is overwritten in full on every run of:",
|
|
@@ -333,6 +475,8 @@ function generateTypeScript(tables) {
|
|
|
333
475
|
"// Anything you add here (hand-written or derived types) WILL BE LOST on the next",
|
|
334
476
|
"// regeneration. Put those in a separate file that imports from this one, e.g.",
|
|
335
477
|
"// src/lib/<domain>-types.ts importing the table types or DB from this file.",
|
|
478
|
+
"// When module manifests are discoverable, per-module DB slices and ModuleDBs are",
|
|
479
|
+
"// appended below the flat DB interface.",
|
|
336
480
|
"",
|
|
337
481
|
'import type { Generated } from "kysely";',
|
|
338
482
|
""
|
|
@@ -363,49 +507,41 @@ function generateTypeScript(tables) {
|
|
|
363
507
|
}
|
|
364
508
|
lines.push("}");
|
|
365
509
|
lines.push("");
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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);
|
|
510
|
+
if (moduleSlices) {
|
|
511
|
+
const trimmed = moduleSlices.trimEnd();
|
|
512
|
+
if (trimmed.length > 0) {
|
|
513
|
+
lines.push(trimmed);
|
|
514
|
+
lines.push("");
|
|
393
515
|
}
|
|
394
516
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
517
|
+
return lines.join("\n");
|
|
518
|
+
}
|
|
519
|
+
async function runGenerateTypes(options) {
|
|
520
|
+
const {
|
|
521
|
+
connectionString,
|
|
522
|
+
outputPath,
|
|
523
|
+
schemaOutputPath,
|
|
524
|
+
emitSchema = true,
|
|
525
|
+
modulesDir,
|
|
526
|
+
noModules,
|
|
527
|
+
introspect = introspectSchema,
|
|
528
|
+
warn = (message) => console.warn(message)
|
|
529
|
+
} = options;
|
|
401
530
|
console.log("Introspecting database schema...");
|
|
402
|
-
const tables = await
|
|
531
|
+
const tables = await introspect(connectionString);
|
|
403
532
|
if (tables.size === 0) {
|
|
404
533
|
console.log("No tables found in database.");
|
|
405
534
|
return;
|
|
406
535
|
}
|
|
407
536
|
console.log(`Found ${tables.size} table(s): ${Array.from(tables.keys()).join(", ")}`);
|
|
408
|
-
|
|
537
|
+
let moduleSlices = "";
|
|
538
|
+
if (shouldLoadModuleManifests(noModules)) {
|
|
539
|
+
const manifests = discoverModuleManifests(modulesDir, warn);
|
|
540
|
+
if (manifests.length > 0) {
|
|
541
|
+
moduleSlices = emitModuleDbSlices(new Set(tables.keys()), manifests, warn);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
const typeScript = generateTypeScript(tables, moduleSlices || void 0);
|
|
409
545
|
const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
|
|
410
546
|
if (dir) {
|
|
411
547
|
const { mkdirSync } = await import("fs");
|
|
@@ -425,7 +561,58 @@ Options:
|
|
|
425
561
|
}
|
|
426
562
|
}
|
|
427
563
|
}
|
|
428
|
-
main(
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
564
|
+
async function main(argv = process.argv.slice(2)) {
|
|
565
|
+
const parsed = parseCliArgs(argv);
|
|
566
|
+
if (parsed.help) {
|
|
567
|
+
console.log(`Usage: stardeck-data-store generate-types [options]
|
|
568
|
+
|
|
569
|
+
Options:
|
|
570
|
+
--connection-string <url> Postgres connection string (default: DATA_STORE_URL env var)
|
|
571
|
+
--output <path> Output file path (default: ./src/generated/data-store-types.ts)
|
|
572
|
+
--schema-output <path> DDL snapshot path (default: data-store-schema.sql next to --output)
|
|
573
|
+
--no-schema Skip the DDL snapshot used by the test harness
|
|
574
|
+
--modules-dir <path> Directory containing module folders (default: ./src/modules)
|
|
575
|
+
--no-modules Skip per-module DB slice generation
|
|
576
|
+
--help Show this help message`);
|
|
577
|
+
process.exit(0);
|
|
578
|
+
}
|
|
579
|
+
if (!parsed.connectionString) {
|
|
580
|
+
console.error(
|
|
581
|
+
"Error: No connection string provided. Set DATA_STORE_URL or use --connection-string."
|
|
582
|
+
);
|
|
583
|
+
process.exit(1);
|
|
584
|
+
}
|
|
585
|
+
await runGenerateTypes({
|
|
586
|
+
connectionString: parsed.connectionString,
|
|
587
|
+
outputPath: parsed.outputPath,
|
|
588
|
+
schemaOutputPath: parsed.schemaOutputPath,
|
|
589
|
+
emitSchema: parsed.emitSchema,
|
|
590
|
+
modulesDir: parsed.modulesDir,
|
|
591
|
+
noModules: parsed.noModules
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
function isDirectCliRun() {
|
|
595
|
+
const entry = process.argv[1];
|
|
596
|
+
if (!entry) return false;
|
|
597
|
+
try {
|
|
598
|
+
return import.meta.url === pathToFileURL(resolve(entry)).href;
|
|
599
|
+
} catch {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (isDirectCliRun()) {
|
|
604
|
+
main().catch((error) => {
|
|
605
|
+
console.error("Failed to generate types:", error);
|
|
606
|
+
process.exit(1);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
export {
|
|
610
|
+
PG_TYPE_MAP,
|
|
611
|
+
generateTypeScript,
|
|
612
|
+
introspectSchema,
|
|
613
|
+
main,
|
|
614
|
+
parseCliArgs,
|
|
615
|
+
pgTypeToTs,
|
|
616
|
+
runGenerateTypes,
|
|
617
|
+
toPascalCase
|
|
618
|
+
};
|
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
|
/**
|
|
@@ -140,7 +140,19 @@ interface DataStoreManifestEntry {
|
|
|
140
140
|
id: string;
|
|
141
141
|
name: string;
|
|
142
142
|
slug: string;
|
|
143
|
-
|
|
143
|
+
/**
|
|
144
|
+
* App-local handle for this project's connection. When present, the resolver
|
|
145
|
+
* matches it before the org-global slug/name.
|
|
146
|
+
*/
|
|
147
|
+
bindingKey?: string;
|
|
148
|
+
/**
|
|
149
|
+
* Store kind. `database` stores carry a `url`; `storage` stores do not (access
|
|
150
|
+
* is API-mediated by store id). Absent on manifests emitted before this field
|
|
151
|
+
* existed — treat absent as `database`.
|
|
152
|
+
*/
|
|
153
|
+
storeType?: "database" | "storage";
|
|
154
|
+
/** Connection string. Present for `database` stores only. */
|
|
155
|
+
url?: string;
|
|
144
156
|
accessLevel: "read" | "write" | "admin";
|
|
145
157
|
}
|
|
146
158
|
/**
|
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
|
/**
|
|
@@ -140,7 +140,19 @@ interface DataStoreManifestEntry {
|
|
|
140
140
|
id: string;
|
|
141
141
|
name: string;
|
|
142
142
|
slug: string;
|
|
143
|
-
|
|
143
|
+
/**
|
|
144
|
+
* App-local handle for this project's connection. When present, the resolver
|
|
145
|
+
* matches it before the org-global slug/name.
|
|
146
|
+
*/
|
|
147
|
+
bindingKey?: string;
|
|
148
|
+
/**
|
|
149
|
+
* Store kind. `database` stores carry a `url`; `storage` stores do not (access
|
|
150
|
+
* is API-mediated by store id). Absent on manifests emitted before this field
|
|
151
|
+
* existed — treat absent as `database`.
|
|
152
|
+
*/
|
|
153
|
+
storeType?: "database" | "storage";
|
|
154
|
+
/** Connection string. Present for `database` stores only. */
|
|
155
|
+
url?: string;
|
|
144
156
|
accessLevel: "read" | "write" | "admin";
|
|
145
157
|
}
|
|
146
158
|
/**
|
package/dist/server/index.js
CHANGED
|
@@ -94,9 +94,14 @@ function readDataStoreManifest() {
|
|
|
94
94
|
try {
|
|
95
95
|
const parsed = JSON.parse(raw);
|
|
96
96
|
if (!Array.isArray(parsed)) return [];
|
|
97
|
-
return parsed.filter(
|
|
98
|
-
(
|
|
99
|
-
|
|
97
|
+
return parsed.filter((e) => {
|
|
98
|
+
if (typeof e !== "object" || e === null) return false;
|
|
99
|
+
const entry = e;
|
|
100
|
+
if (typeof entry.id !== "string" || typeof entry.name !== "string" || typeof entry.slug !== "string") {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return entry.storeType === "storage" || typeof entry.url === "string";
|
|
104
|
+
});
|
|
100
105
|
} catch {
|
|
101
106
|
return [];
|
|
102
107
|
}
|
|
@@ -111,9 +116,13 @@ function resolveManifestEntry(entries, ref) {
|
|
|
111
116
|
}
|
|
112
117
|
if (ref.storeName) {
|
|
113
118
|
const target = ref.storeName;
|
|
119
|
+
const byBindingKey = entries.find((e) => e.bindingKey === target);
|
|
120
|
+
if (byBindingKey) return byBindingKey;
|
|
114
121
|
const exact = entries.find((e) => e.slug === target || e.id === target || e.name === target);
|
|
115
122
|
if (exact) return exact;
|
|
116
123
|
const norm = dataStoreSlug(target);
|
|
124
|
+
const byBindingKeyNorm = entries.find((e) => e.bindingKey === norm);
|
|
125
|
+
if (byBindingKeyNorm) return byBindingKeyNorm;
|
|
117
126
|
return entries.find(
|
|
118
127
|
(e) => e.slug === norm || typeof e.name === "string" && dataStoreSlug(e.name) === norm
|
|
119
128
|
);
|
|
@@ -324,6 +333,12 @@ async function createDataStore(options) {
|
|
|
324
333
|
if (!connectionString) {
|
|
325
334
|
const name = options?.storeName ?? options?.storeId;
|
|
326
335
|
const ref = name ? `data store "${name}"` : "a data store";
|
|
336
|
+
const resolved = options?.storeId || options?.storeName ? resolveDataStore({ storeId: options?.storeId, storeName: options?.storeName }) : void 0;
|
|
337
|
+
if (resolved?.storeType === "storage") {
|
|
338
|
+
throw new Error(
|
|
339
|
+
`${ref} is a storage store and has no SQL connection. Use DataStoreClient for storage operations (upload/download/list) instead of createDataStore.`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
327
342
|
throw new Error(
|
|
328
343
|
`Could not resolve a connection string for ${ref}. Checked the STARDECK_DATA_STORES manifest, the legacy DATA_STORE_*_URL env var, and DATA_STORE_URL. Confirm the store is connected to this project, or pass connectionString in options.`
|
|
329
344
|
);
|
package/dist/server/index.mjs
CHANGED
|
@@ -41,9 +41,14 @@ function readDataStoreManifest() {
|
|
|
41
41
|
try {
|
|
42
42
|
const parsed = JSON.parse(raw);
|
|
43
43
|
if (!Array.isArray(parsed)) return [];
|
|
44
|
-
return parsed.filter(
|
|
45
|
-
(
|
|
46
|
-
|
|
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
|
+
});
|
|
47
52
|
} catch {
|
|
48
53
|
return [];
|
|
49
54
|
}
|
|
@@ -58,9 +63,13 @@ function resolveManifestEntry(entries, ref) {
|
|
|
58
63
|
}
|
|
59
64
|
if (ref.storeName) {
|
|
60
65
|
const target = ref.storeName;
|
|
66
|
+
const byBindingKey = entries.find((e) => e.bindingKey === target);
|
|
67
|
+
if (byBindingKey) return byBindingKey;
|
|
61
68
|
const exact = entries.find((e) => e.slug === target || e.id === target || e.name === target);
|
|
62
69
|
if (exact) return exact;
|
|
63
70
|
const norm = dataStoreSlug(target);
|
|
71
|
+
const byBindingKeyNorm = entries.find((e) => e.bindingKey === norm);
|
|
72
|
+
if (byBindingKeyNorm) return byBindingKeyNorm;
|
|
64
73
|
return entries.find(
|
|
65
74
|
(e) => e.slug === norm || typeof e.name === "string" && dataStoreSlug(e.name) === norm
|
|
66
75
|
);
|
|
@@ -271,6 +280,12 @@ async function createDataStore(options) {
|
|
|
271
280
|
if (!connectionString) {
|
|
272
281
|
const name = options?.storeName ?? options?.storeId;
|
|
273
282
|
const ref = name ? `data store "${name}"` : "a data store";
|
|
283
|
+
const resolved = options?.storeId || options?.storeName ? resolveDataStore({ storeId: options?.storeId, storeName: options?.storeName }) : void 0;
|
|
284
|
+
if (resolved?.storeType === "storage") {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`${ref} is a storage store and has no SQL connection. Use DataStoreClient for storage operations (upload/download/list) instead of createDataStore.`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
274
289
|
throw new Error(
|
|
275
290
|
`Could not resolve a connection string for ${ref}. Checked the STARDECK_DATA_STORES manifest, the legacy DATA_STORE_*_URL env var, and DATA_STORE_URL. Confirm the store is connected to this project, or pass connectionString in options.`
|
|
276
291
|
);
|
|
@@ -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 };
|