@tailor-platform/create-sdk 1.84.0 → 1.85.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/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @tailor-platform/create-sdk
2
2
 
3
+ ## 1.85.0
4
+
3
5
  ## 1.84.0
4
6
 
5
7
  ## 1.83.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/create-sdk",
3
- "version": "1.84.0",
3
+ "version": "1.85.0",
4
4
  "description": "A CLI tool to quickly create a new Tailor Platform SDK project",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "24.13.3",
29
- "oxlint": "1.75.0",
29
+ "oxlint": "1.76.0",
30
30
  "oxlint-tsgolint": "7.0.2001",
31
31
  "tsdown": "0.22.14",
32
32
  "typescript": "6.0.3"
@@ -16,10 +16,10 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
19
- "@tailor-platform/sdk": "1.84.0",
19
+ "@tailor-platform/sdk": "1.85.0",
20
20
  "@types/node": "24.13.3",
21
- "oxfmt": "0.60.0",
22
- "oxlint": "1.75.0",
21
+ "oxfmt": "0.61.0",
22
+ "oxlint": "1.76.0",
23
23
  "oxlint-tsgolint": "7.0.2001",
24
24
  "typescript": "6.0.3",
25
25
  "vitest": "4.1.10"
@@ -16,10 +16,10 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
19
- "@tailor-platform/sdk": "1.84.0",
19
+ "@tailor-platform/sdk": "1.85.0",
20
20
  "@types/node": "24.13.3",
21
- "oxfmt": "0.60.0",
22
- "oxlint": "1.75.0",
21
+ "oxfmt": "0.61.0",
22
+ "oxlint": "1.76.0",
23
23
  "oxlint-tsgolint": "7.0.2001",
24
24
  "typescript": "6.0.3",
25
25
  "vitest": "4.1.10"
@@ -4,7 +4,7 @@
4
4
  * Do not edit by hand: changes will be overwritten on the next `sdk generate`.
5
5
  */
6
6
  import { readFileSync } from "node:fs";
7
- import { join, isAbsolute } from "node:path";
7
+ import { dirname, join, isAbsolute } from "node:path";
8
8
  import { parseArgs, styleText } from "node:util";
9
9
  import { createInterface } from "node:readline";
10
10
  import {
@@ -77,6 +77,7 @@ const { values, positionals } = parseArgs({
77
77
  namespace: { type: "string", short: "n" },
78
78
  "skip-idp": { type: "boolean", default: false },
79
79
  truncate: { type: "boolean", default: false },
80
+ upsert: { type: "boolean", default: false },
80
81
  yes: { type: "boolean", default: false },
81
82
  profile: { type: "string", short: "p" },
82
83
  help: { type: "boolean", short: "h", default: false },
@@ -96,6 +97,7 @@ Options:
96
97
  -n, --namespace <ns> Process all types in specified namespace (excludes _User)
97
98
  --skip-idp Skip IdP user (_User) entity
98
99
  --truncate Truncate tables before seeding
100
+ --upsert Update existing rows instead of failing on duplicate ids
99
101
  --yes Skip confirmation prompts (for truncate)
100
102
  -p, --profile <name> Workspace profile name
101
103
  -h, --help Show help
@@ -109,6 +111,7 @@ Examples:
109
111
  node exec.mjs --truncate --yes # Truncate all tables without confirmation, then seed all
110
112
  node exec.mjs --truncate --namespace <namespace> # Truncate tailordb, then seed tailordb
111
113
  node exec.mjs --truncate User Order # Truncate User and Order, then seed them
114
+ node exec.mjs --upsert # Seed all, updating rows whose id already exists
112
115
  node exec.mjs validate # Validate all seed data
113
116
  node exec.mjs validate ./data/User.jsonl # Validate specific file
114
117
  `);
@@ -133,6 +136,47 @@ const promptConfirmation = (question) => {
133
136
  const configDir = import.meta.dirname;
134
137
  const configPath = join(configDir, "../../tailor.config.ts");
135
138
 
139
+ const loadSeedData = (
140
+ dataDir,
141
+ typeNames,
142
+ { requireId = false, requiredFieldsByType = {} } = {},
143
+ ) => {
144
+ const data = {};
145
+ for (const typeName of typeNames) {
146
+ const jsonlPath = join(dataDir, `${typeName}.jsonl`);
147
+ try {
148
+ const lines = readFileSync(jsonlPath, "utf-8").split("\n");
149
+ const records = [];
150
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
151
+ if (lines[lineIndex].trim() === "") continue;
152
+ const record = JSON.parse(lines[lineIndex]);
153
+ if (requireId && (record?.id === undefined || record?.id === null)) {
154
+ throw new Error(
155
+ `${jsonlPath}:${lineIndex + 1}: \`id\` is required with --upsert`,
156
+ );
157
+ }
158
+ const missingRequiredField = (requiredFieldsByType[typeName] || []).find(
159
+ (field) => record?.[field] === undefined || record?.[field] === null,
160
+ );
161
+ if (missingRequiredField) {
162
+ throw new Error(
163
+ `${jsonlPath}:${lineIndex + 1}: field \`${missingRequiredField}\` is required with --upsert`,
164
+ );
165
+ }
166
+ records.push(record);
167
+ }
168
+ data[typeName] = records;
169
+ } catch (error) {
170
+ if (error.code === "ENOENT") {
171
+ data[typeName] = [];
172
+ } else {
173
+ throw error;
174
+ }
175
+ }
176
+ }
177
+ return data;
178
+ };
179
+
136
180
  // Determine machine user name (CLI argument takes precedence over config default)
