@tailor-platform/sdk 1.55.2 → 1.56.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/application-CC3oaSay.mjs +4 -0
- package/dist/{application-MY7YJJ-C.mjs → application-DuT_ae02.mjs} +179 -56
- package/dist/application-DuT_ae02.mjs.map +1 -0
- package/dist/cli/index.mjs +6 -8
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +1 -1
- package/dist/cli/lib.mjs +3 -3
- package/dist/configure/index.d.mts +2 -2
- package/dist/configure/index.mjs +1 -1
- package/dist/configure/index.mjs.map +1 -1
- package/dist/{index-1APcOP9X.d.mts → index-B61gFI9a.d.mts} +7 -2
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/{runtime-DH5F_z8A.mjs → runtime-745lvg7i.mjs} +24 -3
- package/dist/runtime-745lvg7i.mjs.map +1 -0
- package/dist/{seed-DfLyRh63.mjs → seed-C0fE2sJB.mjs} +5 -5
- package/dist/seed-C0fE2sJB.mjs.map +1 -0
- package/dist/{types-BinLwXM9.mjs → types-BwGth3a1.mjs} +57 -28
- package/dist/types-BwGth3a1.mjs.map +1 -0
- package/dist/{types-UeXbHFXW.mjs → types-Ccwchyj5.mjs} +1 -1
- package/dist/utils/test/index.d.mts +2 -2
- package/dist/{workflow.generated-BGLDYJOq.d.mts → workflow.generated-Kz-nQrTf.d.mts} +20 -1
- package/docs/configuration.md +12 -0
- package/package.json +1 -1
- package/dist/application-D6znmyMd.mjs +0 -4
- package/dist/application-MY7YJJ-C.mjs.map +0 -1
- package/dist/runtime-DH5F_z8A.mjs.map +0 -1
- package/dist/seed-DfLyRh63.mjs.map +0 -1
- package/dist/types-BinLwXM9.mjs.map +0 -1
|
@@ -69,13 +69,13 @@ function generateIdpTruncateScriptCode(idpNamespace) {
|
|
|
69
69
|
let deleted = 0;
|
|
70
70
|
|
|
71
71
|
// List all users with pagination
|
|
72
|
-
let
|
|
72
|
+
let after = undefined;
|
|
73
73
|
const allUsers = [];
|
|
74
74
|
do {
|
|
75
|
-
const response = await client.users(
|
|
75
|
+
const response = await client.users(after ? { after } : undefined);
|
|
76
76
|
allUsers.push(...(response.users || []));
|
|
77
|
-
|
|
78
|
-
} while (
|
|
77
|
+
after = response.nextPageToken;
|
|
78
|
+
} while (after);
|
|
79
79
|
|
|
80
80
|
console.log(\`Found \${allUsers.length} IDP users to delete\`);
|
|
81
81
|
|
|
@@ -1167,4 +1167,4 @@ function seedPlugin(options) {
|
|
|
1167
1167
|
|
|
1168
1168
|
//#endregion
|
|
1169
1169
|
export { seedPlugin as n, isPluginGeneratedType as r, SeedGeneratorID as t };
|
|
1170
|
-
//# sourceMappingURL=seed-
|
|
1170
|
+
//# sourceMappingURL=seed-C0fE2sJB.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-C0fE2sJB.mjs","names":["ml"],"sources":["../src/plugin/builtin/seed/idp-user-processor.ts","../src/types/tailordb.ts","../src/plugin/builtin/seed/lines-db-processor.ts","../src/plugin/builtin/seed/seed-type-processor.ts","../src/plugin/builtin/seed/index.ts"],"sourcesContent":["import ml from \"@/utils/multiline\";\nimport type { GeneratorAuthInput } from \"@/types/plugin-generation\";\n\nexport interface IdpUserMetadata {\n name: \"_User\";\n dependencies: string[];\n dataFile: string;\n idpNamespace: string;\n schema: {\n usernameField: string;\n userTypeName: string;\n };\n}\n\n/**\n * Processes auth configuration to generate IdP user seed metadata\n * @param auth - Auth configuration from generator\n * @returns IdP user metadata or undefined if not applicable\n */\nexport function processIdpUser(auth: GeneratorAuthInput): IdpUserMetadata | undefined {\n // Only process if idProvider is BuiltInIdP and userProfile is defined\n if (auth.idProvider?.kind !== \"BuiltInIdP\" || !auth.userProfile) {\n return undefined;\n }\n\n const { typeName, usernameField } = auth.userProfile;\n\n return {\n name: \"_User\",\n dependencies: [typeName],\n dataFile: \"data/_User.jsonl\",\n idpNamespace: auth.idProvider.namespace,\n schema: {\n usernameField,\n userTypeName: typeName,\n },\n };\n}\n\n/**\n * Generates the server-side IDP seed script code for testExecScript execution.\n * Uses the global tailor.idp.Client - no bundling required.\n * @param idpNamespace - The IDP namespace name\n * @returns Script code string\n */\nexport function generateIdpSeedScriptCode(idpNamespace: string): string {\n return ml /* ts */ `\n export async function main(input) {\n const client = new tailor.idp.Client({ namespace: \"${idpNamespace}\" });\n const errors = [];\n let processed = 0;\n\n for (let i = 0; i < input.users.length; i++) {\n try {\n await client.createUser(input.users[i]);\n processed++;\n console.log(\\`[_User] \\${i + 1}/\\${input.users.length}: \\${input.users[i].name}\\`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n errors.push(\\`Row \\${i} (\\${input.users[i].name}): \\${message}\\`);\n console.error(\\`[_User] Row \\${i} failed: \\${message}\\`);\n }\n }\n\n return {\n success: errors.length === 0,\n processed,\n errors,\n };\n }\n `;\n}\n\n/**\n * Generates the server-side IDP truncation script code for testExecScript execution.\n * Lists all users with pagination and deletes each one.\n * @param idpNamespace - The IDP namespace name\n * @returns Script code string\n */\nexport function generateIdpTruncateScriptCode(idpNamespace: string): string {\n return ml /* ts */ `\n export async function main() {\n const client = new tailor.idp.Client({ namespace: \"${idpNamespace}\" });\n const errors = [];\n let deleted = 0;\n\n // List all users with pagination\n let after = undefined;\n const allUsers = [];\n do {\n const response = await client.users(after ? { after } : undefined);\n allUsers.push(...(response.users || []));\n after = response.nextPageToken;\n } while (after);\n\n console.log(\\`Found \\${allUsers.length} IDP users to delete\\`);\n\n for (const user of allUsers) {\n try {\n await client.deleteUser(user.id);\n deleted++;\n console.log(\\`[_User] Deleted \\${deleted}/\\${allUsers.length}: \\${user.name}\\`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n errors.push(\\`User \\${user.id} (\\${user.name}): \\${message}\\`);\n console.error(\\`[_User] Delete failed for \\${user.name}: \\${message}\\`);\n }\n }\n\n return {\n success: errors.length === 0,\n deleted,\n total: allUsers.length,\n errors,\n };\n }\n `;\n}\n\ntype GenerateIdpUserSchemaFileOptions = {\n usernameField: string;\n userTypeName: string;\n /**\n * When `true` (default), emit a foreign key from `_User.name` to the\n * userProfile type's username field so that seed validation rejects `_User`\n * rows without a matching userProfile row. Set to `false` to seed `_User`\n * rows that do not yet have a corresponding userProfile row.\n */\n includeUserProfileFK?: boolean;\n};\n\n/**\n * Generates the schema file content for IdP users. Emits the\n * `_User.name -> <userProfile>.<usernameField>` foreign key by default; pass\n * `includeUserProfileFK: false` to omit it (e.g. when seeding `_User` rows\n * that do not yet have a corresponding userProfile row).\n * @param options - Schema generation options\n * @param options.usernameField - Username field name\n * @param options.userTypeName - TailorDB user type name\n * @param options.includeUserProfileFK - Whether to emit the `_User -> userProfile` foreign key (default `true`)\n * @returns Schema file contents\n */\nexport function generateIdpUserSchemaFile(options: GenerateIdpUserSchemaFileOptions): string {\n const { usernameField, userTypeName, includeUserProfileFK = true } = options;\n const schemaBody = includeUserProfileFK\n ? ml`\n primaryKey: \"name\",\n indexes: [\n { name: \"_user_name_unique_idx\", columns: [\"name\"], unique: true },\n ],\n foreignKeys: [\n {\n column: \"name\",\n references: {\n table: \"${userTypeName}\",\n column: \"${usernameField}\",\n },\n },\n ],\n `\n : ml`\n primaryKey: \"name\",\n indexes: [\n { name: \"_user_name_unique_idx\", columns: [\"name\"], unique: true },\n ],\n `;\n\n return ml /* ts */ `\n import { t } from \"@tailor-platform/sdk\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createStandardSchema } from \"@tailor-platform/sdk/test\";\n\n const schemaType = t.object({\n name: t.string(),\n password: t.string(),\n });\n\n // Simple identity hook for _User (no TailorDB backing type)\n const hook = <T>(data: unknown) => data as T;\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook),\n {\n ${schemaBody}\n }\n );\n\n `;\n}\n","import type { ValueOperand } from \"./auth-value\";\nimport type { EnumValue } from \"./field-types\";\nimport type { RawRelationConfig, TailorDBTypeMetadata } from \"./tailordb-metadata\";\nimport type {\n DBFieldMetadata as DBFieldMetadataGenerated,\n RawRelationConfig as RawRelationConfigGenerated,\n TailorDBServiceConfig,\n TailorDBServiceConfigInput,\n TailorDBTypeParsedSettings,\n} from \"./tailordb.generated\";\n\n// Re-exports from tailor-db-field (needed because parser cannot import from configure)\nexport type {\n TailorAnyDBField,\n TailorAnyDBType,\n TailorDBField,\n TailorDBInstance,\n} from \"./tailor-db-field\";\n\nexport type {\n DBFieldMetadata,\n DefinedDBFieldMetadata,\n GqlOperationsConfig,\n RawRelationConfig,\n SerialConfig,\n TailorDBTypeMetadata,\n} from \"./tailordb-metadata\";\nexport type { GqlOperations } from \"./tailordb.generated\";\n\n// --- Types from configure/services/tailordb/types.ts ---\n\nexport type IndexDef<T extends { fields: Record<PropertyKey, unknown> }> = {\n fields: [keyof T[\"fields\"], keyof T[\"fields\"], ...(keyof T[\"fields\"])[]];\n unique?: boolean;\n name?: string;\n};\n\n// --- Original types/tailordb.ts types ---\n\nexport type TailorDBFieldOutput = {\n type: string;\n fields?: Record<string, TailorDBFieldOutput>;\n metadata: DBFieldMetadataGenerated;\n rawRelation?: RawRelationConfigGenerated;\n};\n\nexport type TypeSourceInfo = Record<string, TypeSourceInfoEntry>;\n\nexport type RelationType = \"1-1\" | \"oneToOne\" | \"n-1\" | \"manyToOne\" | \"N-1\" | \"keyOnly\";\n\n// Service config types\nexport type TailorDBExternalConfig = { external: true };\n\nexport type TailorDBServiceInput = {\n [namespace: string]: TailorDBServiceConfigInput | TailorDBExternalConfig;\n};\n\nexport type TailorDBMigrationConfig = NonNullable<TailorDBServiceConfig[\"migration\"]>;\n\n// Source info types\nexport interface UserDefinedTypeSource {\n filePath: string;\n exportName: string;\n pluginId?: never;\n}\n\nexport interface PluginGeneratedTypeSource {\n filePath?: never;\n exportName: string;\n pluginId: string;\n pluginImportPath: string;\n originalFilePath: string;\n originalExportName: string;\n generatedTypeKind?: string;\n pluginConfig?: unknown;\n namespace?: string;\n}\n\nexport type TypeSourceInfoEntry = UserDefinedTypeSource | PluginGeneratedTypeSource;\n\n/**\n * Checks if a type source is generated by a plugin.\n * @param source - The type source entry to check.\n * @returns True if the source was generated by a plugin.\n */\nexport function isPluginGeneratedType(\n source: TypeSourceInfoEntry,\n): source is PluginGeneratedTypeSource {\n return source.pluginId !== undefined;\n}\n\n// Operator field types\nexport interface Script {\n expr: string;\n}\n\ninterface OperatorValidateConfig {\n script: Script;\n errorMessage: string;\n}\n\ninterface OperatorFieldHook {\n create?: Script;\n update?: Script;\n}\n\nexport interface OperatorFieldConfig {\n type: string;\n required?: boolean;\n description?: string;\n allowedValues?: EnumValue[];\n array?: boolean;\n index?: boolean;\n unique?: boolean;\n vector?: boolean;\n foreignKey?: boolean;\n foreignKeyType?: string;\n foreignKeyField?: string;\n rawRelation?: RawRelationConfig;\n validate?: OperatorValidateConfig[];\n hooks?: OperatorFieldHook;\n serial?: {\n start: number;\n maxValue?: number;\n format?: string;\n };\n scale?: number;\n fields?: Record<string, OperatorFieldConfig>;\n}\n\n// Permission types (parsed/standard format)\ntype GqlPermissionAction = \"read\" | \"create\" | \"update\" | \"delete\" | \"aggregate\" | \"bulkUpsert\";\n\ntype StandardPermissionOperator = \"eq\" | \"ne\" | \"in\" | \"nin\" | \"hasAny\" | \"nhasAny\";\n\ntype UserOperand = {\n user: string;\n};\n\ntype StandardRecordOperand<Update extends boolean = false> = Update extends true\n ? { oldRecord: string } | { newRecord: string }\n : { record: string };\n\nexport type PermissionOperand<\n Level extends \"record\" | \"gql\" = \"record\" | \"gql\",\n Update extends boolean = boolean,\n> = UserOperand | ValueOperand | (Level extends \"record\" ? StandardRecordOperand<Update> : never);\n\nexport type StandardPermissionCondition<\n Level extends \"record\" | \"gql\" = \"record\" | \"gql\",\n Update extends boolean = boolean,\n> = readonly [\n PermissionOperand<Level, Update>,\n StandardPermissionOperator,\n PermissionOperand<Level, Update>,\n];\n\nexport type StandardActionPermission<\n Level extends \"record\" | \"gql\" = \"record\" | \"gql\",\n Update extends boolean = boolean,\n> = {\n conditions: readonly StandardPermissionCondition<Level, Update>[];\n description?: string;\n permit: \"allow\" | \"deny\";\n};\n\nexport type StandardTailorTypePermission = {\n create: readonly StandardActionPermission<\"record\", false>[];\n read: readonly StandardActionPermission<\"record\", false>[];\n update: readonly StandardActionPermission<\"record\", true>[];\n delete: readonly StandardActionPermission<\"record\", false>[];\n};\n\nexport type StandardGqlPermissionPolicy = {\n conditions: readonly StandardPermissionCondition<\"gql\">[];\n actions: readonly [\"all\"] | readonly GqlPermissionAction[];\n permit: \"allow\" | \"deny\";\n description?: string;\n};\n\nexport type StandardTailorTypeGqlPermission = readonly StandardGqlPermissionPolicy[];\n\nexport interface Permissions {\n record?: StandardTailorTypePermission;\n gql?: StandardTailorTypeGqlPermission;\n}\n\nexport interface ParsedField {\n name: string;\n config: OperatorFieldConfig;\n relation?: {\n targetType: string;\n forwardName: string;\n backwardName: string;\n key: string;\n unique: boolean;\n };\n}\n\nexport interface ParsedRelationship {\n name: string;\n targetType: string;\n targetField: string;\n sourceField: string;\n isArray: boolean;\n description: string;\n}\n\nexport interface TailorDBType {\n name: string;\n pluralForm: string;\n description?: string;\n fields: Record<string, ParsedField>;\n forwardRelationships: Record<string, ParsedRelationship>;\n backwardRelationships: Record<string, ParsedRelationship>;\n settings: TailorDBTypeParsedSettings;\n permissions: Permissions;\n indexes?: TailorDBTypeMetadata[\"indexes\"];\n files?: TailorDBTypeMetadata[\"files\"];\n}\n","import {\n isPluginGeneratedType,\n type PluginGeneratedTypeSource,\n type TailorDBType,\n type TypeSourceInfoEntry,\n} from \"@/types/tailordb\";\nimport ml from \"@/utils/multiline\";\nimport type { LinesDbMetadata } from \"./types\";\nimport type { ForeignKeyDefinition, IndexDefinition } from \"@toiroakr/lines-db\";\n\n/**\n * Processes TailorDB types to generate lines-db metadata\n * @param type - Parsed TailorDB type\n * @param source - Source file info\n * @returns Generated lines-db metadata\n */\nexport function processLinesDb(type: TailorDBType, source: TypeSourceInfoEntry): LinesDbMetadata {\n if (isPluginGeneratedType(source)) {\n // Plugin-generated type\n return processLinesDbForPluginType(type, source);\n }\n\n // User-defined type\n if (!source.filePath) {\n throw new Error(`Missing source info for type ${type.name}`);\n }\n if (!source.exportName) {\n throw new Error(`Missing export name for type ${type.name}`);\n }\n\n const { optionalFields, omitFields, indexes, foreignKeys } = extractFieldMetadata(type);\n\n return {\n typeName: type.name,\n exportName: source.exportName,\n importPath: source.filePath,\n optionalFields,\n omitFields,\n foreignKeys,\n indexes,\n };\n}\n\n/**\n * Process lines-db metadata for plugin-generated types\n * @param type - Parsed TailorDB type\n * @param source - Plugin-generated type source info\n * @returns Generated lines-db metadata with plugin source\n */\nfunction processLinesDbForPluginType(\n type: TailorDBType,\n source: PluginGeneratedTypeSource,\n): LinesDbMetadata {\n const { optionalFields, omitFields, indexes, foreignKeys } = extractFieldMetadata(type);\n\n return {\n typeName: type.name,\n exportName: source.exportName,\n importPath: \"\",\n optionalFields,\n omitFields,\n foreignKeys,\n indexes,\n pluginSource: source,\n };\n}\n\n/**\n * Extract field metadata from TailorDB type\n * @param type - Parsed TailorDB type\n * @returns Field metadata including optional fields, omit fields, indexes, and foreign keys\n */\nfunction extractFieldMetadata(type: TailorDBType): {\n optionalFields: string[];\n omitFields: string[];\n indexes: IndexDefinition[];\n foreignKeys: ForeignKeyDefinition[];\n} {\n const optionalFields = [\"id\"]; // id is always optional\n const omitFields: string[] = [];\n const indexes: IndexDefinition[] = [];\n const foreignKeys: ForeignKeyDefinition[] = [];\n\n // Find fields with hooks.create or serial\n for (const [fieldName, field] of Object.entries(type.fields)) {\n if (field.config.hooks?.create) {\n optionalFields.push(fieldName);\n }\n // Serial fields are auto-generated, so they should be optional in seed data\n if (field.config.serial) {\n omitFields.push(fieldName);\n }\n if (field.config.unique) {\n indexes.push({\n name: `${type.name.toLowerCase()}_${fieldName}_unique_idx`,\n columns: [fieldName],\n unique: true,\n });\n }\n }\n\n // Extract indexes\n if (type.indexes) {\n for (const [indexName, indexDef] of Object.entries(type.indexes)) {\n indexes.push({\n name: indexName,\n columns: indexDef.fields,\n unique: indexDef.unique,\n });\n }\n }\n\n // Extract foreign keys from relations\n for (const [fieldName, field] of Object.entries(type.fields)) {\n if (field.relation) {\n foreignKeys.push({\n column: fieldName,\n references: {\n table: field.relation.targetType,\n column: field.relation.key,\n },\n });\n }\n }\n\n return { optionalFields, omitFields, indexes, foreignKeys };\n}\n\n/**\n * Generate schema options code for lines-db\n * @param foreignKeys - Foreign key definitions\n * @param indexes - Index definitions\n * @returns Schema options code string\n */\nfunction generateSchemaOptions(\n foreignKeys: ForeignKeyDefinition[],\n indexes: IndexDefinition[],\n): string {\n const schemaOptions: string[] = [];\n\n if (foreignKeys.length > 0) {\n schemaOptions.push(`foreignKeys: [`);\n foreignKeys.forEach((fk) => {\n schemaOptions.push(` ${JSON.stringify(fk)},`);\n });\n schemaOptions.push(`],`);\n }\n\n if (indexes.length > 0) {\n schemaOptions.push(`indexes: [`);\n indexes.forEach((index) => {\n schemaOptions.push(` ${JSON.stringify(index)},`);\n });\n schemaOptions.push(\"],\");\n }\n\n return schemaOptions.length > 0\n ? [\"\\n {\", ...schemaOptions.map((option) => ` ${option}`), \" }\"].join(\"\\n\")\n : \"\";\n}\n\n/**\n * Generates the schema file content for lines-db (for user-defined types with import)\n * @param metadata - lines-db metadata\n * @param importPath - Import path for the TailorDB type\n * @returns Schema file contents\n */\nexport function generateLinesDbSchemaFile(metadata: LinesDbMetadata, importPath: string): string {\n const { exportName, optionalFields, omitFields, foreignKeys, indexes } = metadata;\n\n const schemaTypeCode = ml /* ts */ `\n const schemaType = t.object({\n ...${exportName}.pickFields(${JSON.stringify(optionalFields)}, { optional: true }),\n ...${exportName}.omitFields(${JSON.stringify([...optionalFields, ...omitFields])}),\n });\n `;\n\n const schemaOptionsCode = generateSchemaOptions(foreignKeys, indexes);\n\n return ml /* ts */ `\n import { t } from \"@tailor-platform/sdk\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n import { ${exportName} } from \"${importPath}\";\n\n ${schemaTypeCode}\n\n const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook),${schemaOptionsCode}\n );\n\n `;\n}\n\n/**\n * Parameters for generating plugin-type schema file\n */\nexport interface PluginSchemaParams {\n /** Relative path from schema output to tailor.config.ts */\n configImportPath: string;\n /** Relative import path to the original type file (for type-attached plugins) */\n originalImportPath?: string;\n}\n\n/**\n * Generates the schema file content using getGeneratedType API\n * (for plugin-generated types)\n * @param metadata - lines-db metadata (must have pluginSource)\n * @param params - Plugin import paths\n * @returns Schema file contents\n */\nexport function generateLinesDbSchemaFileWithPluginAPI(\n metadata: LinesDbMetadata,\n params: PluginSchemaParams,\n): string {\n const { typeName, exportName, optionalFields, omitFields, foreignKeys, indexes, pluginSource } =\n metadata;\n\n if (!pluginSource) {\n throw new Error(`pluginSource is required for plugin-generated type \"${typeName}\"`);\n }\n\n const { configImportPath, originalImportPath } = params;\n\n const schemaTypeCode = ml /* ts */ `\n const schemaType = t.object({\n ...${exportName}.pickFields(${JSON.stringify(optionalFields)}, { optional: true }),\n ...${exportName}.omitFields(${JSON.stringify([...optionalFields, ...omitFields])}),\n });\n `;\n\n const schemaOptionsCode = generateSchemaOptions(foreignKeys, indexes);\n\n // Type-attached plugin (e.g., changeset): import original type and use getGeneratedType(configPath, pluginId, type, kind)\n if (pluginSource.originalExportName && originalImportPath && pluginSource.generatedTypeKind) {\n return ml /* ts */ `\n import { join } from \"node:path\";\n import { t } from \"@tailor-platform/sdk\";\n import { getGeneratedType } from \"@tailor-platform/sdk/plugin\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n import { ${pluginSource.originalExportName} } from \"${originalImportPath}\";\n\n const configPath = join(import.meta.dirname, \"${configImportPath}\");\n const ${exportName} = await getGeneratedType(configPath, \"${pluginSource.pluginId}\", ${pluginSource.originalExportName}, \"${pluginSource.generatedTypeKind}\");\n\n ${schemaTypeCode}\n\n const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook),${schemaOptionsCode}\n );\n\n `;\n }\n\n // Namespace plugin (e.g., audit-log): use getGeneratedType(configPath, pluginId, null, kind)\n // For namespace plugins, generatedTypeKind is required\n if (!pluginSource.generatedTypeKind) {\n throw new Error(\n `Namespace plugin \"${pluginSource.pluginId}\" must provide generatedTypeKind for type \"${typeName}\"`,\n );\n }\n\n return ml /* ts */ `\n import { join } from \"node:path\";\n import { t } from \"@tailor-platform/sdk\";\n import { getGeneratedType } from \"@tailor-platform/sdk/plugin\";\n import { defineSchema } from \"@tailor-platform/sdk/seed\";\n import { createTailorDBHook, createStandardSchema } from \"@tailor-platform/sdk/test\";\n\n const configPath = join(import.meta.dirname, \"${configImportPath}\");\n const ${exportName} = await getGeneratedType(configPath, \"${pluginSource.pluginId}\", null, \"${pluginSource.generatedTypeKind}\");\n\n ${schemaTypeCode}\n\n const hook = createTailorDBHook(${exportName});\n\n export const schema = defineSchema(\n createStandardSchema(schemaType, hook),${schemaOptionsCode}\n );\n\n `;\n}\n","import type { SeedTypeInfo } from \"./types\";\nimport type { TailorDBType } from \"@/types/tailordb\";\n\n/**\n * Processes TailorDB types to extract seed type information\n * @param type - Parsed TailorDB type\n * @param namespace - Namespace of the type\n * @returns Seed type information\n */\nexport function processSeedTypeInfo(type: TailorDBType, namespace: string): SeedTypeInfo {\n // Extract dependencies from relations (including keyOnly which only sets foreignKeyType)\n const dependencies: Set<string> = new Set();\n const selfRefFields: string[] = [];\n\n for (const [fieldName, field] of Object.entries(type.fields)) {\n const targetType = field.relation?.targetType ?? field.config.foreignKeyType;\n if (!targetType) continue;\n\n if (targetType === type.name) {\n selfRefFields.push(fieldName);\n } else {\n dependencies.add(targetType);\n }\n }\n\n return {\n name: type.name,\n namespace,\n dependencies: Array.from(dependencies),\n selfRefFields,\n dataFile: `data/${type.name}.jsonl`,\n };\n}\n","import * as path from \"pathe\";\nimport ml from \"@/utils/multiline\";\nimport {\n processIdpUser,\n generateIdpUserSchemaFile,\n generateIdpSeedScriptCode,\n generateIdpTruncateScriptCode,\n} from \"./idp-user-processor\";\nimport {\n processLinesDb,\n generateLinesDbSchemaFile,\n generateLinesDbSchemaFileWithPluginAPI,\n type PluginSchemaParams,\n} from \"./lines-db-processor\";\nimport { processSeedTypeInfo } from \"./seed-type-processor\";\nimport type { Plugin } from \"@/types/plugin\";\nimport type { GeneratorResult, TailorDBReadyContext } from \"@/types/plugin-generation\";\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\ntype SeedPluginOptions = {\n distPath: string;\n machineUserName?: string;\n /**\n * Disable individual `_User <-> userProfile` foreign keys emitted into\n * the generated seed schema. Both directions are emitted by default.\n *\n * Set a direction to `true` to relax it — for example to seed invited\n * users that do not yet have an IdP credential.\n */\n disableIdpUserSync?: DisableIdpUserSyncDirections;\n};\n\nfunction resolveIdpUserSyncFKs(option: SeedPluginOptions[\"disableIdpUserSync\"]): {\n emitUserToIdpFK: boolean;\n emitIdpToUserFK: boolean;\n} {\n return {\n emitUserToIdpFK: !(option?.userToIdp ?? false),\n emitIdpToUserFK: !(option?.idpToUser ?? false),\n };\n}\n\ntype NamespaceConfig = {\n namespace: string;\n types: string[];\n dependencies: Record<string, string[]>;\n selfRefTypes: string[];\n};\n\n/**\n * Generate the IdP user seed function code using tailor.idp.Client via testExecScript\n * @param hasIdpUser - Whether IdP user is included\n * @param idpNamespace - The IDP namespace name\n * @returns JavaScript code for IdP user seeding function\n */\nfunction generateIdpUserSeedFunction(hasIdpUser: boolean, idpNamespace: string | null): string {\n if (!hasIdpUser || !idpNamespace) return \"\";\n\n const scriptCode = generateIdpSeedScriptCode(idpNamespace);\n\n return ml`\n // Seed _User via tailor.idp.Client (server-side)\n const seedIdpUser = async () => {\n console.log(styleText(\"cyan\", \" Seeding _User via tailor.idp.Client...\"));\n const dataDir = join(configDir, \"data\");\n const data = loadSeedData(dataDir, [\"_User\"]);\n const rows = data[\"_User\"] || [];\n if (rows.length === 0) {\n console.log(styleText(\"dim\", \" No _User data to seed\"));\n return { success: true };\n }\n console.log(styleText(\"dim\", \\` Processing \\${rows.length} _User records...\\`));\n\n const idpSeedCode = \\/* js *\\/\\`${scriptCode.replace(/`/g, \"\\\\`\").replace(/\\$/g, \"\\\\$\")}\\`;\n\n const result = await executeScript({\n client: operatorClient,\n workspaceId,\n name: \"seed-idp-user.ts\",\n code: idpSeedCode,\n arg: JSON.stringify({ users: rows }),\n invoker: {\n namespace: authNamespace,\n machineUserName,\n },\n });\n\n if (result.logs) {\n for (const line of result.logs.split(\"\\\\n\").filter(Boolean)) {\n console.log(styleText(\"dim\", \\` \\${line}\\`));\n }\n }\n\n if (result.success) {\n let parsed;\n try {\n parsed = JSON.parse(result.result || \"{}\");\n } catch (e) {\n console.error(styleText(\"red\", \\` ✗ Failed to parse seed result: \\${e.message}\\`));\n return { success: false };\n }\n\n if (parsed.processed) {\n console.log(styleText(\"green\", \\` ✓ _User: \\${parsed.processed} rows processed\\`));\n }\n\n if (!parsed.success) {\n const errors = Array.isArray(parsed.errors) ? parsed.errors : [];\n for (const err of errors) {\n console.error(styleText(\"red\", \\` ✗ \\${err}\\`));\n }\n return { success: false };\n }\n\n return { success: true };\n } else {\n console.error(styleText(\"red\", \\` ✗ Seed failed: \\${result.error}\\`));\n return { success: false };\n }\n };\n `;\n}\n\n/**\n * Generate the IdP user seed call code\n * @param hasIdpUser - Whether IdP user is included\n * @returns JavaScript code for calling IdP user seeding\n */\nfunction generateIdpUserSeedCall(hasIdpUser: boolean): string {\n if (!hasIdpUser) return \"\";\n\n return ml`\n // Seed _User if included and not skipped\n const shouldSeedUser = !skipIdp && (!entitiesToProcess || entitiesToProcess.includes(\"_User\"));\n if (hasIdpUser && shouldSeedUser) {\n const result = await seedIdpUser();\n if (!result.success) {\n allSuccess = false;\n }\n }\n `;\n}\n\n/**\n * Generate the IdP user truncation function code using tailor.idp.Client via testExecScript\n * @param hasIdpUser - Whether IdP user is included\n * @param idpNamespace - The IDP namespace name\n * @returns JavaScript code for IdP user truncation function\n */\nfunction generateIdpUserTruncateFunction(hasIdpUser: boolean, idpNamespace: string | null): string {\n if (!hasIdpUser || !idpNamespace) return \"\";\n\n const scriptCode = generateIdpTruncateScriptCode(idpNamespace);\n\n return ml`\n // Truncate _User via tailor.idp.Client (server-side)\n const truncateIdpUser = async () => {\n console.log(styleText(\"cyan\", \"Truncating _User via tailor.idp.Client...\"));\n\n const idpTruncateCode = \\/* js *\\/\\`${scriptCode.replace(/`/g, \"\\\\`\").replace(/\\$/g, \"\\\\$\")}\\`;\n\n const result = await executeScript({\n client: operatorClient,\n workspaceId,\n name: \"truncate-idp-user.ts\",\n code: idpTruncateCode,\n arg: JSON.stringify({}),\n invoker: {\n namespace: authNamespace,\n machineUserName,\n },\n });\n\n if (result.logs) {\n for (const line of result.logs.split(\"\\\\n\").filter(Boolean)) {\n console.log(styleText(\"dim\", \\` \\${line}\\`));\n }\n }\n\n if (result.success) {\n let parsed;\n try {\n parsed = JSON.parse(result.result || \"{}\");\n } catch (e) {\n console.error(styleText(\"red\", \\` ✗ Failed to parse truncation result: \\${e.message}\\`));\n return { success: false };\n }\n\n if (parsed.deleted !== undefined) {\n console.log(styleText(\"green\", \\` ✓ _User: \\${parsed.deleted} users deleted\\`));\n }\n\n if (!parsed.success) {\n const errors = Array.isArray(parsed.errors) ? parsed.errors : [];\n for (const err of errors) {\n console.error(styleText(\"red\", \\` ✗ \\${err}\\`));\n }\n return { success: false };\n }\n\n return { success: true };\n } else {\n console.error(styleText(\"red\", \\` ✗ Truncation failed: \\${result.error}\\`));\n return { success: false };\n }\n };\n `;\n}\n\n/**\n * Generate the IdP user truncation call code within the truncate block\n * @param hasIdpUser - Whether IdP user is included\n * @returns JavaScript code for calling IdP user truncation\n */\nfunction generateIdpUserTruncateCall(hasIdpUser: boolean): string {\n if (!hasIdpUser) return \"\";\n\n return ml`\n // Truncate _User if applicable\n const shouldTruncateUser = !skipIdp && !hasNamespace && (!hasTypes || entitiesToProcess.includes(\"_User\"));\n if (hasIdpUser && shouldTruncateUser) {\n const truncResult = await truncateIdpUser();\n if (!truncResult.success) {\n console.error(styleText(\"red\", \"IDP user truncation failed.\"));\n process.exit(1);\n }\n }\n `;\n}\n\n/**\n * Generates the exec.mjs script content using testExecScript API for TailorDB types\n * and tailor.idp.Client for _User (IdP managed)\n * @param defaultMachineUserName - Default machine user name from generator config (can be overridden at runtime)\n * @param relativeConfigPath - Config path relative to exec script\n * @param namespaceConfigs - Namespace configurations with types and dependencies\n * @param hasIdpUser - Whether _User is included\n * @param idpNamespace - The IDP namespace name, or null if not applicable\n * @returns exec.mjs file contents\n */\nfunction generateExecScript(\n defaultMachineUserName: string | undefined,\n relativeConfigPath: string,\n namespaceConfigs: NamespaceConfig[],\n hasIdpUser: boolean,\n idpNamespace: string | null,\n): string {\n // Generate namespaceEntities object\n const namespaceEntitiesEntries = namespaceConfigs\n .map(({ namespace, types }) => {\n const entitiesFormatted = types.map((e) => ` \"${e}\",`).join(\"\\n\");\n return ` \"${namespace}\": [\\n${entitiesFormatted}\\n ]`;\n })\n .join(\",\\n\");\n\n // Generate dependency map for each namespace\n const namespaceDepsEntries = namespaceConfigs\n .map(({ namespace, dependencies }) => {\n const depsObj = Object.entries(dependencies)\n .map(([type, deps]) => ` \"${type}\": [${deps.map((d) => `\"${d}\"`).join(\", \")}]`)\n .join(\",\\n\");\n return ` \"${namespace}\": {\\n${depsObj}\\n }`;\n })\n .join(\",\\n\");\n\n // Generate self-referencing types map for each namespace\n const namespaceSelfRefEntries = namespaceConfigs\n .map(({ namespace, selfRefTypes }) => {\n const formatted = selfRefTypes.map((t) => `\"${t}\"`).join(\", \");\n return ` \"${namespace}\": [${formatted}]`;\n })\n .join(\",\\n\");\n\n return ml /* js */ `\n /**\n * @generated\n * This file is auto-generated by @tailor-platform/sdk's seedPlugin.\n * Do not edit by hand: changes will be overwritten on the next \\`sdk generate\\`.\n */\n import { readFileSync } from \"node:fs\";\n import { join, isAbsolute } from \"node:path\";\n import { parseArgs, styleText } from \"node:util\";\n import { createInterface } from \"node:readline\";\n import {\n show,\n truncate,\n bundleSeedScript,\n chunkSeedData,\n executeScript,\n initOperatorClient,\n loadAccessToken,\n loadWorkspaceId,\n } from \"@tailor-platform/sdk/cli\";\n\n // Handle \"validate\" subcommand before parseArgs\n const subcommand = process.argv[2];\n if (subcommand === \"validate\") {\n const { validateSeedData } = await import(\"@tailor-platform/sdk/seed\");\n const validateArgs = parseArgs({\n args: process.argv.slice(3),\n options: {\n verbose: { type: \"boolean\", short: \"v\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n allowPositionals: true,\n });\n\n if (validateArgs.values.help) {\n console.log(\\`\n Usage: node exec.mjs validate [options] [path]\n\n Validate JSONL seed data against schema definitions.\n\n Arguments:\n path File or directory to validate (default: ./data)\n\n Options:\n -v, --verbose Show verbose error output\n -h, --help Show help\n\n Examples:\n node exec.mjs validate # Validate all seed data\n node exec.mjs validate ./data/User.jsonl # Validate specific file\n node exec.mjs validate -v # Verbose error output\n \\`);\n process.exit(0);\n }\n\n const configDir = import.meta.dirname;\n const targetPath = validateArgs.positionals[0] || join(configDir, \"data\");\n const resolvedPath = isAbsolute(targetPath) ? targetPath : join(process.cwd(), targetPath);\n\n try {\n const result = await validateSeedData({ path: resolvedPath, verbose: validateArgs.values.verbose });\n if (result.output) console.log(result.output);\n if (!result.valid) {\n console.error(result.error);\n process.exit(1);\n }\n process.exit(0);\n } catch (error) {\n console.error(styleText(\"red\", \\`Error: \\${error instanceof Error ? error.message : String(error)}\\`));\n process.exit(1);\n }\n }\n\n // Parse command-line arguments\n const { values, positionals } = parseArgs({\n options: {\n \"machine-user\": { type: \"string\", short: \"m\" },\n namespace: { type: \"string\", short: \"n\" },\n \"skip-idp\": { type: \"boolean\", default: false },\n truncate: { type: \"boolean\", default: false },\n yes: { type: \"boolean\", default: false },\n profile: { type: \"string\", short: \"p\" },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n allowPositionals: true,\n });\n\n if (values.help) {\n console.log(\\`\n Usage: node exec.mjs [command] [options] [types...]\n\n Commands:\n validate [path] Validate seed data against schema (default: ./data)\n\n Options:\n -m, --machine-user <name> Machine user name for authentication (required if not configured)\n -n, --namespace <ns> Process all types in specified namespace (excludes _User)\n --skip-idp Skip IdP user (_User) entity\n --truncate Truncate tables before seeding\n --yes Skip confirmation prompts (for truncate)\n -p, --profile <name> Workspace profile name\n -h, --help Show help\n\n Examples:\n node exec.mjs -m admin # Process all types with machine user\n node exec.mjs --namespace <namespace> # Process tailordb namespace only (no _User)\n node exec.mjs User Order # Process specific types only\n node exec.mjs --skip-idp # Process all except _User\n node exec.mjs --truncate # Truncate all tables, then seed all\n node exec.mjs --truncate --yes # Truncate all tables without confirmation, then seed all\n node exec.mjs --truncate --namespace <namespace> # Truncate tailordb, then seed tailordb\n node exec.mjs --truncate User Order # Truncate User and Order, then seed them\n node exec.mjs validate # Validate all seed data\n node exec.mjs validate ./data/User.jsonl # Validate specific file\n \\`);\n process.exit(0);\n }\n\n // Helper function to prompt for y/n confirmation\n const promptConfirmation = (question) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n rl.question(styleText(\"yellow\", question), (answer) => {\n rl.close();\n resolve(answer.toLowerCase().trim());\n });\n });\n };\n\n const configDir = import.meta.dirname;\n const configPath = join(configDir, \"${relativeConfigPath}\");\n\n // Determine machine user name (CLI argument takes precedence over config default)\n const defaultMachineUser = ${defaultMachineUserName ? `\"${defaultMachineUserName}\"` : \"undefined\"};\n const machineUserName = values[\"machine-user\"] || defaultMachineUser;\n\n if (!machineUserName) {\n console.error(styleText(\"red\", \"Error: Machine user name is required.\"));\n console.error(styleText(\"yellow\", \"Specify --machine-user <name> or configure machineUserName in generator options.\"));\n process.exit(1);\n }\n\n // Entity configuration\n const namespaceEntities = {\n${namespaceEntitiesEntries}\n };\n const namespaceDeps = {\n${namespaceDepsEntries}\n };\n const namespaceSelfRefTypes = {\n${namespaceSelfRefEntries}\n };\n const entities = Object.values(namespaceEntities).flat();\n const hasIdpUser = ${String(hasIdpUser)};\n\n // Determine which entities to process\n let entitiesToProcess = null;\n\n const hasNamespace = !!values.namespace;\n const hasTypes = positionals.length > 0;\n const skipIdp = values[\"skip-idp\"];\n\n // Validate mutually exclusive options\n const optionCount = [hasNamespace, hasTypes].filter(Boolean).length;\n if (optionCount > 1) {\n console.error(styleText(\"red\", \"Error: Options --namespace and type names are mutually exclusive.\"));\n process.exit(1);\n }\n\n // --skip-idp and --namespace are redundant (namespace already excludes _User)\n if (skipIdp && hasNamespace) {\n console.warn(styleText(\"yellow\", \"Warning: --skip-idp is redundant with --namespace (namespace filtering already excludes _User).\"));\n }\n\n // Filter by namespace (automatically excludes _User as it has no namespace)\n if (hasNamespace) {\n const namespace = values.namespace;\n entitiesToProcess = namespaceEntities[namespace];\n\n if (!entitiesToProcess || entitiesToProcess.length === 0) {\n console.error(styleText(\"red\", \\`Error: No entities found in namespace \"\\${namespace}\"\\`));\n console.error(styleText(\"yellow\", \\`Available namespaces: \\${Object.keys(namespaceEntities).join(\", \")}\\`));\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \\`Filtering by namespace: \\${namespace}\\`));\n console.log(styleText(\"dim\", \\`Entities: \\${entitiesToProcess.join(\", \")}\\`));\n }\n\n // Filter by specific types\n if (hasTypes) {\n const requestedTypes = positionals;\n const notFoundTypes = [];\n const allTypes = hasIdpUser ? [...entities, \"_User\"] : entities;\n\n entitiesToProcess = requestedTypes.filter((type) => {\n if (!allTypes.includes(type)) {\n notFoundTypes.push(type);\n return false;\n }\n return true;\n });\n\n if (notFoundTypes.length > 0) {\n console.error(styleText(\"red\", \\`Error: The following types were not found: \\${notFoundTypes.join(\", \")}\\`));\n console.error(styleText(\"yellow\", \\`Available types: \\${allTypes.join(\", \")}\\`));\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \\`Filtering by types: \\${entitiesToProcess.join(\", \")}\\`));\n }\n\n // Apply --skip-idp filter\n if (skipIdp) {\n if (entitiesToProcess) {\n entitiesToProcess = entitiesToProcess.filter((entity) => entity !== \"_User\");\n } else {\n entitiesToProcess = entities.filter((entity) => entity !== \"_User\");\n }\n }\n\n // Get application info\n const appInfo = await show({ configPath, profile: values.profile });\n const authNamespace = appInfo.auth;\n\n // Initialize operator client (once for all namespaces)\n const accessToken = await loadAccessToken({ profile: values.profile, useProfile: true });\n const workspaceId = await loadWorkspaceId({ profile: values.profile });\n const operatorClient = await initOperatorClient(accessToken);\n\n ${generateIdpUserTruncateFunction(hasIdpUser, idpNamespace)}\n\n // Truncate tables if requested\n if (values.truncate) {\n const answer = values.yes ? \"y\" : await promptConfirmation(\"Are you sure you want to truncate? (y/n): \");\n if (answer !== \"y\") {\n console.log(styleText(\"yellow\", \"Truncate cancelled.\"));\n process.exit(0);\n }\n\n console.log(styleText(\"cyan\", \"Truncating tables...\"));\n\n try {\n if (hasNamespace) {\n await truncate({\n configPath,\n profile: values.profile,\n namespace: values.namespace,\n });\n } else if (hasTypes) {\n const typesToTruncate = entitiesToProcess.filter((t) => t !== \"_User\");\n if (typesToTruncate.length > 0) {\n await truncate({\n configPath,\n profile: values.profile,\n types: typesToTruncate,\n });\n } else {\n console.log(styleText(\"dim\", \"No TailorDB types to truncate (only _User was specified).\"));\n }\n } else {\n await truncate({\n configPath,\n profile: values.profile,\n all: true,\n });\n }\n } catch (error) {\n console.error(styleText(\"red\", \\`Truncate failed: \\${error.message}\\`));\n process.exit(1);\n }\n\n ${generateIdpUserTruncateCall(hasIdpUser)}\n\n console.log(styleText(\"green\", \"Truncate completed.\"));\n }\n\n console.log(styleText(\"cyan\", \"\\\\nStarting seed data generation...\"));\n if (skipIdp) {\n console.log(styleText(\"dim\", \\` Skipping IdP user (_User)\\`));\n }\n\n // Load seed data from JSONL files\n const loadSeedData = (dataDir, typeNames) => {\n const data = {};\n for (const typeName of typeNames) {\n const jsonlPath = join(dataDir, \\`\\${typeName}.jsonl\\`);\n try {\n const content = readFileSync(jsonlPath, \"utf-8\").trim();\n if (content) {\n data[typeName] = content.split(\"\\\\n\").map((line) => JSON.parse(line));\n } else {\n data[typeName] = [];\n }\n } catch (error) {\n if (error.code === \"ENOENT\") {\n data[typeName] = [];\n } else {\n throw error;\n }\n }\n }\n return data;\n };\n\n // Topological sort for dependency order\n const topologicalSort = (types, deps) => {\n const visited = new Set();\n const result = [];\n\n const visit = (type) => {\n if (visited.has(type)) return;\n visited.add(type);\n const typeDeps = deps[type] || [];\n for (const dep of typeDeps) {\n if (types.includes(dep)) {\n visit(dep);\n }\n }\n result.push(type);\n };\n\n for (const type of types) {\n visit(type);\n }\n return result;\n };\n\n // Seed TailorDB types via testExecScript\n const seedViaTestExecScript = async (namespace, typesToSeed, deps, selfRefTypes) => {\n const dataDir = join(configDir, \"data\");\n const sortedTypes = topologicalSort(typesToSeed, deps);\n const data = loadSeedData(dataDir, sortedTypes);\n\n // Skip if no data\n const typesWithData = sortedTypes.filter((t) => data[t] && data[t].length > 0);\n if (typesWithData.length === 0) {\n console.log(styleText(\"dim\", \\` [\\${namespace}] No data to seed\\`));\n return { success: true, processed: {} };\n }\n\n console.log(styleText(\"cyan\", \\` [\\${namespace}] Seeding \\${typesWithData.length} types via Kysely batch insert...\\`));\n\n // Bundle seed script\n const bundled = await bundleSeedScript(namespace, typesWithData);\n\n // Chunk seed data to fit within gRPC message size limits\n const chunks = chunkSeedData({\n data,\n order: sortedTypes,\n codeByteSize: new TextEncoder().encode(bundled.bundledCode).length,\n });\n\n if (chunks.length === 0) {\n console.log(styleText(\"dim\", \\` [\\${namespace}] No data to seed\\`));\n return { success: true, processed: {} };\n }\n\n if (chunks.length > 1) {\n console.log(styleText(\"dim\", \\` Split into \\${chunks.length} chunks\\`));\n }\n\n const allProcessed = {};\n let hasError = false;\n const allErrors = [];\n\n for (const chunk of chunks) {\n if (chunks.length > 1) {\n console.log(styleText(\"dim\", \\` Chunk \\${chunk.index + 1}/\\${chunk.total}: \\${chunk.order.join(\", \")}\\`));\n }\n\n // Execute seed script for this chunk\n const result = await executeScript({\n client: operatorClient,\n workspaceId,\n name: \\`seed-\\${namespace}.ts\\`,\n code: bundled.bundledCode,\n arg: JSON.stringify({ data: chunk.data, order: chunk.order, selfRefTypes }),\n invoker: {\n namespace: authNamespace,\n machineUserName,\n },\n });\n\n // Parse result and display logs\n if (result.logs) {\n for (const line of result.logs.split(\"\\\\n\").filter(Boolean)) {\n console.log(styleText(\"dim\", \\` \\${line}\\`));\n }\n }\n\n if (result.success) {\n let parsed;\n try {\n const parsedResult = JSON.parse(result.result || \"{}\");\n parsed = parsedResult && typeof parsedResult === \"object\" ? parsedResult : {};\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(styleText(\"red\", \\` ✗ Failed to parse seed result: \\${message}\\`));\n hasError = true;\n allErrors.push(message);\n continue;\n }\n\n const processed = parsed.processed || {};\n for (const [type, count] of Object.entries(processed)) {\n allProcessed[type] = (allProcessed[type] || 0) + count;\n console.log(styleText(\"green\", \\` ✓ \\${type}: \\${count} rows inserted\\`));\n }\n\n if (!parsed.success) {\n const errors = Array.isArray(parsed.errors) ? parsed.errors : [];\n const errorMessage =\n errors.length > 0 ? errors.join(\"\\\\n \") : \"Seed script reported failure\";\n console.error(styleText(\"red\", \\` ✗ Seed failed:\\\\n \\${errorMessage}\\`));\n hasError = true;\n allErrors.push(errorMessage);\n }\n } else {\n console.error(styleText(\"red\", \\` ✗ Seed failed: \\${result.error}\\`));\n hasError = true;\n allErrors.push(result.error);\n }\n }\n\n if (hasError) {\n return { success: false, error: allErrors.join(\"\\\\n\") };\n }\n return { success: true, processed: allProcessed };\n };\n\n ${generateIdpUserSeedFunction(hasIdpUser, idpNamespace)}\n\n // Main execution\n try {\n let allSuccess = true;\n\n // Determine which namespaces and types to process\n const namespacesToProcess = hasNamespace\n ? [values.namespace]\n : Object.keys(namespaceEntities);\n\n for (const namespace of namespacesToProcess) {\n const nsTypes = namespaceEntities[namespace] || [];\n const nsDeps = namespaceDeps[namespace] || {};\n const nsSelfRefTypes = namespaceSelfRefTypes[namespace] || [];\n\n // Filter types if specific types requested\n let typesToSeed = entitiesToProcess\n ? nsTypes.filter((t) => entitiesToProcess.includes(t))\n : nsTypes;\n\n if (typesToSeed.length === 0) continue;\n\n const result = await seedViaTestExecScript(namespace, typesToSeed, nsDeps, nsSelfRefTypes);\n if (!result.success) {\n allSuccess = false;\n }\n }\n\n ${generateIdpUserSeedCall(hasIdpUser)}\n\n if (allSuccess) {\n console.log(styleText(\"green\", \"\\\\n✓ Seed data generation completed successfully\"));\n } else {\n console.error(styleText(\"red\", \"\\\\n✗ Seed data generation completed with errors\"));\n process.exit(1);\n }\n } catch (error) {\n console.error(styleText(\"red\", \\`\\\\n✗ Seed data generation failed: \\${error.message}\\`));\n process.exit(1);\n }\n\n `;\n}\n\n/**\n * Plugin that generates seed data files with Kysely batch insert and tailor.idp.Client for _User.\n * @param options - Plugin options\n * @param options.distPath - Output directory path for generated seed files\n * @param options.machineUserName - Default machine user name for authentication\n * @param options.disableIdpUserSync - Skip emitting individual `_User <-> userProfile` foreign keys. Both directions are emitted by default; set a direction to `true` to relax that side.\n * @returns Plugin instance with onTailorDBReady hook\n */\nexport function seedPlugin(options: SeedPluginOptions): Plugin<unknown, SeedPluginOptions> {\n return {\n id: SeedGeneratorID,\n description: \"Generates seed data files (Kysely batch insert + tailor.idp.Client for _User)\",\n pluginConfig: options,\n\n async onTailorDBReady(ctx: TailorDBReadyContext<SeedPluginOptions>): Promise<GeneratorResult> {\n const files: GeneratorResult[\"files\"] = [];\n const namespaceConfigs: NamespaceConfig[] = [];\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 hasIdpUser = idpUser !== null;\n const idpUserSyncFKs = resolveIdpUserSyncFKs(ctx.pluginConfig.disableIdpUserSync);\n\n for (const ns of ctx.tailordb) {\n const types: string[] = [];\n const dependencies: Record<string, string[]> = {};\n const selfRefTypes: string[] = [];\n\n for (const [typeName, type] of Object.entries(ns.types)) {\n const source = ns.sourceInfo.get(typeName)!;\n const typeInfo = processSeedTypeInfo(type, ns.namespace);\n const linesDb = processLinesDb(type, source);\n\n // Add reverse FK from userProfile type to _User (opt-out via disableIdpUserSync.userToIdp: true)\n if (\n idpUserSyncFKs.emitUserToIdpFK &&\n idpUser &&\n typeName === idpUser.schema.userTypeName\n ) {\n linesDb.foreignKeys.push({\n column: idpUser.schema.usernameField,\n references: {\n table: \"_User\",\n column: \"name\",\n },\n });\n }\n\n types.push(typeInfo.name);\n dependencies[typeInfo.name] = typeInfo.dependencies;\n if (typeInfo.selfRefFields.length > 0) {\n selfRefTypes.push(typeInfo.name);\n }\n\n // Generate empty JSONL data file\n files.push({\n path: path.join(ctx.pluginConfig.distPath, typeInfo.dataFile),\n content: \"\",\n skipIfExists: true,\n });\n\n const schemaOutputPath = path.join(\n ctx.pluginConfig.distPath,\n \"data\",\n `${linesDb.typeName}.schema.ts`,\n );\n\n // Plugin-generated type: use getGeneratedType API\n if (linesDb.pluginSource && linesDb.pluginSource.pluginImportPath) {\n // Build original type import path\n let originalImportPath: string | undefined;\n if (linesDb.pluginSource.originalFilePath && linesDb.pluginSource.originalExportName) {\n const relativePath = path.relative(\n path.dirname(schemaOutputPath),\n linesDb.pluginSource.originalFilePath,\n );\n originalImportPath = relativePath.replace(/\\.ts$/, \"\").startsWith(\".\")\n ? relativePath.replace(/\\.ts$/, \"\")\n : `./${relativePath.replace(/\\.ts$/, \"\")}`;\n }\n\n // Compute relative path from schema output to config file\n const configImportPath = path.relative(path.dirname(schemaOutputPath), ctx.configPath);\n\n const params: PluginSchemaParams = {\n configImportPath,\n originalImportPath,\n };\n\n const schemaContent = generateLinesDbSchemaFileWithPluginAPI(linesDb, params);\n\n files.push({\n path: schemaOutputPath,\n content: schemaContent,\n });\n } else {\n // User-defined type: import from source file\n const relativePath = path.relative(path.dirname(schemaOutputPath), linesDb.importPath);\n const typeImportPath = relativePath.replace(/\\.ts$/, \"\").startsWith(\".\")\n ? relativePath.replace(/\\.ts$/, \"\")\n : `./${relativePath.replace(/\\.ts$/, \"\")}`;\n const schemaContent = generateLinesDbSchemaFile(linesDb, typeImportPath);\n\n files.push({\n path: schemaOutputPath,\n content: schemaContent,\n });\n }\n }\n\n namespaceConfigs.push({\n namespace: ns.namespace,\n types,\n dependencies,\n selfRefTypes,\n });\n }\n\n if (idpUser) {\n // Generate empty JSONL data file\n files.push({\n path: path.join(ctx.pluginConfig.distPath, idpUser.dataFile),\n content: \"\",\n skipIfExists: true,\n });\n\n // Generate schema file with foreign key (opt-out via disableIdpUserSync.idpToUser: true)\n files.push({\n path: path.join(ctx.pluginConfig.distPath, \"data\", `${idpUser.name}.schema.ts`),\n content: generateIdpUserSchemaFile({\n usernameField: idpUser.schema.usernameField,\n userTypeName: idpUser.schema.userTypeName,\n includeUserProfileFK: idpUserSyncFKs.emitIdpToUserFK,\n }),\n });\n }\n\n // Generate exec.mjs (machineUserName can be provided at runtime if not configured)\n const relativeConfigPath = path.relative(ctx.pluginConfig.distPath, ctx.configPath);\n files.push({\n path: path.join(ctx.pluginConfig.distPath, \"exec.mjs\"),\n content: generateExecScript(\n ctx.pluginConfig.machineUserName,\n relativeConfigPath,\n namespaceConfigs,\n hasIdpUser,\n idpUser?.idpNamespace ?? null,\n ),\n });\n\n return { files };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -15,15 +15,24 @@ const regex = {
|
|
|
15
15
|
* @param options - Field options
|
|
16
16
|
* @param fields - Nested fields for object-like types
|
|
17
17
|
* @param values - Allowed values for enum-like fields
|
|
18
|
+
* @param metadata - Pre-built metadata to clone from (used by `clone()`); when
|
|
19
|
+
* given, the mutable containers are deep-copied here and `options`/`values` are
|
|
20
|
+
* ignored for metadata construction
|
|
18
21
|
* @returns A new TailorField
|
|
19
22
|
*/
|
|
20
|
-
function createTailorField(type, options, fields, values) {
|
|
21
|
-
const _metadata =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
function createTailorField(type, options, fields, values, metadata) {
|
|
24
|
+
const _metadata = metadata ? {
|
|
25
|
+
...metadata,
|
|
26
|
+
...metadata.allowedValues && { allowedValues: metadata.allowedValues.map((v) => ({ ...v })) },
|
|
27
|
+
...metadata.validate && { validate: metadata.validate.map((v) => Array.isArray(v) ? [...v] : v) }
|
|
28
|
+
} : { required: true };
|
|
29
|
+
if (!metadata) {
|
|
30
|
+
if (options) {
|
|
31
|
+
if (options.optional === true) _metadata.required = false;
|
|
32
|
+
if (options.array === true) _metadata.array = true;
|
|
33
|
+
}
|
|
34
|
+
if (values) _metadata.allowedValues = mapAllowedValues(values);
|
|
25
35
|
}
|
|
26
|
-
if (values) _metadata.allowedValues = mapAllowedValues(values);
|
|
27
36
|
/**
|
|
28
37
|
* Validate a single value (not an array element)
|
|
29
38
|
* Used internally for array element validation
|
|
@@ -33,59 +42,60 @@ function createTailorField(type, options, fields, values) {
|
|
|
33
42
|
function validateValue(args) {
|
|
34
43
|
const { value, data, user, pathArray } = args;
|
|
35
44
|
const issues = [];
|
|
45
|
+
const path = pathArray.length > 0 ? pathArray : void 0;
|
|
36
46
|
switch (type) {
|
|
37
47
|
case "string":
|
|
38
48
|
if (typeof value !== "string") issues.push({
|
|
39
49
|
message: `Expected a string: received ${String(value)}`,
|
|
40
|
-
path
|
|
50
|
+
path
|
|
41
51
|
});
|
|
42
52
|
break;
|
|
43
53
|
case "integer":
|
|
44
54
|
if (typeof value !== "number" || !Number.isInteger(value)) issues.push({
|
|
45
55
|
message: `Expected an integer: received ${String(value)}`,
|
|
46
|
-
path
|
|
56
|
+
path
|
|
47
57
|
});
|
|
48
58
|
break;
|
|
49
59
|
case "float":
|
|
50
60
|
if (typeof value !== "number" || !Number.isFinite(value)) issues.push({
|
|
51
61
|
message: `Expected a number: received ${String(value)}`,
|
|
52
|
-
path
|
|
62
|
+
path
|
|
53
63
|
});
|
|
54
64
|
break;
|
|
55
65
|
case "boolean":
|
|
56
66
|
if (typeof value !== "boolean") issues.push({
|
|
57
67
|
message: `Expected a boolean: received ${String(value)}`,
|
|
58
|
-
path
|
|
68
|
+
path
|
|
59
69
|
});
|
|
60
70
|
break;
|
|
61
71
|
case "uuid":
|
|
62
72
|
if (typeof value !== "string" || !regex.uuid.test(value)) issues.push({
|
|
63
73
|
message: `Expected a valid UUID: received ${String(value)}`,
|
|
64
|
-
path
|
|
74
|
+
path
|
|
65
75
|
});
|
|
66
76
|
break;
|
|
67
77
|
case "date":
|
|
68
78
|
if (typeof value !== "string" || !regex.date.test(value)) issues.push({
|
|
69
79
|
message: `Expected to match "yyyy-MM-dd" format: received ${String(value)}`,
|
|
70
|
-
path
|
|
80
|
+
path
|
|
71
81
|
});
|
|
72
82
|
break;
|
|
73
83
|
case "datetime":
|
|
74
84
|
if (typeof value !== "string" || !regex.datetime.test(value)) issues.push({
|
|
75
85
|
message: `Expected to match ISO format: received ${String(value)}`,
|
|
76
|
-
path
|
|
86
|
+
path
|
|
77
87
|
});
|
|
78
88
|
break;
|
|
79
89
|
case "time":
|
|
80
90
|
if (typeof value !== "string" || !regex.time.test(value)) issues.push({
|
|
81
91
|
message: `Expected to match "HH:mm" format: received ${String(value)}`,
|
|
82
|
-
path
|
|
92
|
+
path
|
|
83
93
|
});
|
|
84
94
|
break;
|
|
85
95
|
case "decimal":
|
|
86
96
|
if (typeof value !== "string" || !regex.decimal.test(value)) issues.push({
|
|
87
97
|
message: `Expected a decimal string: received ${String(value)}`,
|
|
88
|
-
path
|
|
98
|
+
path
|
|
89
99
|
});
|
|
90
100
|
break;
|
|
91
101
|
case "enum":
|
|
@@ -93,14 +103,14 @@ function createTailorField(type, options, fields, values) {
|
|
|
93
103
|
const allowedValues = field._metadata.allowedValues.map((v) => v.value);
|
|
94
104
|
if (typeof value !== "string" || !allowedValues.includes(value)) issues.push({
|
|
95
105
|
message: `Must be one of [${allowedValues.join(", ")}]: received ${String(value)}`,
|
|
96
|
-
path
|
|
106
|
+
path
|
|
97
107
|
});
|
|
98
108
|
}
|
|
99
109
|
break;
|
|
100
110
|
case "nested":
|
|
101
111
|
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date) issues.push({
|
|
102
112
|
message: `Expected an object: received ${String(value)}`,
|
|
103
|
-
path
|
|
113
|
+
path
|
|
104
114
|
});
|
|
105
115
|
else if (field.fields && Object.keys(field.fields).length > 0) for (const [fieldName, nestedField] of Object.entries(field.fields)) {
|
|
106
116
|
const fieldValue = value?.[fieldName];
|
|
@@ -129,7 +139,7 @@ function createTailorField(type, options, fields, values) {
|
|
|
129
139
|
user
|
|
130
140
|
})) issues.push({
|
|
131
141
|
message,
|
|
132
|
-
path
|
|
142
|
+
path
|
|
133
143
|
});
|
|
134
144
|
}
|
|
135
145
|
return issues;
|
|
@@ -142,11 +152,12 @@ function createTailorField(type, options, fields, values) {
|
|
|
142
152
|
function parseInternal(args) {
|
|
143
153
|
const { value, data, user, pathArray } = args;
|
|
144
154
|
const issues = [];
|
|
155
|
+
const path = pathArray.length > 0 ? pathArray : void 0;
|
|
145
156
|
const isNullOrUndefined = value === null || value === void 0;
|
|
146
157
|
if (field._metadata.required && isNullOrUndefined) {
|
|
147
158
|
issues.push({
|
|
148
159
|
message: "Required field is missing",
|
|
149
|
-
path
|
|
160
|
+
path
|
|
150
161
|
});
|
|
151
162
|
return { issues };
|
|
152
163
|
}
|
|
@@ -155,7 +166,7 @@ function createTailorField(type, options, fields, values) {
|
|
|
155
166
|
if (!Array.isArray(value)) {
|
|
156
167
|
issues.push({
|
|
157
168
|
message: "Expected an array",
|
|
158
|
-
path
|
|
169
|
+
path
|
|
159
170
|
});
|
|
160
171
|
return { issues };
|
|
161
172
|
}
|
|
@@ -182,6 +193,18 @@ function createTailorField(type, options, fields, values) {
|
|
|
182
193
|
if (issues.length > 0) return { issues };
|
|
183
194
|
return { value };
|
|
184
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Clone the field and apply metadata updates to the clone.
|
|
198
|
+
* The original instance is never mutated, so a field shared across places
|
|
199
|
+
* cannot leak metadata between them.
|
|
200
|
+
* @param metadataUpdates - Metadata properties to overwrite on the clone
|
|
201
|
+
* @returns A new field with the updated metadata
|
|
202
|
+
*/
|
|
203
|
+
function cloneWith(metadataUpdates) {
|
|
204
|
+
const cloned = field.clone();
|
|
205
|
+
Object.assign(cloned._metadata, metadataUpdates);
|
|
206
|
+
return cloned;
|
|
207
|
+
}
|
|
185
208
|
const field = {
|
|
186
209
|
type,
|
|
187
210
|
fields: fields ?? {},
|
|
@@ -192,16 +215,13 @@ function createTailorField(type, options, fields, values) {
|
|
|
192
215
|
return { ...this._metadata };
|
|
193
216
|
},
|
|
194
217
|
description(description) {
|
|
195
|
-
|
|
196
|
-
return this;
|
|
218
|
+
return cloneWith({ description });
|
|
197
219
|
},
|
|
198
220
|
typeName(typeName) {
|
|
199
|
-
|
|
200
|
-
return this;
|
|
221
|
+
return cloneWith({ typeName });
|
|
201
222
|
},
|
|
202
223
|
validate(...validateInputs) {
|
|
203
|
-
|
|
204
|
-
return this;
|
|
224
|
+
return cloneWith({ validate: validateInputs });
|
|
205
225
|
},
|
|
206
226
|
parse(args) {
|
|
207
227
|
return parseInternal({
|
|
@@ -211,7 +231,16 @@ function createTailorField(type, options, fields, values) {
|
|
|
211
231
|
pathArray: []
|
|
212
232
|
});
|
|
213
233
|
},
|
|
214
|
-
_parseInternal: parseInternal
|
|
234
|
+
_parseInternal: parseInternal,
|
|
235
|
+
clone() {
|
|
236
|
+
let clonedFields = fields;
|
|
237
|
+
if (fields) {
|
|
238
|
+
const cloned = {};
|
|
239
|
+
for (const [key, nestedField] of Object.entries(fields)) cloned[key] = nestedField.clone();
|
|
240
|
+
clonedFields = cloned;
|
|
241
|
+
}
|
|
242
|
+
return createTailorField(type, options, clonedFields, values, this._metadata);
|
|
243
|
+
}
|
|
215
244
|
};
|
|
216
245
|
return field;
|
|
217
246
|
}
|
|
@@ -340,4 +369,4 @@ const t = {
|
|
|
340
369
|
|
|
341
370
|
//#endregion
|
|
342
371
|
export { t };
|
|
343
|
-
//# sourceMappingURL=types-
|
|
372
|
+
//# sourceMappingURL=types-BwGth3a1.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BwGth3a1.mjs","names":[],"sources":["../src/configure/types/type.ts"],"sourcesContent":["import { type AllowedValues, type AllowedValuesOutput, mapAllowedValues } from \"./field\";\nimport type {\n DefinedFieldMetadata,\n TailorFieldType,\n TailorToTs,\n FieldMetadata,\n FieldOptions,\n FieldOutput,\n} from \"@/types/field-types\";\nimport type { InferFieldsOutput, Prettify } from \"@/types/helpers\";\nimport type { TailorField as TailorFieldBase } from \"@/types/tailor-field\";\nimport type { TailorUser } from \"@/types/user\";\nimport type { FieldValidateInput } from \"@/types/validation\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// This helper type intentionally uses `any` as a placeholder for unknown field output.\n// oxlint-disable-next-line no-explicit-any\nexport type TailorAnyField = TailorField<any>;\n\n/**\n * Full TailorField interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorField<\n Defined extends DefinedFieldMetadata = DefinedFieldMetadata,\n // Generic default output type (kept loose on purpose for library ergonomics).\n // oxlint-disable-next-line no-explicit-any\n Output = any,\n M extends FieldMetadata = FieldMetadata,\n T extends TailorFieldType = TailorFieldType,\n> extends TailorFieldBase<Defined, Output, M, T> {\n readonly fields: Record<string, TailorAnyField>;\n _metadata: M;\n\n /**\n * Set a description for the field\n * @param description - The description text\n * @returns The field with updated metadata\n */\n description<CurrentDefined extends Defined>(\n this: CurrentDefined extends { description: unknown }\n ? never\n : TailorField<CurrentDefined, Output>,\n description: string,\n ): TailorField<Prettify<CurrentDefined & { description: true }>, Output>;\n\n /**\n * Set a custom type name for enum or nested types\n * @param typeName - The custom type name\n * @returns The field with updated metadata\n */\n typeName<CurrentDefined extends Defined>(\n this: CurrentDefined extends { typeName: unknown }\n ? never\n : CurrentDefined extends { type: \"enum\" | \"nested\" }\n ? TailorField<CurrentDefined, Output>\n : never,\n typeName: string,\n ): TailorField<Prettify<CurrentDefined & { typeName: true }>, Output>;\n\n /**\n * Add validation functions to the field\n * @param validate - One or more validation functions\n * @returns The field with updated metadata\n */\n validate<CurrentDefined extends Defined>(\n this: CurrentDefined extends { validate: unknown }\n ? never\n : TailorField<CurrentDefined, Output>,\n ...validate: FieldValidateInput<Output>[]\n ): TailorField<Prettify<CurrentDefined & { validate: true }>, Output>;\n\n /**\n * Parse and validate a value against this field's validation rules\n * Returns StandardSchema Result type with success or failure\n * @param args - Value, context data, and user\n * @returns Validation result\n */\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n\n /**\n * Internal parse method that tracks field path for nested validation\n * @private\n * @param args - Parse arguments\n * @returns Validation result\n */\n _parseInternal(args: FieldParseInternalArgs): StandardSchemaV1.Result<Output>;\n}\n\n/**\n * Internal shape carried by every runtime field for clone-on-write support.\n *\n * `clone()` is intentionally kept off the public {@link TailorField} interface:\n * adding it there would force `TailorDBField` (which has a differently-typed\n * `clone`) to stop being assignable to `TailorField`, breaking the supported\n * `t.object({ field: db.string() })` usage. Every `t.*` and `db.*` field carries\n * a `clone()` at runtime, so the internal cast in `clone()` is safe.\n */\ntype CloneableField = { clone(): TailorAnyField };\n\nconst regex = {\n uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n date: /^(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})$/,\n time: /^(?<hour>\\d{2}):(?<minute>\\d{2})$/,\n datetime:\n /^(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})T(?<hour>\\d{2}):(?<minute>\\d{2}):(?<second>\\d{2})(.(?<millisec>\\d{3}))?Z$/,\n decimal: /^-?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$/,\n} as const;\n\ntype FieldParseArgs = {\n value: unknown;\n data: unknown;\n user: TailorUser;\n};\n\ntype FieldValidateValueArgs<T extends TailorFieldType> = {\n value: TailorToTs[T];\n data: unknown;\n user: TailorUser;\n pathArray: string[];\n};\n\ntype FieldParseInternalArgs = {\n // Runtime input is unknown/untyped; we validate and narrow it inside the parser.\n // oxlint-disable-next-line no-explicit-any\n value: any;\n data: unknown;\n user: TailorUser;\n pathArray: string[];\n};\n\n/**\n * Creates a new TailorField instance.\n * @param type - Field type\n * @param options - Field options\n * @param fields - Nested fields for object-like types\n * @param values - Allowed values for enum-like fields\n * @param metadata - Pre-built metadata to clone from (used by `clone()`); when\n * given, the mutable containers are deep-copied here and `options`/`values` are\n * ignored for metadata construction\n * @returns A new TailorField\n */\nfunction createTailorField<\n const T extends TailorFieldType,\n const TOptions extends FieldOptions,\n const OutputBase = TailorToTs[T],\n>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyField>,\n values?: AllowedValues,\n metadata?: FieldMetadata,\n): TailorField<\n { type: T; array: TOptions extends { array: true } ? true : false },\n FieldOutput<OutputBase, TOptions>\n> {\n // When cloning, take ownership of the source metadata and deep-copy its mutable\n // containers (enum value objects and `[fn, message]` validator tuples; validator\n // functions are kept by reference) so no two instances share mutable state.\n const _metadata: FieldMetadata = metadata\n ? {\n ...metadata,\n ...(metadata.allowedValues && {\n allowedValues: metadata.allowedValues.map((v) => ({ ...v })),\n }),\n ...(metadata.validate && {\n validate: metadata.validate.map((v) => (Array.isArray(v) ? ([...v] as typeof v) : v)),\n }),\n }\n : { required: true };\n\n if (!metadata) {\n if (options) {\n if (options.optional === true) {\n _metadata.required = false;\n }\n if (options.array === true) {\n _metadata.array = true;\n }\n }\n if (values) {\n _metadata.allowedValues = mapAllowedValues(values);\n }\n }\n\n /**\n * Validate a single value (not an array element)\n * Used internally for array element validation\n * @param args - Value, context data, and user\n * @returns Array of validation issues\n */\n function validateValue(args: FieldValidateValueArgs<T>): StandardSchemaV1.Issue[] {\n const { value, data, user, pathArray } = args;\n const issues: StandardSchemaV1.Issue[] = [];\n const path = pathArray.length > 0 ? pathArray : undefined;\n\n // Type-specific validation\n switch (type) {\n case \"string\":\n if (typeof value !== \"string\") {\n issues.push({\n message: `Expected a string: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"integer\":\n if (typeof value !== \"number\" || !Number.isInteger(value)) {\n issues.push({\n message: `Expected an integer: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"float\":\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n issues.push({\n message: `Expected a number: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n issues.push({\n message: `Expected a boolean: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"uuid\":\n if (typeof value !== \"string\" || !regex.uuid.test(value)) {\n issues.push({\n message: `Expected a valid UUID: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"date\":\n if (typeof value !== \"string\" || !regex.date.test(value)) {\n issues.push({\n message: `Expected to match \"yyyy-MM-dd\" format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"datetime\":\n if (typeof value !== \"string\" || !regex.datetime.test(value)) {\n issues.push({\n message: `Expected to match ISO format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"time\":\n if (typeof value !== \"string\" || !regex.time.test(value)) {\n issues.push({\n message: `Expected to match \"HH:mm\" format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"decimal\":\n if (typeof value !== \"string\" || !regex.decimal.test(value)) {\n issues.push({\n message: `Expected a decimal string: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"enum\":\n if (field._metadata.allowedValues) {\n const allowedValues = field._metadata.allowedValues.map((v) => v.value);\n if (typeof value !== \"string\" || !allowedValues.includes(value)) {\n issues.push({\n message: `Must be one of [${allowedValues.join(\", \")}]: received ${String(value)}`,\n path,\n });\n }\n }\n break;\n\n case \"nested\":\n // Validate nested object fields\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value) ||\n value instanceof Date\n ) {\n issues.push({\n message: `Expected an object: received ${String(value)}`,\n path,\n });\n } else if (field.fields && Object.keys(field.fields).length > 0) {\n for (const [fieldName, nestedField] of Object.entries(field.fields)) {\n const fieldValue = value?.[fieldName];\n const result = nestedField._parseInternal({\n value: fieldValue,\n data,\n user,\n pathArray: pathArray.concat(fieldName),\n });\n if (result.issues) {\n issues.push(...result.issues);\n }\n }\n }\n break;\n }\n\n // Custom validation functions\n const validateFns = field._metadata.validate;\n if (validateFns && validateFns.length > 0) {\n for (const validateInput of validateFns) {\n const { fn, message } =\n typeof validateInput === \"function\"\n ? { fn: validateInput, message: \"Validation failed\" }\n : { fn: validateInput[0], message: validateInput[1] };\n\n if (!fn({ value, data, user })) {\n issues.push({\n message,\n path,\n });\n }\n }\n }\n\n return issues;\n }\n\n /**\n * Internal parse method that tracks field path for nested validation\n * @param args - Parse arguments\n * @returns Parse result with value or issues\n */\n function parseInternal(\n args: FieldParseInternalArgs,\n ): StandardSchemaV1.Result<FieldOutput<OutputBase, TOptions>> {\n const { value, data, user, pathArray } = args;\n const issues: StandardSchemaV1.Issue[] = [];\n const path = pathArray.length > 0 ? pathArray : undefined;\n\n // 1. Check required/optional\n const isNullOrUndefined = value === null || value === undefined;\n if (field._metadata.required && isNullOrUndefined) {\n issues.push({\n message: \"Required field is missing\",\n path,\n });\n return { issues };\n }\n\n // If optional and null/undefined, skip further validation and normalize to null\n if (!field._metadata.required && isNullOrUndefined) {\n return { value: value ?? null };\n }\n\n // 2. Check array type\n if (field._metadata.array) {\n if (!Array.isArray(value)) {\n issues.push({\n message: \"Expected an array\",\n path,\n });\n return { issues };\n }\n\n // Validate each array element (without array flag)\n for (let i = 0; i < value.length; i++) {\n const elementValue = value[i];\n const elementPath = pathArray.concat(`[${i}]`);\n\n // Validate element with same type but without array flag\n const elementIssues = validateValue({\n value: elementValue,\n data,\n user,\n pathArray: elementPath,\n });\n if (elementIssues.length > 0) {\n issues.push(...elementIssues);\n }\n }\n\n if (issues.length > 0) {\n return { issues };\n }\n return { value: value as FieldOutput<OutputBase, TOptions> };\n }\n\n // 3. Type-specific validation and custom validation\n const valueIssues = validateValue({ value, data, user, pathArray });\n issues.push(...valueIssues);\n\n if (issues.length > 0) {\n return { issues };\n }\n\n return { value };\n }\n\n /**\n * Clone the field and apply metadata updates to the clone.\n * The original instance is never mutated, so a field shared across places\n * cannot leak metadata between them.\n * @param metadataUpdates - Metadata properties to overwrite on the clone\n * @returns A new field with the updated metadata\n */\n function cloneWith(metadataUpdates: Partial<FieldMetadata>) {\n const cloned = field.clone();\n Object.assign(cloned._metadata, metadataUpdates);\n return cloned;\n }\n\n const field: TailorField<\n { type: T; array: TOptions extends { array: true } ? true : false },\n FieldOutput<OutputBase, TOptions>\n > &\n CloneableField = {\n type,\n fields: fields ?? {},\n _defined: undefined as unknown as {\n type: T;\n array: TOptions extends { array: true } ? true : false;\n },\n _output: undefined as FieldOutput<OutputBase, TOptions>,\n _metadata,\n\n get metadata() {\n return { ...this._metadata };\n },\n\n description(description: string) {\n // Clone-on-write so a shared field instance never leaks metadata.\n // oxlint-disable-next-line no-explicit-any\n return cloneWith({ description }) as any;\n },\n\n typeName(typeName: string) {\n // Clone-on-write so a shared field instance never leaks metadata.\n // oxlint-disable-next-line no-explicit-any\n return cloneWith({ typeName }) as any;\n },\n\n validate(...validateInputs: FieldValidateInput<FieldOutput<OutputBase, TOptions>>[]) {\n // Clone-on-write so a shared field instance never leaks metadata.\n // oxlint-disable-next-line no-explicit-any\n return cloneWith({ validate: validateInputs }) as any;\n },\n\n parse(args: FieldParseArgs): StandardSchemaV1.Result<FieldOutput<OutputBase, TOptions>> {\n return parseInternal({\n value: args.value,\n data: args.data,\n user: args.user,\n pathArray: [],\n });\n },\n\n _parseInternal: parseInternal,\n\n clone() {\n // Deep clone nested object fields so the new instance shares no mutable state.\n let clonedFields = fields;\n if (fields) {\n const cloned: Record<string, TailorAnyField> = {};\n for (const [key, nestedField] of Object.entries(fields)) {\n // Both t.* and db.* fields carry clone() at runtime (see CloneableField).\n cloned[key] = (nestedField as TailorAnyField & CloneableField).clone();\n }\n clonedFields = cloned;\n }\n\n // Rebuild via the factory, handing it this field's metadata so the new\n // parseInternal/validateValue closures rebind to the clone and the factory\n // owns the metadata deep-copy.\n // oxlint-disable-next-line no-explicit-any\n return createTailorField(type, options, clonedFields, values, this._metadata) as any;\n },\n };\n\n return field;\n}\n\n/**\n * Create a UUID field for resolver input/output.\n * @param options - Field configuration options\n * @returns A UUID field\n * @example t.uuid()\n */\nfunction uuid<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"uuid\", options);\n}\n\n/**\n * Create a string field for resolver input/output.\n * @param options - Field configuration options\n * @returns A string field\n * @example t.string()\n * @example t.string({ optional: true })\n * @example t.string({ array: true })\n */\nfunction string<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"string\", options);\n}\n\n/**\n * Create a boolean field for resolver input/output.\n * @param options - Field configuration options\n * @returns A boolean field\n * @example t.bool()\n */\nfunction bool<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"boolean\", options);\n}\n\n/**\n * Create an integer field for resolver input/output.\n * @param options - Field configuration options\n * @returns An integer field\n * @example t.int()\n */\nfunction int<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"integer\", options);\n}\n\n/**\n * Create a float field for resolver input/output.\n * @param options - Field configuration options\n * @returns A float field\n * @example t.float()\n */\nfunction float<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"float\", options);\n}\n\n/**\n * Create a decimal field for resolver input/output (stored as string for precision).\n * @param options - Field configuration options\n * @returns A decimal field\n * @example t.decimal()\n * @example t.decimal({ optional: true })\n */\nfunction decimal<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"decimal\", options);\n}\n\n/**\n * Create a date field for resolver input/output.\n * @param options - Field configuration options\n * @returns A date field\n * @example t.date()\n */\nfunction date<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"date\", options);\n}\n\n/**\n * Create a datetime field for resolver input/output.\n * @param options - Field configuration options\n * @returns A datetime field\n * @example t.datetime()\n */\nfunction datetime<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"datetime\", options);\n}\n\n/**\n * Create a time field for resolver input/output.\n * @param options - Field configuration options\n * @returns A time field\n * @example t.time()\n */\nfunction time<const Opt extends FieldOptions>(options?: Opt) {\n return createTailorField(\"time\", options);\n}\n\n/**\n * Create an enum field for resolver input/output.\n * @param values - Array of allowed string values\n * @param options - Field configuration options\n * @returns An enum field\n * @example t.enum([\"active\", \"inactive\"])\n */\nfunction _enum<const V extends AllowedValues, const Opt extends FieldOptions>(\n values: V,\n options?: Opt,\n): TailorField<\n { type: \"enum\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<AllowedValuesOutput<V>, Opt>\n> {\n return createTailorField<\"enum\", Opt, AllowedValuesOutput<V>>(\"enum\", options, undefined, values);\n}\n\n/**\n * Create a nested object field for resolver input/output.\n * @param fields - Record of field definitions\n * @param options - Field options (optional, array)\n * @returns A nested object field\n * @example\n * // Single object:\n * output: t.object({ name: t.string(), email: t.string() })\n * @example\n * // Array of objects:\n * items: t.object({ name: t.string() }, { array: true })\n */\nfunction object<const F extends Record<string, TailorAnyField>, const Opt extends FieldOptions>(\n fields: F,\n options?: Opt,\n) {\n const objectField = createTailorField(\"nested\", options, fields) as TailorField<\n { type: \"nested\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<InferFieldsOutput<F>, Opt>\n >;\n return objectField;\n}\n\nexport const t = {\n uuid,\n string,\n bool,\n int,\n float,\n decimal,\n date,\n datetime,\n time,\n enum: _enum,\n object,\n};\n"],"mappings":";;;;AAoGA,MAAM,QAAQ;CACZ,MAAM;CACN,MAAM;CACN,MAAM;CACN,UACE;CACF,SAAS;AACX;;;;;;;;;;;;AAmCA,SAAS,kBAKP,MACA,SACA,QACA,QACA,UAIA;CAIA,MAAM,YAA2B,WAC7B;EACE,GAAG;EACH,GAAI,SAAS,iBAAiB,EAC5B,eAAe,SAAS,cAAc,KAAK,OAAO,EAAE,GAAG,EAAE,EAAE,EAC7D;EACA,GAAI,SAAS,YAAY,EACvB,UAAU,SAAS,SAAS,KAAK,MAAO,MAAM,QAAQ,CAAC,IAAK,CAAC,GAAG,CAAC,IAAiB,CAAE,EACtF;CACF,IACA,EAAE,UAAU,KAAK;CAErB,IAAI,CAAC,UAAU;EACb,IAAI,SAAS;GACX,IAAI,QAAQ,aAAa,MACvB,UAAU,WAAW;GAEvB,IAAI,QAAQ,UAAU,MACpB,UAAU,QAAQ;EAEtB;EACA,IAAI,QACF,UAAU,gBAAgB,iBAAiB,MAAM;CAErD;;;;;;;CAQA,SAAS,cAAc,MAA2D;EAChF,MAAM,EAAE,OAAO,MAAM,MAAM,cAAc;EACzC,MAAM,SAAmC,CAAC;EAC1C,MAAM,OAAO,UAAU,SAAS,IAAI,YAAY;EAGhD,QAAQ,MAAR;GACE,KAAK;IACH,IAAI,OAAO,UAAU,UACnB,OAAO,KAAK;KACV,SAAS,+BAA+B,OAAO,KAAK;KACpD;IACF,CAAC;IAEH;GAEF,KAAK;IACH,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,UAAU,KAAK,GACtD,OAAO,KAAK;KACV,SAAS,iCAAiC,OAAO,KAAK;KACtD;IACF,CAAC;IAEH;GAEF,KAAK;IACH,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GACrD,OAAO,KAAK;KACV,SAAS,+BAA+B,OAAO,KAAK;KACpD;IACF,CAAC;IAEH;GAEF,KAAK;IACH,IAAI,OAAO,UAAU,WACnB,OAAO,KAAK;KACV,SAAS,gCAAgC,OAAO,KAAK;KACrD;IACF,CAAC;IAEH;GAEF,KAAK;IACH,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,KAAK,KAAK,GACrD,OAAO,KAAK;KACV,SAAS,mCAAmC,OAAO,KAAK;KACxD;IACF,CAAC;IAEH;GACF,KAAK;IACH,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,KAAK,KAAK,GACrD,OAAO,KAAK;KACV,SAAS,mDAAmD,OAAO,KAAK;KACxE;IACF,CAAC;IAEH;GACF,KAAK;IACH,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,SAAS,KAAK,KAAK,GACzD,OAAO,KAAK;KACV,SAAS,0CAA0C,OAAO,KAAK;KAC/D;IACF,CAAC;IAEH;GACF,KAAK;IACH,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,KAAK,KAAK,GACrD,OAAO,KAAK;KACV,SAAS,8CAA8C,OAAO,KAAK;KACnE;IACF,CAAC;IAEH;GACF,KAAK;IACH,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,KAAK,GACxD,OAAO,KAAK;KACV,SAAS,uCAAuC,OAAO,KAAK;KAC5D;IACF,CAAC;IAEH;GAEF,KAAK;IACH,IAAI,MAAM,UAAU,eAAe;KACjC,MAAM,gBAAgB,MAAM,UAAU,cAAc,KAAK,MAAM,EAAE,KAAK;KACtE,IAAI,OAAO,UAAU,YAAY,CAAC,cAAc,SAAS,KAAK,GAC5D,OAAO,KAAK;MACV,SAAS,mBAAmB,cAAc,KAAK,IAAI,EAAE,cAAc,OAAO,KAAK;MAC/E;KACF,CAAC;IAEL;IACA;GAEF,KAAK;IAEH,IACE,OAAO,UAAU,YACjB,UAAU,QACV,MAAM,QAAQ,KAAK,KACnB,iBAAiB,MAEjB,OAAO,KAAK;KACV,SAAS,gCAAgC,OAAO,KAAK;KACrD;IACF,CAAC;SACI,IAAI,MAAM,UAAU,OAAO,KAAK,MAAM,MAAM,EAAE,SAAS,GAC5D,KAAK,MAAM,CAAC,WAAW,gBAAgB,OAAO,QAAQ,MAAM,MAAM,GAAG;KACnE,MAAM,aAAa,QAAQ;KAC3B,MAAM,SAAS,YAAY,eAAe;MACxC,OAAO;MACP;MACA;MACA,WAAW,UAAU,OAAO,SAAS;KACvC,CAAC;KACD,IAAI,OAAO,QACT,OAAO,KAAK,GAAG,OAAO,MAAM;IAEhC;IAEF;EACJ;EAGA,MAAM,cAAc,MAAM,UAAU;EACpC,IAAI,eAAe,YAAY,SAAS,GACtC,KAAK,MAAM,iBAAiB,aAAa;GACvC,MAAM,EAAE,IAAI,YACV,OAAO,kBAAkB,aACrB;IAAE,IAAI;IAAe,SAAS;GAAoB,IAClD;IAAE,IAAI,cAAc;IAAI,SAAS,cAAc;GAAG;GAExD,IAAI,CAAC,GAAG;IAAE;IAAO;IAAM;GAAK,CAAC,GAC3B,OAAO,KAAK;IACV;IACA;GACF,CAAC;EAEL;EAGF,OAAO;CACT;;;;;;CAOA,SAAS,cACP,MAC4D;EAC5D,MAAM,EAAE,OAAO,MAAM,MAAM,cAAc;EACzC,MAAM,SAAmC,CAAC;EAC1C,MAAM,OAAO,UAAU,SAAS,IAAI,YAAY;EAGhD,MAAM,oBAAoB,UAAU,QAAQ,UAAU;EACtD,IAAI,MAAM,UAAU,YAAY,mBAAmB;GACjD,OAAO,KAAK;IACV,SAAS;IACT;GACF,CAAC;GACD,OAAO,EAAE,OAAO;EAClB;EAGA,IAAI,CAAC,MAAM,UAAU,YAAY,mBAC/B,OAAO,EAAE,OAAO,SAAS,KAAK;EAIhC,IAAI,MAAM,UAAU,OAAO;GACzB,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;IACzB,OAAO,KAAK;KACV,SAAS;KACT;IACF,CAAC;IACD,OAAO,EAAE,OAAO;GAClB;GAGA,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,MAAM,eAAe,MAAM;IAI3B,MAAM,gBAAgB,cAAc;KAClC,OAAO;KACP;KACA;KACA,WAPkB,UAAU,OAAO,IAAI,EAAE,EAOpB;IACvB,CAAC;IACD,IAAI,cAAc,SAAS,GACzB,OAAO,KAAK,GAAG,aAAa;GAEhC;GAEA,IAAI,OAAO,SAAS,GAClB,OAAO,EAAE,OAAO;GAElB,OAAO,EAAS,MAA2C;EAC7D;EAGA,MAAM,cAAc,cAAc;GAAE;GAAO;GAAM;GAAM;EAAU,CAAC;EAClE,OAAO,KAAK,GAAG,WAAW;EAE1B,IAAI,OAAO,SAAS,GAClB,OAAO,EAAE,OAAO;EAGlB,OAAO,EAAE,MAAM;CACjB;;;;;;;;CASA,SAAS,UAAU,iBAAyC;EAC1D,MAAM,SAAS,MAAM,MAAM;EAC3B,OAAO,OAAO,OAAO,WAAW,eAAe;EAC/C,OAAO;CACT;CAEA,MAAM,QAIa;EACjB;EACA,QAAQ,UAAU,CAAC;EACnB,UAAU;EAIV,SAAS;EACT;EAEA,IAAI,WAAW;GACb,OAAO,EAAE,GAAG,KAAK,UAAU;EAC7B;EAEA,YAAY,aAAqB;GAG/B,OAAO,UAAU,EAAE,YAAY,CAAC;EAClC;EAEA,SAAS,UAAkB;GAGzB,OAAO,UAAU,EAAE,SAAS,CAAC;EAC/B;EAEA,SAAS,GAAG,gBAAyE;GAGnF,OAAO,UAAU,EAAE,UAAU,eAAe,CAAC;EAC/C;EAEA,MAAM,MAAkF;GACtF,OAAO,cAAc;IACnB,OAAO,KAAK;IACZ,MAAM,KAAK;IACX,MAAM,KAAK;IACX,WAAW,CAAC;GACd,CAAC;EACH;EAEA,gBAAgB;EAEhB,QAAQ;GAEN,IAAI,eAAe;GACnB,IAAI,QAAQ;IACV,MAAM,SAAyC,CAAC;IAChD,KAAK,MAAM,CAAC,KAAK,gBAAgB,OAAO,QAAQ,MAAM,GAEpD,OAAO,OAAQ,YAAgD,MAAM;IAEvE,eAAe;GACjB;GAMA,OAAO,kBAAkB,MAAM,SAAS,cAAc,QAAQ,KAAK,SAAS;EAC9E;CACF;CAEA,OAAO;AACT;;;;;;;AAQA,SAAS,KAAqC,SAAe;CAC3D,OAAO,kBAAkB,QAAQ,OAAO;AAC1C;;;;;;;;;AAUA,SAAS,OAAuC,SAAe;CAC7D,OAAO,kBAAkB,UAAU,OAAO;AAC5C;;;;;;;AAQA,SAAS,KAAqC,SAAe;CAC3D,OAAO,kBAAkB,WAAW,OAAO;AAC7C;;;;;;;AAQA,SAAS,IAAoC,SAAe;CAC1D,OAAO,kBAAkB,WAAW,OAAO;AAC7C;;;;;;;AAQA,SAAS,MAAsC,SAAe;CAC5D,OAAO,kBAAkB,SAAS,OAAO;AAC3C;;;;;;;;AASA,SAAS,QAAwC,SAAe;CAC9D,OAAO,kBAAkB,WAAW,OAAO;AAC7C;;;;;;;AAQA,SAAS,KAAqC,SAAe;CAC3D,OAAO,kBAAkB,QAAQ,OAAO;AAC1C;;;;;;;AAQA,SAAS,SAAyC,SAAe;CAC/D,OAAO,kBAAkB,YAAY,OAAO;AAC9C;;;;;;;AAQA,SAAS,KAAqC,SAAe;CAC3D,OAAO,kBAAkB,QAAQ,OAAO;AAC1C;;;;;;;;AASA,SAAS,MACP,QACA,SAIA;CACA,OAAO,kBAAuD,QAAQ,SAAS,QAAW,MAAM;AAClG;;;;;;;;;;;;;AAcA,SAAS,OACP,QACA,SACA;CAKA,OAJoB,kBAAkB,UAAU,SAAS,MAIxC;AACnB;AAEA,MAAa,IAAI;CACf;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,MAAM;CACN;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { M as TailorInvoker } from "../../tailordb-BlBGmQK-.mjs";
|
|
2
|
-
import { W as TailorDBType } from "../../workflow.generated-
|
|
3
|
-
import { At as TailorField, bt as WORKFLOW_TEST_ENV_KEY, n as output } from "../../index-
|
|
2
|
+
import { W as TailorDBType } from "../../workflow.generated-Kz-nQrTf.mjs";
|
|
3
|
+
import { At as TailorField, bt as WORKFLOW_TEST_ENV_KEY, n as output } from "../../index-B61gFI9a.mjs";
|
|
4
4
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
5
|
|
|
6
6
|
//#region src/utils/test/mock.d.ts
|
|
@@ -36,12 +36,21 @@ type TailorTypePermission<User extends object = InferredAttributeMap, Type exten
|
|
|
36
36
|
type ActionPermission<Level extends "record" | "gql" = "record" | "gql", User extends object = InferredAttributeMap, Type extends object = object, Update extends boolean = boolean> = {
|
|
37
37
|
conditions: PermissionCondition<Level, User, Update, Type> | readonly PermissionCondition<Level, User, Update, Type>[];
|
|
38
38
|
description?: string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Whether matching records are granted (`true`) or denied (`false`).
|
|
41
|
+
* Omitting `permit` in this object form defaults to `deny` and emits a
|
|
42
|
+
* warning; set it explicitly. (The array shorthand defaults to `allow`.)
|
|
43
|
+
*/
|
|
39
44
|
permit?: boolean;
|
|
40
45
|
} | readonly [...PermissionCondition<Level, User, Update, Type>, ...([] | [boolean])] | readonly [...PermissionCondition<Level, User, Update, Type>[], ...([] | [boolean])];
|
|
41
46
|
type TailorTypeGqlPermission<User extends object = InferredAttributeMap, Type extends object = object> = readonly GqlPermissionPolicy<User, Type>[];
|
|
42
47
|
type GqlPermissionPolicy<User extends object = InferredAttributeMap, Type extends object = object> = {
|
|
43
48
|
conditions: readonly PermissionCondition<"gql", User, boolean, Type>[];
|
|
44
49
|
actions: "all" | readonly GqlPermissionAction[];
|
|
50
|
+
/**
|
|
51
|
+
* Whether matching requests are granted (`true`) or denied (`false`).
|
|
52
|
+
* Omitting `permit` defaults to `deny` and emits a warning; set it explicitly.
|
|
53
|
+
*/
|
|
45
54
|
permit?: boolean;
|
|
46
55
|
description?: string;
|
|
47
56
|
};
|
|
@@ -739,6 +748,9 @@ type AuthServiceInputLoose = AuthServiceInput<any, any, any, string, any>;
|
|
|
739
748
|
type AuthOwnConfig = DefinedAuth<string, AuthServiceInputLoose, string>;
|
|
740
749
|
type AuthConfig = AuthOwnConfig | AuthExternalConfig;
|
|
741
750
|
//#endregion
|
|
751
|
+
//#region src/types/app-config.generated.d.ts
|
|
752
|
+
type LogLevelEnum = "DEBUG" | "INFO" | "WARN" | "ERROR" | "SILENT";
|
|
753
|
+
//#endregion
|
|
742
754
|
//#region src/types/idp.generated.d.ts
|
|
743
755
|
/**
|
|
744
756
|
* Configuration for GraphQL operations on IdP users.
|
|
@@ -1282,6 +1294,8 @@ type StaticWebsiteDefinitionBrand = {
|
|
|
1282
1294
|
type StaticWebsiteConfig = StaticWebsiteInput & StaticWebsiteDefinitionBrand;
|
|
1283
1295
|
//#endregion
|
|
1284
1296
|
//#region src/types/app-config.d.ts
|
|
1297
|
+
type LogLevel = LogLevelEnum;
|
|
1298
|
+
type LogLevelInput = LogLevel | (string & {});
|
|
1285
1299
|
type ExecutorServiceConfig = {
|
|
1286
1300
|
files: string[];
|
|
1287
1301
|
ignores?: string[];
|
|
@@ -1363,6 +1377,11 @@ interface AppConfig<Auth extends AuthConfig = AuthConfig, Idp extends IdPConfig[
|
|
|
1363
1377
|
* @default true
|
|
1364
1378
|
*/
|
|
1365
1379
|
inlineSourcemap?: boolean;
|
|
1380
|
+
/**
|
|
1381
|
+
* Controls which `console.*` calls remain in bundled functions.
|
|
1382
|
+
* @default "DEBUG"
|
|
1383
|
+
*/
|
|
1384
|
+
logLevel?: LogLevelInput;
|
|
1366
1385
|
}
|
|
1367
1386
|
//#endregion
|
|
1368
1387
|
//#region src/types/http-adapter.generated.d.ts
|
|
@@ -1393,4 +1412,4 @@ type ConcurrencyPolicy = {
|
|
|
1393
1412
|
};
|
|
1394
1413
|
//#endregion
|
|
1395
1414
|
export { BeforeLoginHookArgs as A, TailorAnyDBField as B, IdPGqlOperationsInput as C, AuthExternalConfig as D, AuthConnectionTokenResult as E, UserAttributeListKey as F, db as G, TailorDBField as H, UserAttributeMap as I, TailorTypePermission as J, PermissionCondition as K, UsernameFieldKey as L, OAuth2ClientGrantType as M, SCIMAttributeType as N, AuthOwnConfig as O, UserAttributeKey as P, AllowedValuesOutput as Q, AuthConnectionConfig as R, IdPGqlOperations as S, AuthConfig as T, TailorDBInstance as U, TailorAnyDBType as V, TailorDBType as W, unsafeAllowAllTypePermission as X, unsafeAllowAllGqlPermission as Y, AllowedValues as Z, IdPConfig as _, ExecutorServiceConfig as a, IdpDefinitionBrand as b, ResolverServiceConfig as c, WorkflowServiceInput as d, StaticWebsiteConfig as f, SecretsDefinitionBrand as g, SecretsConfig as h, AppConfig as i, DefinedAuth as j, AuthServiceInput as k, ResolverServiceInput as l, StaticWebsiteInput as m, RetryPolicy as n, ExecutorServiceInput as o, StaticWebsiteDefinitionBrand as p, TailorTypeGqlPermission as q, HttpAdapterConfigInput as r, ResolverExternalConfig as s, ConcurrencyPolicy as t, WorkflowServiceConfig as u, IdPExternalConfig as v, IdPInput as w, IdPEmailConfig as x, IdPUserField as y, AuthConnectionOAuth2Config as z };
|
|
1396
|
-
//# sourceMappingURL=workflow.generated-
|
|
1415
|
+
//# sourceMappingURL=workflow.generated-Kz-nQrTf.d.mts.map
|
package/docs/configuration.md
CHANGED
|
@@ -25,6 +25,7 @@ export default defineConfig({
|
|
|
25
25
|
cors: ["https://example.com"],
|
|
26
26
|
allowedIpAddresses: ["192.168.1.0/24"],
|
|
27
27
|
disableIntrospection: false,
|
|
28
|
+
logLevel: process.env.LOG_LEVEL ?? "DEBUG",
|
|
28
29
|
});
|
|
29
30
|
```
|
|
30
31
|
|
|
@@ -38,6 +39,17 @@ export default defineConfig({
|
|
|
38
39
|
|
|
39
40
|
**Disable Introspection**: Disable GraphQL introspection. Default is `false`.
|
|
40
41
|
|
|
42
|
+
**Log Level**: Controls which `console.*` calls are kept when deployment functions are bundled. Supported values are `"DEBUG"`, `"INFO"`, `"WARN"`, `"ERROR"`, and `"SILENT"`. The default is `"DEBUG"` and keeps all console calls. For production deployments, use `"WARN"` to keep `console.warn` and `console.error` while dropping debug, log, and info calls:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
export default defineConfig({
|
|
46
|
+
name: "my-app",
|
|
47
|
+
logLevel: process.env.LOG_LEVEL ?? "DEBUG",
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This is a bundle-time setting. Changing `LOG_LEVEL` affects newly bundled deployments; already deployed functions must be redeployed.
|
|
52
|
+
|
|
41
53
|
### Service Configuration
|
|
42
54
|
|
|
43
55
|
Specify glob patterns to load service files:
|
package/package.json
CHANGED