@tailor-platform/sdk 2.1.0 → 2.3.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 +199 -0
- package/{dist/cli/index.mjs → bin/tailor.mjs} +1 -1
- package/dist/application-BZfk4HKm.mjs +3 -0
- package/dist/{application-D77KJFKD.mjs → application-F-nl107y.mjs} +121 -27
- package/dist/application-F-nl107y.mjs.map +1 -0
- package/dist/cli/commands/deploy/tailordb/index.d.mts +2 -1
- package/dist/cli/commands/deploy/types.d.mts +2 -1
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +52 -22
- package/dist/cli/commands/tailordb/migrate/file-state.d.mts +1 -0
- package/dist/cli/commands/tailordb/migrate/generate.d.mts +6 -0
- package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +22 -0
- package/dist/cli/commands/tailordb/migrate/schema-checks.d.mts +2 -2
- package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +9 -2
- package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +30 -9
- package/dist/cli/lib.d.mts +4 -4
- package/dist/cli/lib.mjs +3 -482
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/main.mjs +1151 -208
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/ts-hook.mjs +52 -7
- package/dist/completion/zsh-worker.zsh +167 -4
- package/dist/configure/index.d.mts +3 -3
- package/dist/configure/index.mjs +90 -23
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/index.d.mts +3 -2
- package/dist/configure/services/tailordb/index.d.mts +2 -1
- package/dist/configure/services/tailordb/schema.d.mts +46 -46
- package/dist/configure/services/tailordb/types.d.mts +30 -4
- package/dist/configure/services/workflow/index.d.mts +2 -2
- package/dist/configure/services/workflow/wait-point.d.mts +72 -13
- package/dist/{crashreport-B5LqCTfF.mjs → crashreport-CIIfLnoO.mjs} +2 -2
- package/dist/{crashreport-B5LqCTfF.mjs.map → crashreport-CIIfLnoO.mjs.map} +1 -1
- package/dist/{crashreport-eI7ty7ok.mjs → crashreport-CINZ5dVN.mjs} +1 -1
- package/dist/errors-BVb6vYGy.mjs +89 -0
- package/dist/errors-BVb6vYGy.mjs.map +1 -0
- package/dist/kysely/index.d.mts +53 -5
- package/dist/kysely/index.mjs +7 -2
- package/dist/kysely/index.mjs.map +1 -1
- package/dist/{logger-CXQq9YIp.mjs → logger-DSdExfor.mjs} +83 -42
- package/dist/logger-DSdExfor.mjs.map +1 -0
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/{register-ts-hook-BU18uU44.mjs → register-ts-hook-ClI226n2.mjs} +3541 -703
- package/dist/register-ts-hook-ClI226n2.mjs.map +1 -0
- package/dist/schema--xYWRGfe.mjs.map +1 -1
- package/dist/seed/index.d.mts +52 -3
- package/dist/seed/index.mjs +200 -44
- package/dist/seed/index.mjs.map +1 -1
- package/dist/{seed-B-yckgQY.mjs → seed-xqM9WIHP.mjs} +8 -8
- package/dist/seed-xqM9WIHP.mjs.map +1 -0
- package/dist/{service-k1xy_A5G.mjs → service-C_WpbKHu.mjs} +39 -5
- package/dist/service-C_WpbKHu.mjs.map +1 -0
- package/dist/service-D1RCdzIL.mjs +3 -0
- package/dist/{service-B5WynBHQ.mjs → service-hZskxZmg.mjs} +3 -3
- package/dist/{service-B5WynBHQ.mjs.map → service-hZskxZmg.mjs.map} +1 -1
- package/dist/{service_pb-D5--My_M.mjs → service_pb-BEWj1pOV.mjs} +2 -2
- package/dist/{service_pb-D5--My_M.mjs.map → service_pb-BEWj1pOV.mjs.map} +1 -1
- package/dist/{service_pb-i8CmnQP4.mjs → service_pb-BjlhvK9E.mjs} +1 -1
- package/dist/utils/test/index.d.mts +6 -3
- package/dist/utils/test/index.mjs +25 -13
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/vitest/index.d.mts +2 -1
- package/dist/vitest/index.mjs +167 -3
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mocks/workflow.d.mts +13 -1
- package/dist/vitest/pglite-kysely.d.mts +43 -0
- package/dist/wait-point-invoker-__oE88_P.mjs +148 -0
- package/dist/wait-point-invoker-__oE88_P.mjs.map +1 -0
- package/dist/wait-point-registry-TL99zotw.mjs +47 -0
- package/dist/wait-point-registry-TL99zotw.mjs.map +1 -0
- package/dist/{workspace_resource_pb-DXnO2AT-.mjs → workspace_resource_pb-HNIL_Vfj.mjs} +10 -2
- package/dist/{workspace_resource_pb-DXnO2AT-.mjs.map → workspace_resource_pb-HNIL_Vfj.mjs.map} +1 -1
- package/docs/cli/setup.md +14 -1
- package/docs/cli/tailordb.md +96 -28
- package/docs/cli-reference.md +15 -12
- package/docs/configuration.md +1 -1
- package/docs/github-actions.md +73 -28
- package/docs/migration/v2.md +55 -1
- package/docs/plugin/custom.md +48 -48
- package/docs/plugin/index.md +24 -24
- package/docs/services/auth.md +10 -10
- package/docs/services/executor.md +1 -1
- package/docs/services/resolver.md +2 -2
- package/docs/services/tailordb-migration.md +485 -84
- package/docs/services/tailordb.md +28 -28
- package/docs/services/workflow.md +52 -2
- package/docs/testing.md +14 -0
- package/package.json +12 -7
- package/dist/application-Af1zIqSI.mjs +0 -3
- package/dist/application-D77KJFKD.mjs.map +0 -1
- package/dist/errors-D7583Zz7.mjs +0 -58
- package/dist/errors-D7583Zz7.mjs.map +0 -1
- package/dist/logger-CXQq9YIp.mjs.map +0 -1
- package/dist/register-ts-hook-BU18uU44.mjs.map +0 -1
- package/dist/seed-B-yckgQY.mjs.map +0 -1
- package/dist/service-M7OYziQP.mjs +0 -3
- package/dist/service-k1xy_A5G.mjs.map +0 -1
- package/dist/test-env-key-D7UkZp99.mjs +0 -75
- package/dist/test-env-key-D7UkZp99.mjs.map +0 -1
package/dist/seed/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { basename, dirname, join } from "pathe";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { ErrorFormatter, LinesDB, defineSchema, findSchemaFile } from "@toiroakr/lines-db";
|
|
4
5
|
|
|
5
6
|
//#region src/seed/index.ts
|
|
6
7
|
/**
|
|
@@ -10,49 +11,29 @@ import { ErrorFormatter, LinesDB, defineSchema } from "@toiroakr/lines-db";
|
|
|
10
11
|
* to avoid phantom dependency issues with pnpm, and provides
|
|
11
12
|
* seed-specific utility functions used by the code generator.
|
|
12
13
|
*/
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* validation, and returns formatted output and error messages.
|
|
17
|
-
* @param options - Validation options including path and verbose flag
|
|
18
|
-
* @returns Validation result with output messages and optional error details
|
|
19
|
-
*/
|
|
20
|
-
async function validateSeedData(options) {
|
|
21
|
-
const { path: resolvedPath, verbose = false } = options;
|
|
14
|
+
/** Fields `fillSeedData` writes when the caller names none. */
|
|
15
|
+
const DEFAULT_FILL_FIELDS = ["id"];
|
|
16
|
+
async function resolveSeedDataTarget(resolvedPath) {
|
|
22
17
|
const stats = await stat(resolvedPath);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
await db.close();
|
|
39
|
-
}
|
|
40
|
-
const outputLines = [];
|
|
41
|
-
if (result.warnings.length > 0) {
|
|
42
|
-
for (const warning of result.warnings) outputLines.push(`⚠ ${warning}`);
|
|
43
|
-
outputLines.push("");
|
|
44
|
-
}
|
|
45
|
-
if (result.valid) {
|
|
46
|
-
outputLines.push("✓ All records are valid");
|
|
47
|
-
return {
|
|
48
|
-
valid: true,
|
|
49
|
-
output: outputLines.join("\n")
|
|
50
|
-
};
|
|
51
|
-
}
|
|
18
|
+
if (stats.isDirectory()) return { dataDir: resolvedPath };
|
|
19
|
+
if (stats.isFile() && resolvedPath.endsWith(".jsonl")) return {
|
|
20
|
+
dataDir: dirname(resolvedPath),
|
|
21
|
+
tableName: basename(resolvedPath, ".jsonl")
|
|
22
|
+
};
|
|
23
|
+
throw new Error(`Invalid path: ${resolvedPath}. Must be a directory or .jsonl file.`);
|
|
24
|
+
}
|
|
25
|
+
async function listSeedTables(dataDir) {
|
|
26
|
+
return (await readdir(dataDir)).filter((entry) => entry.endsWith(".jsonl")).map((entry) => basename(entry, ".jsonl")).toSorted();
|
|
27
|
+
}
|
|
28
|
+
function formatWarnings(warnings) {
|
|
29
|
+
if (warnings.length === 0) return [];
|
|
30
|
+
return [...warnings.map((warning) => `⚠ ${warning}`), ""];
|
|
31
|
+
}
|
|
32
|
+
function formatValidationErrors(errors, verbose) {
|
|
52
33
|
const formatter = new ErrorFormatter({ verbose });
|
|
53
34
|
const errorLines = [];
|
|
54
35
|
const errorsByFile = /* @__PURE__ */ new Map();
|
|
55
|
-
for (const error of
|
|
36
|
+
for (const error of errors) {
|
|
56
37
|
const fileErrors = errorsByFile.get(error.file) || [];
|
|
57
38
|
fileErrors.push(error);
|
|
58
39
|
errorsByFile.set(error.file, fileErrors);
|
|
@@ -77,13 +58,188 @@ async function validateSeedData(options) {
|
|
|
77
58
|
}));
|
|
78
59
|
errorLines.push("");
|
|
79
60
|
}
|
|
61
|
+
return errorLines.join("\n");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate JSONL seed data against schema definitions.
|
|
65
|
+
* Resolves the given path (directory or `.jsonl` file), validates the rows it
|
|
66
|
+
* holds, and returns formatted output and error messages.
|
|
67
|
+
* @param options - Validation options including path and verbose flag
|
|
68
|
+
* @returns Validation result with output messages and optional error details
|
|
69
|
+
*/
|
|
70
|
+
async function validateSeedData(options) {
|
|
71
|
+
const { path: resolvedPath, verbose = false } = options;
|
|
72
|
+
const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);
|
|
73
|
+
const db = LinesDB.create({ dataDir });
|
|
74
|
+
let result;
|
|
75
|
+
try {
|
|
76
|
+
result = await db.initialize({
|
|
77
|
+
tableName,
|
|
78
|
+
detailedValidate: true
|
|
79
|
+
});
|
|
80
|
+
} finally {
|
|
81
|
+
await db.close();
|
|
82
|
+
}
|
|
83
|
+
const outputLines = formatWarnings(result.warnings);
|
|
84
|
+
if (result.valid) {
|
|
85
|
+
outputLines.push("✓ All records are valid");
|
|
86
|
+
return {
|
|
87
|
+
valid: true,
|
|
88
|
+
output: outputLines.join("\n")
|
|
89
|
+
};
|
|
90
|
+
}
|
|
80
91
|
return {
|
|
81
92
|
valid: false,
|
|
82
93
|
output: outputLines.join("\n"),
|
|
83
|
-
error:
|
|
94
|
+
error: formatValidationErrors(result.errors, verbose)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function ownValue(row, field) {
|
|
98
|
+
return Object.hasOwn(row, field) ? row[field] : void 0;
|
|
99
|
+
}
|
|
100
|
+
function setField(row, field, value) {
|
|
101
|
+
Object.defineProperty(row, field, {
|
|
102
|
+
value,
|
|
103
|
+
enumerable: true,
|
|
104
|
+
writable: true,
|
|
105
|
+
configurable: true
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function isBlank(value) {
|
|
109
|
+
if (value === void 0 || value === null) return true;
|
|
110
|
+
if (typeof value !== "object" || Array.isArray(value)) return false;
|
|
111
|
+
return Object.values(value).every(isBlank);
|
|
112
|
+
}
|
|
113
|
+
function splitLines(content) {
|
|
114
|
+
if (content === "") return [];
|
|
115
|
+
return content.split("\n").map((raw, index, all) => {
|
|
116
|
+
const eol = index === all.length - 1 ? "" : "\n";
|
|
117
|
+
const text = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
|
|
118
|
+
const carriage = raw.endsWith("\r") ? "\r" : "";
|
|
119
|
+
let row;
|
|
120
|
+
if (text.trim() !== "") try {
|
|
121
|
+
const parsed = JSON.parse(text);
|
|
122
|
+
row = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
123
|
+
} catch {
|
|
124
|
+
row = void 0;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
text,
|
|
128
|
+
eol: `${carriage}${eol}`,
|
|
129
|
+
row
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function serializeRow(row, fieldOrder) {
|
|
134
|
+
const rank = new Map(fieldOrder.map((field, index) => [field, index]));
|
|
135
|
+
const rankOf = (key) => rank.get(key) ?? fieldOrder.length;
|
|
136
|
+
return JSON.stringify(Object.fromEntries(Object.entries(row).toSorted(([a], [b]) => rankOf(a) - rankOf(b))));
|
|
137
|
+
}
|
|
138
|
+
async function loadSeedHook(dataDir, table) {
|
|
139
|
+
const schemaPath = await findSchemaFile(dataDir, table);
|
|
140
|
+
if (!schemaPath) return;
|
|
141
|
+
const hook = (await import(pathToFileURL(schemaPath).href)).hook;
|
|
142
|
+
if (typeof hook !== "function") throw new Error(`${schemaPath} does not export \`hook\`. Run \`tailor generate\` to regenerate the seed schema files.`);
|
|
143
|
+
return hook;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Fill in the values a record gets on create for the JSONL seed data rows that
|
|
147
|
+
* are missing them, so a row can be referenced by `id` or carry a timestamp
|
|
148
|
+
* before it is ever seeded.
|
|
149
|
+
*
|
|
150
|
+
* The values come from the type's own create-time behavior — its `id`, its field
|
|
151
|
+
* defaults, and its create hooks — applied to each row on its own. Nothing is
|
|
152
|
+
* validated, so a row can be filled while the data around it is still
|
|
153
|
+
* incomplete: that is what lets you get the ids you need in order to write the
|
|
154
|
+
* rows that reference them. Run `validateSeedData` when the data is ready.
|
|
155
|
+
*
|
|
156
|
+
* Only the named fields are written, and only into a row that has no value for
|
|
157
|
+
* them, so a value already in the file is never replaced. A line that gains
|
|
158
|
+
* nothing is written back exactly as it was, byte for byte; a line that does get
|
|
159
|
+
* a value is re-serialized with its keys in the order the type declares its
|
|
160
|
+
* fields, so a filled-in `id` lands at the front. A field the type gives no
|
|
161
|
+
* value to — one it does not declare, or one the platform assigns such as a
|
|
162
|
+
* serial field — is skipped, so one field list covers a whole data directory.
|
|
163
|
+
*
|
|
164
|
+
* The values are read from the schema files generated next to the data, and all
|
|
165
|
+
* of them are read before anything is written: a file that predates the current
|
|
166
|
+
* generator stops the run with nothing filled in anywhere.
|
|
167
|
+
* @param options - Fill options including path and fields
|
|
168
|
+
* @returns Which files received which fields
|
|
169
|
+
*/
|
|
170
|
+
async function fillSeedData(options) {
|
|
171
|
+
const { path: resolvedPath, fields = DEFAULT_FILL_FIELDS } = options;
|
|
172
|
+
if (fields.length === 0) throw new Error("No fields to fill. Name at least one field.");
|
|
173
|
+
const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);
|
|
174
|
+
const tables = tableName ? [tableName] : await listSeedTables(dataDir);
|
|
175
|
+
const warnings = [];
|
|
176
|
+
const filled = [];
|
|
177
|
+
const producedFields = /* @__PURE__ */ new Set();
|
|
178
|
+
const hooks = [];
|
|
179
|
+
for (const table of tables) {
|
|
180
|
+
const hook = await loadSeedHook(dataDir, table);
|
|
181
|
+
if (!hook) {
|
|
182
|
+
warnings.push(`No schema file for ${table}, so nothing can be filled in there`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
hooks.push({
|
|
186
|
+
table,
|
|
187
|
+
hook
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
const writes = [];
|
|
191
|
+
for (const { table, hook } of hooks) {
|
|
192
|
+
const file = join(dataDir, `${table}.jsonl`);
|
|
193
|
+
const lines = splitLines(await readFile(file, "utf-8"));
|
|
194
|
+
const written = /* @__PURE__ */ new Set();
|
|
195
|
+
const unreadable = [];
|
|
196
|
+
let count = 0;
|
|
197
|
+
let fieldOrder = [];
|
|
198
|
+
for (const [index, line] of lines.entries()) {
|
|
199
|
+
const row = line.row;
|
|
200
|
+
if (!row) {
|
|
201
|
+
if (line.text.trim() !== "") unreadable.push(index + 1);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const hooked = hook(row);
|
|
205
|
+
fieldOrder = Object.keys(hooked);
|
|
206
|
+
const gained = fields.filter((field) => {
|
|
207
|
+
if (isBlank(ownValue(hooked, field))) return false;
|
|
208
|
+
producedFields.add(field);
|
|
209
|
+
return isBlank(ownValue(row, field));
|
|
210
|
+
});
|
|
211
|
+
if (gained.length === 0) continue;
|
|
212
|
+
for (const field of gained) {
|
|
213
|
+
setField(row, field, hooked[field]);
|
|
214
|
+
written.add(field);
|
|
215
|
+
}
|
|
216
|
+
line.text = serializeRow(row, fieldOrder);
|
|
217
|
+
count += 1;
|
|
218
|
+
}
|
|
219
|
+
if (unreadable.length > 0) warnings.push(`${file}: line(s) ${unreadable.join(", ")} are not JSON objects, so nothing was filled in there`);
|
|
220
|
+
if (count === 0) continue;
|
|
221
|
+
writes.push({
|
|
222
|
+
file,
|
|
223
|
+
content: lines.map((line) => `${line.text}${line.eol}`).join("")
|
|
224
|
+
});
|
|
225
|
+
filled.push({
|
|
226
|
+
table,
|
|
227
|
+
file,
|
|
228
|
+
fields: [...written],
|
|
229
|
+
count
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
for (const { file, content } of writes) await writeFile(file, content);
|
|
233
|
+
const unproducedFields = fields.filter((field) => !producedFields.has(field));
|
|
234
|
+
if (tables.length > 0 && unproducedFields.length > 0) warnings.push(`No seed data produces a value for: ${unproducedFields.join(", ")}`);
|
|
235
|
+
const outputLines = formatWarnings(warnings);
|
|
236
|
+
outputLines.push(filled.length === 0 ? "✓ Nothing to fill" : filled.map(({ file, fields: tableFields, count }) => `\u2713 ${file}: filled ${tableFields.join(", ")} in ${count} row(s)`).join("\n"));
|
|
237
|
+
return {
|
|
238
|
+
output: outputLines.join("\n"),
|
|
239
|
+
filled
|
|
84
240
|
};
|
|
85
241
|
}
|
|
86
242
|
|
|
87
243
|
//#endregion
|
|
88
|
-
export { defineSchema, validateSeedData };
|
|
244
|
+
export { defineSchema, fillSeedData, validateSeedData };
|
|
89
245
|
//# sourceMappingURL=index.mjs.map
|
package/dist/seed/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/seed/index.ts"],"sourcesContent":["/**\n * Seed integration module for generated seed code.\n *\n * Re-exports `@toiroakr/lines-db` through a single import path\n * to avoid phantom dependency issues with pnpm, and provides\n * seed-specific utility functions used by the code generator.\n */\n\nimport { stat } from \"node:fs/promises\";\nimport { basename, dirname } from \"node:path\";\nimport { LinesDB, ErrorFormatter } from \"@toiroakr/lines-db\";\n\nexport { defineSchema } from \"@toiroakr/lines-db\";\nexport type { ForeignKeyDefinition, IndexDefinition } from \"@toiroakr/lines-db\";\n\ntype ValidateSeedDataOptions = {\n /** Resolved absolute path to a data directory or a .jsonl file */\n path: string;\n /** Show verbose error output */\n verbose?: boolean;\n};\n\ntype ValidateSeedResult =\n | { valid: true; output: string }\n | { valid: false; output: string; error: string };\n\n/**\n * Validate JSONL seed data against schema definitions.\n * Resolves the given path (directory or `.jsonl` file), runs LinesDB\n * validation, and returns formatted output and error messages.\n * @param options - Validation options including path and verbose flag\n * @returns Validation result with output messages and optional error details\n */\nexport async function validateSeedData(\n options: ValidateSeedDataOptions,\n): Promise<ValidateSeedResult> {\n const { path: resolvedPath, verbose = false } = options;\n\n const stats = await stat(resolvedPath);\n let dataDir: string;\n let tableName: string | undefined;\n if (stats.isDirectory()) {\n dataDir = resolvedPath;\n } else if (stats.isFile() && resolvedPath.endsWith(\".jsonl\")) {\n dataDir = dirname(resolvedPath);\n tableName = basename(resolvedPath, \".jsonl\");\n } else {\n throw new Error(`Invalid path: ${resolvedPath}. Must be a directory or .jsonl file.`);\n }\n\n const db = LinesDB.create({ dataDir });\n let result;\n try {\n result = await db.initialize({ tableName, detailedValidate: true });\n } finally {\n await db.close();\n }\n\n const outputLines: string[] = [];\n\n if (result.warnings.length > 0) {\n for (const warning of result.warnings) {\n outputLines.push(`⚠ ${warning}`);\n }\n outputLines.push(\"\");\n }\n\n if (result.valid) {\n outputLines.push(\"✓ All records are valid\");\n return { valid: true, output: outputLines.join(\"\\n\") };\n }\n\n const formatter = new ErrorFormatter({ verbose });\n const errorLines: string[] = [];\n const errorsByFile = new Map<string, typeof result.errors>();\n for (const error of result.errors) {\n const fileErrors = errorsByFile.get(error.file) || [];\n fileErrors.push(error);\n errorsByFile.set(error.file, fileErrors);\n }\n for (const [file, fileErrors] of errorsByFile) {\n errorLines.push(formatter.formatErrorHeader(fileErrors.length, file));\n errorLines.push(\"\");\n const validationErrors = fileErrors.filter(\n (e) => e.type !== \"foreignKey\" || !e.foreignKeyError,\n );\n const foreignKeyErrors = fileErrors.filter((e) => e.type === \"foreignKey\" && e.foreignKeyError);\n if (validationErrors.length > 0) {\n errorLines.push(\n formatter.formatValidationErrors(\n validationErrors.map((e) => ({\n file: e.file,\n rowIndex: e.rowIndex,\n issues: e.issues,\n })),\n ),\n );\n }\n for (const fkError of foreignKeyErrors) {\n if (fkError.foreignKeyError) {\n errorLines.push(\n formatter.formatForeignKeyError({\n file: fkError.file,\n rowIndex: fkError.rowIndex,\n column: fkError.foreignKeyError.column,\n value: fkError.foreignKeyError.value,\n referencedTable: fkError.foreignKeyError.referencedTable,\n referencedColumn: fkError.foreignKeyError.referencedColumn,\n }),\n );\n }\n }\n errorLines.push(\"\");\n }\n\n return { valid: false, output: outputLines.join(\"\\n\"), error: errorLines.join(\"\\n\") };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiCA,eAAsB,iBACpB,SAC6B;CAC7B,MAAM,EAAE,MAAM,cAAc,UAAU,UAAU;CAEhD,MAAM,QAAQ,MAAM,KAAK,YAAY;CACrC,IAAI;CACJ,IAAI;CACJ,IAAI,MAAM,YAAY,GACpB,UAAU;MACL,IAAI,MAAM,OAAO,KAAK,aAAa,SAAS,QAAQ,GAAG;EAC5D,UAAU,QAAQ,YAAY;EAC9B,YAAY,SAAS,cAAc,QAAQ;CAC7C,OACE,MAAM,IAAI,MAAM,iBAAiB,aAAa,sCAAsC;CAGtF,MAAM,KAAK,QAAQ,OAAO,EAAE,QAAQ,CAAC;CACrC,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,GAAG,WAAW;GAAE;GAAW,kBAAkB;EAAK,CAAC;CACpE,UAAU;EACR,MAAM,GAAG,MAAM;CACjB;CAEA,MAAM,cAAwB,CAAC;CAE/B,IAAI,OAAO,SAAS,SAAS,GAAG;EAC9B,KAAK,MAAM,WAAW,OAAO,UAC3B,YAAY,KAAK,KAAK,SAAS;EAEjC,YAAY,KAAK,EAAE;CACrB;CAEA,IAAI,OAAO,OAAO;EAChB,YAAY,KAAK,yBAAyB;EAC1C,OAAO;GAAE,OAAO;GAAM,QAAQ,YAAY,KAAK,IAAI;EAAE;CACvD;CAEA,MAAM,YAAY,IAAI,eAAe,EAAE,QAAQ,CAAC;CAChD,MAAM,aAAuB,CAAC;CAC9B,MAAM,+BAAe,IAAI,IAAkC;CAC3D,KAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,MAAM,aAAa,aAAa,IAAI,MAAM,IAAI,KAAK,CAAC;EACpD,WAAW,KAAK,KAAK;EACrB,aAAa,IAAI,MAAM,MAAM,UAAU;CACzC;CACA,KAAK,MAAM,CAAC,MAAM,eAAe,cAAc;EAC7C,WAAW,KAAK,UAAU,kBAAkB,WAAW,QAAQ,IAAI,CAAC;EACpE,WAAW,KAAK,EAAE;EAClB,MAAM,mBAAmB,WAAW,QACjC,MAAM,EAAE,SAAS,gBAAgB,CAAC,EAAE,eACvC;EACA,MAAM,mBAAmB,WAAW,QAAQ,MAAM,EAAE,SAAS,gBAAgB,EAAE,eAAe;EAC9F,IAAI,iBAAiB,SAAS,GAC5B,WAAW,KACT,UAAU,uBACR,iBAAiB,KAAK,OAAO;GAC3B,MAAM,EAAE;GACR,UAAU,EAAE;GACZ,QAAQ,EAAE;EACZ,EAAE,CACJ,CACF;EAEF,KAAK,MAAM,WAAW,kBACpB,IAAI,QAAQ,iBACV,WAAW,KACT,UAAU,sBAAsB;GAC9B,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,QAAQ,QAAQ,gBAAgB;GAChC,OAAO,QAAQ,gBAAgB;GAC/B,iBAAiB,QAAQ,gBAAgB;GACzC,kBAAkB,QAAQ,gBAAgB;EAC5C,CAAC,CACH;EAGJ,WAAW,KAAK,EAAE;CACpB;CAEA,OAAO;EAAE,OAAO;EAAO,QAAQ,YAAY,KAAK,IAAI;EAAG,OAAO,WAAW,KAAK,IAAI;CAAE;AACtF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/seed/index.ts"],"sourcesContent":["/**\n * Seed integration module for generated seed code.\n *\n * Re-exports `@toiroakr/lines-db` through a single import path\n * to avoid phantom dependency issues with pnpm, and provides\n * seed-specific utility functions used by the code generator.\n */\n\nimport { readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { LinesDB, ErrorFormatter, findSchemaFile } from \"@toiroakr/lines-db\";\n// `pathe`, not `node:path`: the file paths reported back are printed and returned\n// to the caller, and these stay separator-stable across platforms.\nimport { basename, dirname, join } from \"pathe\";\nimport type { JsonObject, ValidationErrorDetail } from \"@toiroakr/lines-db\";\n\nexport { defineSchema } from \"@toiroakr/lines-db\";\nexport type { ForeignKeyDefinition, IndexDefinition } from \"@toiroakr/lines-db\";\n\n/** Fields `fillSeedData` writes when the caller names none. */\nconst DEFAULT_FILL_FIELDS = [\"id\"];\n\ntype SeedDataTarget = {\n dataDir: string;\n tableName?: string;\n};\n\ntype ValidateSeedDataOptions = {\n /** Resolved absolute path to a data directory or a .jsonl file */\n path: string;\n /** Show verbose error output */\n verbose?: boolean;\n};\n\ntype ValidateSeedResult =\n | { valid: true; output: string }\n | { valid: false; output: string; error: string };\n\n/**\n * A JSONL seed file that received values.\n */\ntype FilledSeedFile = {\n /** Name of the seeded type, matching the JSONL file name */\n table: string;\n /** Absolute path to the updated JSONL file */\n file: string;\n /** Fields that were written back to the file */\n fields: string[];\n /** How many rows were missing at least one of those fields */\n count: number;\n};\n\ntype FillSeedDataOptions = {\n /** Resolved absolute path to a data directory or a .jsonl file */\n path: string;\n /** Fields to fill. Defaults to `id`. */\n fields?: readonly string[];\n};\n\ntype FillSeedDataResult = {\n output: string;\n filled: FilledSeedFile[];\n};\n\nasync function resolveSeedDataTarget(resolvedPath: string): Promise<SeedDataTarget> {\n const stats = await stat(resolvedPath);\n if (stats.isDirectory()) {\n return { dataDir: resolvedPath };\n }\n if (stats.isFile() && resolvedPath.endsWith(\".jsonl\")) {\n return { dataDir: dirname(resolvedPath), tableName: basename(resolvedPath, \".jsonl\") };\n }\n throw new Error(`Invalid path: ${resolvedPath}. Must be a directory or .jsonl file.`);\n}\n\nasync function listSeedTables(dataDir: string): Promise<string[]> {\n const entries = await readdir(dataDir);\n return entries\n .filter((entry) => entry.endsWith(\".jsonl\"))\n .map((entry) => basename(entry, \".jsonl\"))\n .toSorted();\n}\n\nfunction formatWarnings(warnings: string[]): string[] {\n if (warnings.length === 0) {\n return [];\n }\n return [...warnings.map((warning) => `⚠ ${warning}`), \"\"];\n}\n\nfunction formatValidationErrors(errors: ValidationErrorDetail[], verbose: boolean): string {\n const formatter = new ErrorFormatter({ verbose });\n const errorLines: string[] = [];\n const errorsByFile = new Map<string, ValidationErrorDetail[]>();\n for (const error of errors) {\n const fileErrors = errorsByFile.get(error.file) || [];\n fileErrors.push(error);\n errorsByFile.set(error.file, fileErrors);\n }\n for (const [file, fileErrors] of errorsByFile) {\n errorLines.push(formatter.formatErrorHeader(fileErrors.length, file));\n errorLines.push(\"\");\n const validationErrors = fileErrors.filter(\n (e) => e.type !== \"foreignKey\" || !e.foreignKeyError,\n );\n const foreignKeyErrors = fileErrors.filter((e) => e.type === \"foreignKey\" && e.foreignKeyError);\n if (validationErrors.length > 0) {\n errorLines.push(\n formatter.formatValidationErrors(\n validationErrors.map((e) => ({\n file: e.file,\n rowIndex: e.rowIndex,\n issues: e.issues,\n })),\n ),\n );\n }\n for (const fkError of foreignKeyErrors) {\n if (fkError.foreignKeyError) {\n errorLines.push(\n formatter.formatForeignKeyError({\n file: fkError.file,\n rowIndex: fkError.rowIndex,\n column: fkError.foreignKeyError.column,\n value: fkError.foreignKeyError.value,\n referencedTable: fkError.foreignKeyError.referencedTable,\n referencedColumn: fkError.foreignKeyError.referencedColumn,\n }),\n );\n }\n }\n errorLines.push(\"\");\n }\n\n return errorLines.join(\"\\n\");\n}\n\n/**\n * Validate JSONL seed data against schema definitions.\n * Resolves the given path (directory or `.jsonl` file), validates the rows it\n * holds, and returns formatted output and error messages.\n * @param options - Validation options including path and verbose flag\n * @returns Validation result with output messages and optional error details\n */\nexport async function validateSeedData(\n options: ValidateSeedDataOptions,\n): Promise<ValidateSeedResult> {\n const { path: resolvedPath, verbose = false } = options;\n const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n\n const db = LinesDB.create({ dataDir });\n let result;\n try {\n result = await db.initialize({ tableName, detailedValidate: true });\n } finally {\n await db.close();\n }\n\n const outputLines = formatWarnings(result.warnings);\n\n if (result.valid) {\n outputLines.push(\"✓ All records are valid\");\n return { valid: true, output: outputLines.join(\"\\n\") };\n }\n\n return {\n valid: false,\n output: outputLines.join(\"\\n\"),\n error: formatValidationErrors(result.errors, verbose),\n };\n}\n\n// A row's own value for a field, or undefined when the row has none.\nfunction ownValue(row: Record<string, unknown>, field: string): unknown {\n return Object.hasOwn(row, field) ? row[field] : undefined;\n}\n\n// Not `row[field] = value`: a field named `__proto__` goes through the inherited\n// setter, which leaves no own property for the serializer to read back.\nfunction setField(row: JsonObject, field: string, value: JsonObject[string]): void {\n Object.defineProperty(row, field, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n}\n\nfunction isBlank(value: unknown): boolean {\n if (value === undefined || value === null) {\n return true;\n }\n if (typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n // A nested field the row never had comes back as an object whose keys carry no\n // value of their own, and writing that into the line fills nothing in.\n return Object.values(value).every(isBlank);\n}\n\n/** A JSONL line, kept as text so an untouched line is written back verbatim. */\ntype SeedLine = {\n text: string;\n /** Line separator the file used after this line, kept so CRLF survives. */\n eol: string;\n row: JsonObject | undefined;\n};\n\nfunction splitLines(content: string): SeedLine[] {\n if (content === \"\") {\n return [];\n }\n return content.split(\"\\n\").map((raw, index, all) => {\n const eol = index === all.length - 1 ? \"\" : \"\\n\";\n const text = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n const carriage = raw.endsWith(\"\\r\") ? \"\\r\" : \"\";\n let row: JsonObject | undefined;\n if (text.trim() !== \"\") {\n try {\n const parsed: unknown = JSON.parse(text);\n row =\n parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as JsonObject)\n : undefined;\n } catch {\n row = undefined;\n }\n }\n return { text, eol: `${carriage}${eol}`, row };\n });\n}\n\n// Keys go in the order the hook produced them, which is the order the type\n// declares its fields. Keys the type does not declare follow the declared ones.\nfunction serializeRow(row: JsonObject, fieldOrder: string[]): string {\n const rank = new Map(fieldOrder.map((field, index) => [field, index]));\n const rankOf = (key: string): number => rank.get(key) ?? fieldOrder.length;\n return JSON.stringify(\n Object.fromEntries(Object.entries(row).toSorted(([a], [b]) => rankOf(a) - rankOf(b))),\n );\n}\n\ntype SeedHook = (row: unknown) => Record<string, unknown>;\n\nasync function loadSeedHook(dataDir: string, table: string): Promise<SeedHook | undefined> {\n const schemaPath = await findSchemaFile(dataDir, table);\n if (!schemaPath) {\n return undefined;\n }\n const loaded: unknown = await import(pathToFileURL(schemaPath).href);\n const hook = (loaded as { hook?: unknown }).hook;\n if (typeof hook !== \"function\") {\n throw new Error(\n `${schemaPath} does not export \\`hook\\`. Run \\`tailor generate\\` to regenerate the seed schema files.`,\n );\n }\n return hook as SeedHook;\n}\n\n/**\n * Fill in the values a record gets on create for the JSONL seed data rows that\n * are missing them, so a row can be referenced by `id` or carry a timestamp\n * before it is ever seeded.\n *\n * The values come from the type's own create-time behavior — its `id`, its field\n * defaults, and its create hooks — applied to each row on its own. Nothing is\n * validated, so a row can be filled while the data around it is still\n * incomplete: that is what lets you get the ids you need in order to write the\n * rows that reference them. Run `validateSeedData` when the data is ready.\n *\n * Only the named fields are written, and only into a row that has no value for\n * them, so a value already in the file is never replaced. A line that gains\n * nothing is written back exactly as it was, byte for byte; a line that does get\n * a value is re-serialized with its keys in the order the type declares its\n * fields, so a filled-in `id` lands at the front. A field the type gives no\n * value to — one it does not declare, or one the platform assigns such as a\n * serial field — is skipped, so one field list covers a whole data directory.\n *\n * The values are read from the schema files generated next to the data, and all\n * of them are read before anything is written: a file that predates the current\n * generator stops the run with nothing filled in anywhere.\n * @param options - Fill options including path and fields\n * @returns Which files received which fields\n */\nexport async function fillSeedData(options: FillSeedDataOptions): Promise<FillSeedDataResult> {\n const { path: resolvedPath, fields = DEFAULT_FILL_FIELDS } = options;\n if (fields.length === 0) {\n throw new Error(\"No fields to fill. Name at least one field.\");\n }\n const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n const tables = tableName ? [tableName] : await listSeedTables(dataDir);\n\n const warnings: string[] = [];\n const filled: FilledSeedFile[] = [];\n const producedFields = new Set<string>();\n\n // Every hook loads before anything is written, so a schema file that predates\n // `tailor generate` stops the run instead of leaving half the files filled.\n const hooks: { table: string; hook: SeedHook }[] = [];\n for (const table of tables) {\n const hook = await loadSeedHook(dataDir, table);\n if (!hook) {\n warnings.push(`No schema file for ${table}, so nothing can be filled in there`);\n continue;\n }\n hooks.push({ table, hook });\n }\n\n // Every line is decided before any file is written, so a hook that throws on\n // one table cannot leave another one already rewritten.\n const writes: { file: string; content: string }[] = [];\n for (const { table, hook } of hooks) {\n const file = join(dataDir, `${table}.jsonl`);\n const lines = splitLines(await readFile(file, \"utf-8\"));\n\n const written = new Set<string>();\n const unreadable: number[] = [];\n let count = 0;\n let fieldOrder: string[] = [];\n for (const [index, line] of lines.entries()) {\n const row = line.row;\n if (!row) {\n if (line.text.trim() !== \"\") {\n unreadable.push(index + 1);\n }\n continue;\n }\n const hooked = hook(row);\n fieldOrder = Object.keys(hooked);\n const gained = fields.filter((field) => {\n const value = ownValue(hooked, field);\n if (isBlank(value)) {\n return false;\n }\n producedFields.add(field);\n return isBlank(ownValue(row, field));\n });\n if (gained.length === 0) {\n continue;\n }\n for (const field of gained) {\n setField(row, field, hooked[field] as JsonObject[string]);\n written.add(field);\n }\n line.text = serializeRow(row, fieldOrder);\n count += 1;\n }\n\n if (unreadable.length > 0) {\n warnings.push(\n `${file}: line(s) ${unreadable.join(\", \")} are not JSON objects, so nothing was filled in there`,\n );\n }\n\n if (count === 0) {\n continue;\n }\n writes.push({ file, content: lines.map((line) => `${line.text}${line.eol}`).join(\"\") });\n filled.push({ table, file, fields: [...written], count });\n }\n\n for (const { file, content } of writes) {\n await writeFile(file, content);\n }\n\n const unproducedFields = fields.filter((field) => !producedFields.has(field));\n if (tables.length > 0 && unproducedFields.length > 0) {\n warnings.push(`No seed data produces a value for: ${unproducedFields.join(\", \")}`);\n }\n\n const outputLines = formatWarnings(warnings);\n outputLines.push(\n filled.length === 0\n ? \"\\u2713 Nothing to fill\"\n : filled\n .map(\n ({ file, fields: tableFields, count }) =>\n `\\u2713 ${file}: filled ${tableFields.join(\", \")} in ${count} row(s)`,\n )\n .join(\"\\n\"),\n );\n\n return { output: outputLines.join(\"\\n\"), filled };\n}\n"],"mappings":"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"}
|
|
@@ -164,7 +164,7 @@ function generateIdpUserSchemaFile(options) {
|
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
// Simple identity hook for _User (no TailorDB backing type)
|
|
167
|
-
const hook = <T>(data: unknown) => data as T;
|
|
167
|
+
export const hook = <T>(data: unknown) => data as T;
|
|
168
168
|
|
|
169
169
|
export const schema = defineSchema(
|
|
170
170
|
createStandardSchema(schemaType, hook),
|
|
@@ -324,10 +324,10 @@ function generateLinesDbSchemaFile(metadata, importPath) {
|
|
|
324
324
|
});
|
|
325
325
|
`}
|
|
326
326
|
|
|
327
|
-
const hook = createTailorDBHook(${exportName});
|
|
327
|
+
export const hook = createTailorDBHook(${exportName});
|
|
328
328
|
|
|
329
329
|
export const schema = defineSchema(
|
|
330
|
-
createStandardSchema(schemaType, hook),${generateSchemaOptions(foreignKeys, indexes)}
|
|
330
|
+
createStandardSchema(schemaType, hook, ${exportName}),${generateSchemaOptions(foreignKeys, indexes)}
|
|
331
331
|
);
|
|
332
332
|
|
|
333
333
|
`;
|
|
@@ -363,10 +363,10 @@ function generateLinesDbSchemaFileWithPluginAPI(metadata, params) {
|
|
|
363
363
|
|
|
364
364
|
${schemaTypeCode}
|
|
365
365
|
|
|
366
|
-
const hook = createTailorDBHook(${exportName});
|
|
366
|
+
export const hook = createTailorDBHook(${exportName});
|
|
367
367
|
|
|
368
368
|
export const schema = defineSchema(
|
|
369
|
-
createStandardSchema(schemaType, hook),${schemaOptionsCode}
|
|
369
|
+
createStandardSchema(schemaType, hook, ${exportName}),${schemaOptionsCode}
|
|
370
370
|
);
|
|
371
371
|
|
|
372
372
|
`;
|
|
@@ -383,10 +383,10 @@ function generateLinesDbSchemaFileWithPluginAPI(metadata, params) {
|
|
|
383
383
|
|
|
384
384
|
${schemaTypeCode}
|
|
385
385
|
|
|
386
|
-
const hook = createTailorDBHook(${exportName});
|
|
386
|
+
export const hook = createTailorDBHook(${exportName});
|
|
387
387
|
|
|
388
388
|
export const schema = defineSchema(
|
|
389
|
-
createStandardSchema(schemaType, hook),${schemaOptionsCode}
|
|
389
|
+
createStandardSchema(schemaType, hook, ${exportName}),${schemaOptionsCode}
|
|
390
390
|
);
|
|
391
391
|
|
|
392
392
|
`;
|
|
@@ -480,4 +480,4 @@ function seedPlugin(options) {
|
|
|
480
480
|
|
|
481
481
|
//#endregion
|
|
482
482
|
export { generateIdpTruncateScriptCode as a, generateIdpSeedScriptCode as i, seedPlugin as n, processIdpUser as o, processLinesDb as r, SeedGeneratorID as t };
|
|
483
|
-
//# sourceMappingURL=seed-
|
|
483
|
+
//# sourceMappingURL=seed-xqM9WIHP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-xqM9WIHP.mjs","names":["ml"],"sources":["../src/plugin/builtin/seed/idp-user-processor.ts","../src/plugin/builtin/seed/lines-db-processor.ts","../src/plugin/builtin/seed/index.ts"],"sourcesContent":["import ml from \"#/utils/multiline\";\nimport type { GeneratorAuthInput } from \"#/plugin/types\";\n\nexport interface IdpUserMetadata {\n name: \"_User\";\n dependencies: string[];\n dataFile: string;\n idpNamespace: string;\n schema: {\n usernameField: string;\n userTypeName: string;\n };\n}\n\n/**\n * Processes auth configuration to generate IdP user seed metadata\n * @param auth - Auth configuration from generator\n * @returns IdP user metadata or undefined if not applicable\n */\nexport function processIdpUser(auth: GeneratorAuthInput): IdpUserMetadata | undefined {\n // Only process if idProvider is BuiltInIdP and userProfile is defined\n if (auth.idProvider?.kind !== \"BuiltInIdP\" || !auth.userProfile) {\n return undefined;\n }\n\n const { typeName, usernameField } = auth.userProfile;\n\n return {\n name: \"_User\",\n dependencies: [typeName],\n dataFile: \"data/_User.jsonl\",\n idpNamespace: auth.idProvider.namespace,\n schema: {\n usernameField,\n userTypeName: typeName,\n },\n };\n}\n\n/**\n * Generates the server-side IDP seed script code for testExecScript execution.\n * Uses the global tailor.idp.Client - no bundling required.\n * @param idpNamespace - The IDP namespace name\n * @returns Script code string\n */\nexport function generateIdpSeedScriptCode(idpNamespace: string): string {\n return ml /* ts */ `\n export async function main(input) {\n const client = new tailor.idp.Client({ namespace: \"${idpNamespace}\" });\n const errors = [];\n let processed = 0;\n let created = 0;\n let updated = 0;\n let skipped = 0;\n const upsert = input.upsert === true;\n\n for (let i = 0; i < input.users.length; i++) {\n try {\n if (upsert) {\n let existing;\n let lookupError;\n try {\n existing = await client.userByName(input.users[i].name);\n } catch (error) {\n existing = undefined;\n lookupError = error instanceof Error ? error.message : String(error);\n }\n\n if (existing) {\n const { name, ...attributes } = input.users[i];\n if (Object.keys(attributes).length === 0) {\n skipped++;\n } else {\n await client.updateUser({ id: existing.id, ...attributes });\n updated++;\n }\n } else {\n try {\n await client.createUser(input.users[i]);\n created++;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n lookupError ? \\`create failed (\\${message}); lookup failed (\\${lookupError})\\` : message,\n );\n }\n }\n } else {\n await client.createUser(input.users[i]);\n created++;\n }\n processed++;\n console.log(\\`[_User] \\${i + 1}/\\${input.users.length}: \\${input.users[i].name}\\`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n errors.push(\\`Row \\${i} (\\${input.users[i].name}): \\${message}\\`);\n console.error(\\`[_User] Row \\${i} failed: \\${message}\\`);\n }\n }\n\n return {\n success: errors.length === 0,\n processed,\n created,\n updated,\n skipped,\n errors,\n };\n }\n `;\n}\n\n/**\n * Generates the server-side IDP truncation script code for testExecScript execution.\n * Lists all users with pagination and deletes each one.\n * @param idpNamespace - The IDP namespace name\n * @returns Script code string\n */\nexport function generateIdpTruncateScriptCode(idpNamespace: string): string {\n return ml /* ts */ `\n export async function main() {\n const client = new tailor.idp.Client({ namespace: \"${idpNamespace}\" });\n const errors = [];\n let deleted = 0;\n\n // List all users with pagination\n let after = undefined;\n const allUsers = [];\n do {\n const response = await client.users(after ? { after } : undefined);\n allUsers.push(...(response.users || []));\n after = response.nextPageToken;\n } while (after);\n\n console.log(\\`Found \\${allUsers.length} IDP users to delete\\`);\n\n for (const user of allUsers) {\n try {\n await client.deleteUser(user.id);\n deleted++;\n console.log(\\`[_User] Deleted \\${deleted}/\\${allUsers.length}: \\${user.name}\\`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n errors.push(\\`User \\${user.id} (\\${user.name}): \\${message}\\`);\n console.error(\\`[_User] Delete failed for \\${user.name}: \\${message}\\`);\n }\n }\n\n return {\n success: errors.length === 0,\n deleted,\n total: allUsers.length,\n errors,\n };\n }\n `;\n}\n\ntype GenerateIdpUserSchemaFileOptions = {\n usernameField: string;\n userTypeName: string;\n /**\n * When `true` (default), emit a foreign key from `_User.name` to the\n * userProfile type's username field so that seed validation rejects `_User`\n * rows without a matching userProfile row. Set to `false` to seed `_User`\n * rows that do not yet have a corresponding userProfile row.\n */\n includeUserProfileFK?: boolean;\n};\n\n/**\n * Generates the schema file content for IdP users. Emits the\n * `_User.name -> <userProfile>.<usernameField>` foreign key by default; pass\n * `includeUserProfileFK: false` to omit it (e.g. when seeding `_User` rows\n * that do not yet have a corresponding userProfile row).\n * @param options - Schema generation options\n * @param options.usernameField - Username field name\n * @param options.userTypeName - TailorDB user type name\n * @param options.includeUserProfileFK - Whether to emit the `_User -> userProfile` foreign key (default `true`)\n * @returns Schema file contents\n */\nexport function generateIdpUserSchemaFile(options: GenerateIdpUserSchemaFileOptions): string {\n const { usernameField, userTypeName, includeUserProfileFK = true } = options;\n const schemaBody = includeUserProfileFK\n ? ml`\n primaryKey: \"name\",\n indexes: [\n { name: \"_user_name_unique_idx\", columns: [\"name\"], unique: true },\n ],\n foreignKeys: [\n {\n column: \"name\",\n references: {\n table: \"${userTypeName}\",\n column: \"${usernameField}\",\n },\n },\n ],\n `\n : ml`\n primaryKey: \"name\",\n indexes: [\n { name: \"_user_name_unique_idx\", columns: [\"name\"], unique: true },\n ],\n `;\n\n return ml /* ts */ `\n import { t } from \"@tailor-platform/sdk\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createStandardSchema } from \"@tailor-platform/sdk/test\";\n\n const schemaType = t.object({\n name: t.string(),\n password: t.string(),\n });\n\n // Simple identity hook for _User (no TailorDB backing type)\n export const hook = <T>(data: unknown) => data as T;\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook),\n {\n ${schemaBody}\n }\n );\n\n `;\n}\n","import { isPluginGeneratedType } from \"#/parser/service/tailordb/type-source\";\nimport ml from \"#/utils/multiline\";\nimport type {\n PluginGeneratedTypeSource,\n TailorDBType,\n TypeSourceInfoEntry,\n} from \"#/parser/service/tailordb/types\";\nimport type { LinesDbMetadata } from \"./types\";\nimport type { ForeignKeyDefinition, IndexDefinition } from \"@toiroakr/lines-db\";\n\n/**\n * Processes TailorDB types to generate lines-db metadata\n * @param type - Parsed TailorDB type\n * @param source - Source file info\n * @returns Generated lines-db metadata\n */\nexport function processLinesDb(type: TailorDBType, source: TypeSourceInfoEntry): LinesDbMetadata {\n if (isPluginGeneratedType(source)) {\n // Plugin-generated type\n return processLinesDbForPluginType(type, source);\n }\n\n // User-defined type\n if (!source.filePath) {\n throw new Error(`Missing source info for type ${type.name}`);\n }\n if (!source.exportName) {\n throw new Error(`Missing export name for type ${type.name}`);\n }\n\n const { optionalFields, omitFields, indexes, foreignKeys } = extractFieldMetadata(type);\n\n return {\n typeName: type.name,\n exportName: source.exportName,\n importPath: source.filePath,\n optionalFields,\n omitFields,\n foreignKeys,\n indexes,\n };\n}\n\n/**\n * Process lines-db metadata for plugin-generated types\n * @param type - Parsed TailorDB type\n * @param source - Plugin-generated type source info\n * @returns Generated lines-db metadata with plugin source\n */\nfunction processLinesDbForPluginType(\n type: TailorDBType,\n source: PluginGeneratedTypeSource,\n): LinesDbMetadata {\n const { optionalFields, omitFields, indexes, foreignKeys } = extractFieldMetadata(type);\n\n return {\n typeName: type.name,\n exportName: source.exportName,\n importPath: \"\",\n optionalFields,\n omitFields,\n foreignKeys,\n indexes,\n pluginSource: source,\n };\n}\n\n/**\n * Extract field metadata from TailorDB type\n * @param type - Parsed TailorDB type\n * @returns Field metadata including optional fields, omit fields, indexes, and foreign keys\n */\nfunction extractFieldMetadata(type: TailorDBType): {\n optionalFields: string[];\n omitFields: string[];\n indexes: IndexDefinition[];\n foreignKeys: ForeignKeyDefinition[];\n} {\n const optionalFields = [\"id\"]; // id is always optional\n const omitFields: string[] = [];\n const indexes: IndexDefinition[] = [];\n const foreignKeys: ForeignKeyDefinition[] = [];\n\n // Find fields with hooks.create or serial\n for (const [fieldName, field] of Object.entries(type.fields)) {\n if (field.config.hooks?.create) {\n optionalFields.push(fieldName);\n }\n // Serial fields are auto-generated, so they should be optional in seed data\n if (field.config.serial) {\n omitFields.push(fieldName);\n }\n if (field.config.unique) {\n indexes.push({\n name: `${type.name.toLowerCase()}_${fieldName}_unique_idx`,\n columns: [fieldName],\n unique: true,\n });\n }\n }\n\n // Extract indexes\n if (type.indexes) {\n for (const [indexName, indexDef] of Object.entries(type.indexes)) {\n indexes.push({\n name: indexName,\n columns: indexDef.fields,\n unique: indexDef.unique,\n });\n }\n }\n\n // Extract foreign keys from relations\n for (const [fieldName, field] of Object.entries(type.fields)) {\n if (field.relation) {\n foreignKeys.push({\n column: fieldName,\n references: {\n table: field.relation.targetType,\n column: field.relation.key,\n },\n });\n }\n }\n\n return { optionalFields, omitFields, indexes, foreignKeys };\n}\n\n/**\n * Generate schema options code for lines-db\n * @param foreignKeys - Foreign key definitions\n * @param indexes - Index definitions\n * @returns Schema options code string\n */\nfunction generateSchemaOptions(\n foreignKeys: ForeignKeyDefinition[],\n indexes: IndexDefinition[],\n): string {\n const schemaOptions: string[] = [];\n\n if (foreignKeys.length > 0) {\n schemaOptions.push(`foreignKeys: [`);\n foreignKeys.forEach((fk) => {\n schemaOptions.push(` ${JSON.stringify(fk)},`);\n });\n schemaOptions.push(`],`);\n }\n\n if (indexes.length > 0) {\n schemaOptions.push(`indexes: [`);\n indexes.forEach((index) => {\n schemaOptions.push(` ${JSON.stringify(index)},`);\n });\n schemaOptions.push(\"],\");\n }\n\n return schemaOptions.length > 0\n ? [\"\\n {\", ...schemaOptions.map((option) => ` ${option}`), \" }\"].join(\"\\n\")\n : \"\";\n}\n\n/**\n * Generates the schema file content for lines-db (for user-defined types with import)\n * @param metadata - lines-db metadata\n * @param importPath - Import path for the TailorDB type\n * @returns Schema file contents\n */\nexport function generateLinesDbSchemaFile(metadata: LinesDbMetadata, importPath: string): string {\n const { exportName, optionalFields, omitFields, foreignKeys, indexes } = metadata;\n\n const schemaTypeCode = ml /* ts */ `\n const schemaType = t.object({\n ...${exportName}.pickFields(${JSON.stringify(optionalFields)}, { optional: true }),\n ...${exportName}.omitFields(${JSON.stringify([...optionalFields, ...omitFields])}),\n });\n `;\n\n const schemaOptionsCode = generateSchemaOptions(foreignKeys, indexes);\n\n return ml /* ts */ `\n import { t } from \"@tailor-platform/sdk\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n import { ${exportName} } from \"${importPath}\";\n\n ${schemaTypeCode}\n\n export const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook, ${exportName}),${schemaOptionsCode}\n );\n\n `;\n}\n\n/**\n * Parameters for generating plugin-type schema file\n */\nexport interface PluginSchemaParams {\n /** Relative path from schema output to tailor.config.ts */\n configImportPath: string;\n /** Relative import path to the original type file (for type-attached plugins) */\n originalImportPath?: string;\n}\n\n/**\n * Generates the schema file content using getGeneratedType API\n * (for plugin-generated types)\n * @param metadata - lines-db metadata (must have pluginSource)\n * @param params - Plugin import paths\n * @returns Schema file contents\n */\nexport function generateLinesDbSchemaFileWithPluginAPI(\n metadata: LinesDbMetadata,\n params: PluginSchemaParams,\n): string {\n const { typeName, exportName, optionalFields, omitFields, foreignKeys, indexes, pluginSource } =\n metadata;\n\n if (!pluginSource) {\n throw new Error(`pluginSource is required for plugin-generated type \"${typeName}\"`);\n }\n\n const { configImportPath, originalImportPath } = params;\n\n const schemaTypeCode = ml /* ts */ `\n const schemaType = t.object({\n ...${exportName}.pickFields(${JSON.stringify(optionalFields)}, { optional: true }),\n ...${exportName}.omitFields(${JSON.stringify([...optionalFields, ...omitFields])}),\n });\n `;\n\n const schemaOptionsCode = generateSchemaOptions(foreignKeys, indexes);\n\n // Type-attached plugin (e.g., changeset): import original type and use getGeneratedType(configPath, pluginId, type, kind)\n if (pluginSource.originalExportName && originalImportPath && pluginSource.generatedTypeKind) {\n return ml /* ts */ `\n import { join } from \"node:path\";\n import { t } from \"@tailor-platform/sdk\";\n import { getGeneratedType } from \"@tailor-platform/sdk/plugin\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n import { ${pluginSource.originalExportName} } from \"${originalImportPath}\";\n\n const configPath = join(import.meta.dirname, \"${configImportPath}\");\n const ${exportName} = await getGeneratedType(configPath, \"${pluginSource.pluginId}\", ${pluginSource.originalExportName}, \"${pluginSource.generatedTypeKind}\");\n\n ${schemaTypeCode}\n\n export const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook, ${exportName}),${schemaOptionsCode}\n );\n\n `;\n }\n\n // Namespace plugin (e.g., audit-log): use getGeneratedType(configPath, pluginId, null, kind)\n // For namespace plugins, generatedTypeKind is required\n if (!pluginSource.generatedTypeKind) {\n throw new Error(\n `Namespace plugin \"${pluginSource.pluginId}\" must provide generatedTypeKind for type \"${typeName}\"`,\n );\n }\n\n return ml /* ts */ `\n import { join } from \"node:path\";\n import { t } from \"@tailor-platform/sdk\";\n import { getGeneratedType } from \"@tailor-platform/sdk/plugin\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n\n const configPath = join(import.meta.dirname, \"${configImportPath}\");\n const ${exportName} = await getGeneratedType(configPath, \"${pluginSource.pluginId}\", null, \"${pluginSource.generatedTypeKind}\");\n\n ${schemaTypeCode}\n\n export const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook, ${exportName}),${schemaOptionsCode}\n );\n\n `;\n}\n","import * as path from \"pathe\";\nimport { assertDefined } from \"#/utils/assert\";\nimport { processIdpUser, generateIdpUserSchemaFile } from \"./idp-user-processor\";\nimport {\n processLinesDb,\n generateLinesDbSchemaFile,\n generateLinesDbSchemaFileWithPluginAPI,\n type PluginSchemaParams,\n} from \"./lines-db-processor\";\nimport type { Plugin, GeneratorResult, TailorDBReadyContext } from \"#/plugin/types\";\n\n/** Unique identifier for the seed generator plugin. */\nexport const SeedGeneratorID = \"@tailor-platform/seed\";\n\ntype DisableIdpUserSyncDirections = {\n /**\n * Skip emitting the foreign key from `<userProfile>.<usernameField>` to\n * `_User.name`. Defaults to `false` (FK emitted).\n *\n * Set to `true` to seed pre-registration states such as\n * invited-but-not-registered users.\n */\n userToIdp?: boolean;\n /**\n * Skip emitting the foreign key from `_User.name` to\n * `<userProfile>.<usernameField>`. Defaults to `false` (FK emitted).\n *\n * Set to `true` to seed `_User` rows that do not yet have a corresponding\n * userProfile row.\n */\n idpToUser?: boolean;\n};\n\nexport type SeedPluginOptions = {\n distPath: string;\n machineUserName?: string;\n /**\n * Disable individual `_User <-> userProfile` foreign keys emitted into\n * the generated seed schema. Both directions are emitted by default.\n *\n * Set a direction to `true` to relax it — for example to seed invited\n * users that do not yet have an IdP credential.\n */\n disableIdpUserSync?: DisableIdpUserSyncDirections;\n};\n\nfunction resolveIdpUserSyncFKs(option: SeedPluginOptions[\"disableIdpUserSync\"]): {\n emitUserToIdpFK: boolean;\n emitIdpToUserFK: boolean;\n} {\n return {\n emitUserToIdpFK: !(option?.userToIdp ?? false),\n emitIdpToUserFK: !(option?.idpToUser ?? false),\n };\n}\n/**\n * Plugin that generates seed data and schema files consumed by the\n * `tailor seed` commands (@tailor-platform/sdk-plugin-seed).\n * @param options - Plugin options\n * @param options.distPath - Output directory path for generated seed files\n * @param options.machineUserName - Default machine user name for authentication\n * @param options.disableIdpUserSync - Skip emitting individual `_User <-> userProfile` foreign keys. Both directions are emitted by default; set a direction to `true` to relax that side.\n * @returns Plugin instance with onTailorDBReady hook\n */\nexport function seedPlugin(options: SeedPluginOptions): Plugin<unknown, SeedPluginOptions> {\n return {\n id: SeedGeneratorID,\n description: \"Generates seed data and schema files for the tailor seed CLI plugin\",\n pluginConfig: options,\n\n async onTailorDBReady(ctx: TailorDBReadyContext<SeedPluginOptions>): Promise<GeneratorResult> {\n const files: GeneratorResult[\"files\"] = [];\n\n // Process IdP user early so we can add reverse FK to the user profile type\n const idpUser = ctx.auth ? (processIdpUser(ctx.auth) ?? null) : null;\n const idpUserSyncFKs = resolveIdpUserSyncFKs(ctx.pluginConfig.disableIdpUserSync);\n\n for (const ns of ctx.tailordb) {\n for (const [typeName, type] of Object.entries(ns.types)) {\n const source = assertDefined(\n ns.sourceInfo.get(typeName),\n `source info missing for type: ${typeName}`,\n );\n const linesDb = processLinesDb(type, source);\n\n // Add reverse FK from userProfile type to _User (opt-out via disableIdpUserSync.userToIdp: true)\n if (\n idpUserSyncFKs.emitUserToIdpFK &&\n idpUser &&\n typeName === idpUser.schema.userTypeName\n ) {\n linesDb.foreignKeys.push({\n column: idpUser.schema.usernameField,\n references: {\n table: \"_User\",\n column: \"name\",\n },\n });\n }\n\n // Generate empty JSONL data file\n files.push({\n path: path.join(ctx.pluginConfig.distPath, \"data\", `${linesDb.typeName}.jsonl`),\n content: \"\",\n skipIfExists: true,\n });\n\n const schemaOutputPath = path.join(\n ctx.pluginConfig.distPath,\n \"data\",\n `${linesDb.typeName}.schema.ts`,\n );\n\n // Plugin-generated type: use getGeneratedType API\n if (linesDb.pluginSource && linesDb.pluginSource.pluginImportPath) {\n // Build original type import path\n let originalImportPath: string | undefined;\n if (linesDb.pluginSource.originalFilePath && linesDb.pluginSource.originalExportName) {\n const relativePath = path.relative(\n path.dirname(schemaOutputPath),\n linesDb.pluginSource.originalFilePath,\n );\n originalImportPath = relativePath.replace(/\\.ts$/, \"\").startsWith(\".\")\n ? relativePath.replace(/\\.ts$/, \"\")\n : `./${relativePath.replace(/\\.ts$/, \"\")}`;\n }\n\n // Compute relative path from schema output to config file\n const configImportPath = path.relative(path.dirname(schemaOutputPath), ctx.configPath);\n\n const params: PluginSchemaParams = {\n configImportPath,\n originalImportPath,\n };\n\n const schemaContent = generateLinesDbSchemaFileWithPluginAPI(linesDb, params);\n\n files.push({\n path: schemaOutputPath,\n content: schemaContent,\n });\n } else {\n // User-defined type: import from source file\n const relativePath = path.relative(path.dirname(schemaOutputPath), linesDb.importPath);\n const typeImportPath = relativePath.replace(/\\.ts$/, \"\").startsWith(\".\")\n ? relativePath.replace(/\\.ts$/, \"\")\n : `./${relativePath.replace(/\\.ts$/, \"\")}`;\n const schemaContent = generateLinesDbSchemaFile(linesDb, typeImportPath);\n\n files.push({\n path: schemaOutputPath,\n content: schemaContent,\n });\n }\n }\n }\n\n if (idpUser) {\n // Generate empty JSONL data file\n files.push({\n path: path.join(ctx.pluginConfig.distPath, idpUser.dataFile),\n content: \"\",\n skipIfExists: true,\n });\n\n // Generate schema file with foreign key (opt-out via disableIdpUserSync.idpToUser: true)\n files.push({\n path: path.join(ctx.pluginConfig.distPath, \"data\", `${idpUser.name}.schema.ts`),\n content: generateIdpUserSchemaFile({\n usernameField: idpUser.schema.usernameField,\n userTypeName: idpUser.schema.userTypeName,\n includeUserProfileFK: idpUserSyncFKs.emitIdpToUserFK,\n }),\n });\n }\n\n return { files };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as isSdkBranded } from "./brand-Eo4pLXPJ.mjs";
|
|
2
|
-
import { n as logger, r as styles } from "./logger-
|
|
2
|
+
import { n as logger, r as styles } from "./logger-DSdExfor.mjs";
|
|
3
3
|
import * as path from "pathe";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import * as fs from "node:fs";
|
|
@@ -8,6 +8,40 @@ import { pathToFileURL } from "node:url";
|
|
|
8
8
|
//#region src/parser/service/common.ts
|
|
9
9
|
const functionSchema = z.custom((val) => typeof val === "function");
|
|
10
10
|
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/cli/shared/user-modules.ts
|
|
13
|
+
const IMPORT_NONCE_PARAM = "tailorImportNonce";
|
|
14
|
+
let runCounter = 0;
|
|
15
|
+
/**
|
|
16
|
+
* Start a fresh user-module run. Modules imported through
|
|
17
|
+
* {@link importUserModule} after this call bypass the ESM cache entries of
|
|
18
|
+
* earlier runs, so a repeated in-process run re-evaluates user code instead
|
|
19
|
+
* of reusing stale modules.
|
|
20
|
+
*/
|
|
21
|
+
function beginUserModuleRun() {
|
|
22
|
+
runCounter += 1;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read the cache-busting nonce of the current user-module run.
|
|
26
|
+
* @returns The nonce, or undefined when no run was started
|
|
27
|
+
*/
|
|
28
|
+
function currentImportNonce() {
|
|
29
|
+
return runCounter === 0 ? void 0 : String(runCounter);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Import a user module (config, workflow, resolver, executor, ...) with the
|
|
33
|
+
* current run's cache-busting nonce applied. Outside a run this is a plain
|
|
34
|
+
* import.
|
|
35
|
+
* @param filePath - Absolute path of the user module
|
|
36
|
+
* @returns The imported module namespace
|
|
37
|
+
*/
|
|
38
|
+
function importUserModule(filePath) {
|
|
39
|
+
const url = pathToFileURL(filePath);
|
|
40
|
+
const nonce = currentImportNonce();
|
|
41
|
+
if (nonce) url.searchParams.set(IMPORT_NONCE_PARAM, nonce);
|
|
42
|
+
return import(url.href);
|
|
43
|
+
}
|
|
44
|
+
|
|
11
45
|
//#endregion
|
|
12
46
|
//#region src/cli/services/file-loader.ts
|
|
13
47
|
const DEFAULT_IGNORE_PATTERNS = ["**/*.test.ts", "**/*.spec.ts"];
|
|
@@ -771,7 +805,7 @@ function stripExecutorTriggerArgs(executor) {
|
|
|
771
805
|
* @returns Parsed executor or null if invalid
|
|
772
806
|
*/
|
|
773
807
|
async function loadExecutor(executorFilePath) {
|
|
774
|
-
const executor = (await
|
|
808
|
+
const executor = (await importUserModule(executorFilePath)).default;
|
|
775
809
|
const parseResult = ExecutorSchema.safeParse(stripExecutorTriggerArgs(executor));
|
|
776
810
|
if (!parseResult.success) return null;
|
|
777
811
|
return parseResult.data;
|
|
@@ -791,7 +825,7 @@ function createExecutorService(params) {
|
|
|
791
825
|
let loadPromise;
|
|
792
826
|
const loadExecutorForFile = async (executorFile) => {
|
|
793
827
|
try {
|
|
794
|
-
const executorModule = await
|
|
828
|
+
const executorModule = await importUserModule(executorFile);
|
|
795
829
|
const result = ExecutorSchema.safeParse(stripExecutorTriggerArgs(executorModule.default));
|
|
796
830
|
if (result.success) {
|
|
797
831
|
const relativePath = path.relative(process.cwd(), executorFile);
|
|
@@ -860,5 +894,5 @@ function assertUniqueExecutorNames(executors) {
|
|
|
860
894
|
}
|
|
861
895
|
|
|
862
896
|
//#endregion
|
|
863
|
-
export { AuthConfigSchema as a, stripTailorDBTypeBuilderHelpers as c, applyRelationMetadataToFieldConfig as d, buildRelationInfo as f,
|
|
864
|
-
//# sourceMappingURL=service-
|
|
897
|
+
export { beginUserModuleRun as _, AuthConfigSchema as a, functionSchema as b, stripTailorDBTypeBuilderHelpers as c, applyRelationMetadataToFieldConfig as d, buildRelationInfo as f, IMPORT_NONCE_PARAM as g, loadFilesWithIgnores as h, ExecutorSchema as i, TailorDBServiceConfigSchema as l, validateRelationConfig as m, loadExecutor as n, AuthInvokerSchema as o, processRelationMetadata as p, stripExecutorTriggerArgs as r, TailorFieldSchema as s, createExecutorService as t, TailorDBTypeSchema as u, currentImportNonce as v, importUserModule as y };
|
|
898
|
+
//# sourceMappingURL=service-C_WpbKHu.mjs.map
|