137
181
  const defaultMachineUser = "admin";
138
182
  const machineUserName = values["machine-user"] || defaultMachineUser;
@@ -163,6 +207,12 @@ const namespaceDeps = {
163
207
  const namespaceSelfRefTypes = {
164
208
  "main-db": ["Category"]
165
209
  };
210
+ const requiredFieldsByType = {
211
+ "Category": ["name","slug"],
212
+ "Order": ["productId","userId","quantity","totalPrice","status"],
213
+ "Product": ["name","price","status"],
214
+ "User": ["name","email","role"]
215
+ };
166
216
  const entities = Object.values(namespaceEntities).flat();
167
217
  const hasIdpUser = false;
168
218
 
@@ -232,6 +282,25 @@ if (skipIdp) {
232
282
  }
233
283
  }
234
284
 
285
+ const selectedTailorDbTypes = entities.filter(
286
+ (entity) => !entitiesToProcess || entitiesToProcess.includes(entity),
287
+ );
288
+ const loadSelectedTailorDbSeedData = () =>
289
+ loadSeedData(join(configDir, "data"), selectedTailorDbTypes, {
290
+ requireId: values.upsert,
291
+ requiredFieldsByType: values.upsert ? requiredFieldsByType : {},
292
+ });
293
+ let tailorDbSeedData;
294
+ if (values.upsert) {
295
+ try {
296
+ tailorDbSeedData = loadSelectedTailorDbSeedData();
297
+ } catch (error) {
298
+ const message = error instanceof Error ? error.message : String(error);
299
+ console.error(styleText("red", `\n✗ Seed data generation failed: ${message}`));
300
+ process.exit(1);
301
+ }
302
+ }
303
+
235
304
  // Get application info
236
305
  const appInfo = await show({ configPath, profile: values.profile });
237
306
  const authNamespace = appInfo.auth;
@@ -293,29 +362,6 @@ if (skipIdp) {
293
362
  console.log(styleText("dim", ` Skipping IdP user (_User)`));
294
363
  }
295
364
 
296
- // Load seed data from JSONL files
297
- const loadSeedData = (dataDir, typeNames) => {
298
- const data = {};
299
- for (const typeName of typeNames) {
300
- const jsonlPath = join(dataDir, `${typeName}.jsonl`);
301
- try {
302
- const content = readFileSync(jsonlPath, "utf-8").trim();
303
- if (content) {
304
- data[typeName] = content.split("\n").map((line) => JSON.parse(line));
305
- } else {
306
- data[typeName] = [];
307
- }
308
- } catch (error) {
309
- if (error.code === "ENOENT") {
310
- data[typeName] = [];
311
- } else {
312
- throw error;
313
- }
314
- }
315
- }
316
- return data;
317
- };
318
-
319
365
  // Topological sort for dependency order
