@tailor-platform/sdk 2.29.2 → 2.30.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.
Files changed (127) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/aigateway-DsWDjzk4.mjs.map +1 -1
  3. package/dist/{application-Dm_21z-i.mjs → application-C2bPmmbh.mjs} +1 -1
  4. package/dist/{application-D8jruzwo.mjs → application-D0xNJbag.mjs} +32 -32
  5. package/dist/{application-D8jruzwo.mjs.map → application-D0xNJbag.mjs.map} +1 -1
  6. package/dist/assert-WeXvmG4j.mjs.map +1 -1
  7. package/dist/authconnection-CynFBIv8.mjs.map +1 -1
  8. package/dist/brand-C8nMKhJC.mjs.map +1 -1
  9. package/dist/cli/commands/machineuser/list.d.mts +2 -1
  10. package/dist/cli/lib.mjs +1 -1
  11. package/dist/cli/lib.mjs.map +1 -1
  12. package/dist/cli/main.mjs +16 -16
  13. package/dist/cli/main.mjs.map +1 -1
  14. package/dist/cli/shared/error-json.d.mts +2 -0
  15. package/dist/completion/zsh-worker.zsh +1 -1
  16. package/dist/configure/index.mjs.map +1 -1
  17. package/dist/context-D0QjfxzD.mjs.map +1 -1
  18. package/dist/{crashreport-BAG8c7hB.mjs → crashreport-BCIy33Cz.mjs} +2 -2
  19. package/dist/{crashreport-BAG8c7hB.mjs.map → crashreport-BCIy33Cz.mjs.map} +1 -1
  20. package/dist/crashreport-IN6H7GX3.mjs +1 -0
  21. package/dist/date-CgP7gMG_.mjs.map +1 -1
  22. package/dist/date-representation-Cr7W7Pn2.mjs.map +1 -1
  23. package/dist/errors-BOF68LJ5.mjs +5 -0
  24. package/dist/errors-BOF68LJ5.mjs.map +1 -0
  25. package/dist/es-builtins-SfvXRVoG.mjs.map +1 -1
  26. package/dist/field-parse-C41e3UAF.mjs.map +1 -1
  27. package/dist/file-DKBOj5q4.mjs.map +1 -1
  28. package/dist/globals-BCmGX05J.mjs.map +1 -1
  29. package/dist/guards-yDXyKC9F.mjs.map +1 -1
  30. package/dist/iconv-DlFMt2gW.mjs.map +1 -1
  31. package/dist/idp-G_ojPBB5.mjs.map +1 -1
  32. package/dist/interceptor-DQg3cR_9.mjs.map +1 -1
  33. package/dist/kysely/index.mjs.map +1 -1
  34. package/dist/kysely-type-BWMfV301.mjs.map +1 -1
  35. package/dist/logger-DP2BjQ93.mjs.map +1 -1
  36. package/dist/{logger-DuaT2zY3.mjs → logger-RgtbhJ83.mjs} +3 -3
  37. package/dist/logger-RgtbhJ83.mjs.map +1 -0
  38. package/dist/manager-Bk-7vkwd.mjs.map +1 -1
  39. package/dist/migration-steps-C6F3zNdQ.mjs.map +1 -1
  40. package/dist/multiline-EyzjEwn9.mjs.map +1 -1
  41. package/dist/node-builtins-DYfhPBnz.mjs.map +1 -1
  42. package/dist/package-json-C690ceex.mjs.map +1 -1
  43. package/dist/platform-serialize-DkiTdHOt.mjs.map +1 -1
  44. package/dist/plugin/builtin/enum-constants/index.mjs.map +1 -1
  45. package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
  46. package/dist/plugin/builtin/seed/index.mjs +1 -1
  47. package/dist/plugin/index.mjs.map +1 -1
  48. package/dist/{register-ts-hook-B5ZbbJwS.mjs → register-ts-hook-CDnN_Jx2.mjs} +65 -65
  49. package/dist/register-ts-hook-CDnN_Jx2.mjs.map +1 -0
  50. package/dist/registry-BijIDRVA.mjs.map +1 -1
  51. package/dist/repl-editor-DGJBfIt1.mjs.map +1 -1
  52. package/dist/schema-B6hJQWp6.mjs.map +1 -1
  53. package/dist/secret-file-C9wp_FCX.mjs.map +1 -1
  54. package/dist/secretmanager-vHQoXdQz.mjs.map +1 -1
  55. package/dist/seed/idp-user-password.d.mts +15 -0
  56. package/dist/seed/index.d.mts +19 -1
  57. package/dist/seed/index.mjs +6 -6
  58. package/dist/seed/index.mjs.map +1 -1
  59. package/dist/{seed-C3P_T_Eh.mjs → seed-Cn-3JApy.mjs} +12 -8
  60. package/dist/seed-Cn-3JApy.mjs.map +1 -0
  61. package/dist/{service-l_tEI8sS.mjs → service-9SBPNpK8.mjs} +2 -2
  62. package/dist/{service-l_tEI8sS.mjs.map → service-9SBPNpK8.mjs.map} +1 -1
  63. package/dist/{service-b1g22NN9.mjs → service-9rwi_I4d.mjs} +2 -2
  64. package/dist/{service-b1g22NN9.mjs.map → service-9rwi_I4d.mjs.map} +1 -1
  65. package/dist/service-Ca1WwyIt.mjs +1 -0
  66. package/dist/service_pb-Bbu2QSIr.mjs.map +1 -1
  67. package/dist/telemetry-Xk7J829P.mjs.map +1 -1
  68. package/dist/type-source--ZNcV8RJ.mjs.map +1 -1
  69. package/dist/user-agent-vdHYF3QL.mjs.map +1 -1
  70. package/dist/utils/test/index.mjs.map +1 -1
  71. package/dist/vitest/environment.mjs.map +1 -1
  72. package/dist/vitest/index.mjs.map +1 -1
  73. package/dist/vitest/setup.mjs.map +1 -1
  74. package/dist/wait-point-invoker-eiP-IIux.mjs.map +1 -1
  75. package/dist/wait-point-registry-B-ESkTZX.mjs.map +1 -1
  76. package/dist/workflow-Cs9ISw6j.mjs.map +1 -1
  77. package/dist/workspace_resource_pb-CaoM2npP.mjs.map +1 -1
  78. package/docs/cli/application.md +4 -0
  79. package/docs/cli/auth.md +4 -0
  80. package/docs/cli/completion.md +4 -0
  81. package/docs/cli/crashreport.md +4 -0
  82. package/docs/cli/data.md +4 -0
  83. package/docs/cli/executor.md +4 -0
  84. package/docs/cli/function.md +4 -0
  85. package/docs/cli/organization.md +4 -0
  86. package/docs/cli/plugin.md +4 -0
  87. package/docs/cli/query.md +4 -0
  88. package/docs/cli/secret.md +4 -0
  89. package/docs/cli/skills.md +4 -0
  90. package/docs/cli/staticwebsite.md +4 -0
  91. package/docs/cli/tailordb.md +4 -0
  92. package/docs/cli/upgrade.md +4 -0
  93. package/docs/cli/user.md +4 -0
  94. package/docs/cli/workflow.md +4 -0
  95. package/docs/cli/workspace.md +4 -0
  96. package/docs/cli-reference.md +4 -0
  97. package/docs/configuration.md +4 -0
  98. package/docs/data-clone.md +4 -0
  99. package/docs/github-actions.md +10 -0
  100. package/docs/migration/v2.md +4 -0
  101. package/docs/multi-environment.md +4 -0
  102. package/docs/plugin/custom.md +4 -0
  103. package/docs/plugin/frontend.md +4 -0
  104. package/docs/plugin/index.md +4 -0
  105. package/docs/quickstart.md +4 -0
  106. package/docs/runtime.md +4 -0
  107. package/docs/services/aigateway.md +4 -0
  108. package/docs/services/auth.md +4 -0
  109. package/docs/services/executor.md +4 -0
  110. package/docs/services/http-adapter.md +4 -0
  111. package/docs/services/idp.md +4 -0
  112. package/docs/services/resolver.md +4 -0
  113. package/docs/services/secret.md +4 -0
  114. package/docs/services/staticwebsite.md +4 -0
  115. package/docs/services/tailordb/index.md +4 -0
  116. package/docs/services/tailordb/migration.md +4 -0
  117. package/docs/services/workflow.md +4 -0
  118. package/docs/testing.md +4 -0
  119. package/package.json +3 -3
  120. package/dist/crashreport-BcXuocwI.mjs +0 -1
  121. package/dist/errors-D3qpNHR6.mjs +0 -7
  122. package/dist/errors-D3qpNHR6.mjs.map +0 -1
  123. package/dist/logger-DuaT2zY3.mjs.map +0 -1
  124. package/dist/register-ts-hook-B5ZbbJwS.mjs.map +0 -1
  125. package/dist/seed-C3P_T_Eh.mjs.map +0 -1
  126. package/dist/service-C_CogLQr.mjs +0 -1
  127. package/docs/migration/v3.md +0 -277
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["lines","content","remaining","recordIndex","index","line","first","errors","readFile","DEFAULT_FILL_FIELDS","readdir","dir","isSchemaFile","entry","parent","dirname","dataDir","command","basename","stats","stat","resolvedPath","options","datasetDirs","excludeBase","dataDirs","warnings","dirs","table","findSchemaFile","schemaDir","join","MISSING_REFERENCED_TABLE","warning","error","formatter","ErrorFormatter","errorLines","errorsByFile","fileErrors","file","validationErrors","foreignKeyErrors","fkError","config","db","LinesDB","initialized","unwrap","message","missingTable","referencedTable","result","outputLines","verbose","fields","tables","tableName","fillFields","hook","schemaModule","schemaPath","filled","normalize","unreadableLines","tablesWithoutSchema","unproducedFields","tableFields","count","defineSchema"],"sources":["../../src/seed/record-lines.ts","../../src/seed/index.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { ValidationErrorDetail } from \"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 `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, stat } from \"node:fs/promises\";\nimport {\n LinesDB,\n ErrorFormatter,\n fillFields,\n findSchemaFile,\n isSchemaFile,\n unwrap,\n} from \"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, normalize } from \"pathe\";\nimport { firstErrorLocation } from \"./record-lines\";\nimport type { JsonlParseError, RowFiller, ValidationErrorDetail } from \"lines-db\";\n\nexport { defineSchema } from \"lines-db\";\nexport type { ForeignKeyDefinition, IndexDefinition } from \"lines-db\";\n\n/** Fields `fillSeedData` writes when the caller names none. */\nconst DEFAULT_FILL_FIELDS = [\"id\"];\n\ntype SeedHook = RowFiller;\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 /**\n * Dataset directories whose rows are validated on top of the rows in `path`, which must then be\n * the data directory holding the schema files. A table's rows are those of every directory, in\n * this order, so foreign keys and duplicate ids are checked across them.\n */\n datasetDirs?: readonly string[];\n /** Validate only the rows of `datasetDirs`, leaving the rows in `path` out. Defaults to `false`. */\n excludeBase?: boolean;\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 /** Directory the schema files are looked up in. Defaults to the directory of the data. */\n schemaDir?: string;\n};\n\ntype FillSeedDataResult = {\n output: string;\n filled: FilledSeedFile[];\n};\n\nasync function hasSchemaFiles(dir: string): Promise<boolean> {\n try {\n return (await readdir(dir)).some((entry) => isSchemaFile(entry));\n } catch {\n return false;\n }\n}\n\n/**\n * Point at `--dataset` for a directory without schema files of its own inside one that has them:\n * named as a path, it is checked on its own, not as a dataset on top of the seed data.\n * @param dataDir - Directory of the data the command was pointed at\n * @param command - The `tailor seed` command to suggest\n * @returns The hint as a warning, or none when the directory does not look like a dataset\n */\nasync function datasetHint(dataDir: string, command: \"validate\" | \"fill\"): Promise<string[]> {\n const parent = dirname(dataDir);\n if (parent === dataDir || (await hasSchemaFiles(dataDir)) || !(await hasSchemaFiles(parent))) {\n return [];\n }\n return [\n `${dataDir} has no schema files of its own. To ${command} it as a dataset with the schema files ` +\n `in ${parent}, run \\`tailor seed ${command} --dataset ${basename(dataDir)}\\`.`,\n ];\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\ntype ValidationConfig = {\n dataDir: string | string[];\n schemaDir?: string;\n tableName?: string;\n /** Warnings about how the path was resolved */\n warnings: string[];\n};\n\nasync function resolveValidationConfig(\n options: ValidateSeedDataOptions,\n): Promise<ValidationConfig> {\n const { path: resolvedPath, datasetDirs = [], excludeBase = false } = options;\n if (datasetDirs.length === 0) {\n if (excludeBase) {\n throw new Error(\"Leaving the base data out needs at least one dataset to validate.\");\n }\n const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n return { dataDir, tableName, warnings: await datasetHint(dataDir, \"validate\") };\n }\n if (!(await stat(resolvedPath)).isDirectory()) {\n throw new Error(\n `Invalid path: ${resolvedPath}. Datasets are validated against the schema files of a data directory, so the path must be a directory.`,\n );\n }\n const dataDirs = excludeBase ? [...datasetDirs] : [resolvedPath, ...datasetDirs];\n return {\n dataDir: dataDirs.length === 1 ? (dataDirs[0] as string) : dataDirs,\n schemaDir: resolvedPath,\n warnings: await findFilesWithoutSchema(datasetDirs, resolvedPath),\n };\n}\n\nasync function findFilesWithoutSchema(\n dirs: readonly string[],\n schemaDir: string,\n): Promise<string[]> {\n const warnings: string[] = [];\n for (const dir of dirs) {\n for (const table of await listSeedTables(dir)) {\n if (!(await findSchemaFile(schemaDir, table))) {\n warnings.push(\n `${join(dir, `${table}.jsonl`)} has no schema file in ${schemaDir}, so its rows are not checked`,\n );\n }\n }\n }\n return warnings;\n}\n\nconst MISSING_REFERENCED_TABLE =\n /^Foreign key reference to non-existent table '(.+)' in table '(.+)'$/;\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 isJsonlParseError(error: Error): error is JsonlParseError {\n return error.name === \"JsonlParseError\";\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. With `datasetDirs`,\n * the rows of those directories are validated on top of the rows in `path`,\n * against the schema files in `path`.\n * @param options - Validation options including path, datasets, 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 { verbose = false, excludeBase = false } = options;\n const { tableName, warnings, ...config } = await resolveValidationConfig(options);\n\n const db = LinesDB.create(config);\n let initialized;\n try {\n initialized = await db.initialize({ tableName, detailedValidate: true });\n } finally {\n unwrap(await db.close());\n }\n if (!initialized.ok && isJsonlParseError(initialized.error)) {\n const { file, line, message } = initialized.error;\n return {\n valid: false,\n output: \"\",\n error: [\n new ErrorFormatter({ verbose }).formatErrorHeader(1, file),\n \"\",\n ` Line ${line}: ${message}`,\n \"\",\n ].join(\"\\n\"),\n location: { file, line },\n };\n }\n if (excludeBase && !initialized.ok) {\n const missingTable = MISSING_REFERENCED_TABLE.exec(initialized.error.message);\n if (missingTable) {\n const [, referencedTable, table] = missingTable;\n throw new Error(\n `${table} has a foreign key to ${referencedTable}, but none of the selected datasets has ${referencedTable}.jsonl. ` +\n \"Add the file to one of them, even an empty one, or validate them on top of the base seed data.\",\n { cause: initialized.error },\n );\n }\n }\n const result = unwrap(initialized);\n\n const outputLines = formatWarnings([...warnings, ...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/**\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, or in\n * `schemaDir` when it is given, 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. Likewise, a file that\n * another tool changed after the fill read it is not overwritten: the call\n * rejects with an error naming that file, and no file is written.\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 result = unwrap(\n await fillFields({\n path: resolvedPath,\n fields,\n schemaDir: options.schemaDir,\n loadFiller: (schemaModule, { schemaPath }) => {\n const hook = schemaModule.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 const { tablesWithoutSchema, unproducedFields } = result;\n const filled = result.filled.map((entry) => ({ ...entry, file: normalize(entry.file) }));\n const unreadableLines = result.unreadableLines.map((entry) => ({\n ...entry,\n file: normalize(entry.file),\n }));\n\n const warnings = [\n ...(options.schemaDir ? [] : await datasetHint(dataDir, \"fill\")),\n ...tablesWithoutSchema.map(\n (table) => `No schema file for ${table}, so nothing can be filled in there`,\n ),\n ...unreadableLines.map(\n ({ file, lines }) =>\n `${file}: line(s) ${lines.join(\", \")} are not JSON objects, so nothing was filled in there`,\n ),\n ];\n if (tables.length > 0 && unproducedFields.length > 0) {\n warnings.push(`No seed data produces a value for: ${unproducedFields.join(\", \")}`);\n }\n\n const outputLines = formatWarnings(warnings);\n outputLines.push(\n filled.length === 0\n ? \"\\u2713 Nothing to fill\"\n : filled\n .map(\n ({ file, fields: tableFields, count }) =>\n `\\u2713 ${file}: filled ${tableFields.join(\", \")} in ${count} row(s)`,\n )\n .join(\"\\n\"),\n );\n\n return { output: outputLines.join(\"\\n\"), filled };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["lines","content","remaining","recordIndex","index","line","first","errors","readFile","value","parsed","raw","password","name","row","env","PASSWORD_ENV","DEFAULT_FILL_FIELDS","readdir","dir","isSchemaFile","entry","parent","dirname","dataDir","command","basename","stats","stat","resolvedPath","options","datasetDirs","excludeBase","dataDirs","warnings","dirs","table","findSchemaFile","schemaDir","join","MISSING_REFERENCED_TABLE","warning","error","formatter","ErrorFormatter","errorLines","errorsByFile","fileErrors","file","validationErrors","foreignKeyErrors","fkError","config","db","LinesDB","initialized","unwrap","message","missingTable","referencedTable","result","outputLines","verbose","fields","tables","tableName","fillFields","hook","schemaModule","schemaPath","filled","normalize","unreadableLines","tablesWithoutSchema","unproducedFields","tableFields","count","pathToFileURL","defineSchema"],"sources":["../../src/seed/record-lines.ts","../../src/seed/idp-user-password.ts","../../src/seed/index.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { ValidationErrorDetail } from \"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","const PASSWORD_ENV = \"SEED_USER_PASSWORD\";\n\ntype Env = Record<string, string | undefined>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction passwordFor(name: unknown, raw: string): string | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return raw;\n }\n if (!isRecord(parsed)) return raw;\n const password = typeof name === \"string\" ? parsed[name] : undefined;\n return typeof password === \"string\" ? password : undefined;\n}\n\n/**\n * Fill in the `password` of an IdP `_User` seed row from the `SEED_USER_PASSWORD`\n * environment variable. A row that already has a `password` keeps it.\n *\n * A plain string applies to every user. A JSON object maps each user `name` to its\n * password, and a user missing from the object is left without one.\n * @param row - A `_User` seed row\n * @param env - Environment to read from. Defaults to `process.env`.\n * @returns The row with `password` filled in when the environment supplies one\n */\nexport function fillIdpUserPassword(row: unknown, env: Env = process.env): unknown {\n if (!isRecord(row) || (row.password !== undefined && row.password !== null)) return row;\n const raw = env[PASSWORD_ENV];\n if (!raw) return row;\n const password = passwordFor(row.name, raw);\n return password === undefined ? row : { ...row, password };\n}\n","/**\n * Seed integration module for generated seed code.\n *\n * Re-exports `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, stat } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport {\n LinesDB,\n ErrorFormatter,\n fillFields,\n findSchemaFile,\n isSchemaFile,\n unwrap,\n} from \"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, normalize } from \"pathe\";\nimport { firstErrorLocation } from \"./record-lines\";\nimport type { JsonObject, JsonlParseError, RowFiller, ValidationErrorDetail } from \"lines-db\";\n\nexport { defineSchema } from \"lines-db\";\nexport { fillIdpUserPassword } from \"./idp-user-password\";\nexport type { ForeignKeyDefinition, IndexDefinition } from \"lines-db\";\n\n/** Fields `fillSeedData` writes when the caller names none. */\nconst DEFAULT_FILL_FIELDS = [\"id\"];\n\ntype SeedHook = RowFiller;\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 /**\n * Dataset directories whose rows are validated on top of the rows in `path`, which must then be\n * the data directory holding the schema files. A table's rows are those of every directory, in\n * this order, so foreign keys and duplicate ids are checked across them.\n */\n datasetDirs?: readonly string[];\n /** Validate only the rows of `datasetDirs`, leaving the rows in `path` out. Defaults to `false`. */\n excludeBase?: boolean;\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 /** Directory the schema files are looked up in. Defaults to the directory of the data. */\n schemaDir?: string;\n};\n\ntype FillSeedDataResult = {\n output: string;\n filled: FilledSeedFile[];\n};\n\nasync function hasSchemaFiles(dir: string): Promise<boolean> {\n try {\n return (await readdir(dir)).some((entry) => isSchemaFile(entry));\n } catch {\n return false;\n }\n}\n\n/**\n * Point at `--dataset` for a directory without schema files of its own inside one that has them:\n * named as a path, it is checked on its own, not as a dataset on top of the seed data.\n * @param dataDir - Directory of the data the command was pointed at\n * @param command - The `tailor seed` command to suggest\n * @returns The hint as a warning, or none when the directory does not look like a dataset\n */\nasync function datasetHint(dataDir: string, command: \"validate\" | \"fill\"): Promise<string[]> {\n const parent = dirname(dataDir);\n if (parent === dataDir || (await hasSchemaFiles(dataDir)) || !(await hasSchemaFiles(parent))) {\n return [];\n }\n return [\n `${dataDir} has no schema files of its own. To ${command} it as a dataset with the schema files ` +\n `in ${parent}, run \\`tailor seed ${command} --dataset ${basename(dataDir)}\\`.`,\n ];\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\ntype ValidationConfig = {\n dataDir: string | string[];\n schemaDir?: string;\n tableName?: string;\n /** Warnings about how the path was resolved */\n warnings: string[];\n};\n\nasync function resolveValidationConfig(\n options: ValidateSeedDataOptions,\n): Promise<ValidationConfig> {\n const { path: resolvedPath, datasetDirs = [], excludeBase = false } = options;\n if (datasetDirs.length === 0) {\n if (excludeBase) {\n throw new Error(\"Leaving the base data out needs at least one dataset to validate.\");\n }\n const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n return { dataDir, tableName, warnings: await datasetHint(dataDir, \"validate\") };\n }\n if (!(await stat(resolvedPath)).isDirectory()) {\n throw new Error(\n `Invalid path: ${resolvedPath}. Datasets are validated against the schema files of a data directory, so the path must be a directory.`,\n );\n }\n const dataDirs = excludeBase ? [...datasetDirs] : [resolvedPath, ...datasetDirs];\n return {\n dataDir: dataDirs.length === 1 ? (dataDirs[0] as string) : dataDirs,\n schemaDir: resolvedPath,\n warnings: await findFilesWithoutSchema(datasetDirs, resolvedPath),\n };\n}\n\nasync function findFilesWithoutSchema(\n dirs: readonly string[],\n schemaDir: string,\n): Promise<string[]> {\n const warnings: string[] = [];\n for (const dir of dirs) {\n for (const table of await listSeedTables(dir)) {\n if (!(await findSchemaFile(schemaDir, table))) {\n warnings.push(\n `${join(dir, `${table}.jsonl`)} has no schema file in ${schemaDir}, so its rows are not checked`,\n );\n }\n }\n }\n return warnings;\n}\n\nconst MISSING_REFERENCED_TABLE =\n /^Foreign key reference to non-existent table '(.+)' in table '(.+)'$/;\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 isJsonlParseError(error: Error): error is JsonlParseError {\n return error.name === \"JsonlParseError\";\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. With `datasetDirs`,\n * the rows of those directories are validated on top of the rows in `path`,\n * against the schema files in `path`.\n * @param options - Validation options including path, datasets, 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 { verbose = false, excludeBase = false } = options;\n const { tableName, warnings, ...config } = await resolveValidationConfig(options);\n\n const db = LinesDB.create(config);\n let initialized;\n try {\n initialized = await db.initialize({ tableName, detailedValidate: true });\n } finally {\n unwrap(await db.close());\n }\n if (!initialized.ok && isJsonlParseError(initialized.error)) {\n const { file, line, message } = initialized.error;\n return {\n valid: false,\n output: \"\",\n error: [\n new ErrorFormatter({ verbose }).formatErrorHeader(1, file),\n \"\",\n ` Line ${line}: ${message}`,\n \"\",\n ].join(\"\\n\"),\n location: { file, line },\n };\n }\n if (excludeBase && !initialized.ok) {\n const missingTable = MISSING_REFERENCED_TABLE.exec(initialized.error.message);\n if (missingTable) {\n const [, referencedTable, table] = missingTable;\n throw new Error(\n `${table} has a foreign key to ${referencedTable}, but none of the selected datasets has ${referencedTable}.jsonl. ` +\n \"Add the file to one of them, even an empty one, or validate them on top of the base seed data.\",\n { cause: initialized.error },\n );\n }\n }\n const result = unwrap(initialized);\n\n const outputLines = formatWarnings([...warnings, ...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/**\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, or in\n * `schemaDir` when it is given, 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. Likewise, a file that\n * another tool changed after the fill read it is not overwritten: the call\n * rejects with an error naming that file, and no file is written.\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 result = unwrap(\n await fillFields({\n path: resolvedPath,\n fields,\n schemaDir: options.schemaDir,\n loadFiller: (schemaModule, { schemaPath }) => {\n const hook = schemaModule.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 const { tablesWithoutSchema, unproducedFields } = result;\n const filled = result.filled.map((entry) => ({ ...entry, file: normalize(entry.file) }));\n const unreadableLines = result.unreadableLines.map((entry) => ({\n ...entry,\n file: normalize(entry.file),\n }));\n\n const warnings = [\n ...(options.schemaDir ? [] : await datasetHint(dataDir, \"fill\")),\n ...tablesWithoutSchema.map(\n (table) => `No schema file for ${table}, so nothing can be filled in there`,\n ),\n ...unreadableLines.map(\n ({ file, lines }) =>\n `${file}: line(s) ${lines.join(\", \")} are not JSON objects, so nothing was filled in there`,\n ),\n ];\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\ntype HookSeedRowsOptions = {\n /** Directory holding the generated schema files */\n schemaDir: string;\n /** Name of the seeded table, matching the schema file name */\n table: string;\n /** Rows as read from the JSONL files */\n rows: readonly Record<string, unknown>[];\n};\n\n/**\n * Run each row through the `hook` its generated schema file exports, so the values a row gets\n * from the schema (such as the `_User` password read from the environment) are the ones that\n * are seeded. Rows are returned as they are when the table has no schema file or the file\n * exports no `hook`.\n * @param options - Schema directory, table name and rows\n * @returns The rows after the hook\n */\nexport async function hookSeedRows(options: HookSeedRowsOptions): Promise<unknown[]> {\n const schemaPath = await findSchemaFile(options.schemaDir, options.table);\n if (!schemaPath) return [...options.rows];\n const schemaModule = await import(`${pathToFileURL(schemaPath).href}?t=${Date.now()}`);\n const hook = schemaModule.hook;\n if (typeof hook !== \"function\") return [...options.rows];\n return options.rows.map((row) => (hook as SeedHook)(structuredClone(row) as JsonObject));\n}\n"],"mappings":"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"}
@@ -14,18 +14,23 @@ import{t as e}from"./assert-WeXvmG4j.mjs";import{t}from"./multiline-EyzjEwn9.mjs
14
14
 
