@tailor-platform/sdk 2.19.0 → 2.21.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 +51 -0
- package/dist/application-D4913edS.mjs +199 -0
- package/dist/application-D4913edS.mjs.map +1 -0
- package/dist/application-DBHaxmJE.mjs +1 -0
- package/dist/cli/commands/deploy/application.d.mts +1 -0
- package/dist/cli/commands/deploy/apply-phases.d.mts +1 -1
- package/dist/cli/commands/deploy/deploy.d.mts +1 -1
- package/dist/cli/commands/deploy/deployment-target.d.mts +1 -0
- package/dist/cli/commands/deploy/executor.d.mts +1 -1
- package/dist/cli/commands/deploy/function-registry.d.mts +1 -1
- package/dist/cli/commands/deploy/resolver.d.mts +1 -0
- package/dist/cli/commands/deploy/secret-manager.d.mts +1 -1
- package/dist/cli/commands/deploy/tailordb/migration-validation.d.mts +1 -1
- package/dist/cli/commands/deploy/tailordb/plan.d.mts +1 -1
- package/dist/cli/commands/deploy/types.d.mts +1 -1
- package/dist/cli/commands/deploy/workflow.d.mts +1 -0
- package/dist/cli/commands/tailordb/migrate/schema-checks.d.mts +1 -1
- package/dist/cli/lib.d.mts +1 -1
- package/dist/cli/lib.mjs +1 -1
- package/dist/cli/main.d.mts +1 -0
- package/dist/cli/main.mjs +37 -37
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/services/application.d.mts +2 -0
- package/dist/completion/zsh-worker.zsh +654 -131
- package/dist/configure/index.d.mts +2 -1
- package/dist/configure/index.mjs +12 -1
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/secrets/index.d.mts +14 -0
- package/dist/configure/services/tailordb/schema.d.mts +3 -2
- package/dist/configure/types/secret-vault-name.d.mts +32 -0
- package/dist/{crashreport-Cyuz1qiu.mjs → crashreport-BAz-ph5O.mjs} +3 -3
- package/dist/{crashreport-Cyuz1qiu.mjs.map → crashreport-BAz-ph5O.mjs.map} +1 -1
- package/dist/crashreport-BfOkOFgc.mjs +1 -0
- package/dist/{date-B7-oyTGE.mjs → date-Dri6Yas-.mjs} +2 -2
- package/dist/date-Dri6Yas-.mjs.map +1 -0
- package/dist/{errors-BjJnpXkK.mjs → errors-sNtg2Mv3.mjs} +2 -2
- package/dist/{errors-BjJnpXkK.mjs.map → errors-sNtg2Mv3.mjs.map} +1 -1
- package/dist/es-builtins-SfvXRVoG.mjs +2 -0
- package/dist/{es-builtins-BHCXINQp.mjs.map → es-builtins-SfvXRVoG.mjs.map} +1 -1
- package/dist/field-parse-DhnFs9WG.mjs +2 -0
- package/dist/field-parse-DhnFs9WG.mjs.map +1 -0
- package/dist/{globals-CMHSnj4w.mjs → globals-BCmGX05J.mjs} +2 -2
- package/dist/{globals-CMHSnj4w.mjs.map → globals-BCmGX05J.mjs.map} +1 -1
- package/dist/guards-DsX7dJCP.mjs +2 -0
- package/dist/{guards-ForsrxnH.mjs.map → guards-DsX7dJCP.mjs.map} +1 -1
- package/dist/{kysely-type-B_oA8D1k.mjs → kysely-type-C-iyFFH7.mjs} +2 -2
- package/dist/{kysely-type-B_oA8D1k.mjs.map → kysely-type-C-iyFFH7.mjs.map} +1 -1
- package/dist/{logger-72hM4JWZ.mjs → logger-Cp5U2IDI.mjs} +2 -2
- package/dist/{logger-72hM4JWZ.mjs.map → logger-Cp5U2IDI.mjs.map} +1 -1
- package/dist/manager-C0dE7pd0.mjs +2 -0
- package/dist/{manager-qfbUL7ru.mjs.map → manager-C0dE7pd0.mjs.map} +1 -1
- package/dist/plugin/builtin/enum-constants/index.mjs +1 -1
- package/dist/plugin/builtin/enum-constants/index.mjs.map +1 -1
- package/dist/plugin/builtin/file-utils/index.mjs +1 -1
- package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
- package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
- package/dist/plugin/index.mjs +1 -1
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/register-ts-hook-YtsAGTMD.mjs +921 -0
- package/dist/register-ts-hook-YtsAGTMD.mjs.map +1 -0
- package/dist/registry-BijIDRVA.mjs +2 -0
- package/dist/{registry-HlEaGvl5.mjs.map → registry-BijIDRVA.mjs.map} +1 -1
- package/dist/repl-editor-DGJBfIt1.mjs +2 -0
- package/dist/{repl-editor-BG1aDOfH.mjs.map → repl-editor-DGJBfIt1.mjs.map} +1 -1
- package/dist/runtime/field-parse.d.mts +17 -1
- package/dist/runtime/index.d.mts +2 -1
- package/dist/runtime/index.mjs +1 -1
- package/dist/runtime/secretmanager.d.mts +8 -20
- package/dist/schema-CRtAmDcl.mjs +2 -0
- package/dist/{schema-BKvcWdVf.mjs.map → schema-CRtAmDcl.mjs.map} +1 -1
- package/dist/secretmanager-vHQoXdQz.mjs.map +1 -1
- package/dist/seed/index.mjs +2 -2
- package/dist/seed/index.mjs.map +1 -1
- package/dist/{service-B4Gh_bbL.mjs → service-8xznUrPU.mjs} +2 -2
- package/dist/{service-B4Gh_bbL.mjs.map → service-8xznUrPU.mjs.map} +1 -1
- package/dist/{service-CuqkHP1r.mjs → service-BrGSi2IQ.mjs} +3 -3
- package/dist/{service-CuqkHP1r.mjs.map → service-BrGSi2IQ.mjs.map} +1 -1
- package/dist/service-DWMm-4Vm.mjs +1 -0
- package/dist/service_pb-DNskuJQC.mjs +1 -0
- package/dist/service_pb-y2GYIgfs.mjs +2 -0
- package/dist/{service_pb-DprmLNsq.mjs.map → service_pb-y2GYIgfs.mjs.map} +1 -1
- package/dist/tailordb-ddl-DqInYupv.mjs +7 -0
- package/dist/{tailordb-ddl-Fgm2cNvT.mjs.map → tailordb-ddl-DqInYupv.mjs.map} +1 -1
- package/dist/utils/test/index.mjs +1 -1
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/vitest/environment.mjs +1 -1
- package/dist/vitest/environment.mjs.map +1 -1
- package/dist/vitest/index.mjs +1 -1
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/setup.mjs +1 -1
- package/dist/workspace_resource_pb-D2njwgsE.mjs +2 -0
- package/dist/{workspace_resource_pb-BOoRts_z.mjs.map → workspace_resource_pb-D2njwgsE.mjs.map} +1 -1
- package/docs/configuration.md +18 -1
- package/docs/migration/v3.md +64 -0
- package/docs/services/resolver.md +21 -0
- package/docs/services/secret.md +20 -15
- package/docs/services/staticwebsite.md +17 -0
- package/package.json +14 -14
- package/dist/application-BYlhYQyh.mjs +0 -199
- package/dist/application-BYlhYQyh.mjs.map +0 -1
- package/dist/application-KZRJHv2j.mjs +0 -1
- package/dist/crashreport-CgymxQDu.mjs +0 -1
- package/dist/date-B7-oyTGE.mjs.map +0 -1
- package/dist/es-builtins-BHCXINQp.mjs +0 -2
- package/dist/field-parse-BbNyotv5.mjs +0 -2
- package/dist/field-parse-BbNyotv5.mjs.map +0 -1
- package/dist/guards-ForsrxnH.mjs +0 -2
- package/dist/manager-qfbUL7ru.mjs +0 -2
- package/dist/register-ts-hook-Djjnj5R8.mjs +0 -917
- package/dist/register-ts-hook-Djjnj5R8.mjs.map +0 -1
- package/dist/registry-HlEaGvl5.mjs +0 -2
- package/dist/repl-editor-BG1aDOfH.mjs +0 -2
- package/dist/schema-BKvcWdVf.mjs +0 -2
- package/dist/service-BgrEteZ2.mjs +0 -1
- package/dist/service_pb-DKrsO1_u.mjs +0 -1
- package/dist/service_pb-DprmLNsq.mjs +0 -2
- package/dist/tailordb-ddl-Fgm2cNvT.mjs +0 -7
- package/dist/workspace_resource_pb-BOoRts_z.mjs +0 -2
package/dist/seed/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/seed/record-lines.ts","../../src/seed/index.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { ValidationErrorDetail } from \"@toiroakr/lines-db\";\n\n/**\n * Resolve the 1-based physical line a JSONL record occupies.\n *\n * The seed reader drops blank and whitespace-only lines before indexing its\n * records, so a record's index is not its line number once a file contains\n * one. Reported locations have to point at the line a reader would open.\n * @param content - Raw JSONL file contents\n * @param recordIndex - 0-based index among the file's non-blank lines\n * @returns 1-based physical line, or undefined when the file holds no such record\n */\nexport function physicalLineOfRecord(content: string, recordIndex: number): number | undefined {\n const lines = content.split(\"\\n\");\n let remaining = recordIndex;\n for (const [index, line] of lines.entries()) {\n if (line.trim().length === 0) continue;\n if (remaining === 0) return index + 1;\n remaining -= 1;\n }\n return undefined;\n}\n\n/**\n * Locate the first reported error in its own file.\n *\n * Errors are reported per record, so the index is mapped back to a physical\n * line; a file that cannot be re-read yields the file alone rather than a\n * guessed line.\n * @param errors - Validation errors in the order the validator reported them\n * @returns Location of the first error, or undefined when there is none\n */\nexport async function firstErrorLocation(\n errors: readonly ValidationErrorDetail[],\n): Promise<{ file: string; line?: number } | undefined> {\n const first = errors[0];\n if (!first) return undefined;\n try {\n const content = await readFile(first.file, \"utf-8\");\n return { file: first.file, line: physicalLineOfRecord(content, first.rowIndex) };\n } catch {\n return { file: first.file };\n }\n}\n","/**\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 { firstErrorLocation } from \"./record-lines\";\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 | {\n valid: false;\n output: string;\n error: string;\n /** Where the first reported error sits, for tooling that links to source. */\n location?: { file: string; line?: number };\n };\n\n/**\n * A JSONL seed file that received values.\n */\ntype FilledSeedFile = {\n /** Name of the seeded table, 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 location: await firstErrorLocation(result.errors),\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 table\n// declares its fields. Keys the table 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 table'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 table declares its\n * fields, so a filled-in `id` lands at the front. A field the table 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":"0RAaA,SAAgB,qBAAqB,EAAiB,EAAyC,CAC7F,IAAM,EAAQ,EAAQ,MAAM;CAAI,EAC5B,EAAY,EAChB,IAAK,GAAM,CAAC,EAAO,KAAS,EAAM,QAAQ,EACpC,KAAK,KAAK,CAAC,CAAC,SAAW,EAC3B,IAAI,IAAc,EAAG,OAAO,EAAQ,EACpC,GADoC,CAIxC,CAWA,eAAsB,mBACpB,EACsD,CACtD,IAAM,EAAQ,EAAO,GAChB,KACL,GAAI,CACF,IAAM,EAAU,MAAM,EAAS,EAAM,KAAM,OAAO,EAClD,MAAO,CAAE,KAAM,EAAM,KAAM,KAAM,qBAAqB,EAAS,EAAM,QAAQ,CAAE,CACjF,MAAQ,CACN,MAAO,CAAE,KAAM,EAAM,IAAK,CAC5B,CACF,CCvBA,MAAM,EAAsB,CAAC,IAAI,EAkDjC,eAAe,sBAAsB,EAA+C,CAClF,IAAM,EAAQ,MAAM,EAAK,CAAY,EACrC,GAAI,EAAM,YAAY,EACpB,MAAO,CAAE,QAAS,CAAa,EAEjC,GAAI,EAAM,OAAO,GAAK,EAAa,SAAS,QAAQ,EAClD,MAAO,CAAE,QAAS,EAAQ,CAAY,EAAG,UAAW,EAAS,EAAc,QAAQ,CAAE,EAEvF,MAAU,MAAM,iBAAiB,EAAa,sCAAsC,CACtF,CAEA,eAAe,eAAe,EAAoC,CAEhE,OAAO,MADe,EAAQ,CAAO,EAAA,CAElC,OAAQ,GAAU,EAAM,SAAS,QAAQ,CAAC,CAAC,CAC3C,IAAK,GAAU,EAAS,EAAO,QAAQ,CAAC,CAAC,CACzC,SAAS,CACd,CAEA,SAAS,eAAe,EAA8B,CAIpD,OAHI,EAAS,SAAW,EACf,CAAC,EAEH,CAAC,GAAG,EAAS,IAAK,GAAY,KAAK,GAAS,EAAG,EAAE,CAC1D,CAEA,SAAS,uBAAuB,EAAiC,EAA0B,CACzF,IAAM,EAAY,IAAI,EAAe,CAAE,SAAQ,CAAC,EAC1C,EAAuB,CAAC,EACxB,EAAe,IAAI,IACzB,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAa,EAAa,IAAI,EAAM,IAAI,GAAK,CAAC,EACpD,EAAW,KAAK,CAAK,EACrB,EAAa,IAAI,EAAM,KAAM,CAAU,CACzC,CACA,IAAK,GAAM,CAAC,EAAM,KAAe,EAAc,CAC7C,EAAW,KAAK,EAAU,kBAAkB,EAAW,OAAQ,CAAI,CAAC,EACpE,EAAW,KAAK,EAAE,EAClB,IAAM,EAAmB,EAAW,OACjC,GAAM,EAAE,OAAS,cAAgB,CAAC,EAAE,eACvC,EACM,EAAmB,EAAW,OAAQ,GAAM,EAAE,OAAS,cAAgB,EAAE,eAAe,EAC1F,EAAiB,OAAS,GAC5B,EAAW,KACT,EAAU,uBACR,EAAiB,IAAK,IAAO,CAC3B,KAAM,EAAE,KACR,SAAU,EAAE,SACZ,OAAQ,EAAE,MACZ,EAAE,CACJ,CACF,EAEF,IAAK,IAAM,KAAW,EAChB,EAAQ,iBACV,EAAW,KACT,EAAU,sBAAsB,CAC9B,KAAM,EAAQ,KACd,SAAU,EAAQ,SAClB,OAAQ,EAAQ,gBAAgB,OAChC,MAAO,EAAQ,gBAAgB,MAC/B,gBAAiB,EAAQ,gBAAgB,gBACzC,iBAAkB,EAAQ,gBAAgB,gBAC5C,CAAC,CACH,EAGJ,EAAW,KAAK,EAAE,CACpB,CAEA,OAAO,EAAW,KAAK;CAAI,CAC7B,CASA,eAAsB,iBACpB,EAC6B,CAC7B,GAAM,CAAE,KAAM,EAAc,UAAU,IAAU,EAC1C,CAAE,UAAS,aAAc,MAAM,sBAAsB,CAAY,EAEjE,EAAK,EAAQ,OAAO,CAAE,SAAQ,CAAC,EACjC,EACJ,GAAI,CACF,EAAS,MAAM,EAAG,WAAW,CAAE,YAAW,iBAAkB,EAAK,CAAC,CACpE,QAAU,CACR,MAAM,EAAG,MAAM,CACjB,CAEA,IAAM,EAAc,eAAe,EAAO,QAAQ,EAOlD,OALI,EAAO,OACT,EAAY,KAAK,yBAAyB,EACnC,CAAE,MAAO,GAAM,OAAQ,EAAY,KAAK;CAAI,CAAE,GAGhD,CACL,MAAO,GACP,OAAQ,EAAY,KAAK;CAAI,EAC7B,MAAO,uBAAuB,EAAO,OAAQ,CAAO,EACpD,SAAU,MAAM,mBAAmB,EAAO,MAAM,CAClD,CACF,CAGA,SAAS,SAAS,EAA8B,EAAwB,CACtE,OAAO,OAAO,OAAO,EAAK,CAAK,EAAI,EAAI,GAAS,IAAA,EAClD,CAIA,SAAS,SAAS,EAAiB,EAAe,EAAiC,CACjF,OAAO,eAAe,EAAK,EAAO,CAChC,QACA,WAAY,GACZ,SAAU,GACV,aAAc,EAChB,CAAC,CACH,CAEA,SAAS,QAAQ,EAAyB,CASxC,OARI,GAAiC,KAC5B,GAEL,OAAO,GAAU,UAAY,MAAM,QAAQ,CAAK,EAC3C,GAIF,OAAO,OAAO,CAAK,CAAC,CAAC,MAAM,OAAO,CAC3C,CAUA,SAAS,WAAW,EAA6B,CAI/C,OAHI,IAAY,GACP,CAAC,EAEH,EAAQ,MAAM;CAAI,CAAC,CAAC,KAAK,EAAK,EAAO,IAAQ,CAClD,IAAM,EAAM,IAAU,EAAI,OAAS,EAAI,GAAK;EACtC,EAAO,EAAI,SAAS,IAAI,EAAI,EAAI,MAAM,EAAG,EAAE,EAAI,EAC/C,EAAW,EAAI,SAAS,IAAI,EAAI,KAAO,GACzC,EACJ,GAAI,EAAK,KAAK,IAAM,GAClB,GAAI,CACF,IAAM,EAAkB,KAAK,MAAM,CAAI,EACvC,EACqB,OAAO,GAAW,UAArC,GAAiD,CAAC,MAAM,QAAQ,CAAM,EACjE,EACD,IAAA,EACR,MAAQ,CACN,EAAM,IAAA,EACR,CAEF,MAAO,CAAE,OAAM,IAAK,GAAG,IAAW,IAAO,KAAI,CAC/C,CAAC,CACH,CAIA,SAAS,aAAa,EAAiB,EAA8B,CACnE,IAAM,EAAO,IAAI,IAAI,EAAW,KAAK,EAAO,IAAU,CAAC,EAAO,CAAK,CAAC,CAAC,EAC/D,OAAU,GAAwB,EAAK,IAAI,CAAG,GAAK,EAAW,OACpE,OAAO,KAAK,UACV,OAAO,YAAY,OAAO,QAAQ,CAAG,CAAC,CAAC,UAAU,CAAC,GAAI,CAAC,KAAO,OAAO,CAAC,EAAI,OAAO,CAAC,CAAC,CAAC,CACtF,CACF,CAIA,eAAe,aAAa,EAAiB,EAA8C,CACzF,IAAM,EAAa,MAAM,EAAe,EAAS,CAAK,EACtD,GAAI,CAAC,EACH,OAGF,IAAM,GAAQ,MADgB,OAAO,EAAc,CAAU,CAAC,CAAC,MAAA,CACnB,KAC5C,GAAI,OAAO,GAAS,WAClB,MAAU,MACR,GAAG,EAAW,wFAChB,EAEF,OAAO,CACT,CA2BA,eAAsB,aAAa,EAA2D,CAC5F,GAAM,CAAE,KAAM,EAAc,SAAS,GAAwB,EAC7D,GAAI,EAAO,SAAW,EACpB,MAAU,MAAM,6CAA6C,EAE/D,GAAM,CAAE,UAAS,aAAc,MAAM,sBAAsB,CAAY,EACjE,EAAS,EAAY,CAAC,CAAS,EAAI,MAAM,eAAe,CAAO,EAE/D,EAAqB,CAAC,EACtB,EAA2B,CAAC,EAC5B,EAAiB,IAAI,IAIrB,EAA6C,CAAC,EACpD,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,MAAM,aAAa,EAAS,CAAK,EAC9C,GAAI,CAAC,EAAM,CACT,EAAS,KAAK,sBAAsB,EAAM,oCAAoC,EAC9E,QACF,CACA,EAAM,KAAK,CAAE,QAAO,MAAK,CAAC,CAC5B,CAIA,IAAM,EAA8C,CAAC,EACrD,IAAK,GAAM,CAAE,QAAO,UAAU,EAAO,CACnC,IAAM,EAAO,EAAK,EAAS,GAAG,EAAM,OAAO,EACrC,EAAQ,WAAW,MAAM,EAAS,EAAM,OAAO,CAAC,EAEhD,EAAU,IAAI,IACd,EAAuB,CAAC,EAC1B,EAAQ,EACR,EAAuB,CAAC,EAC5B,IAAK,GAAM,CAAC,EAAO,KAAS,EAAM,QAAQ,EAAG,CAC3C,IAAM,EAAM,EAAK,IACjB,GAAI,CAAC,EAAK,CACJ,EAAK,KAAK,KAAK,IAAM,IACvB,EAAW,KAAK,EAAQ,CAAC,EAE3B,QACF,CACA,IAAM,EAAS,EAAK,CAAG,EACvB,EAAa,OAAO,KAAK,CAAM,EAC/B,IAAM,EAAS,EAAO,OAAQ,GAE5B,CAAI,QADU,SAAS,EAAQ,CACf,CAAC,IAGjB,EAAe,IAAI,CAAK,EACjB,QAAQ,SAAS,EAAK,CAAK,CAAC,EACpC,EACG,KAAO,SAAW,EAGtB,KAAK,IAAM,KAAS,EAClB,SAAS,EAAK,EAAO,EAAO,EAA4B,EACxD,EAAQ,IAAI,CAAK,EAEnB,EAAK,KAAO,aAAa,EAAK,CAAU,EACxC,GAAS,CAFT,CAGF,CAEI,EAAW,OAAS,GACtB,EAAS,KACP,GAAG,EAAK,YAAY,EAAW,KAAK,IAAI,EAAE,sDAC5C,EAGE,IAAU,IAGd,EAAO,KAAK,CAAE,OAAM,QAAS,EAAM,IAAK,GAAS,GAAG,EAAK,OAAO,EAAK,KAAK,CAAC,CAAC,KAAK,EAAE,CAAE,CAAC,EACtF,EAAO,KAAK,CAAE,QAAO,OAAM,OAAQ,CAAC,GAAG,CAAO,EAAG,OAAM,CAAC,EAC1D,CAEA,IAAK,GAAM,CAAE,OAAM,aAAa,EAC9B,MAAM,EAAU,EAAM,CAAO,EAG/B,IAAM,EAAmB,EAAO,OAAQ,GAAU,CAAC,EAAe,IAAI,CAAK,CAAC,EACxE,EAAO,OAAS,GAAK,EAAiB,OAAS,GACjD,EAAS,KAAK,sCAAsC,EAAiB,KAAK,IAAI,GAAG,EAGnF,IAAM,EAAc,eAAe,CAAQ,EAY3C,OAXA,EAAY,KACV,EAAO,SAAW,EACd,oBACA,EACG,KACE,CAAE,OAAM,OAAQ,EAAa,WAC5B,UAAU,EAAK,WAAW,EAAY,KAAK,IAAI,EAAE,MAAM,EAAM,QACjE,CAAC,CACA,KAAK;CAAI,CAClB,EAEO,CAAE,OAAQ,EAAY,KAAK;CAAI,EAAG,QAAO,CAClD"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/seed/record-lines.ts","../../src/seed/index.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { ValidationErrorDetail } from \"@toiroakr/lines-db\";\n\n/**\n * Resolve the 1-based physical line a JSONL record occupies.\n *\n * The seed reader drops blank and whitespace-only lines before indexing its\n * records, so a record's index is not its line number once a file contains\n * one. Reported locations have to point at the line a reader would open.\n * @param content - Raw JSONL file contents\n * @param recordIndex - 0-based index among the file's non-blank lines\n * @returns 1-based physical line, or undefined when the file holds no such record\n */\nexport function physicalLineOfRecord(content: string, recordIndex: number): number | undefined {\n const lines = content.split(\"\\n\");\n let remaining = recordIndex;\n for (const [index, line] of lines.entries()) {\n if (line.trim().length === 0) continue;\n if (remaining === 0) return index + 1;\n remaining -= 1;\n }\n return undefined;\n}\n\n/**\n * Locate the first reported error in its own file.\n *\n * Errors are reported per record, so the index is mapped back to a physical\n * line; a file that cannot be re-read yields the file alone rather than a\n * guessed line.\n * @param errors - Validation errors in the order the validator reported them\n * @returns Location of the first error, or undefined when there is none\n */\nexport async function firstErrorLocation(\n errors: readonly ValidationErrorDetail[],\n): Promise<{ file: string; line?: number } | undefined> {\n const first = errors[0];\n if (!first) return undefined;\n try {\n const content = await readFile(first.file, \"utf-8\");\n return { file: first.file, line: physicalLineOfRecord(content, first.rowIndex) };\n } catch {\n return { file: first.file };\n }\n}\n","/**\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 { firstErrorLocation } from \"./record-lines\";\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 | {\n valid: false;\n output: string;\n error: string;\n /** Where the first reported error sits, for tooling that links to source. */\n location?: { file: string; line?: number };\n };\n\n/**\n * A JSONL seed file that received values.\n */\ntype FilledSeedFile = {\n /** Name of the seeded table, 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 location: await firstErrorLocation(result.errors),\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 table\n// declares its fields. Keys the table 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 table'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 table declares its\n * fields, so a filled-in `id` lands at the front. A field the table 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":"0RAaA,SAAgB,qBAAqB,EAAiB,EAAyC,CAC7F,IAAM,EAAQ,EAAQ,MAAM;CAAI,EAC5B,EAAY,EAChB,IAAK,GAAM,CAAC,EAAO,KAAS,EAAM,QAAQ,EACpC,KAAK,KAAK,CAAC,CAAC,SAAW,EAC3B,IAAI,IAAc,EAAG,OAAO,EAAQ,EACpC,GADoC,CAIxC,CAWA,eAAsB,mBACpB,EACsD,CACtD,IAAM,EAAQ,EAAO,GAChB,KACL,GAAI,CACF,IAAM,EAAU,MAAM,EAAS,EAAM,KAAM,OAAO,EAClD,MAAO,CAAE,KAAM,EAAM,KAAM,KAAM,qBAAqB,EAAS,EAAM,QAAQ,CAAE,CACjF,MAAQ,CACN,MAAO,CAAE,KAAM,EAAM,IAAK,CAC5B,CACF,CCvBA,MAAM,EAAsB,CAAC,IAAI,EAkDjC,eAAe,sBAAsB,EAA+C,CAClF,IAAM,EAAQ,MAAM,EAAK,CAAY,EACrC,GAAI,EAAM,YAAY,EACpB,MAAO,CAAE,QAAS,CAAa,EAEjC,GAAI,EAAM,OAAO,GAAK,EAAa,SAAS,QAAQ,EAClD,MAAO,CAAE,QAAS,EAAQ,CAAY,EAAG,UAAW,EAAS,EAAc,QAAQ,CAAE,EAEvF,MAAU,MAAM,iBAAiB,EAAa,sCAAsC,CACtF,CAEA,eAAe,eAAe,EAAoC,CAEhE,OAAO,MADe,EAAQ,CAAO,EAAA,CAElC,OAAQ,GAAU,EAAM,SAAS,QAAQ,CAAC,CAAC,CAC3C,IAAK,GAAU,EAAS,EAAO,QAAQ,CAAC,CAAC,CACzC,SAAS,CACd,CAEA,SAAS,eAAe,EAA8B,CAIpD,OAHI,EAAS,SAAW,EACf,CAAC,EAEH,CAAC,GAAG,EAAS,IAAK,GAAY,KAAK,GAAS,EAAG,EAAE,CAC1D,CAEA,SAAS,uBAAuB,EAAiC,EAA0B,CACzF,IAAM,EAAY,IAAI,EAAe,CAAE,SAAQ,CAAC,EAC1C,EAAuB,CAAC,EACxB,kBAAe,IAAI,IACzB,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAa,EAAa,IAAI,EAAM,IAAI,GAAK,CAAC,EACpD,EAAW,KAAK,CAAK,EACrB,EAAa,IAAI,EAAM,KAAM,CAAU,CACzC,CACA,IAAK,GAAM,CAAC,EAAM,KAAe,EAAc,CAC7C,EAAW,KAAK,EAAU,kBAAkB,EAAW,OAAQ,CAAI,CAAC,EACpE,EAAW,KAAK,EAAE,EAClB,IAAM,EAAmB,EAAW,OACjC,GAAM,EAAE,OAAS,cAAgB,CAAC,EAAE,eACvC,EACM,EAAmB,EAAW,OAAQ,GAAM,EAAE,OAAS,cAAgB,EAAE,eAAe,EAC1F,EAAiB,OAAS,GAC5B,EAAW,KACT,EAAU,uBACR,EAAiB,IAAK,IAAO,CAC3B,KAAM,EAAE,KACR,SAAU,EAAE,SACZ,OAAQ,EAAE,MACZ,EAAE,CACJ,CACF,EAEF,IAAK,IAAM,KAAW,EAChB,EAAQ,iBACV,EAAW,KACT,EAAU,sBAAsB,CAC9B,KAAM,EAAQ,KACd,SAAU,EAAQ,SAClB,OAAQ,EAAQ,gBAAgB,OAChC,MAAO,EAAQ,gBAAgB,MAC/B,gBAAiB,EAAQ,gBAAgB,gBACzC,iBAAkB,EAAQ,gBAAgB,gBAC5C,CAAC,CACH,EAGJ,EAAW,KAAK,EAAE,CACpB,CAEA,OAAO,EAAW,KAAK;CAAI,CAC7B,CASA,eAAsB,iBACpB,EAC6B,CAC7B,GAAM,CAAE,KAAM,EAAc,UAAU,IAAU,EAC1C,CAAE,UAAS,aAAc,MAAM,sBAAsB,CAAY,EAEjE,EAAK,EAAQ,OAAO,CAAE,SAAQ,CAAC,EACjC,EACJ,GAAI,CACF,EAAS,MAAM,EAAG,WAAW,CAAE,YAAW,iBAAkB,EAAK,CAAC,CACpE,QAAU,CACR,MAAM,EAAG,MAAM,CACjB,CAEA,IAAM,EAAc,eAAe,EAAO,QAAQ,EAOlD,OALI,EAAO,OACT,EAAY,KAAK,yBAAyB,EACnC,CAAE,MAAO,GAAM,OAAQ,EAAY,KAAK;CAAI,CAAE,GAGhD,CACL,MAAO,GACP,OAAQ,EAAY,KAAK;CAAI,EAC7B,MAAO,uBAAuB,EAAO,OAAQ,CAAO,EACpD,SAAU,MAAM,mBAAmB,EAAO,MAAM,CAClD,CACF,CAGA,SAAS,SAAS,EAA8B,EAAwB,CACtE,OAAO,OAAO,OAAO,EAAK,CAAK,EAAI,EAAI,GAAS,IAAA,EAClD,CAIA,SAAS,SAAS,EAAiB,EAAe,EAAiC,CACjF,OAAO,eAAe,EAAK,EAAO,CAChC,QACA,WAAY,GACZ,SAAU,GACV,aAAc,EAChB,CAAC,CACH,CAEA,SAAS,QAAQ,EAAyB,CASxC,OARI,GAAiC,KAC5B,GAEL,OAAO,GAAU,UAAY,MAAM,QAAQ,CAAK,EAC3C,GAIF,OAAO,OAAO,CAAK,CAAC,CAAC,MAAM,OAAO,CAC3C,CAUA,SAAS,WAAW,EAA6B,CAI/C,OAHI,IAAY,GACP,CAAC,EAEH,EAAQ,MAAM;CAAI,CAAC,CAAC,KAAK,EAAK,EAAO,IAAQ,CAClD,IAAM,EAAM,IAAU,EAAI,OAAS,EAAI,GAAK;EACtC,EAAO,EAAI,SAAS,IAAI,EAAI,EAAI,MAAM,EAAG,EAAE,EAAI,EAC/C,EAAW,EAAI,SAAS,IAAI,EAAI,KAAO,GACzC,EACJ,GAAI,EAAK,KAAK,IAAM,GAClB,GAAI,CACF,IAAM,EAAkB,KAAK,MAAM,CAAI,EACvC,EACqB,OAAO,GAAW,UAArC,GAAiD,CAAC,MAAM,QAAQ,CAAM,EACjE,EACD,IAAA,EACR,MAAQ,CACN,EAAM,IAAA,EACR,CAEF,MAAO,CAAE,OAAM,IAAK,GAAG,IAAW,IAAO,KAAI,CAC/C,CAAC,CACH,CAIA,SAAS,aAAa,EAAiB,EAA8B,CACnE,IAAM,EAAO,IAAI,IAAI,EAAW,KAAK,EAAO,IAAU,CAAC,EAAO,CAAK,CAAC,CAAC,EAC/D,OAAU,GAAwB,EAAK,IAAI,CAAG,GAAK,EAAW,OACpE,OAAO,KAAK,UACV,OAAO,YAAY,OAAO,QAAQ,CAAG,CAAC,CAAC,UAAU,CAAC,GAAI,CAAC,KAAO,OAAO,CAAC,EAAI,OAAO,CAAC,CAAC,CAAC,CACtF,CACF,CAIA,eAAe,aAAa,EAAiB,EAA8C,CACzF,IAAM,EAAa,MAAM,EAAe,EAAS,CAAK,EACtD,GAAI,CAAC,EACH,OAGF,IAAM,GAAQ,MADgB,OAAO,EAAc,CAAU,CAAC,CAAC,MAAA,CACnB,KAC5C,GAAI,OAAO,GAAS,WAClB,MAAU,MACR,GAAG,EAAW,wFAChB,EAEF,OAAO,CACT,CA2BA,eAAsB,aAAa,EAA2D,CAC5F,GAAM,CAAE,KAAM,EAAc,SAAS,GAAwB,EAC7D,GAAI,EAAO,SAAW,EACpB,MAAU,MAAM,6CAA6C,EAE/D,GAAM,CAAE,UAAS,aAAc,MAAM,sBAAsB,CAAY,EACjE,EAAS,EAAY,CAAC,CAAS,EAAI,MAAM,eAAe,CAAO,EAE/D,EAAqB,CAAC,EACtB,EAA2B,CAAC,EAC5B,kBAAiB,IAAI,IAIrB,EAA6C,CAAC,EACpD,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,MAAM,aAAa,EAAS,CAAK,EAC9C,GAAI,CAAC,EAAM,CACT,EAAS,KAAK,sBAAsB,EAAM,oCAAoC,EAC9E,QACF,CACA,EAAM,KAAK,CAAE,QAAO,MAAK,CAAC,CAC5B,CAIA,IAAM,EAA8C,CAAC,EACrD,IAAK,GAAM,CAAE,QAAO,UAAU,EAAO,CACnC,IAAM,EAAO,EAAK,EAAS,GAAG,EAAM,OAAO,EACrC,EAAQ,WAAW,MAAM,EAAS,EAAM,OAAO,CAAC,EAEhD,kBAAU,IAAI,IACd,EAAuB,CAAC,EAC1B,EAAQ,EACR,EAAuB,CAAC,EAC5B,IAAK,GAAM,CAAC,EAAO,KAAS,EAAM,QAAQ,EAAG,CAC3C,IAAM,EAAM,EAAK,IACjB,GAAI,CAAC,EAAK,CACJ,EAAK,KAAK,KAAK,IAAM,IACvB,EAAW,KAAK,EAAQ,CAAC,EAE3B,QACF,CACA,IAAM,EAAS,EAAK,CAAG,EACvB,EAAa,OAAO,KAAK,CAAM,EAC/B,IAAM,EAAS,EAAO,OAAQ,GAE5B,CAAI,QADU,SAAS,EAAQ,CACf,CAAC,IAGjB,EAAe,IAAI,CAAK,EACjB,QAAQ,SAAS,EAAK,CAAK,CAAC,EACpC,EACG,KAAO,SAAW,EAGtB,KAAK,IAAM,KAAS,EAClB,SAAS,EAAK,EAAO,EAAO,EAA4B,EACxD,EAAQ,IAAI,CAAK,EAEnB,EAAK,KAAO,aAAa,EAAK,CAAU,EACxC,GAAS,CAFT,CAGF,CAEI,EAAW,OAAS,GACtB,EAAS,KACP,GAAG,EAAK,YAAY,EAAW,KAAK,IAAI,EAAE,sDAC5C,EAGE,IAAU,IAGd,EAAO,KAAK,CAAE,OAAM,QAAS,EAAM,IAAK,GAAS,GAAG,EAAK,OAAO,EAAK,KAAK,CAAC,CAAC,KAAK,EAAE,CAAE,CAAC,EACtF,EAAO,KAAK,CAAE,QAAO,OAAM,OAAQ,CAAC,GAAG,CAAO,EAAG,OAAM,CAAC,EAC1D,CAEA,IAAK,GAAM,CAAE,OAAM,aAAa,EAC9B,MAAM,EAAU,EAAM,CAAO,EAG/B,IAAM,EAAmB,EAAO,OAAQ,GAAU,CAAC,EAAe,IAAI,CAAK,CAAC,EACxE,EAAO,OAAS,GAAK,EAAiB,OAAS,GACjD,EAAS,KAAK,sCAAsC,EAAiB,KAAK,IAAI,GAAG,EAGnF,IAAM,EAAc,eAAe,CAAQ,EAY3C,OAXA,EAAY,KACV,EAAO,SAAW,EACd,oBACA,EACG,KACE,CAAE,OAAM,OAAQ,EAAa,WAC5B,UAAU,EAAK,WAAW,EAAY,KAAK,IAAI,EAAE,MAAM,EAAM,QACjE,CAAC,CACA,KAAK;CAAI,CAClB,EAEO,CAAE,OAAQ,EAAY,KAAK;CAAI,EAAG,QAAO,CAClD"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e,n as t}from"./logger-
|
|
2
|
-
//# sourceMappingURL=service-
|
|
1
|
+
import{i as e,n as t}from"./logger-Cp5U2IDI.mjs";import{t as n}from"./errors-sNtg2Mv3.mjs";import*as r from"pathe";import{readPackageJSON as i}from"pkg-types";import{spawnSync as a}from"node:child_process";async function detectInstalledVersion(e){let t=r.resolve(e);for(;;){try{let e=r.join(t,`node_modules`,`@tailor-platform/sdk`),n=await i(e);if(n.version)return n.version}catch{}let e=r.dirname(t);if(e===t)break;t=e}return null}function printUpgradeSummary(n,r){r&&(t.info(`${e.bold(`[Dry Run]`)} No files were modified.`),t.log(``));let i=n.codemodsApplied+n.codemodsSkipped+n.errors.length;if(t.info(`Upgrade complete: ${e.success(`${n.codemodsApplied} applied`)}, ${e.dim(`${n.codemodsSkipped} skipped`)} (${i} total codemods)`),n.filesModified.length>0){t.log(``),t.info(`${r?`Files that would be modified`:`Modified files`} (${n.filesModified.length}):`);for(let r of n.filesModified)t.log(` ${e.path(r)}`)}if(n.warnings.length>0){t.log(``),t.warn(`Manual attention needed (${n.warnings.length}):`);for(let r of n.warnings)t.log(` ${e.warning(`!`)} ${r}`)}if(n.errors.length>0){t.log(``),t.error(`Failed codemods (${n.errors.length}):`);for(let{codemodId:r,message:i}of n.errors)t.log(` ${e.error(r)}: ${i}`)}}async function upgrade(r){let i=r.path,o=await detectInstalledVersion(i);if(!o)throw n({code:`UPGRADE_SDK_VERSION_UNDETECTED`,message:`Could not detect installed @tailor-platform/sdk version in ${i}`,suggestion:`Ensure @tailor-platform/sdk is installed. Run 'pnpm install' or 'npm install' first.`,command:`upgrade`});t.info(`Upgrading from ${e.highlight(r.from)} → ${e.highlight(o)}`),r.dryRun&&t.info(`${e.bold(`[Dry Run]`)} Changes will be previewed but not applied.`),t.log(``);let s=process.platform===`win32`?`npx.cmd`:`npx`,c=a(s,[`@tailor-platform/sdk-codemod@latest`,`--from`,r.from,`--to`,o,`--target`,i,...r.dryRun?[`--dry-run`]:[]],{cwd:i,stdio:[`ignore`,`pipe`,`pipe`],encoding:`utf-8`,timeout:3e5});if(c.error)throw n({code:`UPGRADE_CODEMOD_SPAWN_FAILED`,message:`Failed to run @tailor-platform/sdk-codemod: ${c.error.message}`,suggestion:`Ensure npx is available and the network is accessible.`,command:`upgrade`});if(c.status!==0&&!c.stdout.trim())throw n({code:`UPGRADE_CODEMOD_FAILED`,message:`@tailor-platform/sdk-codemod exited with code ${c.status}`,details:c.stderr.trim()||`(no stderr output)`,suggestion:`Review the error above. Common causes: invalid version arguments, network issues, or missing package registry access.`,command:`upgrade`});c.stderr&&process.stderr.write(c.stderr);let l;try{l=JSON.parse(c.stdout)}catch{throw n({code:`UPGRADE_CODEMOD_OUTPUT_INVALID`,message:`Failed to parse output from @tailor-platform/sdk-codemod`,details:c.stdout||`(empty stdout)`,suggestion:`This is likely a bug. Please report it.`,command:`upgrade`})}if(t.out(l),printUpgradeSummary(l,r.dryRun),l.errors.length>0)throw n({code:`UPGRADE_COMPLETED_WITH_ERRORS`,message:`Upgrade completed with ${l.errors.length} error(s)`,suggestion:`Review the errors above and re-run the upgrade after fixing the issues.`,command:`upgrade`})}export{upgrade};
|
|
2
|
+
//# sourceMappingURL=service-8xznUrPU.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-
|
|
1
|
+
{"version":3,"file":"service-8xznUrPU.mjs","names":[],"sources":["../src/cli/commands/upgrade/version-detector.ts","../src/cli/commands/upgrade/service.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { readPackageJSON } from \"pkg-types\";\n\nconst SDK_PACKAGE_NAME = \"@tailor-platform/sdk\";\n\n/**\n * Detect the installed SDK version from the user's project.\n * Walks up from projectRoot to find the SDK package in node_modules,\n * matching Node's module resolution for workspace setups with hoisted deps.\n * @param projectRoot - The project root directory to search from\n * @returns The installed SDK version string, or null if not found\n */\nexport async function detectInstalledVersion(projectRoot: string): Promise<string | null> {\n let dir = path.resolve(projectRoot);\n // loop exits when a package.json is found or the root is reached\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n while (true) {\n try {\n const sdkPath = path.join(dir, \"node_modules\", SDK_PACKAGE_NAME);\n const pkg = await readPackageJSON(sdkPath);\n if (pkg.version) return pkg.version;\n } catch {\n // Not found at this level, try parent\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n","import { spawnSync } from \"node:child_process\";\nimport { CLIError } from \"#/cli/shared/errors\";\nimport { logger, styles } from \"#/cli/shared/logger\";\nimport { detectInstalledVersion } from \"./version-detector\";\nimport type { RunOutput } from \"./types\";\n\ninterface UpgradeOptions {\n from: string;\n dryRun: boolean;\n path: string;\n}\n\n/**\n * Print the upgrade summary to the terminal.\n * @param output - The parsed JSON output from sdk-codemod\n * @param dryRun - Whether this was a dry-run\n */\nfunction printUpgradeSummary(output: RunOutput, dryRun: boolean): void {\n if (dryRun) {\n logger.info(`${styles.bold(\"[Dry Run]\")} No files were modified.`);\n logger.log(\"\");\n }\n\n const total = output.codemodsApplied + output.codemodsSkipped + output.errors.length;\n logger.info(\n `Upgrade complete: ${styles.success(`${output.codemodsApplied} applied`)}, ${styles.dim(`${output.codemodsSkipped} skipped`)} (${total} total codemods)`,\n );\n\n if (output.filesModified.length > 0) {\n logger.log(\"\");\n logger.info(\n `${dryRun ? \"Files that would be modified\" : \"Modified files\"} (${output.filesModified.length}):`,\n );\n for (const file of output.filesModified) {\n logger.log(` ${styles.path(file)}`);\n }\n }\n\n if (output.warnings.length > 0) {\n logger.log(\"\");\n logger.warn(`Manual attention needed (${output.warnings.length}):`);\n for (const warning of output.warnings) {\n logger.log(` ${styles.warning(\"!\")} ${warning}`);\n }\n }\n\n if (output.errors.length > 0) {\n logger.log(\"\");\n logger.error(`Failed codemods (${output.errors.length}):`);\n for (const { codemodId, message } of output.errors) {\n logger.log(` ${styles.error(codemodId)}: ${message}`);\n }\n }\n}\n\n/**\n * Run the upgrade pipeline:\n * 1. Detect target SDK version from node_modules\n * 2. Invoke @tailor-platform/sdk-codemod CLI\n * 3. Parse JSON output and display results\n * @param options - Upgrade options\n */\nexport async function upgrade(options: UpgradeOptions): Promise<void> {\n const projectRoot = options.path;\n\n // Step 1: Detect target SDK version (the newly installed version)\n const targetVersion = await detectInstalledVersion(projectRoot);\n if (!targetVersion) {\n throw CLIError({\n code: \"UPGRADE_SDK_VERSION_UNDETECTED\",\n message: `Could not detect installed @tailor-platform/sdk version in ${projectRoot}`,\n suggestion:\n \"Ensure @tailor-platform/sdk is installed. Run 'pnpm install' or 'npm install' first.\",\n command: \"upgrade\",\n });\n }\n\n logger.info(\n `Upgrading from ${styles.highlight(options.from)} → ${styles.highlight(targetVersion)}`,\n );\n\n if (options.dryRun) {\n logger.info(`${styles.bold(\"[Dry Run]\")} Changes will be previewed but not applied.`);\n }\n\n logger.log(\"\");\n\n // Step 2: Invoke sdk-codemod CLI\n // Use \"latest\" because sdk-codemod may not be published at the exact same\n // version as @tailor-platform/sdk. Version filtering is handled internally\n // by sdk-codemod's registry via the --from / --to arguments.\n const npxCommand = process.platform === \"win32\" ? \"npx.cmd\" : \"npx\";\n\n const result = spawnSync(\n npxCommand,\n [\n \"@tailor-platform/sdk-codemod@latest\",\n \"--from\",\n options.from,\n \"--to\",\n targetVersion,\n \"--target\",\n projectRoot,\n ...(options.dryRun ? [\"--dry-run\"] : []),\n ],\n {\n cwd: projectRoot,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n encoding: \"utf-8\",\n timeout: 300_000,\n },\n );\n\n if (result.error) {\n throw CLIError({\n code: \"UPGRADE_CODEMOD_SPAWN_FAILED\",\n message: `Failed to run @tailor-platform/sdk-codemod: ${result.error.message}`,\n suggestion: \"Ensure npx is available and the network is accessible.\",\n command: \"upgrade\",\n });\n }\n\n // Check for non-zero exit without a launch error (e.g. registry/auth/network failures)\n if (result.status !== 0 && !result.stdout.trim()) {\n throw CLIError({\n code: \"UPGRADE_CODEMOD_FAILED\",\n message: `@tailor-platform/sdk-codemod exited with code ${result.status}`,\n details: result.stderr.trim() || \"(no stderr output)\",\n suggestion:\n \"Review the error above. Common causes: invalid version arguments, network issues, or missing package registry access.\",\n command: \"upgrade\",\n });\n }\n\n // Forward captured stderr so users see dry-run diffs and progress messages\n // written by sdk-codemod. stderr is piped (not inherited) so that the error\n // path above can surface it via CLIError, but on success we still need to\n // replay it verbatim to keep the colorized unified diff output visible.\n if (result.stderr) {\n process.stderr.write(result.stderr);\n }\n\n // Step 3: Parse JSON output\n let output: RunOutput;\n try {\n output = JSON.parse(result.stdout);\n } catch {\n throw CLIError({\n code: \"UPGRADE_CODEMOD_OUTPUT_INVALID\",\n message: \"Failed to parse output from @tailor-platform/sdk-codemod\",\n details: result.stdout || \"(empty stdout)\",\n suggestion: \"This is likely a bug. Please report it.\",\n command: \"upgrade\",\n });\n }\n\n // Step 4: Display results\n // Emit structured data on stdout (honors --json via logger.out) and\n // human-readable summary on stderr (via printUpgradeSummary).\n logger.out(output);\n printUpgradeSummary(output, options.dryRun);\n\n if (output.errors.length > 0) {\n throw CLIError({\n code: \"UPGRADE_COMPLETED_WITH_ERRORS\",\n message: `Upgrade completed with ${output.errors.length} error(s)`,\n suggestion: \"Review the errors above and re-run the upgrade after fixing the issues.\",\n command: \"upgrade\",\n });\n }\n}\n"],"mappings":"8MAYA,eAAsB,uBAAuB,EAA6C,CACxF,IAAI,EAAM,EAAK,QAAQ,CAAW,EAGlC,OAAa,CACX,GAAI,CACF,IAAM,EAAU,EAAK,KAAK,EAAK,eAAgB,sBAAgB,EACzD,EAAM,MAAM,EAAgB,CAAO,EACzC,GAAI,EAAI,QAAS,OAAO,EAAI,OAC9B,MAAQ,CAER,CACA,IAAM,EAAS,EAAK,QAAQ,CAAG,EAC/B,GAAI,IAAW,EAAK,MACpB,EAAM,CACR,CACA,OAAO,IACT,CCZA,SAAS,oBAAoB,EAAmB,EAAuB,CACjE,IACF,EAAO,KAAK,GAAG,EAAO,KAAK,WAAW,EAAE,yBAAyB,EACjE,EAAO,IAAI,EAAE,GAGf,IAAM,EAAQ,EAAO,gBAAkB,EAAO,gBAAkB,EAAO,OAAO,OAK9E,GAJA,EAAO,KACL,qBAAqB,EAAO,QAAQ,GAAG,EAAO,gBAAgB,SAAS,EAAE,IAAI,EAAO,IAAI,GAAG,EAAO,gBAAgB,SAAS,EAAE,IAAI,EAAM,iBACzI,EAEI,EAAO,cAAc,OAAS,EAAG,CACnC,EAAO,IAAI,EAAE,EACb,EAAO,KACL,GAAG,EAAS,+BAAiC,iBAAiB,IAAI,EAAO,cAAc,OAAO,GAChG,EACA,IAAK,IAAM,KAAQ,EAAO,cACxB,EAAO,IAAI,KAAK,EAAO,KAAK,CAAI,GAAG,CAEvC,CAEA,GAAI,EAAO,SAAS,OAAS,EAAG,CAC9B,EAAO,IAAI,EAAE,EACb,EAAO,KAAK,4BAA4B,EAAO,SAAS,OAAO,GAAG,EAClE,IAAK,IAAM,KAAW,EAAO,SAC3B,EAAO,IAAI,KAAK,EAAO,QAAQ,GAAG,EAAE,GAAG,GAAS,CAEpD,CAEA,GAAI,EAAO,OAAO,OAAS,EAAG,CAC5B,EAAO,IAAI,EAAE,EACb,EAAO,MAAM,oBAAoB,EAAO,OAAO,OAAO,GAAG,EACzD,IAAK,GAAM,CAAE,YAAW,aAAa,EAAO,OAC1C,EAAO,IAAI,KAAK,EAAO,MAAM,CAAS,EAAE,IAAI,GAAS,CAEzD,CACF,CASA,eAAsB,QAAQ,EAAwC,CACpE,IAAM,EAAc,EAAQ,KAGtB,EAAgB,MAAM,uBAAuB,CAAW,EAC9D,GAAI,CAAC,EACH,MAAM,EAAS,CACb,KAAM,iCACN,QAAS,8DAA8D,IACvE,WACE,uFACF,QAAS,SACX,CAAC,EAGH,EAAO,KACL,kBAAkB,EAAO,UAAU,EAAQ,IAAI,EAAE,KAAK,EAAO,UAAU,CAAa,GACtF,EAEI,EAAQ,QACV,EAAO,KAAK,GAAG,EAAO,KAAK,WAAW,EAAE,4CAA4C,EAGtF,EAAO,IAAI,EAAE,EAMb,IAAM,EAAa,QAAQ,WAAa,QAAU,UAAY,MAExD,EAAS,EACb,EACA,CACE,sCACA,SACA,EAAQ,KACR,OACA,EACA,WACA,EACA,GAAI,EAAQ,OAAS,CAAC,WAAW,EAAI,CAAC,CACxC,EACA,CACE,IAAK,EACL,MAAO,CAAC,SAAU,OAAQ,MAAM,EAChC,SAAU,QACV,QAAS,GACX,CACF,EAEA,GAAI,EAAO,MACT,MAAM,EAAS,CACb,KAAM,+BACN,QAAS,+CAA+C,EAAO,MAAM,UACrE,WAAY,yDACZ,QAAS,SACX,CAAC,EAIH,GAAI,EAAO,SAAW,GAAK,CAAC,EAAO,OAAO,KAAK,EAC7C,MAAM,EAAS,CACb,KAAM,yBACN,QAAS,iDAAiD,EAAO,SACjE,QAAS,EAAO,OAAO,KAAK,GAAK,qBACjC,WACE,wHACF,QAAS,SACX,CAAC,EAOC,EAAO,QACT,QAAQ,OAAO,MAAM,EAAO,MAAM,EAIpC,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,EAAO,MAAM,CACnC,MAAQ,CACN,MAAM,EAAS,CACb,KAAM,iCACN,QAAS,2DACT,QAAS,EAAO,QAAU,iBAC1B,WAAY,0CACZ,QAAS,SACX,CAAC,CACH,CAQA,GAHA,EAAO,IAAI,CAAM,EACjB,oBAAoB,EAAQ,EAAQ,MAAM,EAEtC,EAAO,OAAO,OAAS,EACzB,MAAM,EAAS,CACb,KAAM,gCACN,QAAS,0BAA0B,EAAO,OAAO,OAAO,WACxD,WAAY,0EACZ,QAAS,SACX,CAAC,CAEL"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as e}from"./brand-C8nMKhJC.mjs";import{i as t,n}from"./logger-
|
|
1
|
+
import{n as e}from"./brand-C8nMKhJC.mjs";import{i as t,n}from"./logger-Cp5U2IDI.mjs";import*as r from"pathe";import{z as i}from"zod";import*as a from"node:fs";import{pathToFileURL as o}from"node:url";const s=i.custom(e=>typeof e==`function`),c=`tailorImportNonce`;let l=0;function beginUserModuleRun(){l+=1}function currentImportNonce(){return l===0?void 0:String(l)}function importUserModule(e){let t=o(e),n=currentImportNonce();return n&&t.searchParams.set(c,n),import(t.href)}const ee=[`**/*.test.ts`,`**/*.spec.ts`],u=/* @__PURE__ */ new Set;function loadFilesWithIgnores(e,t){if(e.files.length===0)return[];let i=resolveFiles(e,t),a=e.files.every(e=>r.isAbsolute(e));if(i.matchedAnyPattern||t===process.cwd()||a)return i.files;let o=`${t}\0${e.files.join(`\0`)}`;return u.has(o)||(u.add(o),n.warn(`No files matched "${e.files.join(`, `)}" relative to "${t}"; falling back to process.cwd(). Update this config's file patterns to be relative to its own directory before v2, when this fallback will be removed.`)),resolveFiles(e,process.cwd()).files}function resolveFiles(e,t){let i=e.ignores===void 0,o=e.ignores??ee,s=i?o:o.map(e=>r.resolve(t,e)),c=[],l=!1;for(let i of e.files){let e=r.resolve(t,i);try{let t=a.globSync(e);t.length>0&&(l=!0);let n=t.filter(e=>!s.some(t=>r.matchesGlob(e,t)));c.push(...n)}catch(e){l=!0,n.warn(`Failed to glob pattern "${i}": ${String(e)}`)}}return{files:c,matchedAnyPattern:l}}const d=Object.keys({uuid:!0,string:!0,boolean:!0,integer:!0,float:!0,decimal:!0,enum:!0,date:!0,datetime:!0,time:!0,nested:!0}),f=i.enum(d),p={"1-1":`1-1`,oneToOne:`1-1`,"n-1":`n-1`,manyToOne:`n-1`,"N-1":`n-1`,keyOnly:`keyOnly`},m=Object.keys(p);function fieldRef(e){return`Field "${e.fieldName}" on table "${e.tableName}"`}function validateRelationConfig(e,t){if(!e.type)throw Error(`${fieldRef(t)} has a relation but is missing the required 'type' property. Valid values: ${m.join(`, `)}.`);if(!(e.type in p))throw Error(`${fieldRef(t)} has invalid relation type '${e.type}'. Valid values: ${m.join(`, `)}.`);if(e.toward.table!==`self`&&!t.allTableNames.has(e.toward.table))throw Error(`${fieldRef(t)} references unknown table "${e.toward.table}".`)}function processRelationMetadata(e,t,n=!1){let r=p[e.type]===`1-1`,i=e.toward.key??`id`,a=e.toward.table===`self`?t.tableName:e.toward.table,o=!n,s=!n&&r;return{index:o,foreignKey:!0,relationType:e.type,unique:s,foreignKeyType:a,foreignKeyField:i}}function buildRelationInfo(e,t){if(e.type===`keyOnly`)return;let n=p[e.type]===`1-1`,r=e.toward.key??`id`,i=e.toward.table===`self`?t.tableName:e.toward.table,a=e.toward.as;return a||=t.fieldName.replace(/(ID|Id|id)$/u,``),{targetType:i,forwardName:a,backwardName:e.backward??``,key:r,unique:n}}function applyRelationMetadataToFieldConfig(e,t){return{...e,index:t.index,foreignKey:t.foreignKey,unique:t.unique,foreignKeyType:t.foreignKeyType,foreignKeyField:t.foreignKeyField}}function normalizeGqlOperations(e){return e===`query`?{create:!1,update:!1,delete:!1,read:!0}:e}const h=i.union([i.literal(`query`),i.strictObject({create:i.boolean().optional().describe(`Enable create mutation (default: true)`),update:i.boolean().optional().describe(`Enable update mutation (default: true)`),delete:i.boolean().optional().describe(`Enable delete mutation (default: true)`),read:i.boolean().optional().describe(`Enable read queries - get, list, aggregation (default: true)`)})]).describe(`Configuration for GraphQL operations on a TailorDB table.
|
|
2
2
|
All operations are enabled by default (undefined or true = enabled, false = disabled).`).transform(e=>normalizeGqlOperations(e)),g=i.strictObject({value:i.string(),description:i.string().optional()}),_=i.strictObject({required:i.boolean().optional().describe(`Whether the field is required`),array:i.boolean().optional().describe(`Whether the field is an array`),description:i.string().optional().describe(`Field description`),typeName:i.string().optional().describe(`Type name for nested or enum fields`),allowedValues:i.array(g).optional().describe(`Allowed values for enum fields`),index:i.boolean().optional().describe(`Whether the field is indexed for faster queries`),unique:i.boolean().optional().describe(`Whether the field value must be unique`),vector:i.boolean().optional().describe(`Whether the field is a vector field for similarity search`),foreignKey:i.boolean().optional().describe(`Whether the field is a foreign key`),foreignKeyType:i.string().optional().describe(`Target table name for foreign key relations`),foreignKeyField:i.string().optional().describe(`Target field name for foreign key relations`),hooks:i.strictObject({create:s.optional().describe(`Hook function called on record creation`),update:s.optional().describe(`Hook function called on record update`)}).optional().describe(`Lifecycle hooks for the field`),validate:i.array(s).optional().describe(`Validation functions for the field`),serial:i.strictObject({start:i.number().describe(`Starting value for the serial sequence`),maxValue:i.number().optional().describe(`Maximum value for the serial sequence`),format:i.string().optional().describe(`Format string for serial value (string type only)`)}).optional().describe(`Serial (auto-increment) configuration`),scale:i.number().int().min(0).max(12).optional().describe(`Decimal scale (number of digits after decimal point, 0-12)`),default:i.unknown().optional().describe(`Default value for the field on create`)}),te=i.enum(m),ne=i.strictObject({type:te.describe(`Relation cardinality type`),toward:i.strictObject({table:i.string().describe(`Target table name, or 'self' for self-relations`),as:i.string().optional().describe(`Custom forward relation name`),key:i.string().optional().describe(`Target field to join on (default: 'id')`)}),backward:i.string().optional().describe(`Backward relation name on the target table`)}),v=i.lazy(()=>i.object({type:f,fields:i.record(i.string(),v).optional(),metadata:_,rawRelation:ne.optional()})),y=i.strictObject({pluralForm:i.string().optional().describe(`Custom plural form of the table name for GraphQL`),aggregation:i.boolean().optional().describe(`Enable aggregation queries for this table`),bulkUpsert:i.boolean().optional().describe(`Enable bulk upsert mutation for this table`),gqlOperations:h.optional().describe(`Configure GraphQL operations for this table. Use "query" for read-only mode, or an object for granular control.`),publishEvents:i.boolean().optional().describe(`Enable publishing events for this table.
|
|
3
3
|
When enabled, record creation/update/deletion events are published.
|
|
4
4
|
If not specified, this is automatically set to true when an executor uses this table
|
|
5
5
|
with recordCreated/recordUpdated/recordDeleted triggers. If explicitly set to false
|
|
6
|
-
while an executor uses this table, an error will be thrown during apply.`)}),b=i.union([i.strictObject({user:i.string()}),i.string(),i.boolean(),i.array(i.string()),i.array(i.boolean())],{error:e=>{if(typeof e.input==`object`&&e.input!==null){let t=Object.keys(e.input);return t.length===1?`"${t[0]}" operand is not supported in gqlPermission. Use permission() for record-level conditions.`:`Operand object must have exactly 1 key`}return`Invalid operand in gqlPermission`}}),x=i.union([b,i.strictObject({record:i.string()}),i.strictObject({oldRecord:i.string()}),i.strictObject({newRecord:i.string()})]),S=i.enum([`=`,`!=`,`in`,`not in`,`hasAny`,`not hasAny`]),C=i.tuple([x,S,x]).readonly(),re=i.tuple([b,S,b]).readonly(),w=i.union([i.strictObject({conditions:i.union([C,i.array(C).readonly()]),description:i.string().optional(),permit:i.boolean().optional()}),i.tuple([x,S,x]).readonly(),i.tuple([x,S,x,i.boolean()]).readonly(),i.array(i.union([C,i.boolean()])).refine(e=>{let t=e.findIndex(e=>typeof e==`boolean`);return t===-1||t===e.length-1},{message:`Boolean permit flag must only appear at the end`}).readonly()]),ie=i.enum([`read`,`create`,`update`,`delete`,`aggregate`,`bulkUpsert`]),ae=i.strictObject({conditions:i.array(re).readonly(),actions:i.union([i.literal(`all`),i.array(ie).readonly()]),permit:i.boolean().optional(),description:i.string().optional()}),oe=i.strictObject({record:i.strictObject({create:i.array(w).readonly(),read:i.array(w).readonly(),update:i.array(w).readonly(),delete:i.array(w).readonly()}).optional(),gql:i.array(ae).readonly().optional()}),T=i.strictObject({name:i.string(),fields:i.record(i.string(),v),metadata:i.object({name:i.string(),description:i.string().optional(),settings:y.optional(),permissions:oe,files:i.record(i.string(),i.string()),indexes:i.record(i.string(),i.strictObject({fields:i.array(i.string()),unique:i.boolean().optional()})).optional(),typeHook:i.strictObject({create:s.optional(),update:s.optional()}).optional(),typeValidate:s.optional()}).strict()}),E=i.strictObject({directory:i.string().describe(`Directory containing migration files`),machineUser:i.string().optional().describe(`Machine user name for migration execution`)}),D=i.strictObject({files:i.array(i.string()).describe(`Glob patterns for TailorDB table definition files`),ignores:i.array(i.string()).optional().describe(`Glob patterns to exclude from table discovery`),migration:E.optional().describe(`Migration configuration`),gqlOperations:h.optional().describe(`Default GraphQL operations for all tables in this service`)}),O=T.keyof().options;function stripTailorDBTypeBuilderHelpers(t){return e(t,`tailordb-type`)?pickTailorDBTypeSchemaKeys(t):t}function pickTailorDBTypeSchemaKeys(e){if(typeof e!=`object`||!e)return e;let t={},n=e;for(let e of O)Object.prototype.hasOwnProperty.call(n,e)&&(t[e]=n[e]);return t}const k=i.strictObject({providerUrl:i.string().describe(`OAuth2 provider URL`),issuerUrl:i.string().describe(`OAuth2 issuer URL`),clientId:i.string().describe(`OAuth2 client ID`),clientSecret:i.string().describe(`OAuth2 client secret`),authUrl:i.string().optional().describe(`OAuth2 authorization endpoint override`),tokenUrl:i.string().optional().describe(`OAuth2 token endpoint override`)}).extend({type:i.literal(`oauth2`).describe(`Connection type`)}),A=i.strictObject({value:i.string().describe(`The allowed value`),description:i.string().optional().describe(`Description of the allowed value`)}),j=i.strictObject({as:i.enum([`string`,`date`,`temporal`]).optional().describe(`Date value representation`),required:i.boolean().optional().describe(`Whether the field is required`),array:i.boolean().optional().describe(`Whether the field is an array`),description:i.string().optional().describe(`Field description`),allowedValues:i.array(A).optional().describe(`Allowed values for enum fields`),hooks:i.strictObject({create:s.optional().describe(`Hook function called on creation`),update:s.optional().describe(`Hook function called on update`)}).optional().describe(`Lifecycle hooks`),validate:i.array(s).optional().describe(`Validation functions for the field`),typeName:i.string().optional().describe(`Type name for nested or enum fields`),default:i.unknown().optional().describe(`Default value for the field on create`)}),M=i.object({type:f.describe(`Field data type`),metadata:j.describe(`Field metadata configuration`),get fields(){return i.record(i.string(),M)}}),N=i.strictObject({namespace:i.string().describe(`Auth namespace`),machineUserName:i.string().describe(`Machine user name for authentication`)}),P=i.union([i.string().describe(`Machine user name (namespace auto-resolved from auth service)`),N]),F=i.strictObject({vaultName:i.string().describe(`Vault name containing the secret`),secretKey:i.string().describe(`Key of the secret in the vault`)}),I=i.strictObject({name:i.string().describe(`Identity provider name`),kind:i.literal(`OIDC`),clientID:i.string().describe(`OAuth2 client ID`),clientSecret:F.describe(`OAuth2 client secret`),providerURL:i.string().describe(`OIDC provider URL`),issuerURL:i.string().optional().describe(`OIDC issuer URL (defaults to providerURL)`),usernameClaim:i.string().optional().describe(`JWT claim to use as username`)}),L=i.strictObject({name:i.string().describe(`Identity provider name`),kind:i.literal(`SAML`),enableSignRequest:i.boolean().default(!1).describe(`Enable signing of SAML requests`),metadataURL:i.string().optional().describe(`URL to fetch SAML metadata (mutually exclusive with rawMetadata)`),rawMetadata:i.string().optional().describe(`Raw SAML metadata XML (mutually exclusive with metadataURL)`),defaultRedirectURL:i.string().optional().describe(`URL to redirect to when SAML ACS receives a response with an empty RelayState.`)}).refine(e=>e.metadataURL!==void 0!=(e.rawMetadata!==void 0),`Provide either metadataURL or rawMetadata`),R=i.strictObject({name:i.string().describe(`Identity provider name`),kind:i.literal(`IDToken`),providerURL:i.string().describe(`ID token provider URL`),issuerURL:i.string().optional().describe(`ID token issuer URL`),clientID:i.string().describe(`Client ID for ID token validation`),usernameClaim:i.string().optional().describe(`JWT claim to use as username`)}),z=i.strictObject({name:i.string().describe(`Identity provider name`),kind:i.literal(`BuiltInIdP`),namespace:i.string().describe(`IdP namespace`),clientName:i.string().describe(`OAuth2 client name in the IdP`)}),B=i.discriminatedUnion(`kind`,[I,L,R,z]),V=i.union([i.literal(`authorization_code`),i.literal(`refresh_token`)]).describe(`OAuth2 grant type`),H=i.strictObject({description:i.string().optional().describe(`Client description`),grantTypes:i.array(V).default([`authorization_code`,`refresh_token`]).describe(`Allowed OAuth2 grant types`),redirectURIs:i.array(i.union([i.templateLiteral([`https://`,i.string()]),i.templateLiteral([`http://`,i.string()]),i.templateLiteral([i.string(),`:url`]),i.templateLiteral([i.string(),`:url/`,i.string()])])).describe(`Allowed redirect URIs`),clientType:i.union([i.literal(`confidential`),i.literal(`public`),i.literal(`browser`)]).optional().describe(`OAuth2 client type`),accessTokenLifetimeSeconds:i.number().int().min(60,`Minimum access token lifetime is 60 seconds`).max(86400,`Maximum access token lifetime is 1 day (86400 seconds)`).optional().describe(`Access token lifetime in seconds (60-86400)`).transform(e=>e?{seconds:BigInt(e),nanos:0}:void 0),refreshTokenLifetimeSeconds:i.number().int().min(60,`Minimum refresh token lifetime is 60 seconds`).max(604800,`Maximum refresh token lifetime is 7 days (604800 seconds)`).optional().describe(`Refresh token lifetime in seconds (60-604800)`).transform(e=>e?{seconds:BigInt(e),nanos:0}:void 0),requireDpop:i.boolean().optional().describe(`Require DPoP (Demonstrating Proof-of-Possession) for token requests`)}).refine(e=>e.clientType!==`browser`||e.requireDpop!==!0,{message:`requireDpop cannot be set to true for browser clients as they don't support DPoP`,path:[`requireDpop`]}),U=i.strictObject({type:i.union([i.literal(`oauth2`),i.literal(`bearer`)]).describe(`SCIM authorization type`),bearerSecret:F.optional().describe(`Bearer token secret (required for bearer type)`)}),W=i.union([i.literal(`string`),i.literal(`number`),i.literal(`boolean`),i.literal(`datetime`),i.literal(`complex`)]).describe(`SCIM attribute data type`),G=i.strictObject({type:W.describe(`Attribute data type`),name:i.string().describe(`Attribute name`),description:i.string().optional().describe(`Attribute description`),mutability:i.union([i.literal(`readOnly`),i.literal(`readWrite`),i.literal(`writeOnly`)]).optional().describe(`Attribute mutability`),required:i.boolean().optional().describe(`Whether the attribute is required`),multiValued:i.boolean().optional().describe(`Whether the attribute can have multiple values`),uniqueness:i.union([i.literal(`none`),i.literal(`server`),i.literal(`global`)]).optional().describe(`Uniqueness constraint`),canonicalValues:i.array(i.string()).nullable().optional().describe(`List of canonical values`),get subAttributes(){return i.array(G).nullable().optional()}}),K=i.strictObject({name:i.string().describe(`SCIM schema name`),attributes:i.array(G).describe(`Schema attributes`)}),q=i.strictObject({tailorDBField:i.string().describe(`TailorDB field name to map to`),scimPath:i.string().describe(`SCIM attribute path`)}),se=i.strictObject({name:i.string().describe(`SCIM resource name`),tailorDBNamespace:i.string().describe(`TailorDB namespace for the resource`),tailorDBType:i.string().describe(`TailorDB table name for the resource`),coreSchema:K.describe(`Core SCIM schema definition`),attributeMapping:i.array(q).describe(`Attribute mapping configuration`)}),ce=i.strictObject({machineUserName:i.string().describe(`Machine user name for SCIM operations`),authorization:U.describe(`SCIM authorization configuration`),resources:i.array(se).describe(`SCIM resource definitions`)}),le=i.strictObject({namespace:i.string().describe(`TailorDB namespace for the tenant table`),type:i.string().describe(`TailorDB table name for tenants`),signatureField:i.string().describe(`Field used as the tenant signature`)}),ue=i.strictObject({namespace:i.string().optional().describe(`TailorDB namespace where the user table is defined`),type:i.custom().transform(stripTailorDBTypeBuilderHelpers).pipe(T),usernameField:i.string(),attributes:i.record(i.string(),i.literal(!0)).optional(),attributeList:i.array(i.string()).optional()}),de=i.union([i.string(),i.boolean(),i.array(i.string()),i.array(i.boolean())]),fe=i.strictObject({attributes:i.record(i.string(),de.nullish()).transform(e=>Object.fromEntries(Object.entries(e).filter(e=>e[1]!=null))).optional(),attributeList:i.array(i.uuid()).optional()}),pe=i.strictObject({handler:i.function(),invoker:i.string()}),J=i.strictObject({name:i.string().describe(`Auth service name`),hooks:i.strictObject({beforeLogin:pe.optional().describe(`Before login auth hook`)}).optional().describe(`Auth hooks`),machineUsers:i.record(i.string(),fe).optional().describe(`Machine user definitions`),oauth2Clients:i.record(i.string(),H).optional().describe(`OAuth2 client definitions`),idProvider:B.optional().describe(`Identity provider configuration`),scim:ce.optional().describe(`SCIM provisioning configuration`),tenantProvider:le.optional().describe(`Multi-tenant provider configuration`),connections:i.record(i.string(),k).optional().describe(`Auth connection definitions for external OAuth2 providers`),publishSessionEvents:i.boolean().optional().describe(`Enable publishing session events`)}),me=i.xor([J.extend({userProfile:ue.optional().describe(`User profile configuration`),machineUserAttributes:i.undefined().optional()}),J.extend({userProfile:i.undefined().optional(),machineUserAttributes:i.record(i.string(),M).describe(`Machine user attribute fields`)})],{error:e=>{if(e.code===`invalid_union`&&!(e.errors.length<2)&&e.errors.every(e=>e.every(e=>e.path.length===1&&(e.path[0]===`userProfile`||e.path[0]===`machineUserAttributes`))))return"Specify either `userProfile` or `machineUserAttributes`, not both."}}),he=i.strictObject({kind:i.literal(`tailordb`).describe(`TailorDB record event trigger`),events:i.array(i.enum([`tailordb.type_record.created`,`tailordb.type_record.updated`,`tailordb.type_record.deleted`])).min(1).transform(e=>[...new Set(e)]).describe(`TailorDB event types to trigger on`),tableName:i.string().describe(`TailorDB table name to watch for events`),condition:s.optional().describe(`Condition function to filter events`)}),ge=i.strictObject({kind:i.literal(`resolverExecuted`),resolverName:i.string().describe(`Name of the resolver to trigger on`),condition:s.optional().describe(`Condition function to filter events`)}),_e=i.strictObject({kind:i.literal(`schedule`),cron:i.string().describe(`CRON expression for the schedule`),timezone:i.string().optional().default(`UTC`).describe(`Timezone for the CRON schedule (default: UTC)`)}),ve=i.strictObject({body:s.optional().describe(`Function returning the response body`),statusCode:i.number().int().optional().describe(`HTTP status code for the response`)}),ye=i.strictObject({kind:i.literal(`incomingWebhook`),response:ve.optional().describe(`Response configuration`)}),be=i.strictObject({kind:i.literal(`idpUser`).describe(`IdP user event trigger`),events:i.array(i.enum([`idp.user.created`,`idp.user.updated`,`idp.user.deleted`])).min(1).transform(e=>[...new Set(e)]).describe(`IdP user event types to trigger on`),idp:i.string().optional().describe(`IdP namespace name to subscribe to. If omitted, the project's only IdP is used; throws when multiple IdPs exist.`)}),xe=i.strictObject({kind:i.literal(`authAccessToken`).describe(`Auth access token event trigger`),events:i.array(i.enum([`auth.access_token.issued`,`auth.access_token.refreshed`,`auth.access_token.revoked`])).min(1).transform(e=>[...new Set(e)]).describe(`Auth access token event types to trigger on`)}),Y=i.string().refine(e=>e.trim().length>0,`Workflow name cannot be blank`).describe(`Workflow name to subscribe to.`),Se=i.strictObject({kind:i.literal(`workflowExecution`).describe(`Workflow execution event trigger`),events:i.array(i.enum([`workflow.workflow_execution.started`,`workflow.workflow_execution.completed`,`workflow.workflow_execution.retried`,`workflow.workflow_execution.resumed`,`workflow.workflow_execution.wait_started`,`workflow.workflow_execution.wait_resolved`])).min(1).transform(e=>[...new Set(e)]).describe(`Workflow execution event types to trigger on`),workflowName:Y,condition:s.optional().describe(`Condition function to filter events`)}),Ce=i.strictObject({kind:i.literal(`workflowJobExecution`).describe(`Workflow job execution event trigger`),events:i.array(i.enum([`workflow.workflow_execution.job_execution.started`,`workflow.workflow_execution.job_execution.completed`,`workflow.workflow_execution.job_execution.wait_started`,`workflow.workflow_execution.job_execution.wait_resolved`])).min(1).transform(e=>[...new Set(e)]).describe(`Workflow job execution event types to trigger on`),workflowName:Y,condition:s.optional().describe(`Condition function to filter events`)}),X=i.discriminatedUnion(`kind`,[he,ge,_e,ye,be,xe,Se,Ce]),we=i.strictObject({kind:i.enum([`function`,`jobFunction`]),body:s.describe(`Function implementation`),invoker:P.optional().describe(`Invoker for the function execution`)}),Te=i.strictObject({kind:i.literal(`graphql`),appName:i.string().optional().describe(`Target application name for the GraphQL query`),query:i.preprocess(e=>String(e),i.string().describe(`GraphQL query string`)),variables:s.optional().describe(`Function to compute GraphQL variables`),invoker:P.optional().describe(`Invoker for the GraphQL execution`)}),Ee=i.strictObject({kind:i.literal(`webhook`),url:s.describe(`Function returning the webhook URL`),requestBody:s.optional().describe(`Function to compute the request body`),headers:i.record(i.string(),i.union([i.string(),i.strictObject({vault:i.string(),key:i.string()})])).optional().describe(`HTTP headers for the webhook request`)}),Z=i.lazy(()=>i.union([i.string(),i.number(),i.boolean(),i.null(),i.array(Z),i.record(i.string(),Z)])),De=i.union([i.string(),i.number(),i.boolean(),i.array(Z),i.record(i.string(),Z)]),Oe=i.union([De,s]).describe(`Arguments to pass to the workflow`),Q={kind:i.literal(`workflow`),args:Oe.optional(),invoker:P.optional().describe(`Invoker for the workflow execution`)},ke=i.strictObject({...Q,workflowName:i.string().describe(`Name of the workflow to execute`),workflow:i.never().optional()}),Ae=i.strictObject({...Q,workflow:i.looseObject({name:i.string()}),workflowName:i.string().optional()}).transform(({workflow:e,...t})=>({...t,workflowName:e.name})),je=i.union([Ae,ke]),Me=i.union([we,Te,Ee,je]),$=i.strictObject({name:i.string().describe(`Executor name`),description:i.string().optional().describe(`Executor description`),disabled:i.boolean().optional().default(!1).describe(`Whether the executor is disabled`),trigger:X.describe(`Event trigger configuration`),operation:Me.describe(`Operation to execute when triggered`)});function stripExecutorTriggerArgs(t){if(!e(t,`executor`))return t;let n=t.trigger;if(typeof n!=`object`||!n||!(`__args`in n))return t;let{__args:r,...i}=n;return{...t,trigger:i}}async function loadExecutor(e){let t=(await importUserModule(e)).default,n=$.safeParse(stripExecutorTriggerArgs(t));return n.success?n.data:null}function createExecutorService(i){let{config:a,baseDir:o}=i,s={},c=[],l,loadExecutorForFile=async i=>{try{let a=await importUserModule(i),o=$.safeParse(stripExecutorTriggerArgs(a.default));if(o.success){let e=r.relative(process.cwd(),i);return n.debug(`Executor: ${t.successBright(`"${o.data.name}"`)} loaded from ${t.path(e)}`),s[i]=o.data,o.data}if(e(a.default,`executor`))throw o.error}catch(e){let a=r.relative(process.cwd(),i);throw n.error(`Failed to load executor from ${t.bold(a)}`),n.error(String(e)),e}};return{config:a,get executors(){return s},get pluginExecutors(){return c},loadExecutors:async()=>(l||=(async()=>{if(a.files.length===0)return;let e=loadFilesWithIgnores(a,o);return n.newline(),n.log(`Found ${t.highlight(e.length.toString())} executor files`),await Promise.all(e.map(e=>loadExecutorForFile(e))),assertUniqueExecutorNames(s),s})(),await l??(Object.keys(s).length>0?s:void 0)),loadPluginExecutorFiles:async e=>{if(e.length!==0){n.newline(),n.log(`Loading ${t.highlight(e.length.toString())} plugin-generated executor files`);for(let t of e){let e=await loadExecutorForFile(t);e&&c.push({executor:e,pluginId:`plugin-generated`,sourceTableName:void 0})}assertUniqueExecutorNames(s)}}}}function assertUniqueExecutorNames(e){let t=new Map;for(let[n,i]of Object.entries(e)){let e=r.relative(process.cwd(),n),a=t.get(i.name);if(a)throw Error(`Duplicate executor name "${i.name}" found:\n - ${a}\n - ${e}\nEach executor must have a unique name.`);t.set(i.name,e)}}export{c as _,me as a,importUserModule as b,pickTailorDBTypeSchemaKeys as c,T as d,applyRelationMetadataToFieldConfig as f,loadFilesWithIgnores as g,validateRelationConfig as h,$ as i,stripTailorDBTypeBuilderHelpers as l,processRelationMetadata as m,loadExecutor as n,P as o,buildRelationInfo as p,stripExecutorTriggerArgs as r,M as s,createExecutorService as t,D as u,beginUserModuleRun as v,s as x,currentImportNonce as y};
|
|
7
|
-
//# sourceMappingURL=service-
|
|
6
|
+
while an executor uses this table, an error will be thrown during apply.`)}),b=i.union([i.strictObject({user:i.string()}),i.string(),i.boolean(),i.array(i.string()),i.array(i.boolean())],{error:e=>{if(typeof e.input==`object`&&e.input!==null){let t=Object.keys(e.input);return t.length===1?`"${t[0]}" operand is not supported in gqlPermission. Use permission() for record-level conditions.`:`Operand object must have exactly 1 key`}return`Invalid operand in gqlPermission`}}),x=i.union([b,i.strictObject({record:i.string()}),i.strictObject({oldRecord:i.string()}),i.strictObject({newRecord:i.string()})]),S=i.enum([`=`,`!=`,`in`,`not in`,`hasAny`,`not hasAny`]),C=i.tuple([x,S,x]).readonly(),re=i.tuple([b,S,b]).readonly(),w=i.union([i.strictObject({conditions:i.union([C,i.array(C).readonly()]),description:i.string().optional(),permit:i.boolean().optional()}),i.tuple([x,S,x]).readonly(),i.tuple([x,S,x,i.boolean()]).readonly(),i.array(i.union([C,i.boolean()])).refine(e=>{let t=e.findIndex(e=>typeof e==`boolean`);return t===-1||t===e.length-1},{message:`Boolean permit flag must only appear at the end`}).readonly()]),ie=i.enum([`read`,`create`,`update`,`delete`,`aggregate`,`bulkUpsert`]),ae=i.strictObject({conditions:i.array(re).readonly(),actions:i.union([i.literal(`all`),i.array(ie).readonly()]),permit:i.boolean().optional(),description:i.string().optional()}),oe=i.strictObject({record:i.strictObject({create:i.array(w).readonly(),read:i.array(w).readonly(),update:i.array(w).readonly(),delete:i.array(w).readonly()}).optional(),gql:i.array(ae).readonly().optional()}),T=i.strictObject({name:i.string(),fields:i.record(i.string(),v),metadata:i.object({name:i.string(),description:i.string().optional(),settings:y.optional(),permissions:oe,files:i.record(i.string(),i.string()),indexes:i.record(i.string(),i.strictObject({fields:i.array(i.string()),unique:i.boolean().optional()})).optional(),typeHook:i.strictObject({create:s.optional(),update:s.optional()}).optional(),typeValidate:s.optional()}).strict()}),E=i.strictObject({directory:i.string().describe(`Directory containing migration files`),machineUser:i.string().optional().describe(`Machine user name for migration execution`)}),D=i.strictObject({files:i.array(i.string()).describe(`Glob patterns for TailorDB table definition files`),ignores:i.array(i.string()).optional().describe(`Glob patterns to exclude from table discovery`),migration:E.optional().describe(`Migration configuration`),gqlOperations:h.optional().describe(`Default GraphQL operations for all tables in this service`)}),O=T.keyof().options;function stripTailorDBTypeBuilderHelpers(t){return e(t,`tailordb-type`)?pickTailorDBTypeSchemaKeys(t):t}function pickTailorDBTypeSchemaKeys(e){if(typeof e!=`object`||!e)return e;let t={},n=e;for(let e of O)Object.prototype.hasOwnProperty.call(n,e)&&(t[e]=n[e]);return t}const k=i.strictObject({providerUrl:i.string().describe(`OAuth2 provider URL`),issuerUrl:i.string().describe(`OAuth2 issuer URL`),clientId:i.string().describe(`OAuth2 client ID`),clientSecret:i.string().describe(`OAuth2 client secret`),authUrl:i.string().optional().describe(`OAuth2 authorization endpoint override`),tokenUrl:i.string().optional().describe(`OAuth2 token endpoint override`)}).extend({type:i.literal(`oauth2`).describe(`Connection type`)}),A=i.strictObject({value:i.string().describe(`The allowed value`),description:i.string().optional().describe(`Description of the allowed value`)}),j=i.strictObject({as:i.enum([`string`,`date`,`temporal`]).optional().describe(`Date value representation`),required:i.boolean().optional().describe(`Whether the field is required`),array:i.boolean().optional().describe(`Whether the field is an array`),description:i.string().optional().describe(`Field description`),allowedValues:i.array(A).optional().describe(`Allowed values for enum fields`),hooks:i.strictObject({create:s.optional().describe(`Hook function called on creation`),update:s.optional().describe(`Hook function called on update`)}).optional().describe(`Lifecycle hooks`),validate:i.array(s).optional().describe(`Validation functions for the field`),typeName:i.string().optional().describe(`Type name for nested or enum fields`),default:i.unknown().optional().describe(`Default value for the field on create`)}),M=i.object({type:f.describe(`Field data type`),metadata:j.describe(`Field metadata configuration`),get fields(){return i.record(i.string(),M)}}),N=i.strictObject({namespace:i.string().describe(`Auth namespace`),machineUserName:i.string().describe(`Machine user name for authentication`)}),P=i.union([i.string().describe(`Machine user name (namespace auto-resolved from auth service)`),N]),F=i.strictObject({vaultName:i.string().describe(`Vault name containing the secret`),secretKey:i.string().describe(`Key of the secret in the vault`)}),I=i.strictObject({name:i.string().describe(`Identity provider name`),kind:i.literal(`OIDC`),clientID:i.string().describe(`OAuth2 client ID`),clientSecret:F.describe(`OAuth2 client secret`),providerURL:i.string().describe(`OIDC provider URL`),issuerURL:i.string().optional().describe(`OIDC issuer URL (defaults to providerURL)`),usernameClaim:i.string().optional().describe(`JWT claim to use as username`)}),L=i.strictObject({name:i.string().describe(`Identity provider name`),kind:i.literal(`SAML`),enableSignRequest:i.boolean().default(!1).describe(`Enable signing of SAML requests`),metadataURL:i.string().optional().describe(`URL to fetch SAML metadata (mutually exclusive with rawMetadata)`),rawMetadata:i.string().optional().describe(`Raw SAML metadata XML (mutually exclusive with metadataURL)`),defaultRedirectURL:i.string().optional().describe(`URL to redirect to when SAML ACS receives a response with an empty RelayState.`)}).refine(e=>e.metadataURL!==void 0!=(e.rawMetadata!==void 0),`Provide either metadataURL or rawMetadata`),R=i.strictObject({name:i.string().describe(`Identity provider name`),kind:i.literal(`IDToken`),providerURL:i.string().describe(`ID token provider URL`),issuerURL:i.string().optional().describe(`ID token issuer URL`),clientID:i.string().describe(`Client ID for ID token validation`),usernameClaim:i.string().optional().describe(`JWT claim to use as username`)}),z=i.strictObject({name:i.string().describe(`Identity provider name`),kind:i.literal(`BuiltInIdP`),namespace:i.string().describe(`IdP namespace`),clientName:i.string().describe(`OAuth2 client name in the IdP`)}),B=i.discriminatedUnion(`kind`,[I,L,R,z]),V=i.union([i.literal(`authorization_code`),i.literal(`refresh_token`)]).describe(`OAuth2 grant type`),H=i.strictObject({description:i.string().optional().describe(`Client description`),grantTypes:i.array(V).default([`authorization_code`,`refresh_token`]).describe(`Allowed OAuth2 grant types`),redirectURIs:i.array(i.union([i.templateLiteral([`https://`,i.string()]),i.templateLiteral([`http://`,i.string()]),i.templateLiteral([i.string(),`:url`]),i.templateLiteral([i.string(),`:url/`,i.string()])])).describe(`Allowed redirect URIs`),clientType:i.union([i.literal(`confidential`),i.literal(`public`),i.literal(`browser`)]).optional().describe(`OAuth2 client type`),accessTokenLifetimeSeconds:i.number().int().min(60,`Minimum access token lifetime is 60 seconds`).max(86400,`Maximum access token lifetime is 1 day (86400 seconds)`).optional().describe(`Access token lifetime in seconds (60-86400)`).transform(e=>e?{seconds:BigInt(e),nanos:0}:void 0),refreshTokenLifetimeSeconds:i.number().int().min(60,`Minimum refresh token lifetime is 60 seconds`).max(604800,`Maximum refresh token lifetime is 7 days (604800 seconds)`).optional().describe(`Refresh token lifetime in seconds (60-604800)`).transform(e=>e?{seconds:BigInt(e),nanos:0}:void 0),requireDpop:i.boolean().optional().describe(`Require DPoP (Demonstrating Proof-of-Possession) for token requests`)}).refine(e=>e.clientType!==`browser`||e.requireDpop!==!0,{message:`requireDpop cannot be set to true for browser clients as they don't support DPoP`,path:[`requireDpop`]}),U=i.strictObject({type:i.union([i.literal(`oauth2`),i.literal(`bearer`)]).describe(`SCIM authorization type`),bearerSecret:F.optional().describe(`Bearer token secret (required for bearer type)`)}),W=i.union([i.literal(`string`),i.literal(`number`),i.literal(`boolean`),i.literal(`datetime`),i.literal(`complex`)]).describe(`SCIM attribute data type`),G=i.strictObject({type:W.describe(`Attribute data type`),name:i.string().describe(`Attribute name`),description:i.string().optional().describe(`Attribute description`),mutability:i.union([i.literal(`readOnly`),i.literal(`readWrite`),i.literal(`writeOnly`)]).optional().describe(`Attribute mutability`),required:i.boolean().optional().describe(`Whether the attribute is required`),multiValued:i.boolean().optional().describe(`Whether the attribute can have multiple values`),uniqueness:i.union([i.literal(`none`),i.literal(`server`),i.literal(`global`)]).optional().describe(`Uniqueness constraint`),canonicalValues:i.array(i.string()).nullable().optional().describe(`List of canonical values`),get subAttributes(){return i.array(G).nullable().optional()}}),K=i.strictObject({name:i.string().describe(`SCIM schema name`),attributes:i.array(G).describe(`Schema attributes`)}),q=i.strictObject({tailorDBField:i.string().describe(`TailorDB field name to map to`),scimPath:i.string().describe(`SCIM attribute path`)}),se=i.strictObject({name:i.string().describe(`SCIM resource name`),tailorDBNamespace:i.string().describe(`TailorDB namespace for the resource`),tailorDBType:i.string().describe(`TailorDB table name for the resource`),coreSchema:K.describe(`Core SCIM schema definition`),attributeMapping:i.array(q).describe(`Attribute mapping configuration`)}),ce=i.strictObject({machineUserName:i.string().describe(`Machine user name for SCIM operations`),authorization:U.describe(`SCIM authorization configuration`),resources:i.array(se).describe(`SCIM resource definitions`)}),le=i.strictObject({namespace:i.string().describe(`TailorDB namespace for the tenant table`),type:i.string().describe(`TailorDB table name for tenants`),signatureField:i.string().describe(`Field used as the tenant signature`)}),ue=i.strictObject({namespace:i.string().optional().describe(`TailorDB namespace where the user table is defined`),type:i.custom().transform(stripTailorDBTypeBuilderHelpers).pipe(T),usernameField:i.string(),attributes:i.record(i.string(),i.literal(!0)).optional(),attributeList:i.array(i.string()).optional()}),de=i.union([i.string(),i.boolean(),i.array(i.string()),i.array(i.boolean())]),fe=i.strictObject({attributes:i.record(i.string(),de.nullish()).transform(e=>Object.fromEntries(Object.entries(e).filter(e=>e[1]!=null))).optional(),attributeList:i.array(i.uuid()).optional()}),pe=i.strictObject({handler:i.function(),invoker:i.string()}),J=i.strictObject({name:i.string().describe(`Auth service name`),hooks:i.strictObject({beforeLogin:pe.optional().describe(`Before login auth hook`)}).optional().describe(`Auth hooks`),machineUsers:i.record(i.string(),fe).optional().describe(`Machine user definitions`),oauth2Clients:i.record(i.string(),H).optional().describe(`OAuth2 client definitions`),idProvider:B.optional().describe(`Identity provider configuration`),scim:ce.optional().describe(`SCIM provisioning configuration`),tenantProvider:le.optional().describe(`Multi-tenant provider configuration`),connections:i.record(i.string(),k).optional().describe(`Auth connection definitions for external OAuth2 providers`),publishSessionEvents:i.boolean().optional().describe(`Enable publishing session events`)}),me=i.xor([J.extend({userProfile:ue.optional().describe(`User profile configuration`),machineUserAttributes:i.undefined().optional()}),J.extend({userProfile:i.undefined().optional(),machineUserAttributes:i.record(i.string(),M).describe(`Machine user attribute fields`)})],{error:e=>{if(e.code===`invalid_union`&&!(e.errors.length<2)&&e.errors.every(e=>e.every(e=>e.path.length===1&&(e.path[0]===`userProfile`||e.path[0]===`machineUserAttributes`))))return"Specify either `userProfile` or `machineUserAttributes`, not both."}}),he=i.strictObject({kind:i.literal(`tailordb`).describe(`TailorDB record event trigger`),events:i.array(i.enum([`tailordb.type_record.created`,`tailordb.type_record.updated`,`tailordb.type_record.deleted`])).min(1).transform(e=>[...new Set(e)]).describe(`TailorDB event types to trigger on`),tableName:i.string().describe(`TailorDB table name to watch for events`),condition:s.optional().describe(`Condition function to filter events`)}),ge=i.strictObject({kind:i.literal(`resolverExecuted`),resolverName:i.string().describe(`Name of the resolver to trigger on`),condition:s.optional().describe(`Condition function to filter events`)}),_e=i.strictObject({kind:i.literal(`schedule`),cron:i.string().describe(`CRON expression for the schedule`),timezone:i.string().optional().default(`UTC`).describe(`Timezone for the CRON schedule (default: UTC)`)}),ve=i.strictObject({body:s.optional().describe(`Function returning the response body`),statusCode:i.number().int().optional().describe(`HTTP status code for the response`)}),ye=i.strictObject({kind:i.literal(`incomingWebhook`),response:ve.optional().describe(`Response configuration`)}),be=i.strictObject({kind:i.literal(`idpUser`).describe(`IdP user event trigger`),events:i.array(i.enum([`idp.user.created`,`idp.user.updated`,`idp.user.deleted`])).min(1).transform(e=>[...new Set(e)]).describe(`IdP user event types to trigger on`),idp:i.string().optional().describe(`IdP namespace name to subscribe to. If omitted, the project's only IdP is used; throws when multiple IdPs exist.`)}),xe=i.strictObject({kind:i.literal(`authAccessToken`).describe(`Auth access token event trigger`),events:i.array(i.enum([`auth.access_token.issued`,`auth.access_token.refreshed`,`auth.access_token.revoked`])).min(1).transform(e=>[...new Set(e)]).describe(`Auth access token event types to trigger on`)}),Y=i.string().refine(e=>e.trim().length>0,`Workflow name cannot be blank`).describe(`Workflow name to subscribe to.`),Se=i.strictObject({kind:i.literal(`workflowExecution`).describe(`Workflow execution event trigger`),events:i.array(i.enum([`workflow.workflow_execution.started`,`workflow.workflow_execution.completed`,`workflow.workflow_execution.retried`,`workflow.workflow_execution.resumed`,`workflow.workflow_execution.wait_started`,`workflow.workflow_execution.wait_resolved`])).min(1).transform(e=>[...new Set(e)]).describe(`Workflow execution event types to trigger on`),workflowName:Y,condition:s.optional().describe(`Condition function to filter events`)}),Ce=i.strictObject({kind:i.literal(`workflowJobExecution`).describe(`Workflow job execution event trigger`),events:i.array(i.enum([`workflow.workflow_execution.job_execution.started`,`workflow.workflow_execution.job_execution.completed`,`workflow.workflow_execution.job_execution.wait_started`,`workflow.workflow_execution.job_execution.wait_resolved`])).min(1).transform(e=>[...new Set(e)]).describe(`Workflow job execution event types to trigger on`),workflowName:Y,condition:s.optional().describe(`Condition function to filter events`)}),X=i.discriminatedUnion(`kind`,[he,ge,_e,ye,be,xe,Se,Ce]),we=i.strictObject({kind:i.enum([`function`,`jobFunction`]),body:s.describe(`Function implementation`),invoker:P.optional().describe(`Invoker for the function execution`)}),Te=i.strictObject({kind:i.literal(`graphql`),appName:i.string().optional().describe(`Target application name for the GraphQL query`),query:i.preprocess(e=>String(e),i.string().describe(`GraphQL query string`)),variables:s.optional().describe(`Function to compute GraphQL variables`),invoker:P.optional().describe(`Invoker for the GraphQL execution`)}),Ee=i.strictObject({kind:i.literal(`webhook`),url:s.describe(`Function returning the webhook URL`),requestBody:s.optional().describe(`Function to compute the request body`),headers:i.record(i.string(),i.union([i.string(),i.strictObject({vault:i.string(),key:i.string()})])).optional().describe(`HTTP headers for the webhook request`)}),Z=i.lazy(()=>i.union([i.string(),i.number(),i.boolean(),i.null(),i.array(Z),i.record(i.string(),Z)])),De=i.union([i.string(),i.number(),i.boolean(),i.array(Z),i.record(i.string(),Z)]),Oe=i.union([De,s]).describe(`Arguments to pass to the workflow`),Q={kind:i.literal(`workflow`),args:Oe.optional(),invoker:P.optional().describe(`Invoker for the workflow execution`)},ke=i.strictObject({...Q,workflowName:i.string().describe(`Name of the workflow to execute`),workflow:i.never().optional()}),Ae=i.strictObject({...Q,workflow:i.looseObject({name:i.string()}),workflowName:i.string().optional()}).transform(({workflow:e,...t})=>({...t,workflowName:e.name})),je=i.union([Ae,ke]),Me=i.union([we,Te,Ee,je]),$=i.strictObject({name:i.string().describe(`Executor name`),description:i.string().optional().describe(`Executor description`),disabled:i.boolean().optional().default(!1).describe(`Whether the executor is disabled`),trigger:X.describe(`Event trigger configuration`),operation:Me.describe(`Operation to execute when triggered`)});function stripExecutorTriggerArgs(t){if(!e(t,`executor`))return t;let n=t.trigger;if(typeof n!=`object`||!n||!(`__args`in n))return t;let{__args:r,...i}=n;return{...t,trigger:i}}async function loadExecutor(e){let t=(await importUserModule(e)).default,n=$.safeParse(stripExecutorTriggerArgs(t));return n.success?n.data:null}function createExecutorService(i){let{config:a,baseDir:o}=i,s={},c=[],l,loadExecutorForFile=async i=>{try{let a=await importUserModule(i),o=$.safeParse(stripExecutorTriggerArgs(a.default));if(o.success){let e=r.relative(process.cwd(),i);return n.debug(`Executor: ${t.successBright(`"${o.data.name}"`)} loaded from ${t.path(e)}`),s[i]=o.data,o.data}if(e(a.default,`executor`))throw o.error}catch(e){let a=r.relative(process.cwd(),i);throw n.error(`Failed to load executor from ${t.bold(a)}`),n.error(String(e)),e}};return{config:a,get executors(){return s},get pluginExecutors(){return c},loadExecutors:async()=>(l||=(async()=>{if(a.files.length===0)return;let e=loadFilesWithIgnores(a,o);return n.newline(),n.log(`Found ${t.highlight(e.length.toString())} executor files`),await Promise.all(e.map(e=>loadExecutorForFile(e))),assertUniqueExecutorNames(s),s})(),await l??(Object.keys(s).length>0?s:void 0)),loadPluginExecutorFiles:async e=>{if(e.length!==0){n.newline(),n.log(`Loading ${t.highlight(e.length.toString())} plugin-generated executor files`);for(let t of e){let e=await loadExecutorForFile(t);e&&c.push({executor:e,pluginId:`plugin-generated`,sourceTableName:void 0})}assertUniqueExecutorNames(s)}}}}function assertUniqueExecutorNames(e){let t=/* @__PURE__ */ new Map;for(let[n,i]of Object.entries(e)){let e=r.relative(process.cwd(),n),a=t.get(i.name);if(a)throw Error(`Duplicate executor name "${i.name}" found:\n - ${a}\n - ${e}\nEach executor must have a unique name.`);t.set(i.name,e)}}export{c as _,me as a,importUserModule as b,pickTailorDBTypeSchemaKeys as c,T as d,applyRelationMetadataToFieldConfig as f,loadFilesWithIgnores as g,validateRelationConfig as h,$ as i,stripTailorDBTypeBuilderHelpers as l,processRelationMetadata as m,loadExecutor as n,P as o,buildRelationInfo as p,stripExecutorTriggerArgs as r,M as s,createExecutorService as t,D as u,beginUserModuleRun as v,s as x,currentImportNonce as y};
|
|
7
|
+
//# sourceMappingURL=service-BrGSi2IQ.mjs.map
|