320
366
  const topologicalSort = (types, deps) => {
321
367
  const visited = new Set();
@@ -340,10 +386,17 @@ const topologicalSort = (types, deps) => {
340
386
  };
341
387
 
342
388
  // Seed TailorDB types via testExecScript
343
- const seedViaTestExecScript = async (namespace, typesToSeed, deps, selfRefTypes) => {
344
- const dataDir = join(configDir, "data");
389
+ const seedViaTestExecScript = async (
390
+ namespace,
391
+ typesToSeed,
392
+ deps,
393
+ selfRefTypes,
394
+ seedDataByType,
395
+ ) => {
345
396
  const sortedTypes = topologicalSort(typesToSeed, deps);
346
- const data = loadSeedData(dataDir, sortedTypes);
397
+ const data = Object.fromEntries(
398
+ sortedTypes.map((type) => [type, seedDataByType[type] || []]),
399
+ );
347
400
 
348
401
  // Skip if no data
349
402
  const typesWithData = sortedTypes.filter((t) => data[t] && data[t].length > 0);
@@ -352,10 +405,10 @@ const seedViaTestExecScript = async (namespace, typesToSeed, deps, selfRefTypes)
352
405
  return { success: true, processed: {} };
353
406
  }
354
407
 
355
- console.log(styleText("cyan", ` [${namespace}] Seeding ${typesWithData.length} types via Kysely batch insert...`));
408
+ console.log(styleText("cyan", ` [${namespace}] Seeding ${typesWithData.length} types via Kysely batch ${values.upsert ? "upsert" : "insert"}...`));
356
409
 
357
410
  // Bundle seed script
358
- const bundled = await bundleSeedScript(namespace, typesWithData);
411
+ const bundled = await bundleSeedScript(namespace, typesWithData, dirname(configPath));
359
412
 
360
413
  // Chunk seed data to fit within gRPC message size limits
361
414
  const chunks = chunkSeedData({
@@ -388,7 +441,7 @@ const seedViaTestExecScript = async (namespace, typesToSeed, deps, selfRefTypes)
388
441
  workspaceId,
389
442
  name: `seed-${namespace}.ts`,
390
443
  code: bundled.bundledCode,
391
- arg: JSON.stringify({ data: chunk.data, order: chunk.order, selfRefTypes }),
444
+ arg: JSON.stringify({ data: chunk.data, order: chunk.order, selfRefTypes, upsert: values.upsert }),
392
445
  invoker: {
393
446
  namespace: authNamespace,
394
447
  machineUserName,
@@ -416,9 +469,23 @@ const seedViaTestExecScript = async (namespace, typesToSeed, deps, selfRefTypes)
416
469
  }
417
470
 
418
471
  const processed = parsed.processed || {};
419
- for (const [type, count] of Object.entries(processed)) {
420
- allProcessed[type] = (allProcessed[type] || 0) + count;
421
- console.log(styleText("green", ` ✓ ${type}: ${count} rows inserted`));
472
+ for (const [type, counts] of Object.entries(processed)) {
473
+ const previous = allProcessed[type] || { inserted: 0, updated: 0, skipped: 0 };
474
+ const current = {
475
+ inserted: Number(counts.inserted) || 0,
476
+ updated: Number(counts.updated) || 0,
477
+ skipped: Number(counts.skipped) || 0,
478
+ };
479
+ allProcessed[type] = {
480
+ inserted: previous.inserted + current.inserted,
481
+ updated: previous.updated + current.updated,
482
+ skipped: previous.skipped + current.skipped,
483
+ };
484
+ const skipped = current.skipped > 0 ? `, ${current.skipped} skipped` : "";
485
+ const message = values.upsert
486
+ ? `${current.inserted} inserted, ${current.updated} updated${skipped}`
487
+ : `${current.inserted} rows inserted`;
488
+ console.log(styleText("green", ` ✓ ${type}: ${message}`));
422
489
  }
423
490
 
424
491
  if (!parsed.success) {
@@ -447,6 +514,7 @@ const seedViaTestExecScript = async (namespace, typesToSeed, deps, selfRefTypes)
447
514
  // Main execution
448
515
  try {
449
516
  let allSuccess = true;
517
+ tailorDbSeedData ??= loadSelectedTailorDbSeedData();
450
518
 
451
519
  // Determine which namespaces and types to process
452
520
  const namespacesToProcess = hasNamespace
@@ -465,7 +533,13 @@ try {
465
533
 
466
534
  if (typesToSeed.length === 0) continue;
467
535
 
468
- const result = await seedViaTestExecScript(namespace, typesToSeed, nsDeps, nsSelfRefTypes);
536
+ const result = await seedViaTestExecScript(
537
+ namespace,
538
+ typesToSeed,
539
+ nsDeps,
540
+ nsSelfRefTypes,
541
+ tailorDbSeedData,
542
+ );
469
543
  if (!result.success) {
470
544
  allSuccess = false;
471
545
  }
@@ -14,10 +14,10 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
17
- "@tailor-platform/sdk": "1.84.0",
17
+ "@tailor-platform/sdk": "1.85.0",
18
18
  "@types/node": "24.13.3",
19
- "oxfmt": "0.60.0",
20
- "oxlint": "1.75.0",
19
+ "oxfmt": "0.61.0",
20
+ "oxlint": "1.76.0",
21
21
  "oxlint-tsgolint": "7.0.2001",
22
22
  "typescript": "6.0.3"
23
23
  }
@@ -14,10 +14,10 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
17
- "@tailor-platform/sdk": "1.84.0",
17
+ "@tailor-platform/sdk": "1.85.0",
18
18
  "@types/node": "24.13.3",
19
- "oxfmt": "0.60.0",
20
- "oxlint": "1.75.0",
19
+ "oxfmt": "0.61.0",
20
+ "oxlint": "1.76.0",
21
21
  "oxlint-tsgolint": "7.0.2001",
22
22
  "typescript": "6.0.3"
23
23
  }
@@ -15,10 +15,10 @@
15
15
  },
16
16
  "devDependencies": {
17
17
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
18
- "@tailor-platform/sdk": "1.84.0",
18
+ "@tailor-platform/sdk": "1.85.0",
19
19
  "@types/node": "24.13.3",
20
- "oxfmt": "0.60.0",
21
- "oxlint": "1.75.0",
20
+ "oxfmt": "0.61.0",
21
+ "oxlint": "1.76.0",
22
22
  "oxlint-tsgolint": "7.0.2001",
23
23
  "typescript": "6.0.3"
24
24
  }
@@ -16,10 +16,10 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
19
- "@tailor-platform/sdk": "1.84.0",
19
+ "@tailor-platform/sdk": "1.85.0",
20
20
  "@types/node": "24.13.3",
21
- "oxfmt": "0.60.0",
22
- "oxlint": "1.75.0",
21
+ "oxfmt": "0.61.0",
22
+ "oxlint": "1.76.0",
23
23
  "oxlint-tsgolint": "7.0.2001",
24
24
  "typescript": "6.0.3",
25
25
  "vitest": "4.1.10"
@@ -14,10 +14,10 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
17
- "@tailor-platform/sdk": "1.84.0",
17
+ "@tailor-platform/sdk": "1.85.0",
18
18
  "@types/node": "24.13.3",
19
- "oxfmt": "0.60.0",
20
- "oxlint": "1.75.0",
19
+ "oxfmt": "0.61.0",
20
+ "oxlint": "1.76.0",
21
21
  "oxlint-tsgolint": "7.0.2001",
22
22
  "typescript": "6.0.3"
23
23
  }
@@ -16,10 +16,10 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
19
- "@tailor-platform/sdk": "1.84.0",
19
+ "@tailor-platform/sdk": "1.85.0",
20
20
  "@types/node": "24.13.3",
21
- "oxfmt": "0.60.0",
22
- "oxlint": "1.75.0",
21
+ "oxfmt": "0.61.0",
22
+ "oxlint": "1.76.0",
23
23
  "oxlint-tsgolint": "7.0.2001",
24
24
  "typescript": "6.0.3",
25
25
  "vitest": "4.1.10"
@@ -17,12 +17,12 @@
17
17
  },
18
18
  "devDependencies": {
19
19
  "@tailor-platform/eslint-plugin-sdk": "0.1.0",
20
- "@tailor-platform/sdk": "1.84.0",
20
+ "@tailor-platform/sdk": "1.85.0",
21
21
  "@types/node": "24.13.3",
22
22
  "graphql": "17.0.2",
23
23
  "graphql-request": "7.4.0",
24
- "oxfmt": "0.60.0",
25
- "oxlint": "1.75.0",
24
+ "oxfmt": "0.61.0",
25
+ "oxlint": "1.76.0",
26
26
  "oxlint-tsgolint": "7.0.2001",
27
27
  "typescript": "6.0.3",
28
28
  "vitest": "4.1.10"