15
15
  for (let i = 0; i < input.users.length; i++) {
16
16
  try {
17
+ const { initialPassword, ...row } = input.users[i];
18
+ const createInput =
19
+ initialPassword === undefined || row.password !== undefined
20
+ ? row
21
+ : { ...row, password: initialPassword };
17
22
  if (upsert) {
18
23
  let existing;
19
24
  let lookupError;
20
25
  try {
21
- existing = await client.userByName(input.users[i].name);
26
+ existing = await client.userByName(row.name);
22
27
  } catch (error) {
23
28
  existing = undefined;
24
29
  lookupError = error instanceof Error ? error.message : String(error);
25
30
  }
26
31
 
27
32
  if (existing) {
28
- const { name, ...attributes } = input.users[i];
33
+ const { name, ...attributes } = row;
29
34
  if (Object.keys(attributes).length === 0) {
30
35
  skipped++;
31
36
  } else {
@@ -34,7 +39,7 @@ import{t as e}from"./assert-WeXvmG4j.mjs";import{t}from"./multiline-EyzjEwn9.mjs
34
39
  }
35
40
  } else {
36
41
  try {
37
- await client.createUser(input.users[i]);
42
+ await client.createUser(createInput);
38
43
  created++;
39
44
  } catch (error) {
40
45
  const message = error instanceof Error ? error.message : String(error);
@@ -44,7 +49,7 @@ import{t as e}from"./assert-WeXvmG4j.mjs";import{t}from"./multiline-EyzjEwn9.mjs
44
49
  }
45
50
  }
46
51
  } else {
47
- await client.createUser(input.users[i]);
52
+ await client.createUser(createInput);
48
53
  created++;
49
54
  }
50
55
  processed++;
@@ -146,7 +151,7 @@ import{t as e}from"./assert-WeXvmG4j.mjs";import{t}from"./multiline-EyzjEwn9.mjs
146
151
  ],
147
152
  `;return t`
148
153
  import { t } from "@tailor-platform/sdk";
149
- import { defineSchema } from "@tailor-platform/sdk/seed";
154
+ import { defineSchema, fillIdpUserPassword } from "@tailor-platform/sdk/seed";
150
155
  import { createStandardSchema } from "@tailor-platform/sdk/test";
151
156
 
152
157
  const schemaType = t.object({
@@ -154,8 +159,7 @@ import{t as e}from"./assert-WeXvmG4j.mjs";import{t}from"./multiline-EyzjEwn9.mjs
154
159
  password: t.string(),
155
160
  });
156
161
 
157
- // Simple identity hook for _User (no TailorDB backing table)
158
- export const hook = <T>(data: unknown) => data as T;
162
+ export const hook = <T>(data: unknown) => fillIdpUserPassword(data) as T;
159
163
 
160
164
  export const schema = defineSchema(
161
165
  createStandardSchema(schemaType, hook),
@@ -247,4 +251,4 @@ import{t as e}from"./assert-WeXvmG4j.mjs";import{t}from"./multiline-EyzjEwn9.mjs
247
251
  );
248
252
 
249
253
  `}const a=`@tailor-platform/seed`;function resolveIdpUserSyncFKs(e){return{emitUserToIdpFK:!(e?.userToIdp??!1),emitIdpToUserFK:!(e?.idpToUser??!1)}}function seedPlugin(t){return{id:a,description:`Generates seed data and schema files for the tailor seed CLI plugin`,pluginConfig:t,async onTailorDBReady(t){let n=[],i=t.auth?processIdpUser(t.auth)??null:null,a=resolveIdpUserSyncFKs(t.pluginConfig.disableIdpUserSync);for(let o of t.tailordb)for(let[s,c]of Object.entries(o.tables)){let l=processLinesDb(c,e(o.sourceInfo.get(s),`source info missing for table: ${s}`));a.emitUserToIdpFK&&i&&s===i.schema.userTableName&&l.foreignKeys.push({column:i.schema.usernameField,references:{table:`_User`,column:`name`}}),n.push({path:r.join(t.pluginConfig.distPath,`data`,`${l.tableName}.jsonl`),content:``,skipIfExists:!0});let u=r.join(t.pluginConfig.distPath,`data`,`${l.tableName}.schema.ts`);if(l.pluginSource&&l.pluginSource.pluginImportPath){let e;if(l.pluginSource.originalFilePath&&l.pluginSource.originalExportName){let t=r.relative(r.dirname(u),l.pluginSource.originalFilePath);e=t.replace(/\.ts$/,``).startsWith(`.`)?t.replace(/\.ts$/,``):`./${t.replace(/\.ts$/,``)}`}let i=generateLinesDbSchemaFileWithPluginAPI(l,{configImportPath:r.relative(r.dirname(u),t.configPath),originalImportPath:e});n.push({path:u,content:i})}else{let e=r.relative(r.dirname(u),l.importPath),i=generateLinesDbSchemaFile(l,{typeImportPath:e.replace(/\.ts$/,``).startsWith(`.`)?e.replace(/\.ts$/,``):`./${e.replace(/\.ts$/,``)}`,configImportPath:(o.pluginAttachments.get(s)?.length??0)>0?r.relative(r.dirname(u),t.configPath):void 0});n.push({path:u,content:i})}}return i&&(n.push({path:r.join(t.pluginConfig.distPath,i.dataFile),content:``,skipIfExists:!0}),n.push({path:r.join(t.pluginConfig.distPath,`data`,`${i.name}.schema.ts`),content:generateIdpUserSchemaFile({usernameField:i.schema.usernameField,userTableName:i.schema.userTableName,includeUserProfileFK:a.emitIdpToUserFK})})),{files:n}}}}export{generateIdpSeedScriptCode as a,generateIdpListUsersScriptCode as i,seedPlugin as n,generateIdpTruncateScriptCode as o,processLinesDb as r,processIdpUser as s,a as t};
250
- //# sourceMappingURL=seed-C3P_T_Eh.mjs.map
254
+ //# sourceMappingURL=seed-Cn-3JApy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed-Cn-3JApy.mjs","names":["assertDefined","isPluginGeneratedTable","path","auth","tableName","idpNamespace","listIdpUsersFunction","options","schemaBody","includeUserProfileFK","userTableName","usernameField","source","type","field","optionalFields","omitFields","indexes","foreignKeys","fieldName","indexName","indexDef","schemaOptions","fk","index","option","tableVariable","metadata","params","schemaOptionsCode","configImportPath","exportName","typeImportPath","pluginSource","schemaTypeCode","originalImportPath","SeedGeneratorID","files","idpUser","ctx","idpUserSyncFKs","ns","linesDb","schemaOutputPath","relativePath","schemaContent"],"sources":["../src/plugin/builtin/seed/idp-user-processor.ts","../src/plugin/builtin/seed/lines-db-processor.ts","../src/plugin/builtin/seed/index.ts"],"sourcesContent":["import ml from \"#/utils/multiline\";\nimport type { GeneratorAuthInput } from \"#/plugin/types\";\n\nexport interface IdpUserMetadata {\n name: \"_User\";\n dependencies: string[];\n dataFile: string;\n idpNamespace: string;\n schema: {\n usernameField: string;\n userTableName: string;\n };\n}\n\n/**\n * Processes auth configuration to generate IdP user seed metadata\n * @param auth - Auth configuration from generator\n * @returns IdP user metadata or undefined if not applicable\n */\nexport function processIdpUser(auth: GeneratorAuthInput): IdpUserMetadata | undefined {\n // Only process if idProvider is BuiltInIdP and userProfile is defined\n if (auth.idProvider?.kind !== \"BuiltInIdP\" || !auth.userProfile) {\n return undefined;\n }\n\n const { tableName, usernameField } = auth.userProfile;\n\n return {\n name: \"_User\",\n dependencies: [tableName],\n dataFile: \"data/_User.jsonl\",\n idpNamespace: auth.idProvider.namespace,\n schema: {\n usernameField,\n userTableName: tableName,\n },\n };\n}\n\n/**\n * Generates the server-side IDP seed script code.\n * Uses the global tailor.idp.Client - no bundling required.\n * @param idpNamespace - The IDP namespace name\n * @returns Script code string\n */\nexport function generateIdpSeedScriptCode(idpNamespace: string): string {\n return ml /* ts */ `\n export async function main(input) {\n const client = new tailor.idp.Client({ namespace: \"${idpNamespace}\" });\n const errors = [];\n let processed = 0;\n let created = 0;\n let updated = 0;\n let skipped = 0;\n const upsert = input.upsert === true;\n // Rows may arrive as one chunk of a larger dataset; report row numbers\n // relative to the whole dataset so they match the source JSONL.\n const offset = typeof input.offset === \"number\" ? input.offset : 0;\n const total = typeof input.total === \"number\" ? input.total : input.users.length;\n\n for (let i = 0; i < input.users.length; i++) {\n try {\n const { initialPassword, ...row } = input.users[i];\n const createInput =\n initialPassword === undefined || row.password !== undefined\n ? row\n : { ...row, password: initialPassword };\n if (upsert) {\n let existing;\n let lookupError;\n try {\n existing = await client.userByName(row.name);\n } catch (error) {\n existing = undefined;\n lookupError = error instanceof Error ? error.message : String(error);\n }\n\n if (existing) {\n const { name, ...attributes } = row;\n if (Object.keys(attributes).length === 0) {\n skipped++;\n } else {\n await client.updateUser({ id: existing.id, ...attributes });\n updated++;\n }\n } else {\n try {\n await client.createUser(createInput);\n created++;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n lookupError ? \\`create failed (\\${message}); lookup failed (\\${lookupError})\\` : message,\n );\n }\n }\n } else {\n await client.createUser(createInput);\n created++;\n }\n processed++;\n console.log(\\`[_User] \\${offset + i + 1}/\\${total}: \\${input.users[i].name}\\`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n errors.push(\\`Row \\${offset + i + 1} (\\${input.users[i].name}): \\${message}\\`);\n console.error(\\`[_User] Row \\${offset + i + 1} failed: \\${message}\\`);\n }\n }\n\n return {\n success: errors.length === 0,\n processed,\n created,\n updated,\n skipped,\n errors,\n };\n }\n `;\n}\n\nconst listIdpUsersFunction = ml /* ts */ `\n async function listUsers(client) {\n let after = undefined;\n const users = [];\n do {\n const response = await client.users(after ? { after } : undefined);\n for (const user of response.users || []) {\n users.push({ id: user.id, name: user.name });\n }\n after = response.nextPageToken;\n } while (after);\n console.log(\\`Found \\${users.length} IDP users to delete\\`);\n return users;\n }\n`;\n\n/**\n * Generates the server-side script that lists every IdP user for truncation.\n * @param idpNamespace - The IDP namespace name\n * @returns Script code string\n */\nexport function generateIdpListUsersScriptCode(idpNamespace: string): string {\n return ml /* ts */ `\n ${listIdpUsersFunction}\n\n export async function main() {\n const client = new tailor.idp.Client({ namespace: \"${idpNamespace}\" });\n const users = await listUsers(client);\n return { success: true, users };\n }\n `;\n}\n\n/**\n * Generates the server-side IDP truncation script code.\n * Deletes the users passed in `input.users` (one chunk of the listing produced by\n * {@link generateIdpListUsersScriptCode}), or every user when no chunk is passed so\n * older seed plugins that call it without input keep working. A user that is already\n * gone counts as deleted.\n * @param idpNamespace - The IDP namespace name\n * @returns Script code string\n */\nexport function generateIdpTruncateScriptCode(idpNamespace: string): string {\n return ml /* ts */ `\n ${listIdpUsersFunction}\n\n export async function main(input) {\n const client = new tailor.idp.Client({ namespace: \"${idpNamespace}\" });\n const errors = [];\n let deleted = 0;\n let notFound = 0;\n const users = Array.isArray(input.users) ? input.users : await listUsers(client);\n const offset = typeof input.offset === \"number\" ? input.offset : 0;\n const total = typeof input.total === \"number\" ? input.total : users.length;\n\n for (let i = 0; i < users.length; i++) {\n const user = users[i];\n try {\n await client.deleteUser(user.id);\n deleted++;\n console.log(\\`[_User] Deleted \\${offset + i + 1}/\\${total}: \\${user.name}\\`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (/user not found/i.test(message)) {\n notFound++;\n console.log(\\`[_User] Already deleted \\${offset + i + 1}/\\${total}: \\${user.name}\\`);\n continue;\n }\n errors.push(\\`User \\${user.id} (\\${user.name}): \\${message}\\`);\n console.error(\\`[_User] Delete failed for \\${user.name}: \\${message}\\`);\n }\n }\n\n return {\n success: errors.length === 0,\n deleted,\n notFound,\n total: users.length,\n errors,\n };\n }\n `;\n}\n\ntype GenerateIdpUserSchemaFileOptions = {\n usernameField: string;\n userTableName: string;\n /**\n * When `true` (default), emit a foreign key from `_User.name` to the\n * userProfile table's username field so that seed validation rejects `_User`\n * rows without a matching userProfile row. Set to `false` to seed `_User`\n * rows that do not yet have a corresponding userProfile row.\n */\n includeUserProfileFK?: boolean;\n};\n\n/**\n * Generates the schema file content for IdP users. Emits the\n * `_User.name -> <userProfile>.<usernameField>` foreign key by default; pass\n * `includeUserProfileFK: false` to omit it (e.g. when seeding `_User` rows\n * that do not yet have a corresponding userProfile row).\n * @param options - Schema generation options\n * @param options.usernameField - Username field name\n * @param options.userTableName - TailorDB user table name\n * @param options.includeUserProfileFK - Whether to emit the `_User -> userProfile` foreign key (default `true`)\n * @returns Schema file contents\n */\nexport function generateIdpUserSchemaFile(options: GenerateIdpUserSchemaFileOptions): string {\n const { usernameField, userTableName, includeUserProfileFK = true } = options;\n const schemaBody = includeUserProfileFK\n ? ml`\n primaryKey: \"name\",\n indexes: [\n { name: \"_user_name_unique_idx\", columns: [\"name\"], unique: true },\n ],\n foreignKeys: [\n {\n column: \"name\",\n references: {\n table: \"${userTableName}\",\n column: \"${usernameField}\",\n },\n },\n ],\n `\n : ml`\n primaryKey: \"name\",\n indexes: [\n { name: \"_user_name_unique_idx\", columns: [\"name\"], unique: true },\n ],\n `;\n\n return ml /* ts */ `\n import { t } from \"@tailor-platform/sdk\";\n import { defineSchema, fillIdpUserPassword } from \"@tailor-platform/sdk/seed\";\n import { createStandardSchema } from \"@tailor-platform/sdk/test\";\n\n const schemaType = t.object({\n name: t.string(),\n password: t.string(),\n });\n\n export const hook = <T>(data: unknown) => fillIdpUserPassword(data) as T;\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook),\n {\n ${schemaBody}\n }\n );\n\n `;\n}\n","import { isPluginGeneratedTable } from \"#/parser/service/tailordb/type-source\";\nimport ml from \"#/utils/multiline\";\nimport type {\n ParsedField,\n PluginGeneratedTableSource,\n TailorDBType,\n TypeSourceInfoEntry,\n} from \"#/parser/service/tailordb/types\";\nimport type { LinesDbMetadata } from \"./types\";\nimport type { ForeignKeyDefinition, IndexDefinition } from \"lines-db\";\n\n/**\n * Processes TailorDB tables to generate lines-db metadata\n * @param type - Parsed TailorDB table\n * @param source - Source file info\n * @returns Generated lines-db metadata\n */\nexport function processLinesDb(type: TailorDBType, source: TypeSourceInfoEntry): LinesDbMetadata {\n if (isPluginGeneratedTable(source)) {\n // Plugin-generated table\n return processLinesDbForPluginTable(type, source);\n }\n\n // User-defined table\n if (!source.filePath) {\n throw new Error(`Missing source info for table ${type.name}`);\n }\n if (!source.exportName) {\n throw new Error(`Missing export name for table ${type.name}`);\n }\n\n const { optionalFields, omitFields, indexes, foreignKeys } = extractFieldMetadata(type);\n\n return {\n tableName: type.name,\n exportName: source.exportName,\n importPath: source.filePath,\n optionalFields,\n omitFields,\n foreignKeys,\n indexes,\n };\n}\n\n/**\n * Process lines-db metadata for plugin-generated tables\n * @param type - Parsed TailorDB table\n * @param source - Plugin-generated table source info\n * @returns Generated lines-db metadata with plugin source\n */\nfunction processLinesDbForPluginTable(\n type: TailorDBType,\n source: PluginGeneratedTableSource,\n): LinesDbMetadata {\n const { optionalFields, omitFields, indexes, foreignKeys } = extractFieldMetadata(type);\n\n return {\n tableName: type.name,\n exportName: source.exportName,\n importPath: \"\",\n optionalFields,\n omitFields,\n foreignKeys,\n indexes,\n pluginSource: source,\n };\n}\n\n/**\n * Whether the platform produces a value for the field on create when a row omits it,\n * either from a create hook or from a schema default. Such fields stay optional in seed\n * data even when the table marks them required.\n * @param field - Parsed TailorDB field\n * @returns True when a seed row does not have to supply the field\n */\nfunction isGeneratedOnCreate(field: ParsedField): boolean {\n return field.config.hooks?.create !== undefined || field.config.default !== undefined;\n}\n\n/**\n * Extract field metadata from TailorDB table\n * @param type - Parsed TailorDB table\n * @returns Field metadata including optional fields, omit fields, indexes, and foreign keys\n */\nfunction extractFieldMetadata(type: TailorDBType): {\n optionalFields: string[];\n omitFields: string[];\n indexes: IndexDefinition[];\n foreignKeys: ForeignKeyDefinition[];\n} {\n const optionalFields = [\"id\"]; // id is always optional\n const omitFields: string[] = [];\n const indexes: IndexDefinition[] = [];\n const foreignKeys: ForeignKeyDefinition[] = [];\n\n // Find fields generated on create, or serial\n for (const [fieldName, field] of Object.entries(type.fields)) {\n if (isGeneratedOnCreate(field)) {\n optionalFields.push(fieldName);\n }\n // Serial fields are auto-generated, so they are excluded from the seed schema entirely\n if (field.config.serial) {\n omitFields.push(fieldName);\n }\n if (field.config.unique) {\n indexes.push({\n name: `${type.name.toLowerCase()}_${fieldName}_unique_idx`,\n columns: [fieldName],\n unique: true,\n });\n }\n }\n\n // Extract indexes\n if (type.indexes) {\n for (const [indexName, indexDef] of Object.entries(type.indexes)) {\n indexes.push({\n name: indexName,\n columns: indexDef.fields,\n unique: indexDef.unique,\n });\n }\n }\n\n // Extract foreign keys from relations\n for (const [fieldName, field] of Object.entries(type.fields)) {\n if (field.relation) {\n foreignKeys.push({\n column: fieldName,\n references: {\n table: field.relation.targetType,\n column: field.relation.key,\n },\n });\n }\n }\n\n return { optionalFields, omitFields, indexes, foreignKeys };\n}\n\n/**\n * Generate schema options code for lines-db\n * @param foreignKeys - Foreign key definitions\n * @param indexes - Index definitions\n * @returns Schema options code string\n */\nfunction generateSchemaOptions(\n foreignKeys: ForeignKeyDefinition[],\n indexes: IndexDefinition[],\n): string {\n const schemaOptions: string[] = [];\n\n if (foreignKeys.length > 0) {\n schemaOptions.push(`foreignKeys: [`);\n foreignKeys.forEach((fk) => {\n schemaOptions.push(` ${JSON.stringify(fk)},`);\n });\n schemaOptions.push(`],`);\n }\n\n if (indexes.length > 0) {\n schemaOptions.push(`indexes: [`);\n indexes.forEach((index) => {\n schemaOptions.push(` ${JSON.stringify(index)},`);\n });\n schemaOptions.push(\"],\");\n }\n\n return schemaOptions.length > 0\n ? [\"\\n {\", ...schemaOptions.map((option) => ` ${option}`), \" }\"].join(\"\\n\")\n : \"\";\n}\n\n/**\n * Parameters for generating a user-defined table's schema file\n */\nexport interface UserTableSchemaParams {\n /** Relative import path to the table's source file */\n typeImportPath: string;\n /**\n * Relative path from the schema output to tailor.config.ts. Set when plugins are\n * attached to the table: the schema then loads the table through `getExtendedTable`\n * so the fields those plugins add are part of the seed schema.\n */\n configImportPath?: string;\n}\n\nfunction generateSchemaTypeCode(\n tableVariable: string,\n optionalFields: string[],\n omitFields: string[],\n): string {\n return ml /* ts */ `\n const schemaType = t.object({\n ...${tableVariable}.pickFields(${JSON.stringify(optionalFields)}, { optional: true }),\n ...${tableVariable}.omitFields(${JSON.stringify([...optionalFields, ...omitFields])}),\n });\n `;\n}\n\n/**\n * Generates the schema file content for lines-db (for user-defined tables with import)\n * @param metadata - lines-db metadata\n * @param params - Import paths for the table and, when plugins are attached, the config\n * @returns Schema file contents\n */\nexport function generateLinesDbSchemaFile(\n metadata: LinesDbMetadata,\n params: UserTableSchemaParams,\n): string {\n const { exportName, optionalFields, omitFields, foreignKeys, indexes } = metadata;\n const { typeImportPath, configImportPath } = params;\n\n const schemaOptionsCode = generateSchemaOptions(foreignKeys, indexes);\n\n if (configImportPath === undefined) {\n return ml /* ts */ `\n import { t } from \"@tailor-platform/sdk\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n import { ${exportName} } from \"${typeImportPath}\";\n\n ${generateSchemaTypeCode(exportName, optionalFields, omitFields)}\n\n export const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook, ${exportName}),${schemaOptionsCode}\n );\n\n `;\n }\n\n // The source file exports the table without the fields its plugins add, so the\n // schema is built from the table `getExtendedTable` returns instead.\n return ml /* ts */ `\n import { join } from \"node:path\";\n import { t } from \"@tailor-platform/sdk\";\n import { getExtendedTable } from \"@tailor-platform/sdk/plugin\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n import { ${exportName} as sourceTable } from ${JSON.stringify(typeImportPath)};\n\n const configPath = join(import.meta.dirname, ${JSON.stringify(configImportPath)});\n const table = await getExtendedTable(configPath, sourceTable);\n\n ${generateSchemaTypeCode(\"table\", optionalFields, omitFields)}\n\n export const hook = createTailorDBHook(table);\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook, table),${schemaOptionsCode}\n );\n\n `;\n}\n\n/**\n * Parameters for generating a plugin-generated table's schema file\n */\nexport interface PluginSchemaParams {\n /** Relative path from schema output to tailor.config.ts */\n configImportPath: string;\n /** Relative import path to the original table file (for table-attached plugins) */\n originalImportPath?: string;\n}\n\n/**\n * Generates the schema file content using getGeneratedTable API\n * (for plugin-generated tables)\n * @param metadata - lines-db metadata (must have pluginSource)\n * @param params - Plugin import paths\n * @returns Schema file contents\n */\nexport function generateLinesDbSchemaFileWithPluginAPI(\n metadata: LinesDbMetadata,\n params: PluginSchemaParams,\n): string {\n const { tableName, exportName, optionalFields, omitFields, foreignKeys, indexes, pluginSource } =\n metadata;\n\n if (!pluginSource) {\n throw new Error(`pluginSource is required for plugin-generated table \"${tableName}\"`);\n }\n\n const { configImportPath, originalImportPath } = params;\n\n const schemaTypeCode = ml /* ts */ `\n const schemaType = t.object({\n ...${exportName}.pickFields(${JSON.stringify(optionalFields)}, { optional: true }),\n ...${exportName}.omitFields(${JSON.stringify([...optionalFields, ...omitFields])}),\n });\n `;\n\n const schemaOptionsCode = generateSchemaOptions(foreignKeys, indexes);\n\n // Table-attached plugin (e.g., changeset): import the original table and use getGeneratedTable(configPath, pluginId, table, kind)\n if (pluginSource.originalExportName && originalImportPath && pluginSource.generatedTableKind) {\n return ml /* ts */ `\n import { join } from \"node:path\";\n import { t } from \"@tailor-platform/sdk\";\n import { getGeneratedTable } from \"@tailor-platform/sdk/plugin\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n import { ${pluginSource.originalExportName} } from \"${originalImportPath}\";\n\n const configPath = join(import.meta.dirname, \"${configImportPath}\");\n const ${exportName} = await getGeneratedTable(configPath, \"${pluginSource.pluginId}\", ${pluginSource.originalExportName}, \"${pluginSource.generatedTableKind}\");\n\n ${schemaTypeCode}\n\n export const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook, ${exportName}),${schemaOptionsCode}\n );\n\n `;\n }\n\n // Namespace plugin (e.g., audit-log): use getGeneratedTable(configPath, pluginId, null, kind)\n // For namespace plugins, generatedTableKind is required\n if (!pluginSource.generatedTableKind) {\n throw new Error(\n `Namespace plugin \"${pluginSource.pluginId}\" must provide generatedTableKind for table \"${tableName}\"`,\n );\n }\n\n return ml /* ts */ `\n import { join } from \"node:path\";\n import { t } from \"@tailor-platform/sdk\";\n import { getGeneratedTable } from \"@tailor-platform/sdk/plugin\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n\n const configPath = join(import.meta.dirname, \"${configImportPath}\");\n const ${exportName} = await getGeneratedTable(configPath, \"${pluginSource.pluginId}\", null, \"${pluginSource.generatedTableKind}\");\n\n ${schemaTypeCode}\n\n export const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook, ${exportName}),${schemaOptionsCode}\n );\n\n `;\n}\n","import * as path from \"pathe\";\nimport { assertDefined } from \"#/utils/assert\";\nimport { processIdpUser, generateIdpUserSchemaFile } from \"./idp-user-processor\";\nimport {\n processLinesDb,\n generateLinesDbSchemaFile,\n generateLinesDbSchemaFileWithPluginAPI,\n type PluginSchemaParams,\n} from \"./lines-db-processor\";\nimport type { Plugin, GeneratorResult, TailorDBReadyContext } from \"#/plugin/types\";\n\n/** Unique identifier for the seed generator plugin. */\nexport const SeedGeneratorID = \"@tailor-platform/seed\";\n\ntype DisableIdpUserSyncDirections = {\n /**\n * Skip emitting the foreign key from `<userProfile>.<usernameField>` to\n * `_User.name`. Defaults to `false` (FK emitted).\n *\n * Set to `true` to seed pre-registration states such as\n * invited-but-not-registered users.\n */\n userToIdp?: boolean;\n /**\n * Skip emitting the foreign key from `_User.name` to\n * `<userProfile>.<usernameField>`. Defaults to `false` (FK emitted).\n *\n * Set to `true` to seed `_User` rows that do not yet have a corresponding\n * userProfile row.\n */\n idpToUser?: boolean;\n};\n\nexport type SeedPluginOptions = {\n distPath: string;\n machineUserName?: string;\n /**\n * Disable individual `_User <-> userProfile` foreign keys emitted into\n * the generated seed schema. Both directions are emitted by default.\n *\n * Set a direction to `true` to relax it — for example to seed invited\n * users that do not yet have an IdP credential.\n */\n disableIdpUserSync?: DisableIdpUserSyncDirections;\n /**\n * Named combinations of datasets, so `tailor seed apply --dataset preview`\n * stands for every dataset directory the name lists. A dataset directory is\n * a directory under `<distPath>/data/` holding JSONL files for some of the\n * tables; its rows are applied on top of the rows in `<distPath>/data/`.\n * @example { preview: [\"cogs\", \"shipments\"], dev: [\"local\"] }\n */\n datasets?: Record<string, readonly string[]>;\n};\n\ndeclare module \"@tailor-platform/sdk/plugin\" {\n interface PluginConfigRegistry {\n \"@tailor-platform/seed\": SeedPluginOptions;\n }\n}\n\nfunction resolveIdpUserSyncFKs(option: SeedPluginOptions[\"disableIdpUserSync\"]): {\n emitUserToIdpFK: boolean;\n emitIdpToUserFK: boolean;\n} {\n return {\n emitUserToIdpFK: !(option?.userToIdp ?? false),\n emitIdpToUserFK: !(option?.idpToUser ?? false),\n };\n}\n/**\n * Plugin that generates seed data and schema files consumed by the\n * `tailor seed` commands (@tailor-platform/sdk-plugin-seed).\n * @param options - Plugin options\n * @param options.distPath - Output directory path for generated seed files\n * @param options.machineUserName - Default machine user name for authentication\n * @param options.disableIdpUserSync - Skip emitting individual `_User <-> userProfile` foreign keys. Both directions are emitted by default; set a direction to `true` to relax that side.\n * @param options.datasets - Named combinations of dataset directories under `<distPath>/data/`, selected with `tailor seed apply --dataset <name>`.\n * @returns Plugin instance with onTailorDBReady hook\n */\nexport function seedPlugin(options: SeedPluginOptions): Plugin<unknown, SeedPluginOptions> {\n return {\n id: SeedGeneratorID,\n description: \"Generates seed data and schema files for the tailor seed CLI plugin\",\n pluginConfig: options,\n\n async onTailorDBReady(ctx: TailorDBReadyContext<SeedPluginOptions>): Promise<GeneratorResult> {\n const files: GeneratorResult[\"files\"] = [];\n\n // Process IdP user early so we can add reverse FK to the user profile type\n const idpUser = ctx.auth ? (processIdpUser(ctx.auth) ?? null) : null;\n const idpUserSyncFKs = resolveIdpUserSyncFKs(ctx.pluginConfig.disableIdpUserSync);\n\n for (const ns of ctx.tailordb) {\n for (const [tableName, type] of Object.entries(ns.tables)) {\n const source = assertDefined(\n ns.sourceInfo.get(tableName),\n `source info missing for table: ${tableName}`,\n );\n const linesDb = processLinesDb(type, source);\n\n // Add reverse FK from userProfile table to _User (opt-out via disableIdpUserSync.userToIdp: true)\n if (\n idpUserSyncFKs.emitUserToIdpFK &&\n idpUser &&\n tableName === idpUser.schema.userTableName\n ) {\n linesDb.foreignKeys.push({\n column: idpUser.schema.usernameField,\n references: {\n table: \"_User\",\n column: \"name\",\n },\n });\n }\n\n // Generate empty JSONL data file\n files.push({\n path: path.join(ctx.pluginConfig.distPath, \"data\", `${linesDb.tableName}.jsonl`),\n content: \"\",\n skipIfExists: true,\n });\n\n const schemaOutputPath = path.join(\n ctx.pluginConfig.distPath,\n \"data\",\n `${linesDb.tableName}.schema.ts`,\n );\n\n // Plugin-generated table: use getGeneratedTable API\n if (linesDb.pluginSource && linesDb.pluginSource.pluginImportPath) {\n // Build original type import path\n let originalImportPath: string | undefined;\n if (linesDb.pluginSource.originalFilePath && linesDb.pluginSource.originalExportName) {\n const relativePath = path.relative(\n path.dirname(schemaOutputPath),\n linesDb.pluginSource.originalFilePath,\n );\n originalImportPath = relativePath.replace(/\\.ts$/, \"\").startsWith(\".\")\n ? relativePath.replace(/\\.ts$/, \"\")\n : `./${relativePath.replace(/\\.ts$/, \"\")}`;\n }\n\n // Compute relative path from schema output to config file\n const configImportPath = path.relative(path.dirname(schemaOutputPath), ctx.configPath);\n\n const params: PluginSchemaParams = {\n configImportPath,\n originalImportPath,\n };\n\n const schemaContent = generateLinesDbSchemaFileWithPluginAPI(linesDb, params);\n\n files.push({\n path: schemaOutputPath,\n content: schemaContent,\n });\n } else {\n // User-defined type: import from source file\n const relativePath = path.relative(path.dirname(schemaOutputPath), linesDb.importPath);\n const typeImportPath = relativePath.replace(/\\.ts$/, \"\").startsWith(\".\")\n ? relativePath.replace(/\\.ts$/, \"\")\n : `./${relativePath.replace(/\\.ts$/, \"\")}`;\n // A table with plugins attached may carry fields the source file does not\n // declare, so its schema loads the table through the config.\n const hasPlugins = (ns.pluginAttachments.get(tableName)?.length ?? 0) > 0;\n const configImportPath = hasPlugins\n ? path.relative(path.dirname(schemaOutputPath), ctx.configPath)\n : undefined;\n const schemaContent = generateLinesDbSchemaFile(linesDb, {\n typeImportPath,\n configImportPath,\n });\n\n files.push({\n path: schemaOutputPath,\n content: schemaContent,\n });\n }\n }\n }\n\n if (idpUser) {\n // Generate empty JSONL data file\n files.push({\n path: path.join(ctx.pluginConfig.distPath, idpUser.dataFile),\n content: \"\",\n skipIfExists: true,\n });\n\n // Generate schema file with foreign key (opt-out via disableIdpUserSync.idpToUser: true)\n files.push({\n path: path.join(ctx.pluginConfig.distPath, \"data\", `${idpUser.name}.schema.ts`),\n content: generateIdpUserSchemaFile({\n usernameField: idpUser.schema.usernameField,\n userTableName: idpUser.schema.userTableName,\n includeUserProfileFK: idpUserSyncFKs.emitIdpToUserFK,\n }),\n });\n }\n\n return { files };\n },\n };\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import{n as e}from"./brand-C8nMKhJC.mjs";import{a as t,c as n}from"./logger-DuaT2zY3.mjs";import{i as r,t as i}from"./errors-D3qpNHR6.mjs";import*as a from"pathe";import{z as o}from"zod";import*as s from"node:fs";import{Code as c,ConnectError as l}from"@connectrpc/connect";import{pathToFileURL as ee}from"node:url";const u=/* @__PURE__ */ new WeakMap;function withErrorDiagnostics(e,t){return u.set(e,{...u.get(e),...t}),e}function getErrorDiagnostics(e){return{...e instanceof l?{suggestion:rpcSuggestion(e.code)}:{},...u.get(e)}}function rpcSuggestion(e){switch(e){case c.Unauthenticated:return"Check the active token and profile with `tailor auth status` (include the same --profile option). For an environment token, replace TAILOR_PLATFORM_TOKEN; otherwise use the original browser or --machine-user login method with the same profile. See `tailor login --help`.";case c.PermissionDenied:return"Check the active identity and profile with `tailor auth status` (include the same --profile option), then verify its workspace role and token permissions.";case c.Unavailable:case c.DeadlineExceeded:return`Check network connectivity and platform availability. A write may already have completed; inspect the current resource state before retrying.`;default:return}}const d=o.custom(e=>typeof e==`function`);function flattenZodIssues(e,t=[]){return e.flatMap(e=>{let n=[...t,...e.path];return e.code===`invalid_union`?flattenUnion(e,n):[toFlatIssue(e,n)]})}function formatIssueList(e){return e.map(e=>` - ${e.path.join(`.`)||`(root)`}: ${e.message}`).join(`
1
+ import{n as e}from"./brand-C8nMKhJC.mjs";import{a as t,u as n}from"./logger-RgtbhJ83.mjs";import{i as r,t as i}from"./errors-BOF68LJ5.mjs";import*as a from"pathe";import{z as o}from"zod";import*as s from"node:fs";import{Code as c,ConnectError as l}from"@connectrpc/connect";import{pathToFileURL as ee}from"node:url";const u=/* @__PURE__ */ new WeakMap;function withErrorDiagnostics(e,t){return u.set(e,{...u.get(e),...t}),e}function getErrorDiagnostics(e){return{...e instanceof l?{suggestion:rpcSuggestion(e.code)}:{},...u.get(e)}}function rpcSuggestion(e){switch(e){case c.Unauthenticated:return"Check the active token and profile with `tailor auth status` (include the same --profile option). For an environment token, replace TAILOR_PLATFORM_TOKEN; otherwise use the original browser or --machine-user login method with the same profile. See `tailor login --help`.";case c.PermissionDenied:return"Check the active identity and profile with `tailor auth status` (include the same --profile option), then verify its workspace role and token permissions.";case c.Unavailable:case c.DeadlineExceeded:return`Check network connectivity and platform availability. A write may already have completed; inspect the current resource state before retrying.`;default:return}}const d=o.custom(e=>typeof e==`function`);function flattenZodIssues(e,t=[]){return e.flatMap(e=>{let n=[...t,...e.path];return e.code===`invalid_union`?flattenUnion(e,n):[toFlatIssue(e,n)]})}function formatIssueList(e){return e.map(e=>` - ${e.path.join(`.`)||`(root)`}: ${e.message}`).join(`
2
2
  `)}function flattenUnion(e,t){let n=e.errors,r=n.map(e=>new Set(e.map(issueKey))),i=(n[0]??[]).filter(e=>r.every(t=>t.has(issueKey(e)))),a=new Set(i.map(issueKey)),o=n.map(e=>{let t=e.filter(e=>!a.has(issueKey(e)));return{own:t,depth:t.length===0?1/0:Math.max(...t.map(e=>e.path.length))}}).toSorted((e,t)=>t.depth-e.depth||e.own.length-t.own.length),[s,c]=o;return s===void 0||s.own.length>0&&c!==void 0&&s.depth===c.depth&&s.own.length===c.own.length?[...flattenZodIssues(i,t),{...toFlatIssue(e,t),message:tiedUnionMessage(e,o)}]:[...flattenZodIssues(i,t),...flattenZodIssues(s.own,t)]}function tiedUnionMessage(e,t){if(e.message!==`Invalid input`)return e.message;let n=/* @__PURE__ */ new Set;for(let r of t.flatMap(e=>e.own)){if(r.path.length>0)return e.message;if(r.code===`invalid_type`)n.add(r.expected);else if(r.code===`invalid_value`)for(let e of r.values)n.add(typeof e==`string`?JSON.stringify(e):String(e));else return e.message}return n.size===0?e.message:`${e.message}: expected ${[...n].join(` | `)}`}function issueKey(e){return JSON.stringify([e.path.map(String),e.code,e.message])}function toFlatIssue(e,t){return{path:t.map(e=>typeof e==`symbol`?String(e):e),message:e.message}}function configInvalidError(e){let{file:t,exportName:n,subject:r,error:a,pathPrefix:o,service:s}=e,c=flattenZodIssues(a.issues,o);return withErrorDiagnostics(i({code:`CONFIG_INVALID`,message:`Invalid ${r} in ${relativeFile(t)}:\n${formatIssueList(c)}`,context:{file:t,exportName:n,...s,issues:c},cause:a}),{location:{file:t}})}const f={resolver:`resolver`,executor:`executor`,"tailordb-type":`TailorDB type`,"http-adapter":`HTTP adapter`,workflow:`workflow`,"workflow-job":`workflow job`};function definitionInvalidError(e){let{kind:t,file:n,exportName:r,error:a}=e,o=flattenZodIssues(a.issues),s=r==="default"?``:` \`${r}\``;return withErrorDiagnostics(i({code:`DEFINITION_INVALID`,message:`Invalid ${f[t]}${s} in ${relativeFile(n)}:\n${formatIssueList(o)}`,context:{kind:t,file:n,exportName:r,issues:o},cause:a}),{location:{file:n}})}function definitionExportError(e){let{kind:t,file:n,exportName:r}=e,a=f[t],o=`${a.charAt(0).toUpperCase()}${a.slice(1)} in ${relativeFile(n)}`,s=r==="default"?{message:`${o} must not be the default export.`,suggestion:`Export it under a name instead.`}:{message:`${o} must not be a named export, but it is exported as \`${r}\`.`,suggestion:`Export it only as the default export.`};return withErrorDiagnostics(i({code:`DEFINITION_EXPORT_INVALID`,...s,context:{kind:t,file:n,exportName:r}}),{location:{file:n}})}function invalidDataError(e,t){return r(`Invalid ${e}:\n${formatIssueList(flattenZodIssues(t.issues))}`,{cause:t})}function relativeFile(e){return a.relative(process.cwd(),e)}const p=`tailorImportNonce`;let m=0;function beginUserModuleRun(){m+=1}function currentImportNonce(){return m===0?void 0:String(m)}function importUserModule(e){let t=ee(e),n=currentImportNonce();return n&&t.searchParams.set(p,n),import(t.href)}const h=[`**/*.test.ts`,`**/*.spec.ts`],g=/* @__PURE__ */ new Set;function loadFilesWithIgnores(e,n){if(e.files.length===0)return[];let r=resolveFiles(e,n),i=e.files.every(e=>a.isAbsolute(e));if(r.matchedAnyPattern||n===process.cwd()||i)return r.files;let o=`${n}\0${e.files.join(`\0`)}`;return g.has(o)||(g.add(o),t.warn(`No files matched "${e.files.join(`, `)}" relative to "${n}"; 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,n){let r=e.ignores===void 0,i=e.ignores??h,o=r?i:i.map(e=>a.resolve(n,e)),c=[],l=!1;for(let r of e.files){let e=a.resolve(n,r);try{let t=s.globSync(e);t.length>0&&(l=!0);let n=t.filter(e=>!o.some(t=>a.matchesGlob(e,t)));c.push(...n)}catch(e){l=!0,t.warn(`Failed to glob pattern "${r}": ${String(e)}`)}}return{files:c,matchedAnyPattern:l}}const _=Object.keys({uuid:!0,string:!0,boolean:!0,integer:!0,float:!0,decimal:!0,enum:!0,date:!0,datetime:!0,time:!0,nested:!0}),v=o.enum(_),y={"1-1":`1-1`,oneToOne:`1-1`,"n-1":`n-1`,manyToOne:`n-1`,"N-1":`n-1`,keyOnly:`keyOnly`},b=Object.keys(y);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: ${b.join(`, `)}.`);if(!(e.type in y))throw Error(`${fieldRef(t)} has invalid relation type '${e.type}'. Valid values: ${b.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=y[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=y[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 x=o.union([o.literal(`query`),o.strictObject({create:o.boolean().optional().describe(`Enable create mutation (default: true)`),update:o.boolean().optional().describe(`Enable update mutation (default: true)`),delete:o.boolean().optional().describe(`Enable delete mutation (default: true)`),read:o.boolean().optional().describe(`Enable read queries - get, list, aggregation (default: true)`)})]).describe(`Configuration for GraphQL operations on a TailorDB table.
3
3
  All operations are enabled by default (undefined or true = enabled, false = disabled).`).transform(e=>normalizeGqlOperations(e)),te=o.strictObject({value:o.string(),description:o.string().optional()}),ne=o.strictObject({required:o.boolean().optional().describe(`Whether the field is required`),array:o.boolean().optional().describe(`Whether the field is an array`),description:o.string().optional().describe(`Field description`),typeName:o.string().optional().describe(`Type name for nested or enum fields`),allowedValues:o.array(te).optional().describe(`Allowed values for enum fields`),index:o.boolean().optional().describe(`Whether the field is indexed for faster queries`),unique:o.boolean().optional().describe(`Whether the field value must be unique`),vector:o.boolean().optional().describe(`Whether the field is a vector field for similarity search`),foreignKey:o.boolean().optional().describe(`Whether the field is a foreign key`),foreignKeyType:o.string().optional().describe(`Target table name for foreign key relations`),foreignKeyField:o.string().optional().describe(`Target field name for foreign key relations`),hooks:o.strictObject({create:d.optional().describe(`Hook function called on record creation`),update:d.optional().describe(`Hook function called on record update`)}).optional().describe(`Lifecycle hooks for the field`),validate:o.array(d).optional().describe(`Validation functions for the field`),serial:o.strictObject({start:o.number().describe(`Starting value for the serial sequence`),maxValue:o.number().optional().describe(`Maximum value for the serial sequence`),format:o.string().optional().describe(`Format string for serial value (string type only)`),gapless:o.boolean().optional().describe(`Number records without gaps`)}).optional().describe(`Serial (auto-increment) configuration`),scale:o.number().int().min(0).max(12).optional().describe(`Decimal scale (number of digits after decimal point, 0-12)`),default:o.unknown().optional().describe(`Default value for the field on create`)}),re=o.enum(b),ie=o.strictObject({type:re.describe(`Relation cardinality type`),toward:o.strictObject({table:o.string().describe(`Target table name, or 'self' for self-relations`),as:o.string().optional().describe(`Custom forward relation name`),key:o.string().optional().describe(`Target field to join on (default: 'id')`)}),backward:o.string().optional().describe(`Backward relation name on the target table`)}),S=o.lazy(()=>o.object({type:v,fields:o.record(o.string(),S).optional(),metadata:ne,rawRelation:ie.optional()})),C=o.strictObject({pluralForm:o.string().optional().describe(`Custom plural form of the table name for GraphQL`),aggregation:o.boolean().optional().describe(`Enable aggregation queries for this table`),bulkUpsert:o.boolean().optional().describe(`Enable bulk upsert mutation for this table`),gqlOperations:x.optional().describe(`Configure GraphQL operations for this table. Use "query" for read-only mode, or an object for granular control.`),publishEvents:o.boolean().optional().describe(`Enable publishing events for this table.
4
4
  When enabled, record creation/update/deletion events are published.
5
5
  If not specified, this is automatically set to true when an executor uses this table
6
6
  with recordCreated/recordUpdated/recordDeleted triggers. If explicitly set to false
7
7
  while an executor uses this table, an error will be thrown during apply.`)}),w=o.union([o.strictObject({user:o.string()}),o.string(),o.boolean(),o.array(o.string()),o.array(o.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`}}),T=o.union([w,o.strictObject({record:o.string()}),o.strictObject({oldRecord:o.string()}),o.strictObject({newRecord:o.string()})]),E=o.enum([`=`,`!=`,`in`,`not in`,`hasAny`,`not hasAny`]),D=o.tuple([T,E,T]).readonly(),O=o.tuple([w,E,w]).readonly(),k=o.union([o.strictObject({conditions:o.union([D,o.array(D).readonly()]),description:o.string().optional(),permit:o.boolean().optional()}),o.tuple([T,E,T]).readonly(),o.tuple([T,E,T,o.boolean()]).readonly(),o.array(o.union([D,o.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()]),ae=o.enum([`read`,`create`,`update`,`delete`,`aggregate`,`bulkUpsert`]),oe=o.strictObject({conditions:o.array(O).readonly(),actions:o.union([o.literal(`all`),o.array(ae).readonly()]),permit:o.boolean().optional(),description:o.string().optional()}),se=o.strictObject({record:o.strictObject({create:o.array(k).readonly(),read:o.array(k).readonly(),update:o.array(k).readonly(),delete:o.array(k).readonly()}).optional(),gql:o.array(oe).readonly().optional()}),A=o.strictObject({name:o.string(),fields:o.record(o.string(),S),metadata:o.object({name:o.string(),description:o.string().optional(),settings:C.optional(),permissions:se,files:o.record(o.string(),o.string()),indexes:o.record(o.string(),o.strictObject({fields:o.array(o.string()),unique:o.boolean().optional()})).optional(),typeHook:o.strictObject({create:d.optional(),update:d.optional()}).optional(),typeValidate:d.optional()}).strict()}),j=o.strictObject({directory:o.string().describe(`Directory containing migration files`),machineUser:o.string().optional().describe(`Machine user name for migration execution`)}),M=o.strictObject({files:o.array(o.string()).describe(`Glob patterns for TailorDB table definition files`),ignores:o.array(o.string()).optional().describe(`Glob patterns to exclude from table discovery`),migration:j.optional().describe(`Migration configuration`),gqlOperations:x.optional().describe(`Default GraphQL operations for all tables in this service`)}),N=A.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 N)Object.prototype.hasOwnProperty.call(n,e)&&(t[e]=n[e]);return t}const P=o.strictObject({providerUrl:o.string().describe(`OAuth2 provider URL`),issuerUrl:o.string().describe(`OAuth2 issuer URL`),clientId:o.string().describe(`OAuth2 client ID`),clientSecret:o.string().describe(`OAuth2 client secret`),authUrl:o.string().optional().describe(`OAuth2 authorization endpoint override`),tokenUrl:o.string().optional().describe(`OAuth2 token endpoint override`)}).extend({type:o.literal(`oauth2`).describe(`Connection type`)}),F=o.strictObject({value:o.string().describe(`The allowed value`),description:o.string().optional().describe(`Description of the allowed value`)}),I=o.strictObject({as:o.enum([`string`,`date`,`temporal`,`default`]).optional().describe(`Date value representation; "default" follows defaultDateRepresentation`),required:o.boolean().optional().describe(`Whether the field is required`),array:o.boolean().optional().describe(`Whether the field is an array`),description:o.string().optional().describe(`Field description`),allowedValues:o.array(F).optional().describe(`Allowed values for enum fields`),hooks:o.strictObject({create:d.optional().describe(`Hook function called on creation`),update:d.optional().describe(`Hook function called on update`)}).optional().describe(`Lifecycle hooks`),validate:o.array(d).optional().describe(`Validation functions for the field`),typeName:o.string().optional().describe(`Type name for nested or enum fields`),default:o.unknown().optional().describe(`Default value for the field on create`)}),L=o.object({type:v.describe(`Field data type`),metadata:I.describe(`Field metadata configuration`),get fields(){return o.record(o.string(),L)}}),R=o.strictObject({namespace:o.string().describe(`Auth namespace`),machineUserName:o.string().describe(`Machine user name for authentication`)}),z=o.union([o.string().describe(`Machine user name (namespace auto-resolved from auth service)`),R]),B=o.strictObject({vaultName:o.string().describe(`Vault name containing the secret`),secretKey:o.string().describe(`Key of the secret in the vault`)}),V=o.strictObject({name:o.string().describe(`Identity provider name`),kind:o.literal(`OIDC`),clientID:o.string().describe(`OAuth2 client ID`),clientSecret:B.describe(`OAuth2 client secret`),providerURL:o.string().describe(`OIDC provider URL`),issuerURL:o.string().optional().describe(`OIDC issuer URL (defaults to providerURL)`),usernameClaim:o.string().optional().describe(`JWT claim to use as username`)}),H=o.strictObject({name:o.string().describe(`Identity provider name`),kind:o.literal(`SAML`),enableSignRequest:o.boolean().default(!1).describe(`Enable signing of SAML requests`),metadataURL:o.string().optional().describe(`URL to fetch SAML metadata (mutually exclusive with rawMetadata)`),rawMetadata:o.string().optional().describe(`Raw SAML metadata XML (mutually exclusive with metadataURL)`),defaultRedirectURL:o.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`),U=o.strictObject({name:o.string().describe(`Identity provider name`),kind:o.literal(`IDToken`),providerURL:o.string().describe(`ID token provider URL`),issuerURL:o.string().optional().describe(`ID token issuer URL`),clientID:o.string().describe(`Client ID for ID token validation`),usernameClaim:o.string().optional().describe(`JWT claim to use as username`)}),W=o.strictObject({name:o.string().describe(`Identity provider name`),kind:o.literal(`BuiltInIdP`),namespace:o.string().describe(`IdP namespace`),clientName:o.string().describe(`OAuth2 client name in the IdP`)}),G=o.discriminatedUnion(`kind`,[V,H,U,W]),K=o.union([o.literal(`authorization_code`),o.literal(`refresh_token`)]).describe(`OAuth2 grant type`),ce=o.strictObject({description:o.string().optional().describe(`Client description`),grantTypes:o.array(K).default([`authorization_code`,`refresh_token`]).describe(`Allowed OAuth2 grant types`),redirectURIs:o.array(o.union([o.templateLiteral([`https://`,o.string()]),o.templateLiteral([`http://`,o.string()]),o.templateLiteral([o.string(),`:url`]),o.templateLiteral([o.string(),`:url/`,o.string()])])).describe(`Allowed redirect URIs`),clientType:o.union([o.literal(`confidential`),o.literal(`public`),o.literal(`browser`)]).optional().describe(`OAuth2 client type`),accessTokenLifetimeSeconds:o.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:o.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:o.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`]}),le=o.strictObject({type:o.union([o.literal(`oauth2`),o.literal(`bearer`)]).describe(`SCIM authorization type`),bearerSecret:B.optional().describe(`Bearer token secret (required for bearer type)`)}),ue=o.union([o.literal(`string`),o.literal(`number`),o.literal(`boolean`),o.literal(`datetime`),o.literal(`complex`)]).describe(`SCIM attribute data type`),q=o.strictObject({type:ue.describe(`Attribute data type`),name:o.string().describe(`Attribute name`),description:o.string().optional().describe(`Attribute description`),mutability:o.union([o.literal(`readOnly`),o.literal(`readWrite`),o.literal(`writeOnly`)]).optional().describe(`Attribute mutability`),required:o.boolean().optional().describe(`Whether the attribute is required`),multiValued:o.boolean().optional().describe(`Whether the attribute can have multiple values`),uniqueness:o.union([o.literal(`none`),o.literal(`server`),o.literal(`global`)]).optional().describe(`Uniqueness constraint`),canonicalValues:o.array(o.string()).nullable().optional().describe(`List of canonical values`),get subAttributes(){return o.array(q).nullable().optional()}}),de=o.strictObject({name:o.string().describe(`SCIM schema name`),attributes:o.array(q).describe(`Schema attributes`)}),fe=o.strictObject({tailorDBField:o.string().describe(`TailorDB field name to map to`),scimPath:o.string().describe(`SCIM attribute path`)}),pe=o.strictObject({name:o.string().describe(`SCIM resource name`),tailorDBNamespace:o.string().describe(`TailorDB namespace for the resource`),tailorDBType:o.string().describe(`TailorDB table name for the resource`),coreSchema:de.describe(`Core SCIM schema definition`),attributeMapping:o.array(fe).describe(`Attribute mapping configuration`)}),me=o.strictObject({machineUserName:o.string().describe(`Machine user name for SCIM operations`),authorization:le.describe(`SCIM authorization configuration`),resources:o.array(pe).describe(`SCIM resource definitions`)}),he=o.strictObject({namespace:o.string().describe(`TailorDB namespace for the tenant table`),type:o.string().describe(`TailorDB table name for tenants`),signatureField:o.string().describe(`Field used as the tenant signature`)}),ge=o.strictObject({namespace:o.string().optional().describe(`TailorDB namespace where the user table is defined`),type:o.custom().transform(stripTailorDBTypeBuilderHelpers).pipe(A),usernameField:o.string(),attributes:o.record(o.string(),o.literal(!0)).optional(),attributeList:o.array(o.string()).optional()}),J=o.union([o.string(),o.boolean(),o.array(o.string()),o.array(o.boolean())]),_e=o.strictObject({attributes:o.record(o.string(),J.nullish()).transform(e=>Object.fromEntries(Object.entries(e).filter(e=>e[1]!=null))).optional(),attributeList:o.array(o.uuid()).optional()}),ve=o.strictObject({handler:o.function(),invoker:o.string()}),Y=o.strictObject({name:o.string().describe(`Auth service name`),hooks:o.strictObject({beforeLogin:ve.optional().describe(`Before login auth hook`)}).optional().describe(`Auth hooks`),machineUsers:o.record(o.string(),_e).optional().describe(`Machine user definitions`),oauth2Clients:o.record(o.string(),ce).optional().describe(`OAuth2 client definitions`),idProvider:G.optional().describe(`Identity provider configuration`),scim:me.optional().describe(`SCIM provisioning configuration`),tenantProvider:he.optional().describe(`Multi-tenant provider configuration`),connections:o.record(o.string(),P).optional().describe(`Auth connection definitions for external OAuth2 providers`),publishSessionEvents:o.boolean().optional().describe(`Enable publishing session events`)}),ye=o.xor([Y.extend({userProfile:ge.optional().describe(`User profile configuration`),machineUserAttributes:o.undefined().optional()}),Y.extend({userProfile:o.undefined().optional(),machineUserAttributes:o.record(o.string(),L).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."}}),be=o.strictObject({kind:o.literal(`tailordb`).describe(`TailorDB record event trigger`),events:o.array(o.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:o.string().describe(`TailorDB table name to watch for events`),condition:d.optional().describe(`Condition function to filter events`)}),xe=o.strictObject({kind:o.literal(`resolverExecuted`),resolverName:o.string().describe(`Name of the resolver to trigger on`),condition:d.optional().describe(`Condition function to filter events`)}),Se=o.strictObject({kind:o.literal(`schedule`),cron:o.string().describe(`CRON expression for the schedule`),timezone:o.string().optional().default(`UTC`).describe(`Timezone for the CRON schedule (default: UTC)`)}),Ce=o.strictObject({body:d.optional().describe(`Function returning the response body`),statusCode:o.number().int().optional().describe(`HTTP status code for the response`)}),we=o.strictObject({kind:o.literal(`incomingWebhook`),response:Ce.optional().describe(`Response configuration`)}),Te=o.strictObject({kind:o.literal(`idpUser`).describe(`IdP user event trigger`),events:o.array(o.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:o.string().optional().describe(`IdP namespace name to subscribe to. If omitted, the project's only IdP is used; throws when multiple IdPs exist.`)}),Ee=o.strictObject({kind:o.literal(`authAccessToken`).describe(`Auth access token event trigger`),events:o.array(o.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`)}),X=o.string().refine(e=>e.trim().length>0,`Workflow name cannot be blank`).describe(`Workflow name to subscribe to.`),De=o.strictObject({kind:o.literal(`workflowExecution`).describe(`Workflow execution event trigger`),events:o.array(o.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:X,condition:d.optional().describe(`Condition function to filter events`)}),Oe=o.strictObject({kind:o.literal(`workflowJobExecution`).describe(`Workflow job execution event trigger`),events:o.array(o.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:X,condition:d.optional().describe(`Condition function to filter events`)}),ke=o.discriminatedUnion(`kind`,[be,xe,Se,we,Te,Ee,De,Oe]),Ae=o.strictObject({kind:o.enum([`function`,`jobFunction`]),body:d.describe(`Function implementation`),invoker:z.optional().describe(`Invoker for the function execution`)}),je=o.strictObject({kind:o.literal(`graphql`),appName:o.string().optional().describe(`Target application name for the GraphQL query`),query:o.preprocess(e=>String(e),o.string().describe(`GraphQL query string`)),variables:d.optional().describe(`Function to compute GraphQL variables`),invoker:z.optional().describe(`Invoker for the GraphQL execution`)}),Me=o.strictObject({kind:o.literal(`webhook`),url:d.describe(`Function returning the webhook URL`),requestBody:d.optional().describe(`Function to compute the request body`),headers:o.record(o.string(),o.union([o.string(),o.strictObject({vault:o.string(),key:o.string()})])).optional().describe(`HTTP headers for the webhook request`)}),Z=o.lazy(()=>o.union([o.string(),o.number(),o.boolean(),o.null(),o.array(Z),o.record(o.string(),Z)])),Ne=o.union([o.string(),o.number(),o.boolean(),o.array(Z),o.record(o.string(),Z)]),Pe=o.union([Ne,d]).describe(`Arguments to pass to the workflow`),Q={kind:o.literal(`workflow`),args:Pe.optional(),invoker:z.optional().describe(`Invoker for the workflow execution`)},Fe=o.strictObject({...Q,workflowName:o.string().describe(`Name of the workflow to execute`),workflow:o.never().optional()}),Ie=o.strictObject({...Q,workflow:o.looseObject({name:o.string()}),workflowName:o.string().optional()}).transform(({workflow:e,...t})=>({...t,workflowName:e.name})),Le=o.union([Ie,Fe]),Re=o.union([Ae,je,Me,Le]),$=o.strictObject({name:o.string().describe(`Executor name`),description:o.string().optional().describe(`Executor description`),disabled:o.boolean().optional().default(!1).describe(`Whether the executor is disabled`),trigger:ke.describe(`Event trigger configuration`),operation:Re.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(r){let{config:i,baseDir:o}=r,s={},c=[],l,loadExecutorForFile=async r=>{try{let i=await importUserModule(r),o=$.safeParse(stripExecutorTriggerArgs(i.default));if(o.success){let e=a.relative(process.cwd(),r);return t.debug(`Executor: ${n.successBright(`"${o.data.name}"`)} loaded from ${n.path(e)}`),s[r]=o.data,o.data}if(e(i.default,`executor`))throw definitionInvalidError({kind:`executor`,file:r,exportName:`default`,error:o.error})}catch(e){let i=a.relative(process.cwd(),r);throw t.error(`Failed to load executor from ${n.bold(i)}`),t.error(String(e)),e}};return{config:i,get executors(){return s},get pluginExecutors(){return c},loadExecutors:async()=>(l||=(async()=>{if(i.files.length===0)return;let e=loadFilesWithIgnores(i,o);return t.newline(),t.log(`Found ${n.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){t.newline(),t.log(`Loading ${n.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,r]of Object.entries(e)){let e=a.relative(process.cwd(),n),i=t.get(r.name);if(i)throw Error(`Duplicate executor name "${r.name}" found:\n - ${i}\n - ${e}\nEach executor must have a unique name.`);t.set(r.name,e)}}export{definitionInvalidError as C,d as D,formatIssueList as E,getErrorDiagnostics as O,definitionExportError as S,flattenZodIssues as T,p as _,ye as a,importUserModule as b,pickTailorDBTypeSchemaKeys as c,A as d,applyRelationMetadataToFieldConfig as f,loadFilesWithIgnores as g,validateRelationConfig as h,$ as i,withErrorDiagnostics as k,stripTailorDBTypeBuilderHelpers as l,processRelationMetadata as m,loadExecutor as n,z as o,buildRelationInfo as p,stripExecutorTriggerArgs as r,L as s,createExecutorService as t,M as u,beginUserModuleRun as v,invalidDataError as w,configInvalidError as x,currentImportNonce as y};
8
- //# sourceMappingURL=service-l_tEI8sS.mjs.map
8
+ //# sourceMappingURL=service-9SBPNpK8.mjs.map