@ankhorage/infra 0.0.0 → 0.1.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/datasets.js DELETED
@@ -1,397 +0,0 @@
1
- const DEFAULT_DATASET_OPERATIONS = [
2
- 'create',
3
- 'delete',
4
- 'list',
5
- 'read',
6
- 'update',
7
- ];
8
- const DEFAULT_DATABASE_SCHEMA = 'public';
9
- export function generateDatasetInfrastructureArtifacts(args) {
10
- const datasets = listDatasets(args.data);
11
- if (datasets.length === 0)
12
- return { files: [], warnings: [] };
13
- if (args.databaseProvider !== undefined && args.databaseProvider !== 'supabase') {
14
- return {
15
- files: [],
16
- warnings: [
17
- `Dataset generation is only implemented for the supabase database provider; received ${args.databaseProvider}.`,
18
- ],
19
- };
20
- }
21
- const seedManifest = createSeedManifest(datasets);
22
- return {
23
- files: [
24
- {
25
- path: 'infra/minikube/db/datasets.json',
26
- content: `${JSON.stringify(datasets.map((dataset) => dataset.collection), null, 2)}\n`,
27
- },
28
- {
29
- path: 'infra/minikube/db/dataset-seed.json',
30
- content: `${JSON.stringify(seedManifest, null, 2)}\n`,
31
- },
32
- {
33
- path: 'infra/minikube/db/migrations/001_datasets.sql',
34
- content: generateSupabaseMigrationSql(datasets),
35
- },
36
- {
37
- path: 'infra/minikube/db/seeds/001_dataset_seed.sql',
38
- content: generateSupabaseSeedSql(seedManifest),
39
- },
40
- {
41
- path: 'infra/minikube/db/README.md',
42
- content: generateReadme(datasets),
43
- },
44
- {
45
- path: 'src/generated/datasets/collections.ts',
46
- content: generateCollectionsTs(datasets),
47
- },
48
- {
49
- path: 'src/generated/datasets/appDatasetApi.ts',
50
- content: generateApiTs(datasets),
51
- },
52
- {
53
- path: 'src/generated/datasets/datasetApiHandlers.ts',
54
- content: generateApiHandlersTs(datasets, seedManifest),
55
- },
56
- {
57
- path: 'src/generated/datasets/openapi.json',
58
- content: `${JSON.stringify(createOpenApi(datasets), null, 2)}\n`,
59
- },
60
- ],
61
- warnings: createSeedWarnings(datasets),
62
- };
63
- }
64
- function listDatasets(data) {
65
- return Object.values(data?.datasets ?? {}).sort((left, right) => left.id.localeCompare(right.id));
66
- }
67
- function createSeedManifest(datasets) {
68
- return datasets.map((dataset) => ({
69
- datasetId: dataset.id,
70
- collection: dataset.collection,
71
- records: materializeSeedRecords(dataset),
72
- }));
73
- }
74
- function materializeSeedRecords(dataset) {
75
- const { primaryKey } = dataset.collection;
76
- const primaryKeyField = findPrimaryKeyField(dataset);
77
- return (dataset.seed ?? []).map((record, index) => {
78
- if (primaryKey === undefined || record[primaryKey] !== undefined) {
79
- return record;
80
- }
81
- if (primaryKeyField?.type !== 'uuid') {
82
- return record;
83
- }
84
- return {
85
- ...record,
86
- [primaryKey]: createDeterministicSeedUuid(dataset.id, index),
87
- };
88
- });
89
- }
90
- function createSeedWarnings(datasets) {
91
- return datasets.flatMap((dataset) => {
92
- const { primaryKey } = dataset.collection;
93
- const seed = dataset.seed ?? [];
94
- const primaryKeyField = findPrimaryKeyField(dataset);
95
- if (primaryKey === undefined || primaryKeyField?.type === 'uuid') {
96
- return [];
97
- }
98
- const hasMissingPrimaryKey = seed.some((record) => record[primaryKey] === undefined);
99
- if (!hasMissingPrimaryKey) {
100
- return [];
101
- }
102
- return [
103
- `Dataset ${dataset.id} has seed records without primary key ${primaryKey}; seed SQL cannot fully guarantee idempotence for those records.`,
104
- ];
105
- });
106
- }
107
- function generateReadme(datasets) {
108
- const rows = datasets
109
- .map((dataset) => {
110
- const seedCount = dataset.seed?.length ?? 0;
111
- return `- \`${dataset.id}\` maps to collection \`${formatCollectionName(dataset)}\` with ${dataset.collection.fields.length} fields and ${seedCount} starter records.`;
112
- })
113
- .join('\n');
114
- return `# Generated Dataset Artifacts\n\nThese files are generated from \`ankh.config.json\` -> \`data.datasets\`.\n\n## Datasets\n\n${rows}\n\n## Files\n\n- \`datasets.json\` stores provider-neutral collection definitions for database adapters.\n- \`dataset-seed.json\` stores starter records configured in Studio, with deterministic UUID primary keys added when needed.\n- \`migrations/001_datasets.sql\` creates Supabase/Postgres schemas and tables from the dataset collection definitions.\n- \`seeds/001_dataset_seed.sql\` inserts starter records using idempotent conflict handling where possible.\n- \`src/generated/datasets/appDatasetApi.ts\` exposes bindable dataset operation metadata.\n- \`src/generated/datasets/datasetApiHandlers.ts\` exposes app-owned CRUD handler helpers for generated dataset routes.\n- \`src/generated/datasets/openapi.json\` exposes the generated dataset API surface.\n`;
115
- }
116
- function generateCollectionsTs(datasets) {
117
- return `import type { AppDatasetDefinition } from '@ankhorage/contracts';\n\nexport const GENERATED_DATASETS = ${JSON.stringify(datasets, null, 2)} as const satisfies readonly AppDatasetDefinition[];\n`;
118
- }
119
- function generateApiTs(datasets) {
120
- return `export interface GeneratedDatasetOperation {\n readonly datasetId: string;\n readonly operationId: string;\n readonly intent: 'create' | 'delete' | 'list' | 'read' | 'update';\n readonly method: 'DELETE' | 'GET' | 'PATCH' | 'POST';\n readonly path: string;\n}\n\nexport const GENERATED_DATASET_OPERATIONS = ${JSON.stringify(datasets.flatMap(createDatasetOperations), null, 2)} as const satisfies readonly GeneratedDatasetOperation[];\n`;
121
- }
122
- function generateApiHandlersTs(datasets, seedManifest) {
123
- return `export type GeneratedDatasetRecord = Readonly<Record<string, unknown>>;\n\nexport interface GeneratedDatasetSeedManifestEntry {\n readonly datasetId: string;\n readonly records: readonly GeneratedDatasetRecord[];\n}\n\nexport interface GeneratedDatasetStore {\n list(datasetId: string): Promise<readonly GeneratedDatasetRecord[]>;\n read(datasetId: string, id: string): Promise<GeneratedDatasetRecord | null>;\n create(datasetId: string, values: GeneratedDatasetRecord): Promise<GeneratedDatasetRecord>;\n update(datasetId: string, id: string, values: GeneratedDatasetRecord): Promise<GeneratedDatasetRecord | null>;\n delete(datasetId: string, id: string): Promise<GeneratedDatasetRecord | null>;\n}\n\nexport interface GeneratedDatasetRequest {\n readonly datasetId: string;\n readonly id?: string;\n readonly body?: GeneratedDatasetRecord;\n}\n\nexport interface GeneratedDatasetResponse<TData = unknown> {\n readonly status: number;\n readonly data: TData;\n}\n\nconst GENERATED_DATASET_PRIMARY_KEYS = ${JSON.stringify(createPrimaryKeyRegistry(datasets), null, 2)} as const satisfies Readonly<Record<string, string>>;\n\nexport const GENERATED_DATASET_SEED = ${JSON.stringify(seedManifest.map((entry) => ({ datasetId: entry.datasetId, records: entry.records })), null, 2)} as const satisfies readonly GeneratedDatasetSeedManifestEntry[];\n\nexport function isGeneratedDatasetId(datasetId: string): datasetId is Extract<keyof typeof GENERATED_DATASET_PRIMARY_KEYS, string> {\n return Object.prototype.hasOwnProperty.call(GENERATED_DATASET_PRIMARY_KEYS, datasetId);\n}\n\nexport function getGeneratedDatasetPrimaryKey(datasetId: string): string | null {\n if (!isGeneratedDatasetId(datasetId)) {\n return null;\n }\n\n return GENERATED_DATASET_PRIMARY_KEYS[datasetId];\n}\n\nexport function createGeneratedDatasetMemoryStore(\n seed: readonly GeneratedDatasetSeedManifestEntry[] = GENERATED_DATASET_SEED,\n): GeneratedDatasetStore {\n const records = new Map<string, GeneratedDatasetRecord[]>();\n\n for (const datasetId of Object.keys(GENERATED_DATASET_PRIMARY_KEYS)) {\n records.set(datasetId, []);\n }\n\n for (const entry of seed) {\n records.set(entry.datasetId, entry.records.map(cloneRecord));\n }\n\n return {\n async list(datasetId: string): Promise<readonly GeneratedDatasetRecord[]> {\n return getRows(records, datasetId).map(cloneRecord);\n },\n\n async read(datasetId: string, id: string): Promise<GeneratedDatasetRecord | null> {\n const primaryKey = getGeneratedDatasetPrimaryKey(datasetId);\n if (primaryKey === null) return null;\n\n const record = getRows(records, datasetId).find((row) => String(row[primaryKey]) === id);\n return record === undefined ? null : cloneRecord(record);\n },\n\n async create(datasetId: string, values: GeneratedDatasetRecord): Promise<GeneratedDatasetRecord> {\n const primaryKey = getGeneratedDatasetPrimaryKey(datasetId);\n const nextRecord =\n primaryKey === null || values[primaryKey] !== undefined\n ? cloneRecord(values)\n : { ...values, [primaryKey]: createRuntimeRecordId() };\n const nextRows = [...getRows(records, datasetId), nextRecord];\n records.set(datasetId, nextRows);\n return cloneRecord(nextRecord);\n },\n\n async update(\n datasetId: string,\n id: string,\n values: GeneratedDatasetRecord,\n ): Promise<GeneratedDatasetRecord | null> {\n const primaryKey = getGeneratedDatasetPrimaryKey(datasetId);\n if (primaryKey === null) return null;\n\n const rows = getRows(records, datasetId);\n const index = rows.findIndex((row) => String(row[primaryKey]) === id);\n if (index < 0) return null;\n\n const currentRecord = rows[index] ?? {};\n const nextRecord = { ...currentRecord, ...values, [primaryKey]: currentRecord[primaryKey] ?? id };\n const nextRows = rows.map((row, rowIndex) => (rowIndex === index ? nextRecord : row));\n records.set(datasetId, nextRows);\n return cloneRecord(nextRecord);\n },\n\n async delete(datasetId: string, id: string): Promise<GeneratedDatasetRecord | null> {\n const primaryKey = getGeneratedDatasetPrimaryKey(datasetId);\n if (primaryKey === null) return null;\n\n const rows = getRows(records, datasetId);\n const deleted = rows.find((row) => String(row[primaryKey]) === id);\n if (deleted === undefined) return null;\n\n records.set(\n datasetId,\n rows.filter((row) => String(row[primaryKey]) !== id),\n );\n return cloneRecord(deleted);\n },\n };\n}\n\nexport async function handleGeneratedDatasetList(\n store: GeneratedDatasetStore,\n request: GeneratedDatasetRequest,\n): Promise<GeneratedDatasetResponse> {\n if (!isGeneratedDatasetId(request.datasetId)) {\n return notFoundResponse(request.datasetId);\n }\n\n return { status: 200, data: await store.list(request.datasetId) };\n}\n\nexport async function handleGeneratedDatasetRead(\n store: GeneratedDatasetStore,\n request: GeneratedDatasetRequest,\n): Promise<GeneratedDatasetResponse> {\n if (!isGeneratedDatasetId(request.datasetId) || request.id === undefined) {\n return notFoundResponse(request.datasetId);\n }\n\n const record = await store.read(request.datasetId, request.id);\n return record === null ? notFoundResponse(request.datasetId) : { status: 200, data: record };\n}\n\nexport async function handleGeneratedDatasetCreate(\n store: GeneratedDatasetStore,\n request: GeneratedDatasetRequest,\n): Promise<GeneratedDatasetResponse> {\n if (!isGeneratedDatasetId(request.datasetId)) {\n return notFoundResponse(request.datasetId);\n }\n\n return { status: 201, data: await store.create(request.datasetId, request.body ?? {}) };\n}\n\nexport async function handleGeneratedDatasetUpdate(\n store: GeneratedDatasetStore,\n request: GeneratedDatasetRequest,\n): Promise<GeneratedDatasetResponse> {\n if (!isGeneratedDatasetId(request.datasetId) || request.id === undefined) {\n return notFoundResponse(request.datasetId);\n }\n\n const record = await store.update(request.datasetId, request.id, request.body ?? {});\n return record === null ? notFoundResponse(request.datasetId) : { status: 200, data: record };\n}\n\nexport async function handleGeneratedDatasetDelete(\n store: GeneratedDatasetStore,\n request: GeneratedDatasetRequest,\n): Promise<GeneratedDatasetResponse> {\n if (!isGeneratedDatasetId(request.datasetId) || request.id === undefined) {\n return notFoundResponse(request.datasetId);\n }\n\n const record = await store.delete(request.datasetId, request.id);\n return record === null ? notFoundResponse(request.datasetId) : { status: 200, data: record };\n}\n\nfunction getRows(\n records: ReadonlyMap<string, readonly GeneratedDatasetRecord[]>,\n datasetId: string,\n): readonly GeneratedDatasetRecord[] {\n return records.get(datasetId) ?? [];\n}\n\nfunction cloneRecord(record: GeneratedDatasetRecord): GeneratedDatasetRecord {\n return { ...record };\n}\n\nfunction createRuntimeRecordId(): string {\n const randomPart = Math.random().toString(16).slice(2);\n return \`generated-\${Date.now()}-\${randomPart}\`;\n}\n\nfunction notFoundResponse(datasetId: string): GeneratedDatasetResponse {\n return {\n status: 404,\n data: {\n error: \`Unknown dataset or record: \${datasetId}\`,\n },\n };\n}\n`;
124
- }
125
- function createDatasetOperations(dataset) {
126
- const operations = dataset.operations ?? DEFAULT_DATASET_OPERATIONS;
127
- return operations.map((operation) => createDatasetOperation(dataset.id, operation));
128
- }
129
- function createDatasetOperation(datasetId, operation) {
130
- const collectionPath = `/api/datasets/${datasetId}`;
131
- const recordPath = `${collectionPath}/{id}`;
132
- switch (operation) {
133
- case 'create':
134
- return {
135
- datasetId,
136
- operationId: `${datasetId}.create`,
137
- intent: operation,
138
- method: 'POST',
139
- path: collectionPath,
140
- };
141
- case 'delete':
142
- return {
143
- datasetId,
144
- operationId: `${datasetId}.delete`,
145
- intent: operation,
146
- method: 'DELETE',
147
- path: recordPath,
148
- };
149
- case 'list':
150
- return {
151
- datasetId,
152
- operationId: `${datasetId}.list`,
153
- intent: operation,
154
- method: 'GET',
155
- path: collectionPath,
156
- };
157
- case 'read':
158
- return {
159
- datasetId,
160
- operationId: `${datasetId}.read`,
161
- intent: operation,
162
- method: 'GET',
163
- path: recordPath,
164
- };
165
- case 'update':
166
- return {
167
- datasetId,
168
- operationId: `${datasetId}.update`,
169
- intent: operation,
170
- method: 'PATCH',
171
- path: recordPath,
172
- };
173
- }
174
- }
175
- function createOpenApi(datasets) {
176
- const paths = {};
177
- for (const dataset of datasets) {
178
- for (const operation of createDatasetOperations(dataset)) {
179
- addOpenApiOperation(paths, operation, {
180
- operationId: operation.operationId,
181
- summary: `${operation.intent} ${dataset.label ?? dataset.id}`,
182
- responses: {
183
- [operation.intent === 'create' ? '201' : '200']: { description: 'Successful response.' },
184
- },
185
- 'x-ankh-dataset': {
186
- datasetId: dataset.id,
187
- intent: operation.intent,
188
- },
189
- });
190
- }
191
- }
192
- return {
193
- openapi: '3.1.0',
194
- info: {
195
- title: 'Generated app dataset API',
196
- version: '1.0.0',
197
- },
198
- paths,
199
- };
200
- }
201
- function addOpenApiOperation(paths, operation, openApiOperation) {
202
- const path = paths[operation.path] ?? {};
203
- switch (operation.method) {
204
- case 'DELETE':
205
- paths[operation.path] = { ...path, delete: openApiOperation };
206
- return;
207
- case 'GET':
208
- paths[operation.path] = { ...path, get: openApiOperation };
209
- return;
210
- case 'PATCH':
211
- paths[operation.path] = { ...path, patch: openApiOperation };
212
- return;
213
- case 'POST':
214
- paths[operation.path] = { ...path, post: openApiOperation };
215
- return;
216
- }
217
- }
218
- function generateSupabaseMigrationSql(datasets) {
219
- const statements = datasets.flatMap((dataset) => [
220
- `create schema if not exists ${quoteSqlIdentifier(dataset.collection.schema ?? DEFAULT_DATABASE_SCHEMA)};`,
221
- generateCreateTableSql(dataset),
222
- ]);
223
- return [
224
- '-- Generated from ankh.config.json -> data.datasets.',
225
- '-- Safe to re-run: schemas and tables are created with if not exists.',
226
- 'create extension if not exists pgcrypto;',
227
- '',
228
- ...statements,
229
- '',
230
- ].join('\n');
231
- }
232
- function generateCreateTableSql(dataset) {
233
- const schema = quoteSqlIdentifier(dataset.collection.schema ?? DEFAULT_DATABASE_SCHEMA);
234
- const table = quoteSqlIdentifier(dataset.collection.name);
235
- const columns = collectSqlFields(dataset).map((field) => formatSqlColumn(field, field.name === (dataset.collection.primaryKey ?? 'id')));
236
- return `create table if not exists ${schema}.${table} (\n ${columns.join(',\n ')}\n);`;
237
- }
238
- function collectSqlFields(dataset) {
239
- const primaryKey = dataset.collection.primaryKey ?? 'id';
240
- const hasPrimaryKeyField = dataset.collection.fields.some((field) => field.name === primaryKey);
241
- if (hasPrimaryKeyField) {
242
- return dataset.collection.fields;
243
- }
244
- return [
245
- { name: primaryKey, type: 'uuid', required: true, unique: true },
246
- ...dataset.collection.fields,
247
- ];
248
- }
249
- function generateSupabaseSeedSql(seedManifest) {
250
- const inserts = seedManifest.flatMap((entry) => generateSeedInsertStatements(entry));
251
- return [
252
- '-- Generated from ankh.config.json -> data.datasets seed records.',
253
- '-- Safe to re-run when seed records have stable primary keys or unique constraints.',
254
- ...(inserts.length === 0 ? ['-- No dataset seed records configured.'] : inserts),
255
- '',
256
- ].join('\n');
257
- }
258
- function generateSeedInsertStatements(entry) {
259
- if (entry.records.length === 0) {
260
- return [];
261
- }
262
- const { primaryKey } = entry.collection;
263
- const columns = collectSeedColumns(entry);
264
- if (columns.length === 0) {
265
- return [];
266
- }
267
- const schema = quoteSqlIdentifier(entry.collection.schema ?? DEFAULT_DATABASE_SCHEMA);
268
- const table = quoteSqlIdentifier(entry.collection.name);
269
- const quotedColumns = columns.map(quoteSqlIdentifier).join(', ');
270
- const values = entry.records
271
- .map((record) => `(${columns.map((column) => formatSqlValue(record[column])).join(', ')})`)
272
- .join(',\n ');
273
- const conflict = primaryKey === undefined
274
- ? 'on conflict do nothing'
275
- : `on conflict (${quoteSqlIdentifier(primaryKey)}) do nothing`;
276
- return [`insert into ${schema}.${table} (${quotedColumns}) values\n ${values}\n${conflict};`];
277
- }
278
- function collectSeedColumns(entry) {
279
- const columns = new Set();
280
- if (entry.collection.primaryKey !== undefined) {
281
- columns.add(entry.collection.primaryKey);
282
- }
283
- for (const field of entry.collection.fields) {
284
- if (entry.records.some((record) => record[field.name] !== undefined)) {
285
- columns.add(field.name);
286
- }
287
- }
288
- const fieldNames = new Set(entry.collection.fields.map((field) => field.name));
289
- const extraColumns = entry.records
290
- .flatMap((record) => Object.keys(record))
291
- .filter((column) => !fieldNames.has(column) && column !== entry.collection.primaryKey)
292
- .sort();
293
- for (const column of extraColumns) {
294
- columns.add(column);
295
- }
296
- return [...columns];
297
- }
298
- function formatSqlColumn(field, primaryKey) {
299
- const defaultValue = formatSqlDefaultValue(field, primaryKey);
300
- const primaryKeyClause = primaryKey ? ' primary key' : '';
301
- const required = field.required === true && !primaryKey ? ' not null' : '';
302
- const unique = field.unique === true && !primaryKey ? ' unique' : '';
303
- return `${quoteSqlIdentifier(field.name)} ${mapSqlFieldType(field.type)}${defaultValue}${primaryKeyClause}${required}${unique}`;
304
- }
305
- function mapSqlFieldType(type) {
306
- switch (type) {
307
- case 'text':
308
- return 'text';
309
- case 'number':
310
- return 'double precision';
311
- case 'boolean':
312
- return 'boolean';
313
- case 'datetime':
314
- return 'timestamptz';
315
- case 'json':
316
- return 'jsonb';
317
- case 'uuid':
318
- return 'uuid';
319
- }
320
- }
321
- function formatSqlDefaultValue(field, primaryKey) {
322
- if (primaryKey && field.type === 'uuid' && field.defaultValue === undefined) {
323
- return ' default gen_random_uuid()';
324
- }
325
- if (field.defaultValue === undefined) {
326
- return '';
327
- }
328
- if (field.defaultValue === null) {
329
- return ' default null';
330
- }
331
- if (typeof field.defaultValue === 'string') {
332
- return ` default '${escapeSqlString(field.defaultValue)}'`;
333
- }
334
- return ` default ${String(field.defaultValue)}`;
335
- }
336
- function formatSqlValue(value) {
337
- if (value === null || value === undefined) {
338
- return 'null';
339
- }
340
- if (typeof value === 'string') {
341
- return `'${escapeSqlString(value)}'`;
342
- }
343
- if (typeof value === 'number') {
344
- return Number.isFinite(value) ? String(value) : 'null';
345
- }
346
- if (typeof value === 'boolean') {
347
- return value ? 'true' : 'false';
348
- }
349
- if (typeof value === 'bigint') {
350
- return String(value);
351
- }
352
- return `'${escapeSqlString(stringifyJsonValue(value))}'::jsonb`;
353
- }
354
- function stringifyJsonValue(value) {
355
- try {
356
- return JSON.stringify(value);
357
- }
358
- catch {
359
- return 'null';
360
- }
361
- }
362
- function quoteSqlIdentifier(identifier) {
363
- return `"${identifier.replaceAll('"', '""')}"`;
364
- }
365
- function escapeSqlString(value) {
366
- return value.replaceAll("'", "''");
367
- }
368
- function createPrimaryKeyRegistry(datasets) {
369
- return Object.fromEntries(datasets.map((dataset) => [dataset.id, dataset.collection.primaryKey ?? 'id']));
370
- }
371
- function findPrimaryKeyField(dataset) {
372
- const { primaryKey } = dataset.collection;
373
- if (primaryKey === undefined) {
374
- return undefined;
375
- }
376
- return dataset.collection.fields.find((field) => field.name === primaryKey);
377
- }
378
- function createDeterministicSeedUuid(datasetId, index) {
379
- const input = `${datasetId}:${index}`;
380
- const hex = `${hashHex(input, 0)}${hashHex(input, 1)}${hashHex(input, 2)}${hashHex(input, 3)}`;
381
- const variant = ((Number.parseInt(hex.charAt(16), 16) & 0x3) | 0x8).toString(16);
382
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-${variant}${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
383
- }
384
- function hashHex(input, salt) {
385
- let hash = 2166136261 ^ salt;
386
- for (const char of input) {
387
- hash ^= char.charCodeAt(0);
388
- hash = Math.imul(hash, 16777619);
389
- }
390
- return (hash >>> 0).toString(16).padStart(8, '0');
391
- }
392
- function formatCollectionName(dataset) {
393
- return dataset.collection.schema
394
- ? `${dataset.collection.schema}.${dataset.collection.name}`
395
- : dataset.collection.name;
396
- }
397
- //# sourceMappingURL=datasets.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"datasets.js","sourceRoot":"","sources":["../src/datasets.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AppDataManifest,\n AppDatasetDefinition,\n AppDatasetOperation,\n DbFieldDefinition,\n} from '@ankhorage/contracts';\n\nimport type { GeneratedInfrastructureFile } from './types';\n\nexport interface DatasetInfrastructureArtifacts {\n readonly files: readonly GeneratedInfrastructureFile[];\n readonly warnings: readonly string[];\n}\n\ninterface GeneratedDatasetOperation {\n readonly datasetId: string;\n readonly operationId: string;\n readonly intent: AppDatasetOperation;\n readonly method: 'DELETE' | 'GET' | 'PATCH' | 'POST';\n readonly path: string;\n}\n\ninterface GeneratedDatasetOpenApiOperation {\n readonly operationId: string;\n readonly summary: string;\n readonly responses: Readonly<Record<string, { readonly description: string }>>;\n readonly 'x-ankh-dataset': {\n readonly datasetId: string;\n readonly intent: AppDatasetOperation;\n };\n}\n\ninterface GeneratedDatasetOpenApiPath {\n readonly get?: GeneratedDatasetOpenApiOperation;\n readonly post?: GeneratedDatasetOpenApiOperation;\n readonly patch?: GeneratedDatasetOpenApiOperation;\n readonly delete?: GeneratedDatasetOpenApiOperation;\n}\n\ninterface GeneratedDatasetOpenApiDocument {\n readonly openapi: '3.1.0';\n readonly info: {\n readonly title: string;\n readonly version: string;\n };\n readonly paths: Readonly<Record<string, GeneratedDatasetOpenApiPath>>;\n}\n\ninterface GeneratedDatasetSeedManifestEntry {\n readonly datasetId: string;\n readonly collection: AppDatasetDefinition['collection'];\n readonly records: readonly Record<string, unknown>[];\n}\n\nconst DEFAULT_DATASET_OPERATIONS: readonly AppDatasetOperation[] = [\n 'create',\n 'delete',\n 'list',\n 'read',\n 'update',\n];\n\nconst DEFAULT_DATABASE_SCHEMA = 'public';\n\nexport function generateDatasetInfrastructureArtifacts(args: {\n readonly data: AppDataManifest | undefined;\n readonly databaseProvider: string | undefined;\n}): DatasetInfrastructureArtifacts {\n const datasets = listDatasets(args.data);\n if (datasets.length === 0) return { files: [], warnings: [] };\n\n if (args.databaseProvider !== undefined && args.databaseProvider !== 'supabase') {\n return {\n files: [],\n warnings: [\n `Dataset generation is only implemented for the supabase database provider; received ${args.databaseProvider}.`,\n ],\n };\n }\n\n const seedManifest = createSeedManifest(datasets);\n\n return {\n files: [\n {\n path: 'infra/minikube/db/datasets.json',\n content: `${JSON.stringify(\n datasets.map((dataset) => dataset.collection),\n null,\n 2,\n )}\\n`,\n },\n {\n path: 'infra/minikube/db/dataset-seed.json',\n content: `${JSON.stringify(seedManifest, null, 2)}\\n`,\n },\n {\n path: 'infra/minikube/db/migrations/001_datasets.sql',\n content: generateSupabaseMigrationSql(datasets),\n },\n {\n path: 'infra/minikube/db/seeds/001_dataset_seed.sql',\n content: generateSupabaseSeedSql(seedManifest),\n },\n {\n path: 'infra/minikube/db/README.md',\n content: generateReadme(datasets),\n },\n {\n path: 'src/generated/datasets/collections.ts',\n content: generateCollectionsTs(datasets),\n },\n {\n path: 'src/generated/datasets/appDatasetApi.ts',\n content: generateApiTs(datasets),\n },\n {\n path: 'src/generated/datasets/datasetApiHandlers.ts',\n content: generateApiHandlersTs(datasets, seedManifest),\n },\n {\n path: 'src/generated/datasets/openapi.json',\n content: `${JSON.stringify(createOpenApi(datasets), null, 2)}\\n`,\n },\n ],\n warnings: createSeedWarnings(datasets),\n };\n}\n\nfunction listDatasets(data: AppDataManifest | undefined): readonly AppDatasetDefinition[] {\n return Object.values(data?.datasets ?? {}).sort((left, right) => left.id.localeCompare(right.id));\n}\n\nfunction createSeedManifest(\n datasets: readonly AppDatasetDefinition[],\n): readonly GeneratedDatasetSeedManifestEntry[] {\n return datasets.map((dataset) => ({\n datasetId: dataset.id,\n collection: dataset.collection,\n records: materializeSeedRecords(dataset),\n }));\n}\n\nfunction materializeSeedRecords(dataset: AppDatasetDefinition): readonly Record<string, unknown>[] {\n const { primaryKey } = dataset.collection;\n const primaryKeyField = findPrimaryKeyField(dataset);\n\n return (dataset.seed ?? []).map((record, index) => {\n if (primaryKey === undefined || record[primaryKey] !== undefined) {\n return record;\n }\n\n if (primaryKeyField?.type !== 'uuid') {\n return record;\n }\n\n return {\n ...record,\n [primaryKey]: createDeterministicSeedUuid(dataset.id, index),\n };\n });\n}\n\nfunction createSeedWarnings(datasets: readonly AppDatasetDefinition[]): readonly string[] {\n return datasets.flatMap((dataset) => {\n const { primaryKey } = dataset.collection;\n const seed = dataset.seed ?? [];\n const primaryKeyField = findPrimaryKeyField(dataset);\n\n if (primaryKey === undefined || primaryKeyField?.type === 'uuid') {\n return [];\n }\n\n const hasMissingPrimaryKey = seed.some((record) => record[primaryKey] === undefined);\n if (!hasMissingPrimaryKey) {\n return [];\n }\n\n return [\n `Dataset ${dataset.id} has seed records without primary key ${primaryKey}; seed SQL cannot fully guarantee idempotence for those records.`,\n ];\n });\n}\n\nfunction generateReadme(datasets: readonly AppDatasetDefinition[]): string {\n const rows = datasets\n .map((dataset) => {\n const seedCount = dataset.seed?.length ?? 0;\n return `- \\`${dataset.id}\\` maps to collection \\`${formatCollectionName(dataset)}\\` with ${dataset.collection.fields.length} fields and ${seedCount} starter records.`;\n })\n .join('\\n');\n\n return `# Generated Dataset Artifacts\\n\\nThese files are generated from \\`ankh.config.json\\` -> \\`data.datasets\\`.\\n\\n## Datasets\\n\\n${rows}\\n\\n## Files\\n\\n- \\`datasets.json\\` stores provider-neutral collection definitions for database adapters.\\n- \\`dataset-seed.json\\` stores starter records configured in Studio, with deterministic UUID primary keys added when needed.\\n- \\`migrations/001_datasets.sql\\` creates Supabase/Postgres schemas and tables from the dataset collection definitions.\\n- \\`seeds/001_dataset_seed.sql\\` inserts starter records using idempotent conflict handling where possible.\\n- \\`src/generated/datasets/appDatasetApi.ts\\` exposes bindable dataset operation metadata.\\n- \\`src/generated/datasets/datasetApiHandlers.ts\\` exposes app-owned CRUD handler helpers for generated dataset routes.\\n- \\`src/generated/datasets/openapi.json\\` exposes the generated dataset API surface.\\n`;\n}\n\nfunction generateCollectionsTs(datasets: readonly AppDatasetDefinition[]): string {\n return `import type { AppDatasetDefinition } from '@ankhorage/contracts';\\n\\nexport const GENERATED_DATASETS = ${JSON.stringify(\n datasets,\n null,\n 2,\n )} as const satisfies readonly AppDatasetDefinition[];\\n`;\n}\n\nfunction generateApiTs(datasets: readonly AppDatasetDefinition[]): string {\n return `export interface GeneratedDatasetOperation {\\n readonly datasetId: string;\\n readonly operationId: string;\\n readonly intent: 'create' | 'delete' | 'list' | 'read' | 'update';\\n readonly method: 'DELETE' | 'GET' | 'PATCH' | 'POST';\\n readonly path: string;\\n}\\n\\nexport const GENERATED_DATASET_OPERATIONS = ${JSON.stringify(\n datasets.flatMap(createDatasetOperations),\n null,\n 2,\n )} as const satisfies readonly GeneratedDatasetOperation[];\\n`;\n}\n\nfunction generateApiHandlersTs(\n datasets: readonly AppDatasetDefinition[],\n seedManifest: readonly GeneratedDatasetSeedManifestEntry[],\n): string {\n return `export type GeneratedDatasetRecord = Readonly<Record<string, unknown>>;\\n\\nexport interface GeneratedDatasetSeedManifestEntry {\\n readonly datasetId: string;\\n readonly records: readonly GeneratedDatasetRecord[];\\n}\\n\\nexport interface GeneratedDatasetStore {\\n list(datasetId: string): Promise<readonly GeneratedDatasetRecord[]>;\\n read(datasetId: string, id: string): Promise<GeneratedDatasetRecord | null>;\\n create(datasetId: string, values: GeneratedDatasetRecord): Promise<GeneratedDatasetRecord>;\\n update(datasetId: string, id: string, values: GeneratedDatasetRecord): Promise<GeneratedDatasetRecord | null>;\\n delete(datasetId: string, id: string): Promise<GeneratedDatasetRecord | null>;\\n}\\n\\nexport interface GeneratedDatasetRequest {\\n readonly datasetId: string;\\n readonly id?: string;\\n readonly body?: GeneratedDatasetRecord;\\n}\\n\\nexport interface GeneratedDatasetResponse<TData = unknown> {\\n readonly status: number;\\n readonly data: TData;\\n}\\n\\nconst GENERATED_DATASET_PRIMARY_KEYS = ${JSON.stringify(createPrimaryKeyRegistry(datasets), null, 2)} as const satisfies Readonly<Record<string, string>>;\\n\\nexport const GENERATED_DATASET_SEED = ${JSON.stringify(\n seedManifest.map((entry) => ({ datasetId: entry.datasetId, records: entry.records })),\n null,\n 2,\n )} as const satisfies readonly GeneratedDatasetSeedManifestEntry[];\\n\\nexport function isGeneratedDatasetId(datasetId: string): datasetId is Extract<keyof typeof GENERATED_DATASET_PRIMARY_KEYS, string> {\\n return Object.prototype.hasOwnProperty.call(GENERATED_DATASET_PRIMARY_KEYS, datasetId);\\n}\\n\\nexport function getGeneratedDatasetPrimaryKey(datasetId: string): string | null {\\n if (!isGeneratedDatasetId(datasetId)) {\\n return null;\\n }\\n\\n return GENERATED_DATASET_PRIMARY_KEYS[datasetId];\\n}\\n\\nexport function createGeneratedDatasetMemoryStore(\\n seed: readonly GeneratedDatasetSeedManifestEntry[] = GENERATED_DATASET_SEED,\\n): GeneratedDatasetStore {\\n const records = new Map<string, GeneratedDatasetRecord[]>();\\n\\n for (const datasetId of Object.keys(GENERATED_DATASET_PRIMARY_KEYS)) {\\n records.set(datasetId, []);\\n }\\n\\n for (const entry of seed) {\\n records.set(entry.datasetId, entry.records.map(cloneRecord));\\n }\\n\\n return {\\n async list(datasetId: string): Promise<readonly GeneratedDatasetRecord[]> {\\n return getRows(records, datasetId).map(cloneRecord);\\n },\\n\\n async read(datasetId: string, id: string): Promise<GeneratedDatasetRecord | null> {\\n const primaryKey = getGeneratedDatasetPrimaryKey(datasetId);\\n if (primaryKey === null) return null;\\n\\n const record = getRows(records, datasetId).find((row) => String(row[primaryKey]) === id);\\n return record === undefined ? null : cloneRecord(record);\\n },\\n\\n async create(datasetId: string, values: GeneratedDatasetRecord): Promise<GeneratedDatasetRecord> {\\n const primaryKey = getGeneratedDatasetPrimaryKey(datasetId);\\n const nextRecord =\\n primaryKey === null || values[primaryKey] !== undefined\\n ? cloneRecord(values)\\n : { ...values, [primaryKey]: createRuntimeRecordId() };\\n const nextRows = [...getRows(records, datasetId), nextRecord];\\n records.set(datasetId, nextRows);\\n return cloneRecord(nextRecord);\\n },\\n\\n async update(\\n datasetId: string,\\n id: string,\\n values: GeneratedDatasetRecord,\\n ): Promise<GeneratedDatasetRecord | null> {\\n const primaryKey = getGeneratedDatasetPrimaryKey(datasetId);\\n if (primaryKey === null) return null;\\n\\n const rows = getRows(records, datasetId);\\n const index = rows.findIndex((row) => String(row[primaryKey]) === id);\\n if (index < 0) return null;\\n\\n const currentRecord = rows[index] ?? {};\\n const nextRecord = { ...currentRecord, ...values, [primaryKey]: currentRecord[primaryKey] ?? id };\\n const nextRows = rows.map((row, rowIndex) => (rowIndex === index ? nextRecord : row));\\n records.set(datasetId, nextRows);\\n return cloneRecord(nextRecord);\\n },\\n\\n async delete(datasetId: string, id: string): Promise<GeneratedDatasetRecord | null> {\\n const primaryKey = getGeneratedDatasetPrimaryKey(datasetId);\\n if (primaryKey === null) return null;\\n\\n const rows = getRows(records, datasetId);\\n const deleted = rows.find((row) => String(row[primaryKey]) === id);\\n if (deleted === undefined) return null;\\n\\n records.set(\\n datasetId,\\n rows.filter((row) => String(row[primaryKey]) !== id),\\n );\\n return cloneRecord(deleted);\\n },\\n };\\n}\\n\\nexport async function handleGeneratedDatasetList(\\n store: GeneratedDatasetStore,\\n request: GeneratedDatasetRequest,\\n): Promise<GeneratedDatasetResponse> {\\n if (!isGeneratedDatasetId(request.datasetId)) {\\n return notFoundResponse(request.datasetId);\\n }\\n\\n return { status: 200, data: await store.list(request.datasetId) };\\n}\\n\\nexport async function handleGeneratedDatasetRead(\\n store: GeneratedDatasetStore,\\n request: GeneratedDatasetRequest,\\n): Promise<GeneratedDatasetResponse> {\\n if (!isGeneratedDatasetId(request.datasetId) || request.id === undefined) {\\n return notFoundResponse(request.datasetId);\\n }\\n\\n const record = await store.read(request.datasetId, request.id);\\n return record === null ? notFoundResponse(request.datasetId) : { status: 200, data: record };\\n}\\n\\nexport async function handleGeneratedDatasetCreate(\\n store: GeneratedDatasetStore,\\n request: GeneratedDatasetRequest,\\n): Promise<GeneratedDatasetResponse> {\\n if (!isGeneratedDatasetId(request.datasetId)) {\\n return notFoundResponse(request.datasetId);\\n }\\n\\n return { status: 201, data: await store.create(request.datasetId, request.body ?? {}) };\\n}\\n\\nexport async function handleGeneratedDatasetUpdate(\\n store: GeneratedDatasetStore,\\n request: GeneratedDatasetRequest,\\n): Promise<GeneratedDatasetResponse> {\\n if (!isGeneratedDatasetId(request.datasetId) || request.id === undefined) {\\n return notFoundResponse(request.datasetId);\\n }\\n\\n const record = await store.update(request.datasetId, request.id, request.body ?? {});\\n return record === null ? notFoundResponse(request.datasetId) : { status: 200, data: record };\\n}\\n\\nexport async function handleGeneratedDatasetDelete(\\n store: GeneratedDatasetStore,\\n request: GeneratedDatasetRequest,\\n): Promise<GeneratedDatasetResponse> {\\n if (!isGeneratedDatasetId(request.datasetId) || request.id === undefined) {\\n return notFoundResponse(request.datasetId);\\n }\\n\\n const record = await store.delete(request.datasetId, request.id);\\n return record === null ? notFoundResponse(request.datasetId) : { status: 200, data: record };\\n}\\n\\nfunction getRows(\\n records: ReadonlyMap<string, readonly GeneratedDatasetRecord[]>,\\n datasetId: string,\\n): readonly GeneratedDatasetRecord[] {\\n return records.get(datasetId) ?? [];\\n}\\n\\nfunction cloneRecord(record: GeneratedDatasetRecord): GeneratedDatasetRecord {\\n return { ...record };\\n}\\n\\nfunction createRuntimeRecordId(): string {\\n const randomPart = Math.random().toString(16).slice(2);\\n return \\`generated-\\${Date.now()}-\\${randomPart}\\`;\\n}\\n\\nfunction notFoundResponse(datasetId: string): GeneratedDatasetResponse {\\n return {\\n status: 404,\\n data: {\\n error: \\`Unknown dataset or record: \\${datasetId}\\`,\\n },\\n };\\n}\\n`;\n}\n\nfunction createDatasetOperations(\n dataset: AppDatasetDefinition,\n): readonly GeneratedDatasetOperation[] {\n const operations = dataset.operations ?? DEFAULT_DATASET_OPERATIONS;\n return operations.map((operation) => createDatasetOperation(dataset.id, operation));\n}\n\nfunction createDatasetOperation(\n datasetId: string,\n operation: AppDatasetOperation,\n): GeneratedDatasetOperation {\n const collectionPath = `/api/datasets/${datasetId}`;\n const recordPath = `${collectionPath}/{id}`;\n\n switch (operation) {\n case 'create':\n return {\n datasetId,\n operationId: `${datasetId}.create`,\n intent: operation,\n method: 'POST',\n path: collectionPath,\n };\n case 'delete':\n return {\n datasetId,\n operationId: `${datasetId}.delete`,\n intent: operation,\n method: 'DELETE',\n path: recordPath,\n };\n case 'list':\n return {\n datasetId,\n operationId: `${datasetId}.list`,\n intent: operation,\n method: 'GET',\n path: collectionPath,\n };\n case 'read':\n return {\n datasetId,\n operationId: `${datasetId}.read`,\n intent: operation,\n method: 'GET',\n path: recordPath,\n };\n case 'update':\n return {\n datasetId,\n operationId: `${datasetId}.update`,\n intent: operation,\n method: 'PATCH',\n path: recordPath,\n };\n }\n}\n\nfunction createOpenApi(datasets: readonly AppDatasetDefinition[]): GeneratedDatasetOpenApiDocument {\n const paths: Record<string, GeneratedDatasetOpenApiPath> = {};\n\n for (const dataset of datasets) {\n for (const operation of createDatasetOperations(dataset)) {\n addOpenApiOperation(paths, operation, {\n operationId: operation.operationId,\n summary: `${operation.intent} ${dataset.label ?? dataset.id}`,\n responses: {\n [operation.intent === 'create' ? '201' : '200']: { description: 'Successful response.' },\n },\n 'x-ankh-dataset': {\n datasetId: dataset.id,\n intent: operation.intent,\n },\n });\n }\n }\n\n return {\n openapi: '3.1.0',\n info: {\n title: 'Generated app dataset API',\n version: '1.0.0',\n },\n paths,\n };\n}\n\nfunction addOpenApiOperation(\n paths: Record<string, GeneratedDatasetOpenApiPath>,\n operation: GeneratedDatasetOperation,\n openApiOperation: GeneratedDatasetOpenApiOperation,\n) {\n const path = paths[operation.path] ?? {};\n\n switch (operation.method) {\n case 'DELETE':\n paths[operation.path] = { ...path, delete: openApiOperation };\n return;\n case 'GET':\n paths[operation.path] = { ...path, get: openApiOperation };\n return;\n case 'PATCH':\n paths[operation.path] = { ...path, patch: openApiOperation };\n return;\n case 'POST':\n paths[operation.path] = { ...path, post: openApiOperation };\n return;\n }\n}\n\nfunction generateSupabaseMigrationSql(datasets: readonly AppDatasetDefinition[]): string {\n const statements = datasets.flatMap((dataset) => [\n `create schema if not exists ${quoteSqlIdentifier(dataset.collection.schema ?? DEFAULT_DATABASE_SCHEMA)};`,\n generateCreateTableSql(dataset),\n ]);\n\n return [\n '-- Generated from ankh.config.json -> data.datasets.',\n '-- Safe to re-run: schemas and tables are created with if not exists.',\n 'create extension if not exists pgcrypto;',\n '',\n ...statements,\n '',\n ].join('\\n');\n}\n\nfunction generateCreateTableSql(dataset: AppDatasetDefinition): string {\n const schema = quoteSqlIdentifier(dataset.collection.schema ?? DEFAULT_DATABASE_SCHEMA);\n const table = quoteSqlIdentifier(dataset.collection.name);\n const columns = collectSqlFields(dataset).map((field) =>\n formatSqlColumn(field, field.name === (dataset.collection.primaryKey ?? 'id')),\n );\n\n return `create table if not exists ${schema}.${table} (\\n ${columns.join(',\\n ')}\\n);`;\n}\n\nfunction collectSqlFields(dataset: AppDatasetDefinition): readonly DbFieldDefinition[] {\n const primaryKey = dataset.collection.primaryKey ?? 'id';\n const hasPrimaryKeyField = dataset.collection.fields.some((field) => field.name === primaryKey);\n\n if (hasPrimaryKeyField) {\n return dataset.collection.fields;\n }\n\n return [\n { name: primaryKey, type: 'uuid', required: true, unique: true },\n ...dataset.collection.fields,\n ];\n}\n\nfunction generateSupabaseSeedSql(\n seedManifest: readonly GeneratedDatasetSeedManifestEntry[],\n): string {\n const inserts = seedManifest.flatMap((entry) => generateSeedInsertStatements(entry));\n\n return [\n '-- Generated from ankh.config.json -> data.datasets seed records.',\n '-- Safe to re-run when seed records have stable primary keys or unique constraints.',\n ...(inserts.length === 0 ? ['-- No dataset seed records configured.'] : inserts),\n '',\n ].join('\\n');\n}\n\nfunction generateSeedInsertStatements(entry: GeneratedDatasetSeedManifestEntry): readonly string[] {\n if (entry.records.length === 0) {\n return [];\n }\n\n const { primaryKey } = entry.collection;\n const columns = collectSeedColumns(entry);\n if (columns.length === 0) {\n return [];\n }\n\n const schema = quoteSqlIdentifier(entry.collection.schema ?? DEFAULT_DATABASE_SCHEMA);\n const table = quoteSqlIdentifier(entry.collection.name);\n const quotedColumns = columns.map(quoteSqlIdentifier).join(', ');\n const values = entry.records\n .map((record) => `(${columns.map((column) => formatSqlValue(record[column])).join(', ')})`)\n .join(',\\n ');\n const conflict =\n primaryKey === undefined\n ? 'on conflict do nothing'\n : `on conflict (${quoteSqlIdentifier(primaryKey)}) do nothing`;\n\n return [`insert into ${schema}.${table} (${quotedColumns}) values\\n ${values}\\n${conflict};`];\n}\n\nfunction collectSeedColumns(entry: GeneratedDatasetSeedManifestEntry): readonly string[] {\n const columns = new Set<string>();\n if (entry.collection.primaryKey !== undefined) {\n columns.add(entry.collection.primaryKey);\n }\n\n for (const field of entry.collection.fields) {\n if (entry.records.some((record) => record[field.name] !== undefined)) {\n columns.add(field.name);\n }\n }\n\n const fieldNames = new Set(entry.collection.fields.map((field) => field.name));\n const extraColumns = entry.records\n .flatMap((record) => Object.keys(record))\n .filter((column) => !fieldNames.has(column) && column !== entry.collection.primaryKey)\n .sort();\n\n for (const column of extraColumns) {\n columns.add(column);\n }\n\n return [...columns];\n}\n\nfunction formatSqlColumn(field: DbFieldDefinition, primaryKey: boolean): string {\n const defaultValue = formatSqlDefaultValue(field, primaryKey);\n const primaryKeyClause = primaryKey ? ' primary key' : '';\n const required = field.required === true && !primaryKey ? ' not null' : '';\n const unique = field.unique === true && !primaryKey ? ' unique' : '';\n\n return `${quoteSqlIdentifier(field.name)} ${mapSqlFieldType(field.type)}${defaultValue}${primaryKeyClause}${required}${unique}`;\n}\n\nfunction mapSqlFieldType(type: DbFieldDefinition['type']): string {\n switch (type) {\n case 'text':\n return 'text';\n case 'number':\n return 'double precision';\n case 'boolean':\n return 'boolean';\n case 'datetime':\n return 'timestamptz';\n case 'json':\n return 'jsonb';\n case 'uuid':\n return 'uuid';\n }\n}\n\nfunction formatSqlDefaultValue(field: DbFieldDefinition, primaryKey: boolean): string {\n if (primaryKey && field.type === 'uuid' && field.defaultValue === undefined) {\n return ' default gen_random_uuid()';\n }\n\n if (field.defaultValue === undefined) {\n return '';\n }\n\n if (field.defaultValue === null) {\n return ' default null';\n }\n\n if (typeof field.defaultValue === 'string') {\n return ` default '${escapeSqlString(field.defaultValue)}'`;\n }\n\n return ` default ${String(field.defaultValue)}`;\n}\n\nfunction formatSqlValue(value: unknown): string {\n if (value === null || value === undefined) {\n return 'null';\n }\n\n if (typeof value === 'string') {\n return `'${escapeSqlString(value)}'`;\n }\n\n if (typeof value === 'number') {\n return Number.isFinite(value) ? String(value) : 'null';\n }\n\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false';\n }\n\n if (typeof value === 'bigint') {\n return String(value);\n }\n\n return `'${escapeSqlString(stringifyJsonValue(value))}'::jsonb`;\n}\n\nfunction stringifyJsonValue(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return 'null';\n }\n}\n\nfunction quoteSqlIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction escapeSqlString(value: string): string {\n return value.replaceAll(\"'\", \"''\");\n}\n\nfunction createPrimaryKeyRegistry(\n datasets: readonly AppDatasetDefinition[],\n): Readonly<Record<string, string>> {\n return Object.fromEntries(\n datasets.map((dataset) => [dataset.id, dataset.collection.primaryKey ?? 'id']),\n );\n}\n\nfunction findPrimaryKeyField(dataset: AppDatasetDefinition): DbFieldDefinition | undefined {\n const { primaryKey } = dataset.collection;\n if (primaryKey === undefined) {\n return undefined;\n }\n\n return dataset.collection.fields.find((field) => field.name === primaryKey);\n}\n\nfunction createDeterministicSeedUuid(datasetId: string, index: number): string {\n const input = `${datasetId}:${index}`;\n const hex = `${hashHex(input, 0)}${hashHex(input, 1)}${hashHex(input, 2)}${hashHex(input, 3)}`;\n const variant = ((Number.parseInt(hex.charAt(16), 16) & 0x3) | 0x8).toString(16);\n\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-${variant}${hex.slice(17, 20)}-${hex.slice(20, 32)}`;\n}\n\nfunction hashHex(input: string, salt: number): string {\n let hash = 2166136261 ^ salt;\n\n for (const char of input) {\n hash ^= char.charCodeAt(0);\n hash = Math.imul(hash, 16777619);\n }\n\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\nfunction formatCollectionName(dataset: AppDatasetDefinition): string {\n return dataset.collection.schema\n ? `${dataset.collection.schema}.${dataset.collection.name}`\n : dataset.collection.name;\n}\n"]}