@tailor-platform/sdk 2.17.0 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/{application-ChuNPwnl.mjs → application-BYlhYQyh.mjs} +14 -14
  3. package/dist/application-BYlhYQyh.mjs.map +1 -0
  4. package/dist/application-KZRJHv2j.mjs +1 -0
  5. package/dist/cli/commands/deploy/event-subscriptions.d.mts +1 -0
  6. package/dist/cli/commands/generate/service.d.mts +1 -0
  7. package/dist/cli/lib.d.mts +3 -2
  8. package/dist/cli/lib.mjs +1 -1
  9. package/dist/cli/lib.mjs.map +1 -1
  10. package/dist/cli/main.mjs +28 -28
  11. package/dist/cli/main.mjs.map +1 -1
  12. package/dist/cli/services/executor/service.d.mts +1 -0
  13. package/dist/cli/shared/error-diagnostics.d.mts +10 -0
  14. package/dist/cli/shared/github-actions.d.mts +11 -0
  15. package/dist/completion/zsh-worker.zsh +1 -1
  16. package/dist/configure/index.d.mts +7 -7
  17. package/dist/configure/index.mjs +1 -1
  18. package/dist/configure/index.mjs.map +1 -1
  19. package/dist/configure/types/field.types.d.mts +24 -4
  20. package/dist/configure/types/type.d.mts +1 -0
  21. package/dist/date-B7-oyTGE.mjs +2 -0
  22. package/dist/date-B7-oyTGE.mjs.map +1 -0
  23. package/dist/field-parse-BbNyotv5.mjs +2 -0
  24. package/dist/field-parse-BbNyotv5.mjs.map +1 -0
  25. package/dist/{manager-C26Gi1bX.mjs → manager-qfbUL7ru.mjs} +2 -2
  26. package/dist/{manager-C26Gi1bX.mjs.map → manager-qfbUL7ru.mjs.map} +1 -1
  27. package/dist/parser/service/auth/schema.d.mts +1 -0
  28. package/dist/plugin/index.mjs +1 -1
  29. package/dist/{register-ts-hook-Dn-XlVSD.mjs → register-ts-hook-Djjnj5R8.mjs} +52 -52
  30. package/dist/register-ts-hook-Djjnj5R8.mjs.map +1 -0
  31. package/dist/runtime/date.d.mts +3 -3
  32. package/dist/runtime/field-parse.d.mts +1 -0
  33. package/dist/runtime/index.d.mts +2 -1
  34. package/dist/runtime/index.mjs +1 -1
  35. package/dist/runtime/temporal.d.mts +31 -0
  36. package/dist/{schema-DRyQEabV.mjs → schema-BKvcWdVf.mjs} +2 -2
  37. package/dist/{schema-DRyQEabV.mjs.map → schema-BKvcWdVf.mjs.map} +1 -1
  38. package/dist/seed/index.d.mts +5 -0
  39. package/dist/seed/index.mjs +3 -2
  40. package/dist/seed/index.mjs.map +1 -1
  41. package/dist/service-BgrEteZ2.mjs +1 -0
  42. package/dist/service-CuqkHP1r.mjs +7 -0
  43. package/dist/service-CuqkHP1r.mjs.map +1 -0
  44. package/dist/types/field.generated.d.mts +1 -1
  45. package/dist/types/helpers.d.mts +8 -6
  46. package/dist/utils/test/index.mjs +1 -1
  47. package/dist/vitest/environment.mjs +1 -1
  48. package/dist/vitest/environment.mjs.map +1 -1
  49. package/docs/cli-reference.md +11 -2
  50. package/docs/github-actions.md +22 -15
  51. package/docs/migration/v3.md +0 -33
  52. package/docs/services/resolver.md +53 -1
  53. package/docs/services/tailordb-migration.md +4 -0
  54. package/docs/testing.md +17 -0
  55. package/package.json +12 -10
  56. package/dist/application-ChuNPwnl.mjs.map +0 -1
  57. package/dist/application-Oh5dMmb5.mjs +0 -1
  58. package/dist/date-CGBZbMW5.mjs +0 -2
  59. package/dist/date-CGBZbMW5.mjs.map +0 -1
  60. package/dist/field-parse-CzlKC4b7.mjs +0 -2
  61. package/dist/field-parse-CzlKC4b7.mjs.map +0 -1
  62. package/dist/register-ts-hook-Dn-XlVSD.mjs.map +0 -1
  63. package/dist/service-B3OiWCYg.mjs +0 -1
  64. package/dist/service-eM7Fd8zS.mjs +0 -7
  65. package/dist/service-eM7Fd8zS.mjs.map +0 -1
@@ -6,10 +6,10 @@ type DateField = {
6
6
  readonly fields: Record<string, DateField>;
7
7
  };
8
8
  /**
9
- * Convert fields using the Date representation to YYYY-MM-DD strings in UTC.
10
- * @param field - Date or object field defining the value's shape
9
+ * Convert fields using Date or Temporal representations to date, datetime, or time strings.
10
+ * @param field - Field defining the value's shape
11
11
  * @param value - Value to serialize
12
- * @returns Value with Date fields converted to strings
12
+ * @returns Value with Date and Temporal fields converted to strings
13
13
  * @internal
14
14
  */
15
15
  export declare function serializeDateFields(field: DateField, value: unknown): unknown;
@@ -1,3 +1,4 @@
1
+ import "../configure/types/field.types.mjs";
1
2
  import { TailorPrincipal } from "./types.mjs";
2
3
  import { StandardSchemaV1 } from "@standard-schema/spec";
3
4
  //#region src/runtime/field-parse.d.ts
@@ -7,6 +7,7 @@ import { TailorIconvAPI, iconv } from "./iconv.mjs";
7
7
  import { TailorIdpAPI, idp } from "./idp.mjs";
8
8
  import { TailorLoggerAPI, logger } from "./logger.mjs";
9
9
  import { TailorSecretmanagerAPI, secretmanager } from "./secretmanager.mjs";
10
+ import { Temporal } from "./temporal.mjs";
10
11
  import { serializeDateFields } from "./date.mjs";
11
12
  //#region src/runtime/index.d.ts
12
13
  /** SQL command type recorded on a {@link TailordbQueryResult}. */
@@ -46,4 +47,4 @@ export interface TailordbRuntime {
46
47
  file: TailorDBFileAPI;
47
48
  }
48
49
  //#endregion
49
- export { aigateway, authconnection, context, file, iconv, idp, logger, secretmanager, serializeDateFields, workflow };
50
+ export { Temporal, aigateway, authconnection, context, file, iconv, idp, logger, secretmanager, serializeDateFields, workflow };
@@ -1 +1 @@
1
- import{n as e}from"../date-CGBZbMW5.mjs";import{t}from"../iconv-DlFMt2gW.mjs";import{t as n}from"../secretmanager-vHQoXdQz.mjs";import{t as r}from"../authconnection-CynFBIv8.mjs";import{t as i}from"../idp-G_ojPBB5.mjs";import{t as a}from"../workflow-Cs9ISw6j.mjs";import{t as o}from"../context-D0QjfxzD.mjs";import{t as s}from"../file-DKBOj5q4.mjs";import{t as c}from"../aigateway-DsWDjzk4.mjs";import{i as l}from"../logger-DP2BjQ93.mjs";export{c as aigateway,r as authconnection,o as context,s as file,t as iconv,i as idp,l as logger,n as secretmanager,e as serializeDateFields,a as workflow};
1
+ import{n as e,r as t}from"../date-B7-oyTGE.mjs";import{t as n}from"../iconv-DlFMt2gW.mjs";import{t as r}from"../secretmanager-vHQoXdQz.mjs";import{t as i}from"../authconnection-CynFBIv8.mjs";import{t as a}from"../idp-G_ojPBB5.mjs";import{t as o}from"../workflow-Cs9ISw6j.mjs";import{t as s}from"../context-D0QjfxzD.mjs";import{t as c}from"../file-DKBOj5q4.mjs";import{t as l}from"../aigateway-DsWDjzk4.mjs";import{i as u}from"../logger-DP2BjQ93.mjs";export{t as Temporal,l as aigateway,i as authconnection,s as context,c as file,n as iconv,a as idp,u as logger,r as secretmanager,e as serializeDateFields,o as workflow};
@@ -0,0 +1,31 @@
1
+ import { Temporal } from "temporal-spec";
2
+ //#region src/runtime/temporal.d.ts
3
+ /**
4
+ * Typed access to the runtime's Temporal API, without installing a polyfill.
5
+ * Constructors are resolved on access so configuration files can load in Node.js.
6
+ * @example
7
+ * import { Temporal } from "@tailor-platform/sdk/runtime";
8
+ * const tomorrow: Temporal.PlainDate = Temporal.Now.plainDateISO().add({ days: 1 });
9
+ */
10
+ declare const Temporal$1: typeof Temporal;
11
+ /** Temporal instance types supplied by the SDK, independent of TypeScript's lib settings. */
12
+ declare namespace Temporal$1 {
13
+ /** The runtime Temporal.PlainDate instance. */
14
+ type PlainDate = Temporal.PlainDate;
15
+ /** The runtime Temporal.PlainTime instance. */
16
+ type PlainTime = Temporal.PlainTime;
17
+ /** The runtime Temporal.PlainDateTime instance. */
18
+ type PlainDateTime = Temporal.PlainDateTime;
19
+ /** The runtime Temporal.PlainYearMonth instance. */
20
+ type PlainYearMonth = Temporal.PlainYearMonth;
21
+ /** The runtime Temporal.PlainMonthDay instance. */
22
+ type PlainMonthDay = Temporal.PlainMonthDay;
23
+ /** The runtime Temporal.ZonedDateTime instance. */
24
+ type ZonedDateTime = Temporal.ZonedDateTime;
25
+ /** The runtime Temporal.Duration instance. */
26
+ type Duration = Temporal.Duration;
27
+ /** The runtime Temporal.Instant instance. */
28
+ type Instant = Temporal.Instant;
29
+ }
30
+ //#endregion
31
+ export { Temporal$1 as Temporal };
@@ -1,2 +1,2 @@
1
- import{r as e}from"./field-parse-CzlKC4b7.mjs";import{t}from"./brand-C8nMKhJC.mjs";import{cloneDeep as n}from"es-toolkit";function mapAllowedValues(e){return e.map(e=>typeof e==`string`?{value:e,description:``}:{...e,description:e.description??``})}function createTailorDBField(e,t,n,r){return createTailorDBFieldRuntime(e,t,n,r)}function createTailorDBFieldRuntime(t,r,i,a){let o={required:!0},s;r&&(r.optional===!0&&(o.required=!1),r.array===!0&&(o.array=!0)),a&&(o.allowedValues=mapAllowedValues(a));function parseInternal$1(t){return e({...t,field:c})}function cloneWith(e){let t=c.clone();return Object.assign(t._metadata,e),t}let c={type:t,fields:i??{},_defined:void 0,_output:void 0,_metadata:o,get metadata(){return{...this._metadata}},get rawRelation(){return s?{...s,toward:{...s.toward}}:void 0},description(e){return cloneWith({description:e})},typeName(e){return cloneWith({typeName:e})},validate(...e){return cloneWith({validate:e})},parse(e){return parseInternal$1({value:e.value,data:e.data,invoker:e.invoker,pathArray:[]})},relation(e){let t=c.clone(),n=e.toward,r=`table`in n?n.table:n.type,i=r===`self`?`self`:r.name;return t._setRawRelation({type:e.type,toward:{table:i,as:e.toward.as,key:e.toward.key},backward:e.backward}),t},index(){return cloneWith({index:!0})},unique(){return cloneWith({unique:!0,index:!0})},vector(){return cloneWith({vector:!0})},default(e){return cloneWith({default:e})},hooks(e){return cloneWith({hooks:e})},serial(e){return cloneWith({serial:e})},clone(e){if(this._metadata.validate?.length&&e?.array!==void 0&&e.array!==(this._metadata.array===!0))throw Error(`Cannot change the array option on a field with custom validation`);let o=i;if(i){let e={};for(let[t,n]of Object.entries(i))e[t]=n.clone();o=e}let c=createTailorDBFieldRuntime(t,r,o,a);return Object.assign(c._metadata,n(this._metadata)),e&&(e.optional!==void 0&&(c._metadata.required=!e.optional),e.array!==void 0&&(c._metadata.array=e.array)),s&&c._setRawRelation(n(s)),c},_setRawRelation(e){s=e}};return c}const r=createTailorDBField;function uuid(e){return r(`uuid`,e)}function string(e){return r(`string`,e)}function bool(e){return r(`boolean`,e)}function int(e){return r(`integer`,e)}function float(e){return r(`float`,e)}function decimal(e){if(e?.scale!==void 0&&(!Number.isInteger(e.scale)||e.scale<0||e.scale>12))throw Error(`scale must be an integer between 0 and 12`);let t=r(`decimal`,e);return e?.scale!==void 0&&(t._metadata.scale=e.scale),t}function date(e){return r(`date`,e)}function datetime(e){return r(`datetime`,e)}function time(e){return r(`time`,e)}function _enum(e,t){return r(`enum`,t,void 0,e)}function object(e,t){return r(`nested`,t,e)}function createTailorDBType(e,n,r){let i=r.description,a={},o=[],s={},c={},l=[],u,d,f=new Set;r.description!==void 0&&f.add(`description`);function runMethodOnce(e,t){if(f.has(e))throw Error(`.${e}() has already been set`);let n=t();return f.add(e),n}if(r.pluralForm){if(e===r.pluralForm)throw Error(`The name and the plural form must be different. name=${e}`);a.pluralForm=r.pluralForm}let p={name:e,fields:{...n},_output:null,_description:i,get metadata(){let e={};return o.length>0&&o.forEach(t=>{let n=t.fields.map(e=>String(e)),r=t.name||`idx_${n.join(`_`)}`;e[r]={fields:n,unique:t.unique}}),{name:this.name,description:i,settings:a,permissions:s,files:c,...Object.keys(e).length>0&&{indexes:e},...u&&{typeHook:u},...d&&{typeValidate:d}}},hooks(e){return runMethodOnce(`hooks`,()=>(u=e,this))},validate(e){return runMethodOnce(`validate`,()=>(d=e,this))},features(e){return runMethodOnce(`features`,()=>(a={...a,...e},this))},indexes(...e){return runMethodOnce(`indexes`,()=>(o=e,this))},files(e){return runMethodOnce(`files`,()=>(c=e,this))},permission(e){return runMethodOnce(`permission`,()=>{let t=this;return s.record=e,t})},gqlPermission(e){return runMethodOnce(`gqlPermission`,()=>{let t=this;return s.gql=e,t})},description(e){return runMethodOnce(`description`,()=>(i=e,this._description=e,this))},pickFields(e,t){let n={};for(let r of e){let e=this.fields[r];if(!e)throw Error(`pickFields(): field "${String(r)}" does not exist on this table yet. If it comes from a plugin's .plugin() call, it is only added to the table after \`tailor generate\` runs.`);n[r]=t?e.clone(t):e}return n},omitFields(e){let t=new Set(e),n={};for(let e in this.fields)Object.hasOwn(this.fields,e)&&!t.has(e)&&(n[e]=this.fields[e]);return n},get plugins(){return l},plugin(e){for(let[t,n]of Object.entries(e))l.push({pluginId:t,config:n});return this}};return t(p,`tailordb-type`)}const i=uuid();function dbTable(e,t,n){let r=Array.isArray(e)?e[0]:e,a=Array.isArray(e)?e[1]:void 0,o,s;return typeof t==`string`?(o=t,s=n):s=t,createTailorDBType(r,{id:i,...s},{pluralForm:a,description:o})}const timestampsUpdatedAtHook=({input:e,now:t})=>e??t,a=Symbol.for(`tailor-platform/sdk:precompiled-script-expr`);timestampsUpdatedAtHook[a]={"hooks.update":`(({ input, now }) => input ?? now)({ input: _value, oldValue: _oldValue, invoker: _principal, now: _now })`};const o={table:dbTable,uuid,string,bool,int,float,decimal,date,datetime,time,enum:_enum,object,fields:{timestamps:()=>({createdAt:datetime().default(`now`).description(`Record creation timestamp`),updatedAt:datetime().default(`now`).hooks({update:timestampsUpdatedAtHook}).description(`Record update timestamp`)})}};export{mapAllowedValues as n,o as t};
2
- //# sourceMappingURL=schema-DRyQEabV.mjs.map
1
+ import{r as e}from"./field-parse-BbNyotv5.mjs";import{t}from"./brand-C8nMKhJC.mjs";import{cloneDeep as n}from"es-toolkit";function mapAllowedValues(e){return e.map(e=>typeof e==`string`?{value:e,description:``}:{...e,description:e.description??``})}function createTailorDBField(e,t,n,r){return createTailorDBFieldRuntime(e,t,n,r)}function createTailorDBFieldRuntime(t,r,i,a){let o={required:!0},s;r&&(r.optional===!0&&(o.required=!1),r.array===!0&&(o.array=!0)),a&&(o.allowedValues=mapAllowedValues(a));function parseInternal$1(t){return e({...t,field:c})}function cloneWith(e){let t=c.clone();return Object.assign(t._metadata,e),t}let c={type:t,fields:i??{},_defined:void 0,_output:void 0,_metadata:o,get metadata(){return{...this._metadata}},get rawRelation(){return s?{...s,toward:{...s.toward}}:void 0},description(e){return cloneWith({description:e})},typeName(e){return cloneWith({typeName:e})},validate(...e){return cloneWith({validate:e})},parse(e){return parseInternal$1({value:e.value,data:e.data,invoker:e.invoker,pathArray:[]})},relation(e){let t=c.clone(),n=e.toward,r=`table`in n?n.table:n.type,i=r===`self`?`self`:r.name;return t._setRawRelation({type:e.type,toward:{table:i,as:e.toward.as,key:e.toward.key},backward:e.backward}),t},index(){return cloneWith({index:!0})},unique(){return cloneWith({unique:!0,index:!0})},vector(){return cloneWith({vector:!0})},default(e){return cloneWith({default:e})},hooks(e){return cloneWith({hooks:e})},serial(e){return cloneWith({serial:e})},clone(e){if(this._metadata.validate?.length&&e?.array!==void 0&&e.array!==(this._metadata.array===!0))throw Error(`Cannot change the array option on a field with custom validation`);let o=i;if(i){let e={};for(let[t,n]of Object.entries(i))e[t]=n.clone();o=e}let c=createTailorDBFieldRuntime(t,r,o,a);return Object.assign(c._metadata,n(this._metadata)),e&&(e.optional!==void 0&&(c._metadata.required=!e.optional),e.array!==void 0&&(c._metadata.array=e.array)),s&&c._setRawRelation(n(s)),c},_setRawRelation(e){s=e}};return c}const r=createTailorDBField;function uuid(e){return r(`uuid`,e)}function string(e){return r(`string`,e)}function bool(e){return r(`boolean`,e)}function int(e){return r(`integer`,e)}function float(e){return r(`float`,e)}function decimal(e){if(e?.scale!==void 0&&(!Number.isInteger(e.scale)||e.scale<0||e.scale>12))throw Error(`scale must be an integer between 0 and 12`);let t=r(`decimal`,e);return e?.scale!==void 0&&(t._metadata.scale=e.scale),t}function date(e){return r(`date`,e)}function datetime(e){return r(`datetime`,e)}function time(e){return r(`time`,e)}function _enum(e,t){return r(`enum`,t,void 0,e)}function object(e,t){return r(`nested`,t,e)}function createTailorDBType(e,n,r){let i=r.description,a={},o=[],s={},c={},l=[],u,d,f=new Set;r.description!==void 0&&f.add(`description`);function runMethodOnce(e,t){if(f.has(e))throw Error(`.${e}() has already been set`);let n=t();return f.add(e),n}if(r.pluralForm){if(e===r.pluralForm)throw Error(`The name and the plural form must be different. name=${e}`);a.pluralForm=r.pluralForm}let p={name:e,fields:{...n},_output:null,_description:i,get metadata(){let e={};return o.length>0&&o.forEach(t=>{let n=t.fields.map(e=>String(e)),r=t.name||`idx_${n.join(`_`)}`;e[r]={fields:n,unique:t.unique}}),{name:this.name,description:i,settings:a,permissions:s,files:c,...Object.keys(e).length>0&&{indexes:e},...u&&{typeHook:u},...d&&{typeValidate:d}}},hooks(e){return runMethodOnce(`hooks`,()=>(u=e,this))},validate(e){return runMethodOnce(`validate`,()=>(d=e,this))},features(e){return runMethodOnce(`features`,()=>(a={...a,...e},this))},indexes(...e){return runMethodOnce(`indexes`,()=>(o=e,this))},files(e){return runMethodOnce(`files`,()=>(c=e,this))},permission(e){return runMethodOnce(`permission`,()=>{let t=this;return s.record=e,t})},gqlPermission(e){return runMethodOnce(`gqlPermission`,()=>{let t=this;return s.gql=e,t})},description(e){return runMethodOnce(`description`,()=>(i=e,this._description=e,this))},pickFields(e,t){let n={};for(let r of e){let e=this.fields[r];if(!e)throw Error(`pickFields(): field "${String(r)}" does not exist on this table yet. If it comes from a plugin's .plugin() call, it is only added to the table after \`tailor generate\` runs.`);n[r]=t?e.clone(t):e}return n},omitFields(e){let t=new Set(e),n={};for(let e in this.fields)Object.hasOwn(this.fields,e)&&!t.has(e)&&(n[e]=this.fields[e]);return n},get plugins(){return l},plugin(e){for(let[t,n]of Object.entries(e))l.push({pluginId:t,config:n});return this}};return t(p,`tailordb-type`)}const i=uuid();function dbTable(e,t,n){let r=Array.isArray(e)?e[0]:e,a=Array.isArray(e)?e[1]:void 0,o,s;return typeof t==`string`?(o=t,s=n):s=t,createTailorDBType(r,{id:i,...s},{pluralForm:a,description:o})}const timestampsUpdatedAtHook=({input:e,now:t})=>e??t,a=Symbol.for(`tailor-platform/sdk:precompiled-script-expr`);timestampsUpdatedAtHook[a]={"hooks.update":`(({ input, now }) => input ?? now)({ input: _value, oldValue: _oldValue, invoker: _principal, now: _now })`};const o={table:dbTable,uuid,string,bool,int,float,decimal,date,datetime,time,enum:_enum,object,fields:{timestamps:()=>({createdAt:datetime().default(`now`).description(`Record creation timestamp`),updatedAt:datetime().default(`now`).hooks({update:timestampsUpdatedAtHook}).description(`Record update timestamp`)})}};export{mapAllowedValues as n,o as t};
2
+ //# sourceMappingURL=schema-BKvcWdVf.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-DRyQEabV.mjs","names":["parseInternal","parseFieldInternal"],"sources":["../src/configure/types/field.ts","../src/configure/services/tailordb/schema.ts"],"sourcesContent":["import type { EnumValue } from \"#/configure/types/field.types\";\n\nexport type AllowedValues = readonly [string | EnumValue, ...(string | EnumValue)[]];\n\n/**\n * Normalize allowed values into EnumValue objects with descriptions.\n * @param values - Allowed values as strings or EnumValue objects\n * @returns Normalized allowed values\n */\nexport function mapAllowedValues(values: AllowedValues): EnumValue[] {\n return values.map((value) => {\n if (typeof value === \"string\") {\n return { value, description: \"\" };\n }\n return { ...value, description: value.description ?? \"\" };\n });\n}\n\nexport type AllowedValuesOutput<V extends AllowedValues> = V[number] extends infer T\n ? T extends string\n ? T\n : T extends { value: infer K }\n ? K\n : never\n : never;\n","import { cloneDeep } from \"es-toolkit\";\nimport {\n type AllowedValues,\n type AllowedValuesOutput,\n mapAllowedValues,\n} from \"#/configure/types/field\";\nimport {\n parseInternal as parseFieldInternal,\n type FieldParseArgs,\n type FieldParseInternalArgs,\n} from \"#/runtime/field-parse\";\nimport { brandValue } from \"#/utils/brand\";\nimport type {\n FieldOptions,\n FieldOutput,\n TailorFieldType,\n TailorToTs,\n FieldValidateInput,\n} from \"#/configure/types/field.types\";\nimport type {\n PrecompiledScriptExprKey,\n PrecompiledScriptExprMap,\n} from \"#/parser/service/tailordb/types\";\nimport type { PluginAttachment, PluginConfigs, PluginFieldExtensions } from \"#/plugin/types\";\nimport type { InferredAttributes } from \"#/runtime/types\";\nimport type {\n output,\n InferFieldsOutput,\n IsUnion,\n TypeLevelError,\n UnionToIntersection,\n} from \"#/types/helpers\";\nimport type { RawPermissions } from \"#/types/tailordb.generated\";\nimport type { TailorTypeGqlPermission, TailorTypePermission } from \"./permission\";\nimport type {\n TailorDBField as TailorDBFieldBase,\n TailorDBType as TailorDBTypeBase,\n DBFieldMetadata,\n DefinedDBFieldMetadata,\n SerialConfig,\n IndexDef,\n TailorDBTypeMetadata,\n RawRelationConfig,\n RelationType,\n Hook,\n TypeHook,\n UpdateHookFn,\n ExcludeNestedDBFields,\n ExcludeHookedDBFields,\n ExcludeDefaultedDBFields,\n TypeFeatures,\n TypeValidateFn,\n} from \"./types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// Erased DB fields stay assignable across builder method-state changes.\n// oxlint-disable-next-line no-explicit-any\ntype AnyBuilderMethod = any;\n\nexport type TailorAnyDBField = Omit<\n TailorDBFieldBase<AnyBuilderMethod, AnyBuilderMethod>,\n \"fields\"\n> & {\n readonly fields: Record<string, AnyBuilderMethod>;\n _metadata: DBFieldMetadata;\n parse: AnyBuilderMethod;\n readonly typeName: TypeLevelError<string>;\n description: AnyBuilderMethod;\n relation: AnyBuilderMethod;\n index: AnyBuilderMethod;\n unique: AnyBuilderMethod;\n vector: AnyBuilderMethod;\n default: AnyBuilderMethod;\n hooks: AnyBuilderMethod;\n validate: AnyBuilderMethod;\n serial: AnyBuilderMethod;\n clone: AnyBuilderMethod;\n};\n\n// Helper alias\n// oxlint-disable-next-line no-explicit-any\nexport type TailorAnyDBType = TailorDBType<any, any, any>;\n\ntype IsAny<T> = 0 extends 1 & T ? true : false;\ntype DBFieldTypeNameMethod<Defined extends DefinedDBFieldMetadata> =\n IsAny<Defined> extends true\n ? TypeLevelError<string>\n : TypeLevelError<\"typeName cannot be used on TailorDB fields\">;\n\ntype WithDBFieldDescription<Defined> = Defined & { description: true };\ntype WithDBFieldRelation<Defined, S extends RelationType | RelationSelfConfig> = S extends\n | \"oneToOne\"\n | \"1-1\"\n ? Defined & { unique: true; index: true; relation: true }\n : S extends { type: \"oneToOne\" | \"1-1\" }\n ? Defined & { unique: true; index: true; relation: true }\n : Defined & { index: true; relation: true };\ntype WithDBFieldIndex<Defined> = Defined & { index: true };\ntype WithDBFieldUnique<Defined> = Defined & { unique: true; index: true };\ntype WithDBFieldVector<Defined> = Defined & { vector: true };\ntype WithDBFieldHooks<Defined, H> = Defined & {\n hooks: {\n create: H extends { create: unknown } ? true : false;\n update: H extends { update: unknown } ? true : false;\n };\n serial: false;\n};\ntype WithDBFieldDefault<Defined> = Defined & { default: true };\ntype WithDBFieldValidate<Defined> = Defined & { validate: true };\ntype WithDBFieldSerial<Defined> = Defined & {\n serial: true;\n hooks: { create: false; update: false };\n};\ntype WithDBFieldCloneOptions<Defined extends DefinedDBFieldMetadata, NewOpt extends FieldOptions> =\n IsAny<Defined> extends true\n ? Defined\n : Omit<Defined, \"array\"> & {\n array: NewOpt extends { array: true }\n ? true\n : NewOpt extends { array: false }\n ? false\n : Defined[\"array\"];\n };\ntype NonNullableDBFieldOutput<Output> = Exclude<Output, null>;\ntype DBFieldScalarOutput<Output> =\n NonNullableDBFieldOutput<Output> extends (infer Item)[] ? Item : NonNullableDBFieldOutput<Output>;\ntype DBFieldCloneArrayOutput<Output, NewOpt extends FieldOptions> = NewOpt extends {\n array: true;\n}\n ? DBFieldScalarOutput<Output>[]\n : NewOpt extends { array: false }\n ? DBFieldScalarOutput<Output>\n : NonNullableDBFieldOutput<Output>;\ntype DBFieldCloneOutput<Output, NewOpt extends FieldOptions> = NewOpt extends { optional: true }\n ? DBFieldCloneArrayOutput<Output, NewOpt> | null\n : NewOpt extends { optional: false }\n ? DBFieldCloneArrayOutput<Output, NewOpt>\n : null extends Output\n ? DBFieldCloneArrayOutput<Output, NewOpt> | null\n : DBFieldCloneArrayOutput<Output, NewOpt>;\ntype DBFieldCloneOptions<Defined extends DefinedDBFieldMetadata> = Omit<FieldOptions, \"array\"> & {\n array?: Defined extends { validate: unknown } ? Defined[\"array\"] : boolean;\n};\ntype InvalidValidatedArrayCloneKeys<\n Fields extends Record<string, TailorAnyDBField>,\n K extends keyof Fields,\n Opt extends FieldOptions,\n> = Opt extends { array: infer ArrayOption }\n ? {\n [P in K]: Fields[P] extends TailorDBField<infer Defined, infer _Output>\n ? Defined extends { validate: unknown }\n ? ArrayOption extends Defined[\"array\"]\n ? never\n : P\n : never\n : never;\n }[K]\n : never;\ntype DBFieldsCloneOptionsGuard<\n Fields extends Record<string, TailorAnyDBField>,\n K extends keyof Fields,\n Opt extends FieldOptions,\n> = [InvalidValidatedArrayCloneKeys<Fields, K, Opt>] extends [never]\n ? unknown\n : TypeLevelError<\"array cannot be changed on fields with custom validation\">;\ntype DefinedDBTypeMetadata = {\n hooks?: true;\n validate?: true;\n features?: true;\n indexes?: true;\n files?: true;\n permission?: true;\n gqlPermission?: true;\n description?: true;\n};\ntype WithDBTypeMetadata<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n> = Defined & Record<Key, true>;\ntype DBTypeDuplicateInputGuard<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n Input,\n Message extends string,\n> =\n IsAny<Defined> extends true\n ? Input\n : Defined extends Record<Key, unknown>\n ? TypeLevelError<Message>\n : Input;\ntype DBTypeDuplicateRestGuard<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n Input extends unknown[],\n Message extends string,\n> =\n IsAny<Defined> extends true\n ? Input\n : Defined extends Record<Key, unknown>\n ? [TypeLevelError<Message>, ...TypeLevelError<Message>[]]\n : Input;\ntype FileKeyConflictError<\n Fields extends Record<string, TailorAnyDBField>,\n User extends object,\n> = Partial<\n Record<\n keyof output<TailorDBType<Fields, User>> & string,\n TypeLevelError<\"file keys cannot use existing field names\">\n >\n>;\n// A plugin id with no PluginFieldExtensions entry contributes no keys — using\n// Record<never, never> (rather than Record<string, never>) keeps its `keyof`\n// empty so it can't spuriously collide with every existing field.\ntype PluginFieldExtensionFor<\n Fields extends Record<string, TailorAnyDBField>,\n Id extends string,\n IdConfig,\n> = Id extends keyof PluginFieldExtensions<keyof Fields & string, IdConfig>\n ? PluginFieldExtensions<keyof Fields & string, IdConfig>[Id]\n : Record<never, never>;\ntype PluginFieldExtensionsUnion<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n> = {\n [Id in keyof Config & string]: PluginFieldExtensionFor<Fields, Id, Config[Id]>;\n}[keyof Config & string];\ntype AllExtensionKeys<Ext> = Ext extends unknown ? keyof Ext : never;\n// True when plugin `Id` has a PluginFieldExtensions entry whose type does not\n// extend Record<string, TailorAnyDBField> — e.g. a plugin author registered\n// a non-record shape by mistake. Checked ahead of PluginFieldConflict, since\n// `keyof` on a non-record shape (e.g. `keyof string`) would otherwise produce\n// nonsense candidate keys for the collision check.\n// `Id extends string` (not `keyof Config & string`) deliberately: a mapped\n// type's key variable, used inside a nested conditional branch and then\n// intersected with `string`, does not structurally satisfy a `keyof Config &\n// string`-constrained type parameter under `skipLibCheck: false` (verified\n// against a standalone consumer — TS2344 on both call sites otherwise, even\n// for code that never calls .plugin()). `Config[Id & keyof Config]` recovers\n// the same indexed-access result without that constraint.\ntype PluginFieldExtensionShapeError<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n> =\n PluginFieldExtensionFor<Fields, Id, Config[Id & keyof Config]> extends Record<\n string,\n TailorAnyDBField\n >\n ? false\n : true;\n// True when the fields plugin `Id` would inject collide with an existing\n// field, or with a field injected by another plugin id attached in the same\n// .plugin() call.\ntype PluginFieldConflict<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n FileKeys extends string,\n> = [\n AllExtensionKeys<PluginFieldExtensionFor<Fields, Id, Config[Id & keyof Config]>> &\n (\n | keyof Fields\n | FileKeys\n | AllExtensionKeys<PluginFieldExtensionsUnion<Fields, Omit<Config, Id>>>\n ),\n] extends [never]\n ? false\n : true;\n// Rejects a property on the caller's config literal for plugin `Id` that\n// isn't part of its registered PluginConfigs shape. `Config` is captured by\n// a `const` type parameter from the argument itself, so intersecting the\n// success branch with `PluginConfigs<...>[Id]` alone would not trigger\n// TypeScript's excess-property check — that check only fires when an object\n// literal is validated directly against a target type, not after its shape\n// has already been inferred through a generic parameter.\ntype PluginConfigExcessProps<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n> = Record<\n Exclude<\n keyof Config[Id & keyof Config],\n keyof PluginConfigs<keyof Fields & string>[Id & keyof PluginConfigs<keyof Fields & string>]\n >,\n never\n>;\ntype PluginExtendedFields<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n> =\n IsAny<Fields> extends true\n ? Fields\n : // For `Config = {}`, PluginFieldExtensionsUnion is `never`, and\n // UnionToIntersection<never> is `unknown` (not `never`), so this\n // correctly reduces to `Fields` — see the \"empty config\" test.\n Fields & UnionToIntersection<PluginFieldExtensionsUnion<Fields, Config>>;\n// Validates each key of the passed config object against PluginConfigs and\n// flags field-extension conflicts (see PluginFieldConflict above), keeping\n// type errors localized to the offending plugin id instead of requiring\n// every registered plugin id to be present (as a `P extends keyof\n// PluginConfigs<...>` generic would need, defeating inference of which\n// plugin ids were actually passed).\ntype PluginConfigGuard<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n FileKeys extends string,\n> =\n IsAny<Fields> extends true\n ? unknown\n : {\n [K in keyof Config]: K extends keyof PluginConfigs<keyof Fields & string>\n ? // A union config (e.g. from a ternary passed directly as a plugin's\n // value) distributes through PluginFieldExtensionFor's conditional\n // and silently collapses the injected field to `never` once\n // intersected back together — reject it here instead, before it\n // can produce that confusing downstream error.\n IsUnion<Config[K]> extends true\n ? TypeLevelError<\"plugin config must be a single object literal, not a union — assign the config to a variable first if it comes from a conditional expression\">\n : PluginFieldExtensionShapeError<Fields, Config, K & string> extends true\n ? TypeLevelError<\"PluginFieldExtensions entry must be a Record<string, TailorAnyDBField>\">\n : PluginFieldConflict<Fields, Config, K & string, FileKeys> extends true\n ? TypeLevelError<\"plugin field extension conflicts with an existing field, a file key declared via .files(), or another plugin's field\">\n : PluginConfigs<keyof Fields & string>[K] &\n PluginConfigExcessProps<Fields, Config, K & string>\n : TypeLevelError<\"unknown plugin id\">;\n };\ntype DBFieldDescriptionFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (description: string) => TailorDBField<WithDBFieldDescription<Defined>, Output, Nested>;\ntype DBFieldRelationFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = {\n <S extends RelationType, T extends TailorAnyDBType>(\n config: RelationConfig<S, T>,\n ): TailorDBField<WithDBFieldRelation<Defined, S>, Output, Nested>;\n <S extends RelationSelfConfig>(\n config: S,\n ): TailorDBField<WithDBFieldRelation<Defined, S>, Output, Nested>;\n};\ntype DBFieldIndexFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldIndex<Defined>, Output, Nested>;\ntype DBFieldUniqueFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldUnique<Defined>, Output, Nested>;\ntype DBFieldVectorFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldVector<Defined>, Output, Nested>;\ntype DBFieldHooksFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = <\n const H extends Hook<\n Output,\n Defined extends { default: true } ? Output | null | undefined : Output\n >,\n>(\n hooks: H,\n) => TailorDBField<WithDBFieldHooks<Defined, H>, Output, Nested>;\ntype DBFieldValidateFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n ...validate: FieldValidateInput<Output>[]\n) => TailorDBField<WithDBFieldValidate<Defined>, Output, Nested>;\ntype DBFieldSerialFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n config: SerialConfig<Defined[\"type\"] & (\"integer\" | \"string\")>,\n) => TailorDBField<WithDBFieldSerial<Defined>, Output, Nested>;\ntype DBFieldDescriptionMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldDescriptionFn<Defined, Output, Nested>\n : Defined extends { description: unknown }\n ? TypeLevelError<\".description() has already been set\">\n : DBFieldDescriptionFn<Defined, Output, Nested>;\ntype DBFieldRelationMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldRelationFn<Defined, Output, Nested>\n : Defined extends { relation: unknown }\n ? TypeLevelError<\".relation() has already been set\">\n : DBFieldRelationFn<Defined, Output, Nested>;\ntype DBFieldArrayCheck<A extends boolean, Ok, Msg extends string> = A extends true\n ? TypeLevelError<Msg>\n : Ok;\ntype DBFieldIndexMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldIndexFn<Defined, Output, Nested>\n : Defined extends { index: unknown }\n ? TypeLevelError<\".index() has already been set\">\n : DBFieldArrayCheck<\n Defined[\"array\"],\n DBFieldIndexFn<Defined, Output, Nested>,\n \"index cannot be set on array fields\"\n >;\ntype DBFieldUniqueMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldUniqueFn<Defined, Output, Nested>\n : Defined extends { unique: unknown }\n ? TypeLevelError<\".unique() has already been set\">\n : DBFieldArrayCheck<\n Defined[\"array\"],\n DBFieldUniqueFn<Defined, Output, Nested>,\n \"unique cannot be set on array fields\"\n >;\ntype DBFieldVectorMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldVectorFn<Defined, Output, Nested>\n : Defined extends { vector: unknown }\n ? TypeLevelError<\".vector() has already been set\">\n : Defined extends { type: \"string\"; array: false }\n ? DBFieldVectorFn<Defined, Output, Nested>\n : TypeLevelError<\"vector can only be set on non-array string fields\">;\ntype DBFieldHooksMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldHooksFn<Defined, Output, Nested>\n : Defined extends {\n serial: true;\n hooks: { create: false; update: false };\n }\n ? TypeLevelError<\"hooks cannot be set after serial\">\n : Defined extends {\n hooks: unknown;\n }\n ? TypeLevelError<\".hooks() has already been set\">\n : Defined extends { type: \"nested\" }\n ? TypeLevelError<\"hooks cannot be set on nested type fields\">\n : DBFieldHooksFn<Defined, Output, Nested>;\ntype DBFieldValidateMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldValidateFn<Defined, Output, Nested>\n : Defined extends { validate: unknown }\n ? TypeLevelError<\".validate() has already been set\">\n : DBFieldValidateFn<Defined, Output, Nested>;\ntype DBFieldSerialMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldSerialFn<Defined, Output, Nested>\n : Defined extends { serial: true }\n ? TypeLevelError<\".serial() has already been set\">\n : Defined extends { serial: false }\n ? TypeLevelError<\"serial cannot be set after hooks\">\n : IsAny<Output> extends true\n ? Defined extends { type: \"integer\" | \"string\"; array: false }\n ? DBFieldSerialFn<Defined, Output, Nested>\n : TypeLevelError<\"serial can only be set on non-array integer or string fields\">\n : null extends Output\n ? TypeLevelError<\"serial can only be set on non-array integer or string fields\">\n : Defined extends { type: \"integer\" | \"string\"; array: false }\n ? DBFieldSerialFn<Defined, Output, Nested>\n : TypeLevelError<\"serial can only be set on non-array integer or string fields\">;\ntype DBFieldDefaultFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n value: Output extends null ? NonNullable<Output> : Output,\n) => TailorDBField<WithDBFieldDefault<Defined>, Output, Nested>;\ntype DBFieldDefaultMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldDefaultFn<Defined, Output, Nested>\n : Defined extends { default: unknown }\n ? TypeLevelError<\".default() has already been set\">\n : Defined extends { type: \"nested\" }\n ? TypeLevelError<\"default cannot be set on nested type fields\">\n : Defined extends { serial: true }\n ? TypeLevelError<\"default cannot be set on serial fields\">\n : null extends Output\n ? TypeLevelError<\"default cannot be set on optional fields\">\n : DBFieldDefaultFn<Defined, Output, Nested>;\n\n/**\n * Full TailorDBField interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorDBField<\n Defined extends DefinedDBFieldMetadata = DefinedDBFieldMetadata,\n // oxlint-disable-next-line no-explicit-any\n Output = any,\n // Nested object fields, so a `db.object()` field keeps the shape it was declared with.\n // Every builder method passes it through; dropping it here would erase the shape as\n // soon as one is chained.\n Nested extends Record<string, TailorAnyDBField> = Record<string, TailorAnyDBField>,\n> extends Omit<TailorDBFieldBase<Defined, Output, Nested>, \"fields\"> {\n readonly fields: Nested;\n _metadata: DBFieldMetadata;\n\n /**\n * Parse and validate a value against this field's validation rules\n */\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n\n /**\n * typeName is not available on TailorDB fields.\n * Use typeName on pipeline fields (t.enum / t.object) instead.\n */\n typeName: DBFieldTypeNameMethod<Defined>;\n\n /**\n * Set a description for the field\n */\n description: DBFieldDescriptionMethod<Defined, Output, Nested>;\n\n /**\n * Define a relation to another table.\n */\n relation: DBFieldRelationMethod<Defined, Output, Nested>;\n\n /**\n * Add an index to the field\n */\n index: DBFieldIndexMethod<Defined, Output, Nested>;\n\n /**\n * Make the field unique (also adds an index)\n */\n unique: DBFieldUniqueMethod<Defined, Output, Nested>;\n\n /**\n * Enable vector search on the field (string type only)\n */\n vector: DBFieldVectorMethod<Defined, Output, Nested>;\n\n /**\n * Set a default value for the field on create. When the field is required,\n * this makes it optional in the Create input — the default fills in when\n * no value (or a nullish hook result) is provided.\n *\n * For datetime/date/time fields, pass `\"now\"` to use the operation timestamp.\n */\n default: DBFieldDefaultMethod<Defined, Output, Nested>;\n\n /**\n * Add hooks for create/update operations on this field.\n */\n hooks: DBFieldHooksMethod<Defined, Output, Nested>;\n\n /**\n * Add validation functions to the field.\n *\n * Validators receive `{ value, data, user }` and run after hooks and\n * built-in type validation; they are skipped when built-in validation\n * fails. For array fields, `value` is the complete array.\n */\n validate: DBFieldValidateMethod<Defined, Output, Nested>;\n\n /**\n * Configure serial/auto-increment behavior\n */\n serial: DBFieldSerialMethod<Defined, Output, Nested>;\n\n /**\n * Clone the field with optional overrides for field options.\n * The `array` option cannot change on fields with custom validation.\n */\n clone<const NewOpt extends DBFieldCloneOptions<Defined>>(\n options?: NewOpt,\n ): TailorDBField<\n WithDBFieldCloneOptions<Defined, NewOpt>,\n DBFieldCloneOutput<Output, NewOpt>,\n Nested\n >;\n}\n\n/**\n * Full TailorDBType interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorDBType<\n // oxlint-disable-next-line no-explicit-any\n Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n // oxlint-disable-next-line no-explicit-any\n Defined extends DefinedDBTypeMetadata = any,\n FileKeys extends string = never,\n> extends TailorDBTypeBase<Fields, User> {\n _description?: string;\n\n hooks(\n hook: DBTypeDuplicateInputGuard<\n Defined,\n \"hooks\",\n TypeHook<Fields>,\n \".hooks() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"hooks\">, FileKeys>;\n validate(\n fn: DBTypeDuplicateInputGuard<\n Defined,\n \"validate\",\n TypeValidateFn<Fields>,\n \".validate() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"validate\">, FileKeys>;\n features(\n features: DBTypeDuplicateInputGuard<\n Defined,\n \"features\",\n Omit<TypeFeatures, \"pluralForm\">,\n \".features() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"features\">, FileKeys>;\n indexes(\n ...indexes: DBTypeDuplicateRestGuard<\n Defined,\n \"indexes\",\n IndexDef<TailorDBType<Fields, User, Defined, FileKeys>>[],\n \".indexes() has already been set\"\n >\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"indexes\">, FileKeys>;\n files<const F extends string>(\n files: DBTypeDuplicateInputGuard<\n Defined,\n \"files\",\n Record<F, string> & FileKeyConflictError<Fields, User>,\n \".files() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"files\">, FileKeys | F>;\n permission<\n U extends object = User,\n P extends TailorTypePermission<U, output<TailorDBType<Fields, User, Defined>>> =\n TailorTypePermission<U, output<TailorDBType<Fields, User, Defined>>>,\n >(\n permission: DBTypeDuplicateInputGuard<\n Defined,\n \"permission\",\n P,\n \".permission() has already been set\"\n >,\n ): TailorDBType<Fields, U, WithDBTypeMetadata<Defined, \"permission\">, FileKeys>;\n gqlPermission<\n U extends object = User,\n P extends TailorTypeGqlPermission<U> = TailorTypeGqlPermission<U>,\n >(\n permission: DBTypeDuplicateInputGuard<\n Defined,\n \"gqlPermission\",\n P,\n \".gqlPermission() has already been set\"\n >,\n ): TailorDBType<Fields, U, WithDBTypeMetadata<Defined, \"gqlPermission\">, FileKeys>;\n description(\n description: DBTypeDuplicateInputGuard<\n Defined,\n \"description\",\n string,\n \".description() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"description\">, FileKeys>;\n pickFields<K extends keyof Fields>(keys: K[]): Pick<Fields, K>;\n pickFields<K extends keyof Fields, const Opt extends FieldOptions>(\n keys: K[],\n options: Opt & DBFieldsCloneOptionsGuard<Fields, K, Opt>,\n ): {\n [P in K]: Fields[P] extends TailorDBField<infer D, infer O>\n ? TailorDBField<WithDBFieldCloneOptions<D, Opt>, DBFieldCloneOutput<O, Opt>>\n : never;\n };\n omitFields<K extends keyof Fields>(keys: K[]): Omit<Fields, K>;\n plugin<const Config extends Record<string, unknown>>(\n config: Config & PluginConfigGuard<Fields, Config, FileKeys>,\n ): TailorDBType<PluginExtendedFields<Fields, Config>, User, Defined, FileKeys>;\n}\n\nexport type TailorDBInstance<\n // oxlint-disable-next-line no-explicit-any\n Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n // oxlint-disable-next-line no-explicit-any\n Defined extends DefinedDBTypeMetadata = any,\n FileKeys extends string = never,\n> = TailorDBType<Fields, User, Defined, FileKeys>;\n\ninterface RelationConfig<S extends RelationType, T extends TailorDBType> {\n type: S;\n toward:\n | {\n table: T;\n as?: string;\n key?: keyof T[\"fields\"] & string;\n type?: never;\n }\n | {\n /**\n * @deprecated since 2.6.0 — use `table` instead. codemod: v3/relation-toward-table\n */\n type: T;\n as?: string;\n key?: keyof T[\"fields\"] & string;\n table?: never;\n };\n backward?: string;\n}\n\n// Special config variant for self-referencing relations\ntype RelationSelfConfig = {\n type: RelationType;\n toward:\n | {\n table: \"self\";\n as?: string;\n key?: string;\n type?: never;\n }\n | {\n /**\n * @deprecated since 2.6.0 — use `table` instead. codemod: v3/relation-toward-table\n */\n type: \"self\";\n as?: string;\n key?: string;\n table?: never;\n };\n backward?: string;\n};\n\ntype DBFieldDefined<T extends TailorFieldType, Opt extends FieldOptions> = {\n type: T;\n array: Opt extends { array: true } ? true : false;\n};\ntype DBFieldOutput<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = FieldOutput<OutputBase, Opt>;\ntype TailorDBFieldInstance<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = TailorDBField<DBFieldDefined<T, Opt>, DBFieldOutput<T, Opt, OutputBase>>;\ntype TailorDBFieldRuntimeInstance<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = TailorDBFieldRuntime<DBFieldDefined<T, Opt>, DBFieldOutput<T, Opt, OutputBase>>;\ntype TailorDBFieldRuntime<Defined extends DefinedDBFieldMetadata, Output> = Omit<\n TailorDBFieldBase<Defined, Output>,\n \"fields\"\n> & {\n readonly fields: Record<string, TailorAnyDBField>;\n _metadata: DBFieldMetadata;\n description(description: string): object;\n typeName(typeName: string): object;\n validate(...validate: FieldValidateInput<Output>[]): object;\n relation(config: RelationConfig<RelationType, TailorDBType> | RelationSelfConfig): object;\n index(): object;\n unique(): object;\n vector(): object;\n default(value: unknown): object;\n hooks(hooks: Hook<Output>): object;\n serial(config: SerialConfig): object;\n clone(options?: FieldOptions): TailorDBFieldRuntime<DefinedDBFieldMetadata, AnyBuilderMethod>;\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n _setRawRelation(relation: RawRelationConfig): void;\n};\n\n/**\n * Creates a new TailorDBField 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 * @returns A new TailorDBField\n */\nfunction createTailorDBField<\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, TailorAnyDBField>,\n values?: AllowedValues,\n): TailorDBFieldInstance<T, TOptions, OutputBase>;\nfunction createTailorDBField<const T extends TailorFieldType, const TOptions extends FieldOptions>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyDBField>,\n values?: AllowedValues,\n): object {\n return createTailorDBFieldRuntime(type, options, fields, values);\n}\n\nfunction createTailorDBFieldRuntime<\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, TailorAnyDBField>,\n values?: AllowedValues,\n): TailorDBFieldRuntimeInstance<T, TOptions, OutputBase> {\n type FieldValue = DBFieldOutput<T, TOptions, OutputBase>;\n type FieldType = TailorDBFieldRuntimeInstance<T, TOptions, OutputBase>;\n\n const _metadata: DBFieldMetadata = { required: true };\n let _rawRelation: RawRelationConfig | undefined;\n\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 function parseInternal(args: FieldParseInternalArgs): StandardSchemaV1.Result<FieldValue> {\n return parseFieldInternal<T, FieldValue>({\n ...args,\n field,\n });\n }\n\n function cloneWith(metadataUpdates: Partial<DBFieldMetadata>) {\n const cloned = field.clone();\n Object.assign(cloned._metadata, metadataUpdates);\n return cloned;\n }\n\n const field: FieldType = {\n type,\n fields: fields ?? {},\n _defined: undefined as unknown as DBFieldDefined<T, TOptions>,\n _output: undefined as FieldValue,\n _metadata,\n\n get metadata() {\n return { ...this._metadata };\n },\n\n get rawRelation(): Readonly<RawRelationConfig> | undefined {\n return _rawRelation ? { ..._rawRelation, toward: { ..._rawRelation.toward } } : undefined;\n },\n\n description(description: string) {\n return cloneWith({ description });\n },\n\n typeName(typeName: string) {\n return cloneWith({ typeName });\n },\n\n validate(...validateInputs: FieldValidateInput<FieldValue>[]) {\n return cloneWith({ validate: validateInputs });\n },\n\n parse(args: FieldParseArgs): StandardSchemaV1.Result<FieldValue> {\n return parseInternal({\n value: args.value,\n data: args.data,\n invoker: args.invoker,\n pathArray: [],\n });\n },\n\n // TailorDBField specific methods\n relation(config: RelationConfig<RelationType, TailorDBType> | RelationSelfConfig) {\n const cloned = field.clone();\n // The public type is a nested union (RelationConfig | RelationSelfConfig,\n // each with a `{ table } | { type }` toward), which TS's \"in\" narrowing\n // can't discriminate across cleanly. Re-view it as the flat two-branch\n // shape it always is at runtime — the `table?: never` / `type?: never`\n // markers above already forbid both keys from being set together.\n const toward = config.toward as\n | { table: TailorDBType | \"self\" }\n | { type: TailorDBType | \"self\" };\n const towardTarget = \"table\" in toward ? toward.table : toward.type;\n const targetTable = towardTarget === \"self\" ? \"self\" : towardTarget.name;\n cloned._setRawRelation({\n type: config.type,\n toward: {\n table: targetTable,\n as: config.toward.as,\n key: config.toward.key,\n },\n backward: config.backward,\n });\n return cloned;\n },\n\n index() {\n return cloneWith({ index: true });\n },\n\n unique() {\n return cloneWith({ unique: true, index: true });\n },\n\n vector() {\n return cloneWith({ vector: true });\n },\n\n // oxlint-disable-next-line no-explicit-any\n default(value: any) {\n // oxlint-disable-next-line no-explicit-any\n return cloneWith({ default: value }) as any;\n },\n\n hooks(hooks: Hook<FieldValue>) {\n return cloneWith({ hooks });\n },\n\n serial(config: SerialConfig) {\n return cloneWith({ serial: config });\n },\n\n clone(cloneOptions?: FieldOptions) {\n if (\n this._metadata.validate?.length &&\n cloneOptions?.array !== undefined &&\n cloneOptions.array !== (this._metadata.array === true)\n ) {\n throw new Error(\"Cannot change the array option on a field with custom validation\");\n }\n\n // Deep clone nested object fields if present\n let clonedFields = fields;\n if (fields) {\n const cloned: Record<string, TailorAnyDBField> = {};\n for (const [key, field] of Object.entries(fields)) {\n cloned[key] = field.clone();\n }\n clonedFields = cloned;\n }\n\n // Create a new field with cloned configuration\n const clonedField = createTailorDBFieldRuntime(type, options, clonedFields, values);\n\n // Deep copy metadata using cloneDeep (preserves function references)\n Object.assign(clonedField._metadata, cloneDeep(this._metadata));\n\n // Apply new options if provided\n if (cloneOptions) {\n if (cloneOptions.optional !== undefined) {\n clonedField._metadata.required = !cloneOptions.optional;\n }\n if (cloneOptions.array !== undefined) {\n clonedField._metadata.array = cloneOptions.array;\n }\n }\n\n // Copy raw relation if exists\n if (_rawRelation) {\n clonedField._setRawRelation(cloneDeep(_rawRelation));\n }\n\n return clonedField;\n },\n\n _setRawRelation(relation: RawRelationConfig) {\n _rawRelation = relation;\n },\n };\n\n return field;\n}\n\nconst createField = createTailorDBField;\n\n/**\n * Create a UUID field.\n * @param options - Field configuration options\n * @returns A UUID field\n * @example db.uuid()\n * @example db.uuid({ optional: true })\n */\nfunction uuid<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"uuid\", options);\n}\n\n/**\n * Create a string field.\n * @param options - Field configuration options\n * @returns A string field\n * @example db.string()\n * @example db.string({ optional: true })\n */\nfunction string<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"string\", options);\n}\n\n/**\n * Create a boolean field.\n * Note: The method name is `bool` but creates a `boolean` type field.\n * @param options - Field configuration options\n * @returns A boolean field\n * @example db.bool()\n * @example db.bool({ optional: true })\n */\nfunction bool<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"boolean\", options);\n}\n\n/**\n * Create an integer field.\n * @param options - Field configuration options\n * @returns An integer field\n * @example db.int()\n * @example db.int({ optional: true })\n */\nfunction int<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"integer\", options);\n}\n\n/**\n * Create a float (decimal number) field.\n * @param options - Field configuration options\n * @returns A float field\n * @example db.float()\n * @example db.float({ optional: true })\n */\nfunction float<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"float\", options);\n}\n\ninterface DecimalFieldOptions extends FieldOptions {\n scale?: number;\n}\n\n/**\n * Create a decimal field (stored as string for precision).\n * @param options - Field configuration options including optional scale (0-12)\n * @returns A decimal field\n * @example db.decimal()\n * @example db.decimal({ scale: 2 })\n * @example db.decimal({ scale: 2, optional: true })\n */\nfunction decimal<const Opt extends DecimalFieldOptions>(options?: Opt) {\n if (options?.scale !== undefined) {\n if (!Number.isInteger(options.scale) || options.scale < 0 || options.scale > 12) {\n throw new Error(\"scale must be an integer between 0 and 12\");\n }\n }\n const field = createField(\"decimal\", options);\n if (options?.scale !== undefined) {\n field._metadata.scale = options.scale;\n }\n return field;\n}\n\n/**\n * Create a date field (date only, no time component).\n * Format: \"yyyy-MM-dd\"\n * @param options - Field configuration options\n * @returns A date field\n * @example db.date()\n */\nfunction date<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"date\", options);\n}\n\n/**\n * Create a datetime field (date and time).\n * Format: ISO 8601, such as \"yyyy-MM-ddTHH:mm:ssZ\" or \"yyyy-MM-ddTHH:mm:ss+09:00\"\n * @param options - Field configuration options\n * @returns A datetime field\n * @example db.datetime()\n */\nfunction datetime<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"datetime\", options);\n}\n\n/**\n * Create a time field (time only, no date component).\n * Format: \"HH:mm\"\n * @param options - Field configuration options\n * @returns A time field\n * @example db.time()\n */\nfunction time<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"time\", options);\n}\n\n/**\n * Create an enum field with at least one allowed string value.\n * @param values - Array of allowed string values, or array of `{ value, description }` objects\n * @param options - Field configuration options\n * @returns An enum field\n * @example db.enum([\"active\", \"inactive\", \"suspended\"])\n * @example db.enum([\"small\", \"medium\", \"large\"], { optional: true })\n */\nfunction _enum<const V extends AllowedValues, const Opt extends FieldOptions>(\n values: V,\n options?: Opt,\n): TailorDBField<\n { type: \"enum\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<AllowedValuesOutput<V>, Opt>\n> {\n return createField<\"enum\", Opt, AllowedValuesOutput<V>>(\"enum\", options, undefined, values);\n}\n\n/**\n * Create a nested object field with sub-fields.\n * @param fields - Record of nested field definitions\n * @param options - Field configuration options\n * @returns A nested object field\n * @example db.object({ street: db.string(), city: db.string(), zip: db.string() })\n * @example db.object({ name: db.string() }, { optional: true })\n */\nfunction object<\n const F extends Record<string, TailorAnyDBField> &\n ExcludeNestedDBFields<F> &\n ExcludeHookedDBFields<F> &\n ExcludeDefaultedDBFields<F>,\n const Opt extends FieldOptions,\n>(fields: F, options?: Opt) {\n return createField(\"nested\", options, fields) as unknown as TailorDBField<\n { type: \"nested\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<InferFieldsOutput<F>, Opt>,\n F\n >;\n}\n\n/**\n * Creates a new TailorDBType instance.\n * @param name - Table name\n * @param fields - Field definitions\n * @param options - Table options\n * @param options.pluralForm - Optional plural form\n * @param options.description - Optional description\n * @returns A new TailorDBType\n */\nfunction createTailorDBType<\n // oxlint-disable-next-line no-explicit-any\n const Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n>(\n name: string,\n fields: Fields,\n options: { pluralForm?: string; description?: string },\n): TailorDBType<Fields, User, DefinedDBTypeMetadata> {\n let _description = options.description;\n let _settings: TypeFeatures = {};\n let _indexes: IndexDef<TailorDBType<Fields, User, DefinedDBTypeMetadata>>[] = [];\n const _permissions: RawPermissions = {};\n let _files: Record<string, string> = {};\n const _plugins: PluginAttachment[] = [];\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n let _typeHook: { create?: Function; update?: Function } | undefined;\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n let _typeValidate: Function | undefined;\n const _definedMethods = new Set<keyof DefinedDBTypeMetadata>();\n if (options.description !== undefined) {\n _definedMethods.add(\"description\");\n }\n\n function runMethodOnce<T>(method: keyof DefinedDBTypeMetadata, action: () => T): T {\n if (_definedMethods.has(method)) {\n throw new Error(`.${method}() has already been set`);\n }\n const result = action();\n _definedMethods.add(method);\n return result;\n }\n type TypeAfter<Key extends keyof DefinedDBTypeMetadata> = TailorDBType<\n Fields,\n User,\n WithDBTypeMetadata<DefinedDBTypeMetadata, Key>\n >;\n type TypeAfterUser<\n NextUser extends object,\n Key extends keyof DefinedDBTypeMetadata,\n > = TailorDBType<Fields, NextUser, WithDBTypeMetadata<DefinedDBTypeMetadata, Key>>;\n type TypeAfterPlugin<Config extends Record<string, unknown>> = TailorDBType<\n PluginExtendedFields<Fields, Config>,\n User,\n DefinedDBTypeMetadata\n >;\n\n if (options.pluralForm) {\n if (name === options.pluralForm) {\n throw new Error(`The name and the plural form must be different. name=${name}`);\n }\n _settings.pluralForm = options.pluralForm;\n }\n\n const dbType: TailorDBType<Fields, User, DefinedDBTypeMetadata> = {\n name,\n fields: { ...fields },\n _output: null as unknown as InferFieldsOutput<Fields>,\n _description,\n\n get metadata(): TailorDBTypeMetadata {\n // Convert indexes to the format expected by the manifest\n const indexes: Record<string, { fields: string[]; unique?: boolean }> = {};\n if (_indexes.length > 0) {\n _indexes.forEach((index) => {\n const fieldNames = index.fields.map((field) => String(field));\n const key = index.name || `idx_${fieldNames.join(\"_\")}`;\n indexes[key] = {\n fields: fieldNames,\n unique: index.unique,\n };\n });\n }\n\n return {\n name: this.name,\n description: _description,\n settings: _settings,\n permissions: _permissions,\n files: _files,\n ...(Object.keys(indexes).length > 0 && { indexes }),\n ...(_typeHook && { typeHook: _typeHook }),\n ...(_typeValidate && { typeValidate: _typeValidate }),\n };\n },\n\n hooks(hook: TypeHook<Fields>): TypeAfter<\"hooks\"> {\n return runMethodOnce(\"hooks\", () => {\n _typeHook = hook;\n return this as TypeAfter<\"hooks\">;\n });\n },\n\n validate(fn: TypeValidateFn<Fields>): TypeAfter<\"validate\"> {\n return runMethodOnce(\"validate\", () => {\n _typeValidate = fn;\n return this as TypeAfter<\"validate\">;\n });\n },\n\n features(features: Omit<TypeFeatures, \"pluralForm\">): TypeAfter<\"features\"> {\n return runMethodOnce(\"features\", () => {\n _settings = {\n ..._settings,\n ...features,\n };\n return this as TypeAfter<\"features\">;\n });\n },\n\n indexes(\n ...indexes: IndexDef<TailorDBType<Fields, User, DefinedDBTypeMetadata>>[]\n ): TypeAfter<\"indexes\"> {\n return runMethodOnce(\"indexes\", () => {\n _indexes = indexes;\n return this as TypeAfter<\"indexes\">;\n });\n },\n\n files<const F extends string>(\n files: Record<F, string> & FileKeyConflictError<Fields, User>,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<DefinedDBTypeMetadata, \"files\">, F> {\n return runMethodOnce(\"files\", () => {\n _files = files;\n return this as TailorDBType<\n Fields,\n User,\n WithDBTypeMetadata<DefinedDBTypeMetadata, \"files\">,\n F\n >;\n });\n },\n\n permission<\n U extends object = User,\n P extends TailorTypePermission<U, output<TailorDBType<Fields, User, DefinedDBTypeMetadata>>> =\n TailorTypePermission<U, output<TailorDBType<Fields, User, DefinedDBTypeMetadata>>>,\n >(permission: P): TypeAfterUser<U, \"permission\"> {\n return runMethodOnce(\"permission\", () => {\n const ret = this as unknown as TypeAfterUser<U, \"permission\">;\n _permissions.record = permission as RawPermissions[\"record\"];\n return ret;\n });\n },\n\n gqlPermission<\n U extends object = User,\n P extends TailorTypeGqlPermission<U> = TailorTypeGqlPermission<U>,\n >(permission: P): TypeAfterUser<U, \"gqlPermission\"> {\n return runMethodOnce(\"gqlPermission\", () => {\n const ret = this as unknown as TypeAfterUser<U, \"gqlPermission\">;\n _permissions.gql = permission as RawPermissions[\"gql\"];\n return ret;\n });\n },\n\n description(description: string): TypeAfter<\"description\"> {\n return runMethodOnce(\"description\", () => {\n _description = description;\n this._description = description;\n return this as TypeAfter<\"description\">;\n });\n },\n\n pickFields<K extends keyof Fields, const Opt extends FieldOptions>(keys: K[], options?: Opt) {\n const result = {} as Record<K, TailorAnyDBField>;\n for (const key of keys) {\n const field = this.fields[key] as TailorAnyDBField | undefined;\n if (!field) {\n // A plugin-injected field is only added to `fields` after `tailor\n // generate` runs — see the \"Injecting fields into the attached\n // table's type\" section of the plugin docs.\n throw new Error(\n `pickFields(): field \"${String(key)}\" does not exist on this table yet. If it comes from a plugin's .plugin() call, it is only added to the table after \\`tailor generate\\` runs.`,\n );\n }\n if (options) {\n result[key] = field.clone(options);\n } else {\n result[key] = field;\n }\n }\n // oxlint-disable-next-line no-explicit-any\n return result as any;\n },\n\n omitFields<K extends keyof Fields>(keys: K[]): Omit<Fields, K> {\n const keysSet = new Set(keys);\n const result = {} as Record<string, TailorAnyDBField>;\n for (const key in this.fields) {\n if (Object.hasOwn(this.fields, key) && !keysSet.has(key as unknown as K)) {\n result[key] = this.fields[key] as TailorAnyDBField;\n }\n }\n return result as Omit<Fields, K>;\n },\n\n get plugins(): PluginAttachment[] {\n return _plugins;\n },\n\n plugin<const Config extends Record<string, unknown>>(\n config: Config & PluginConfigGuard<Fields, Config, never>,\n ): TypeAfterPlugin<Config> {\n for (const [pluginId, pluginConfig] of Object.entries(config)) {\n _plugins.push({ pluginId, config: pluginConfig });\n }\n return this as unknown as TypeAfterPlugin<Config>;\n },\n };\n\n return brandValue(dbType, \"tailordb-type\");\n}\n\nconst idField = uuid();\ntype idField = typeof idField;\ntype DBTable<\n F extends { id?: never } & Record<string, TailorAnyDBField>,\n Defined extends DefinedDBTypeMetadata = DefinedDBTypeMetadata,\n> = TailorDBInstance<{ id: idField } & F, InferredAttributes, Defined>;\n\n/**\n * Creates a new database table with the specified fields.\n * An `id` field (UUID) is automatically added to every table.\n * @param name - The name of the table, or a tuple of [name, pluralForm]\n * @param fields - The field definitions for the table\n * @returns A new TailorDB table instance\n * @example\n * export const user = db.table(\"User\", {\n * name: db.string(),\n * email: db.string(),\n * age: db.int({ optional: true }),\n * role: db.enum([\"admin\", \"member\"]),\n * ...db.fields.timestamps(),\n * });\n * // Always export both the value and type:\n * export type user = typeof user;\n */\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n fields: F,\n): DBTable<F>;\n/**\n * Creates a new database table with the specified fields and description.\n * An `id` field (UUID) is automatically added to every table.\n * @param name - The name of the table, or a tuple of [name, pluralForm]\n * @param description - A description of the table\n * @param fields - The field definitions for the table\n * @returns A new TailorDB table instance\n */\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n description: string,\n fields: F,\n): DBTable<F, WithDBTypeMetadata<DefinedDBTypeMetadata, \"description\">>;\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n fieldsOrDescription: string | F,\n fields?: F,\n): DBTable<F> | DBTable<F, WithDBTypeMetadata<DefinedDBTypeMetadata, \"description\">> {\n const typeName = Array.isArray(name) ? name[0] : name;\n const pluralForm = Array.isArray(name) ? name[1] : undefined;\n\n let description: string | undefined;\n let fieldDef: F;\n if (typeof fieldsOrDescription === \"string\") {\n description = fieldsOrDescription;\n fieldDef = fields as F;\n } else {\n fieldDef = fieldsOrDescription;\n }\n return createTailorDBType<{ id: idField } & F>(\n typeName,\n {\n id: idField,\n ...fieldDef,\n },\n { pluralForm, description },\n );\n}\n\n// `Function.prototype.toString()` of this hook is embedded verbatim into deployed\n// schemas and migration diffs (see parser/service/tailordb/hooks-validate-precompiled-expr.ts\n// and field.ts). Its source text depends on how the SDK itself was built (e.g.\n// minification), so a fixed expression is pinned onto it directly here instead -\n// configure cannot import parser's `setPrecompiledScriptExpr` runtime helper across\n// the module boundary, only the symbol registry key and map types. Keep\n// this literal in sync with the \"timestamps() updatedAt hook resolves to the pinned\n// expr\" test in parser/service/tailordb/field.precompiled.test.ts, which fails if it\n// ever drifts from what this hook's own source naturally produces.\ntype TimestampsUpdatedAtHookFn = UpdateHookFn<string | Date | null, string | Date>;\nconst timestampsUpdatedAtHook: TimestampsUpdatedAtHookFn = ({ input, now }) => input ?? now;\nconst PRECOMPILED_EXPR_KEY: PrecompiledScriptExprKey =\n \"tailor-platform/sdk:precompiled-script-expr\";\nconst PRECOMPILED_EXPR_SYMBOL = Symbol.for(PRECOMPILED_EXPR_KEY);\n(timestampsUpdatedAtHook as unknown as Record<symbol, PrecompiledScriptExprMap>)[\n PRECOMPILED_EXPR_SYMBOL\n] = {\n \"hooks.update\":\n \"(({ input, now }) => input ?? now)({ input: _value, oldValue: _oldValue, invoker: _principal, now: _now })\",\n};\n\n/** TailorDB schema builder utilities for defining tables and fields. */\nexport const db = {\n table: dbTable,\n uuid,\n string,\n bool,\n int,\n float,\n decimal,\n date,\n datetime,\n time,\n enum: _enum,\n object,\n fields: {\n /**\n * Creates standard timestamp fields (createdAt, updatedAt) with automatic defaults.\n * Both fields default to the current time on create. updatedAt is also refreshed on update.\n * User-specified values are respected when provided (e.g. seeding historical records).\n * @returns An object with createdAt and updatedAt fields\n * @example\n * const model = db.table(\"Model\", {\n * name: db.string(),\n * ...db.fields.timestamps(),\n * });\n */\n timestamps: () => ({\n createdAt: datetime().default(\"now\").description(\"Record creation timestamp\"),\n updatedAt: datetime()\n .default(\"now\")\n .hooks({ update: timestampsUpdatedAtHook })\n .description(\"Record update timestamp\"),\n }),\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"schema-BKvcWdVf.mjs","names":["parseInternal","parseFieldInternal"],"sources":["../src/configure/types/field.ts","../src/configure/services/tailordb/schema.ts"],"sourcesContent":["import type { EnumValue } from \"#/configure/types/field.types\";\n\nexport type AllowedValues = readonly [string | EnumValue, ...(string | EnumValue)[]];\n\n/**\n * Normalize allowed values into EnumValue objects with descriptions.\n * @param values - Allowed values as strings or EnumValue objects\n * @returns Normalized allowed values\n */\nexport function mapAllowedValues(values: AllowedValues): EnumValue[] {\n return values.map((value) => {\n if (typeof value === \"string\") {\n return { value, description: \"\" };\n }\n return { ...value, description: value.description ?? \"\" };\n });\n}\n\nexport type AllowedValuesOutput<V extends AllowedValues> = V[number] extends infer T\n ? T extends string\n ? T\n : T extends { value: infer K }\n ? K\n : never\n : never;\n","import { cloneDeep } from \"es-toolkit\";\nimport {\n type AllowedValues,\n type AllowedValuesOutput,\n mapAllowedValues,\n} from \"#/configure/types/field\";\nimport {\n parseInternal as parseFieldInternal,\n type FieldParseArgs,\n type FieldParseInternalArgs,\n} from \"#/runtime/field-parse\";\nimport { brandValue } from \"#/utils/brand\";\nimport type {\n FieldOptions,\n FieldOutput,\n TailorFieldType,\n TailorToTs,\n FieldValidateInput,\n} from \"#/configure/types/field.types\";\nimport type {\n PrecompiledScriptExprKey,\n PrecompiledScriptExprMap,\n} from \"#/parser/service/tailordb/types\";\nimport type { PluginAttachment, PluginConfigs, PluginFieldExtensions } from \"#/plugin/types\";\nimport type { InferredAttributes } from \"#/runtime/types\";\nimport type {\n output,\n InferFieldsOutput,\n IsUnion,\n TypeLevelError,\n UnionToIntersection,\n} from \"#/types/helpers\";\nimport type { RawPermissions } from \"#/types/tailordb.generated\";\nimport type { TailorTypeGqlPermission, TailorTypePermission } from \"./permission\";\nimport type {\n TailorDBField as TailorDBFieldBase,\n TailorDBType as TailorDBTypeBase,\n DBFieldMetadata,\n DefinedDBFieldMetadata,\n SerialConfig,\n IndexDef,\n TailorDBTypeMetadata,\n RawRelationConfig,\n RelationType,\n Hook,\n TypeHook,\n UpdateHookFn,\n ExcludeNestedDBFields,\n ExcludeHookedDBFields,\n ExcludeDefaultedDBFields,\n TypeFeatures,\n TypeValidateFn,\n} from \"./types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// Erased DB fields stay assignable across builder method-state changes.\n// oxlint-disable-next-line no-explicit-any\ntype AnyBuilderMethod = any;\n\nexport type TailorAnyDBField = Omit<\n TailorDBFieldBase<AnyBuilderMethod, AnyBuilderMethod>,\n \"fields\"\n> & {\n readonly fields: Record<string, AnyBuilderMethod>;\n _metadata: DBFieldMetadata;\n parse: AnyBuilderMethod;\n readonly typeName: TypeLevelError<string>;\n description: AnyBuilderMethod;\n relation: AnyBuilderMethod;\n index: AnyBuilderMethod;\n unique: AnyBuilderMethod;\n vector: AnyBuilderMethod;\n default: AnyBuilderMethod;\n hooks: AnyBuilderMethod;\n validate: AnyBuilderMethod;\n serial: AnyBuilderMethod;\n clone: AnyBuilderMethod;\n};\n\n// Helper alias\n// oxlint-disable-next-line no-explicit-any\nexport type TailorAnyDBType = TailorDBType<any, any, any>;\n\ntype IsAny<T> = 0 extends 1 & T ? true : false;\ntype DBFieldTypeNameMethod<Defined extends DefinedDBFieldMetadata> =\n IsAny<Defined> extends true\n ? TypeLevelError<string>\n : TypeLevelError<\"typeName cannot be used on TailorDB fields\">;\n\ntype WithDBFieldDescription<Defined> = Defined & { description: true };\ntype WithDBFieldRelation<Defined, S extends RelationType | RelationSelfConfig> = S extends\n | \"oneToOne\"\n | \"1-1\"\n ? Defined & { unique: true; index: true; relation: true }\n : S extends { type: \"oneToOne\" | \"1-1\" }\n ? Defined & { unique: true; index: true; relation: true }\n : Defined & { index: true; relation: true };\ntype WithDBFieldIndex<Defined> = Defined & { index: true };\ntype WithDBFieldUnique<Defined> = Defined & { unique: true; index: true };\ntype WithDBFieldVector<Defined> = Defined & { vector: true };\ntype WithDBFieldHooks<Defined, H> = Defined & {\n hooks: {\n create: H extends { create: unknown } ? true : false;\n update: H extends { update: unknown } ? true : false;\n };\n serial: false;\n};\ntype WithDBFieldDefault<Defined> = Defined & { default: true };\ntype WithDBFieldValidate<Defined> = Defined & { validate: true };\ntype WithDBFieldSerial<Defined> = Defined & {\n serial: true;\n hooks: { create: false; update: false };\n};\ntype WithDBFieldCloneOptions<Defined extends DefinedDBFieldMetadata, NewOpt extends FieldOptions> =\n IsAny<Defined> extends true\n ? Defined\n : Omit<Defined, \"array\"> & {\n array: NewOpt extends { array: true }\n ? true\n : NewOpt extends { array: false }\n ? false\n : Defined[\"array\"];\n };\ntype NonNullableDBFieldOutput<Output> = Exclude<Output, null>;\ntype DBFieldScalarOutput<Output> =\n NonNullableDBFieldOutput<Output> extends (infer Item)[] ? Item : NonNullableDBFieldOutput<Output>;\ntype DBFieldCloneArrayOutput<Output, NewOpt extends FieldOptions> = NewOpt extends {\n array: true;\n}\n ? DBFieldScalarOutput<Output>[]\n : NewOpt extends { array: false }\n ? DBFieldScalarOutput<Output>\n : NonNullableDBFieldOutput<Output>;\ntype DBFieldCloneOutput<Output, NewOpt extends FieldOptions> = NewOpt extends { optional: true }\n ? DBFieldCloneArrayOutput<Output, NewOpt> | null\n : NewOpt extends { optional: false }\n ? DBFieldCloneArrayOutput<Output, NewOpt>\n : null extends Output\n ? DBFieldCloneArrayOutput<Output, NewOpt> | null\n : DBFieldCloneArrayOutput<Output, NewOpt>;\ntype DBFieldCloneOptions<Defined extends DefinedDBFieldMetadata> = Omit<FieldOptions, \"array\"> & {\n array?: Defined extends { validate: unknown } ? Defined[\"array\"] : boolean;\n};\ntype InvalidValidatedArrayCloneKeys<\n Fields extends Record<string, TailorAnyDBField>,\n K extends keyof Fields,\n Opt extends FieldOptions,\n> = Opt extends { array: infer ArrayOption }\n ? {\n [P in K]: Fields[P] extends TailorDBField<infer Defined, infer _Output>\n ? Defined extends { validate: unknown }\n ? ArrayOption extends Defined[\"array\"]\n ? never\n : P\n : never\n : never;\n }[K]\n : never;\ntype DBFieldsCloneOptionsGuard<\n Fields extends Record<string, TailorAnyDBField>,\n K extends keyof Fields,\n Opt extends FieldOptions,\n> = [InvalidValidatedArrayCloneKeys<Fields, K, Opt>] extends [never]\n ? unknown\n : TypeLevelError<\"array cannot be changed on fields with custom validation\">;\ntype DefinedDBTypeMetadata = {\n hooks?: true;\n validate?: true;\n features?: true;\n indexes?: true;\n files?: true;\n permission?: true;\n gqlPermission?: true;\n description?: true;\n};\ntype WithDBTypeMetadata<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n> = Defined & Record<Key, true>;\ntype DBTypeDuplicateInputGuard<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n Input,\n Message extends string,\n> =\n IsAny<Defined> extends true\n ? Input\n : Defined extends Record<Key, unknown>\n ? TypeLevelError<Message>\n : Input;\ntype DBTypeDuplicateRestGuard<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n Input extends unknown[],\n Message extends string,\n> =\n IsAny<Defined> extends true\n ? Input\n : Defined extends Record<Key, unknown>\n ? [TypeLevelError<Message>, ...TypeLevelError<Message>[]]\n : Input;\ntype FileKeyConflictError<\n Fields extends Record<string, TailorAnyDBField>,\n User extends object,\n> = Partial<\n Record<\n keyof output<TailorDBType<Fields, User>> & string,\n TypeLevelError<\"file keys cannot use existing field names\">\n >\n>;\n// A plugin id with no PluginFieldExtensions entry contributes no keys — using\n// Record<never, never> (rather than Record<string, never>) keeps its `keyof`\n// empty so it can't spuriously collide with every existing field.\ntype PluginFieldExtensionFor<\n Fields extends Record<string, TailorAnyDBField>,\n Id extends string,\n IdConfig,\n> = Id extends keyof PluginFieldExtensions<keyof Fields & string, IdConfig>\n ? PluginFieldExtensions<keyof Fields & string, IdConfig>[Id]\n : Record<never, never>;\ntype PluginFieldExtensionsUnion<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n> = {\n [Id in keyof Config & string]: PluginFieldExtensionFor<Fields, Id, Config[Id]>;\n}[keyof Config & string];\ntype AllExtensionKeys<Ext> = Ext extends unknown ? keyof Ext : never;\n// True when plugin `Id` has a PluginFieldExtensions entry whose type does not\n// extend Record<string, TailorAnyDBField> — e.g. a plugin author registered\n// a non-record shape by mistake. Checked ahead of PluginFieldConflict, since\n// `keyof` on a non-record shape (e.g. `keyof string`) would otherwise produce\n// nonsense candidate keys for the collision check.\n// `Id extends string` (not `keyof Config & string`) deliberately: a mapped\n// type's key variable, used inside a nested conditional branch and then\n// intersected with `string`, does not structurally satisfy a `keyof Config &\n// string`-constrained type parameter under `skipLibCheck: false` (verified\n// against a standalone consumer — TS2344 on both call sites otherwise, even\n// for code that never calls .plugin()). `Config[Id & keyof Config]` recovers\n// the same indexed-access result without that constraint.\ntype PluginFieldExtensionShapeError<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n> =\n PluginFieldExtensionFor<Fields, Id, Config[Id & keyof Config]> extends Record<\n string,\n TailorAnyDBField\n >\n ? false\n : true;\n// True when the fields plugin `Id` would inject collide with an existing\n// field, or with a field injected by another plugin id attached in the same\n// .plugin() call.\ntype PluginFieldConflict<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n FileKeys extends string,\n> = [\n AllExtensionKeys<PluginFieldExtensionFor<Fields, Id, Config[Id & keyof Config]>> &\n (\n | keyof Fields\n | FileKeys\n | AllExtensionKeys<PluginFieldExtensionsUnion<Fields, Omit<Config, Id>>>\n ),\n] extends [never]\n ? false\n : true;\n// Rejects a property on the caller's config literal for plugin `Id` that\n// isn't part of its registered PluginConfigs shape. `Config` is captured by\n// a `const` type parameter from the argument itself, so intersecting the\n// success branch with `PluginConfigs<...>[Id]` alone would not trigger\n// TypeScript's excess-property check — that check only fires when an object\n// literal is validated directly against a target type, not after its shape\n// has already been inferred through a generic parameter.\ntype PluginConfigExcessProps<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n> = Record<\n Exclude<\n keyof Config[Id & keyof Config],\n keyof PluginConfigs<keyof Fields & string>[Id & keyof PluginConfigs<keyof Fields & string>]\n >,\n never\n>;\ntype PluginExtendedFields<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n> =\n IsAny<Fields> extends true\n ? Fields\n : // For `Config = {}`, PluginFieldExtensionsUnion is `never`, and\n // UnionToIntersection<never> is `unknown` (not `never`), so this\n // correctly reduces to `Fields` — see the \"empty config\" test.\n Fields & UnionToIntersection<PluginFieldExtensionsUnion<Fields, Config>>;\n// Validates each key of the passed config object against PluginConfigs and\n// flags field-extension conflicts (see PluginFieldConflict above), keeping\n// type errors localized to the offending plugin id instead of requiring\n// every registered plugin id to be present (as a `P extends keyof\n// PluginConfigs<...>` generic would need, defeating inference of which\n// plugin ids were actually passed).\ntype PluginConfigGuard<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n FileKeys extends string,\n> =\n IsAny<Fields> extends true\n ? unknown\n : {\n [K in keyof Config]: K extends keyof PluginConfigs<keyof Fields & string>\n ? // A union config (e.g. from a ternary passed directly as a plugin's\n // value) distributes through PluginFieldExtensionFor's conditional\n // and silently collapses the injected field to `never` once\n // intersected back together — reject it here instead, before it\n // can produce that confusing downstream error.\n IsUnion<Config[K]> extends true\n ? TypeLevelError<\"plugin config must be a single object literal, not a union — assign the config to a variable first if it comes from a conditional expression\">\n : PluginFieldExtensionShapeError<Fields, Config, K & string> extends true\n ? TypeLevelError<\"PluginFieldExtensions entry must be a Record<string, TailorAnyDBField>\">\n : PluginFieldConflict<Fields, Config, K & string, FileKeys> extends true\n ? TypeLevelError<\"plugin field extension conflicts with an existing field, a file key declared via .files(), or another plugin's field\">\n : PluginConfigs<keyof Fields & string>[K] &\n PluginConfigExcessProps<Fields, Config, K & string>\n : TypeLevelError<\"unknown plugin id\">;\n };\ntype DBFieldDescriptionFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (description: string) => TailorDBField<WithDBFieldDescription<Defined>, Output, Nested>;\ntype DBFieldRelationFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = {\n <S extends RelationType, T extends TailorAnyDBType>(\n config: RelationConfig<S, T>,\n ): TailorDBField<WithDBFieldRelation<Defined, S>, Output, Nested>;\n <S extends RelationSelfConfig>(\n config: S,\n ): TailorDBField<WithDBFieldRelation<Defined, S>, Output, Nested>;\n};\ntype DBFieldIndexFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldIndex<Defined>, Output, Nested>;\ntype DBFieldUniqueFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldUnique<Defined>, Output, Nested>;\ntype DBFieldVectorFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldVector<Defined>, Output, Nested>;\ntype DBFieldHooksFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = <\n const H extends Hook<\n Output,\n Defined extends { default: true } ? Output | null | undefined : Output\n >,\n>(\n hooks: H,\n) => TailorDBField<WithDBFieldHooks<Defined, H>, Output, Nested>;\ntype DBFieldValidateFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n ...validate: FieldValidateInput<Output>[]\n) => TailorDBField<WithDBFieldValidate<Defined>, Output, Nested>;\ntype DBFieldSerialFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n config: SerialConfig<Defined[\"type\"] & (\"integer\" | \"string\")>,\n) => TailorDBField<WithDBFieldSerial<Defined>, Output, Nested>;\ntype DBFieldDescriptionMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldDescriptionFn<Defined, Output, Nested>\n : Defined extends { description: unknown }\n ? TypeLevelError<\".description() has already been set\">\n : DBFieldDescriptionFn<Defined, Output, Nested>;\ntype DBFieldRelationMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldRelationFn<Defined, Output, Nested>\n : Defined extends { relation: unknown }\n ? TypeLevelError<\".relation() has already been set\">\n : DBFieldRelationFn<Defined, Output, Nested>;\ntype DBFieldArrayCheck<A extends boolean, Ok, Msg extends string> = A extends true\n ? TypeLevelError<Msg>\n : Ok;\ntype DBFieldIndexMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldIndexFn<Defined, Output, Nested>\n : Defined extends { index: unknown }\n ? TypeLevelError<\".index() has already been set\">\n : DBFieldArrayCheck<\n Defined[\"array\"],\n DBFieldIndexFn<Defined, Output, Nested>,\n \"index cannot be set on array fields\"\n >;\ntype DBFieldUniqueMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldUniqueFn<Defined, Output, Nested>\n : Defined extends { unique: unknown }\n ? TypeLevelError<\".unique() has already been set\">\n : DBFieldArrayCheck<\n Defined[\"array\"],\n DBFieldUniqueFn<Defined, Output, Nested>,\n \"unique cannot be set on array fields\"\n >;\ntype DBFieldVectorMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldVectorFn<Defined, Output, Nested>\n : Defined extends { vector: unknown }\n ? TypeLevelError<\".vector() has already been set\">\n : Defined extends { type: \"string\"; array: false }\n ? DBFieldVectorFn<Defined, Output, Nested>\n : TypeLevelError<\"vector can only be set on non-array string fields\">;\ntype DBFieldHooksMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldHooksFn<Defined, Output, Nested>\n : Defined extends {\n serial: true;\n hooks: { create: false; update: false };\n }\n ? TypeLevelError<\"hooks cannot be set after serial\">\n : Defined extends {\n hooks: unknown;\n }\n ? TypeLevelError<\".hooks() has already been set\">\n : Defined extends { type: \"nested\" }\n ? TypeLevelError<\"hooks cannot be set on nested type fields\">\n : DBFieldHooksFn<Defined, Output, Nested>;\ntype DBFieldValidateMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldValidateFn<Defined, Output, Nested>\n : Defined extends { validate: unknown }\n ? TypeLevelError<\".validate() has already been set\">\n : DBFieldValidateFn<Defined, Output, Nested>;\ntype DBFieldSerialMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldSerialFn<Defined, Output, Nested>\n : Defined extends { serial: true }\n ? TypeLevelError<\".serial() has already been set\">\n : Defined extends { serial: false }\n ? TypeLevelError<\"serial cannot be set after hooks\">\n : IsAny<Output> extends true\n ? Defined extends { type: \"integer\" | \"string\"; array: false }\n ? DBFieldSerialFn<Defined, Output, Nested>\n : TypeLevelError<\"serial can only be set on non-array integer or string fields\">\n : null extends Output\n ? TypeLevelError<\"serial can only be set on non-array integer or string fields\">\n : Defined extends { type: \"integer\" | \"string\"; array: false }\n ? DBFieldSerialFn<Defined, Output, Nested>\n : TypeLevelError<\"serial can only be set on non-array integer or string fields\">;\ntype DBFieldDefaultFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n value: Output extends null ? NonNullable<Output> : Output,\n) => TailorDBField<WithDBFieldDefault<Defined>, Output, Nested>;\ntype DBFieldDefaultMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldDefaultFn<Defined, Output, Nested>\n : Defined extends { default: unknown }\n ? TypeLevelError<\".default() has already been set\">\n : Defined extends { type: \"nested\" }\n ? TypeLevelError<\"default cannot be set on nested type fields\">\n : Defined extends { serial: true }\n ? TypeLevelError<\"default cannot be set on serial fields\">\n : null extends Output\n ? TypeLevelError<\"default cannot be set on optional fields\">\n : DBFieldDefaultFn<Defined, Output, Nested>;\n\n/**\n * Full TailorDBField interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorDBField<\n Defined extends DefinedDBFieldMetadata = DefinedDBFieldMetadata,\n // oxlint-disable-next-line no-explicit-any\n Output = any,\n // Nested object fields, so a `db.object()` field keeps the shape it was declared with.\n // Every builder method passes it through; dropping it here would erase the shape as\n // soon as one is chained.\n Nested extends Record<string, TailorAnyDBField> = Record<string, TailorAnyDBField>,\n> extends Omit<TailorDBFieldBase<Defined, Output, Nested>, \"fields\"> {\n readonly fields: Nested;\n _metadata: DBFieldMetadata;\n\n /**\n * Parse and validate a value against this field's validation rules\n */\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n\n /**\n * typeName is not available on TailorDB fields.\n * Use typeName on pipeline fields (t.enum / t.object) instead.\n */\n typeName: DBFieldTypeNameMethod<Defined>;\n\n /**\n * Set a description for the field\n */\n description: DBFieldDescriptionMethod<Defined, Output, Nested>;\n\n /**\n * Define a relation to another table.\n */\n relation: DBFieldRelationMethod<Defined, Output, Nested>;\n\n /**\n * Add an index to the field\n */\n index: DBFieldIndexMethod<Defined, Output, Nested>;\n\n /**\n * Make the field unique (also adds an index)\n */\n unique: DBFieldUniqueMethod<Defined, Output, Nested>;\n\n /**\n * Enable vector search on the field (string type only)\n */\n vector: DBFieldVectorMethod<Defined, Output, Nested>;\n\n /**\n * Set a default value for the field on create. When the field is required,\n * this makes it optional in the Create input — the default fills in when\n * no value (or a nullish hook result) is provided.\n *\n * For datetime/date/time fields, pass `\"now\"` to use the operation timestamp.\n */\n default: DBFieldDefaultMethod<Defined, Output, Nested>;\n\n /**\n * Add hooks for create/update operations on this field.\n */\n hooks: DBFieldHooksMethod<Defined, Output, Nested>;\n\n /**\n * Add validation functions to the field.\n *\n * Validators receive `{ value, data, user }` and run after hooks and\n * built-in type validation; they are skipped when built-in validation\n * fails. For array fields, `value` is the complete array.\n */\n validate: DBFieldValidateMethod<Defined, Output, Nested>;\n\n /**\n * Configure serial/auto-increment behavior\n */\n serial: DBFieldSerialMethod<Defined, Output, Nested>;\n\n /**\n * Clone the field with optional overrides for field options.\n * The `array` option cannot change on fields with custom validation.\n */\n clone<const NewOpt extends DBFieldCloneOptions<Defined>>(\n options?: NewOpt,\n ): TailorDBField<\n WithDBFieldCloneOptions<Defined, NewOpt>,\n DBFieldCloneOutput<Output, NewOpt>,\n Nested\n >;\n}\n\n/**\n * Full TailorDBType interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorDBType<\n // oxlint-disable-next-line no-explicit-any\n Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n // oxlint-disable-next-line no-explicit-any\n Defined extends DefinedDBTypeMetadata = any,\n FileKeys extends string = never,\n> extends TailorDBTypeBase<Fields, User> {\n _description?: string;\n\n hooks(\n hook: DBTypeDuplicateInputGuard<\n Defined,\n \"hooks\",\n TypeHook<Fields>,\n \".hooks() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"hooks\">, FileKeys>;\n validate(\n fn: DBTypeDuplicateInputGuard<\n Defined,\n \"validate\",\n TypeValidateFn<Fields>,\n \".validate() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"validate\">, FileKeys>;\n features(\n features: DBTypeDuplicateInputGuard<\n Defined,\n \"features\",\n Omit<TypeFeatures, \"pluralForm\">,\n \".features() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"features\">, FileKeys>;\n indexes(\n ...indexes: DBTypeDuplicateRestGuard<\n Defined,\n \"indexes\",\n IndexDef<TailorDBType<Fields, User, Defined, FileKeys>>[],\n \".indexes() has already been set\"\n >\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"indexes\">, FileKeys>;\n files<const F extends string>(\n files: DBTypeDuplicateInputGuard<\n Defined,\n \"files\",\n Record<F, string> & FileKeyConflictError<Fields, User>,\n \".files() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"files\">, FileKeys | F>;\n permission<\n U extends object = User,\n P extends TailorTypePermission<U, output<TailorDBType<Fields, User, Defined>>> =\n TailorTypePermission<U, output<TailorDBType<Fields, User, Defined>>>,\n >(\n permission: DBTypeDuplicateInputGuard<\n Defined,\n \"permission\",\n P,\n \".permission() has already been set\"\n >,\n ): TailorDBType<Fields, U, WithDBTypeMetadata<Defined, \"permission\">, FileKeys>;\n gqlPermission<\n U extends object = User,\n P extends TailorTypeGqlPermission<U> = TailorTypeGqlPermission<U>,\n >(\n permission: DBTypeDuplicateInputGuard<\n Defined,\n \"gqlPermission\",\n P,\n \".gqlPermission() has already been set\"\n >,\n ): TailorDBType<Fields, U, WithDBTypeMetadata<Defined, \"gqlPermission\">, FileKeys>;\n description(\n description: DBTypeDuplicateInputGuard<\n Defined,\n \"description\",\n string,\n \".description() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"description\">, FileKeys>;\n pickFields<K extends keyof Fields>(keys: K[]): Pick<Fields, K>;\n pickFields<K extends keyof Fields, const Opt extends FieldOptions>(\n keys: K[],\n options: Opt & DBFieldsCloneOptionsGuard<Fields, K, Opt>,\n ): {\n [P in K]: Fields[P] extends TailorDBField<infer D, infer O>\n ? TailorDBField<WithDBFieldCloneOptions<D, Opt>, DBFieldCloneOutput<O, Opt>>\n : never;\n };\n omitFields<K extends keyof Fields>(keys: K[]): Omit<Fields, K>;\n plugin<const Config extends Record<string, unknown>>(\n config: Config & PluginConfigGuard<Fields, Config, FileKeys>,\n ): TailorDBType<PluginExtendedFields<Fields, Config>, User, Defined, FileKeys>;\n}\n\nexport type TailorDBInstance<\n // oxlint-disable-next-line no-explicit-any\n Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n // oxlint-disable-next-line no-explicit-any\n Defined extends DefinedDBTypeMetadata = any,\n FileKeys extends string = never,\n> = TailorDBType<Fields, User, Defined, FileKeys>;\n\ninterface RelationConfig<S extends RelationType, T extends TailorDBType> {\n type: S;\n toward:\n | {\n table: T;\n as?: string;\n key?: keyof T[\"fields\"] & string;\n type?: never;\n }\n | {\n /**\n * @deprecated since 2.6.0 — use `table` instead. codemod: v3/relation-toward-table\n */\n type: T;\n as?: string;\n key?: keyof T[\"fields\"] & string;\n table?: never;\n };\n backward?: string;\n}\n\n// Special config variant for self-referencing relations\ntype RelationSelfConfig = {\n type: RelationType;\n toward:\n | {\n table: \"self\";\n as?: string;\n key?: string;\n type?: never;\n }\n | {\n /**\n * @deprecated since 2.6.0 — use `table` instead. codemod: v3/relation-toward-table\n */\n type: \"self\";\n as?: string;\n key?: string;\n table?: never;\n };\n backward?: string;\n};\n\ntype DBFieldDefined<T extends TailorFieldType, Opt extends FieldOptions> = {\n type: T;\n array: Opt extends { array: true } ? true : false;\n};\ntype DBFieldOutput<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = FieldOutput<OutputBase, Opt>;\ntype TailorDBFieldInstance<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = TailorDBField<DBFieldDefined<T, Opt>, DBFieldOutput<T, Opt, OutputBase>>;\ntype TailorDBFieldRuntimeInstance<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = TailorDBFieldRuntime<DBFieldDefined<T, Opt>, DBFieldOutput<T, Opt, OutputBase>>;\ntype TailorDBFieldRuntime<Defined extends DefinedDBFieldMetadata, Output> = Omit<\n TailorDBFieldBase<Defined, Output>,\n \"fields\"\n> & {\n readonly fields: Record<string, TailorAnyDBField>;\n _metadata: DBFieldMetadata;\n description(description: string): object;\n typeName(typeName: string): object;\n validate(...validate: FieldValidateInput<Output>[]): object;\n relation(config: RelationConfig<RelationType, TailorDBType> | RelationSelfConfig): object;\n index(): object;\n unique(): object;\n vector(): object;\n default(value: unknown): object;\n hooks(hooks: Hook<Output>): object;\n serial(config: SerialConfig): object;\n clone(options?: FieldOptions): TailorDBFieldRuntime<DefinedDBFieldMetadata, AnyBuilderMethod>;\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n _setRawRelation(relation: RawRelationConfig): void;\n};\n\n/**\n * Creates a new TailorDBField 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 * @returns A new TailorDBField\n */\nfunction createTailorDBField<\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, TailorAnyDBField>,\n values?: AllowedValues,\n): TailorDBFieldInstance<T, TOptions, OutputBase>;\nfunction createTailorDBField<const T extends TailorFieldType, const TOptions extends FieldOptions>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyDBField>,\n values?: AllowedValues,\n): object {\n return createTailorDBFieldRuntime(type, options, fields, values);\n}\n\nfunction createTailorDBFieldRuntime<\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, TailorAnyDBField>,\n values?: AllowedValues,\n): TailorDBFieldRuntimeInstance<T, TOptions, OutputBase> {\n type FieldValue = DBFieldOutput<T, TOptions, OutputBase>;\n type FieldType = TailorDBFieldRuntimeInstance<T, TOptions, OutputBase>;\n\n const _metadata: DBFieldMetadata = { required: true };\n let _rawRelation: RawRelationConfig | undefined;\n\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 function parseInternal(args: FieldParseInternalArgs): StandardSchemaV1.Result<FieldValue> {\n return parseFieldInternal<T, FieldValue>({\n ...args,\n field,\n });\n }\n\n function cloneWith(metadataUpdates: Partial<DBFieldMetadata>) {\n const cloned = field.clone();\n Object.assign(cloned._metadata, metadataUpdates);\n return cloned;\n }\n\n const field: FieldType = {\n type,\n fields: fields ?? {},\n _defined: undefined as unknown as DBFieldDefined<T, TOptions>,\n _output: undefined as FieldValue,\n _metadata,\n\n get metadata() {\n return { ...this._metadata };\n },\n\n get rawRelation(): Readonly<RawRelationConfig> | undefined {\n return _rawRelation ? { ..._rawRelation, toward: { ..._rawRelation.toward } } : undefined;\n },\n\n description(description: string) {\n return cloneWith({ description });\n },\n\n typeName(typeName: string) {\n return cloneWith({ typeName });\n },\n\n validate(...validateInputs: FieldValidateInput<FieldValue>[]) {\n return cloneWith({ validate: validateInputs });\n },\n\n parse(args: FieldParseArgs): StandardSchemaV1.Result<FieldValue> {\n return parseInternal({\n value: args.value,\n data: args.data,\n invoker: args.invoker,\n pathArray: [],\n });\n },\n\n // TailorDBField specific methods\n relation(config: RelationConfig<RelationType, TailorDBType> | RelationSelfConfig) {\n const cloned = field.clone();\n // The public type is a nested union (RelationConfig | RelationSelfConfig,\n // each with a `{ table } | { type }` toward), which TS's \"in\" narrowing\n // can't discriminate across cleanly. Re-view it as the flat two-branch\n // shape it always is at runtime — the `table?: never` / `type?: never`\n // markers above already forbid both keys from being set together.\n const toward = config.toward as\n | { table: TailorDBType | \"self\" }\n | { type: TailorDBType | \"self\" };\n const towardTarget = \"table\" in toward ? toward.table : toward.type;\n const targetTable = towardTarget === \"self\" ? \"self\" : towardTarget.name;\n cloned._setRawRelation({\n type: config.type,\n toward: {\n table: targetTable,\n as: config.toward.as,\n key: config.toward.key,\n },\n backward: config.backward,\n });\n return cloned;\n },\n\n index() {\n return cloneWith({ index: true });\n },\n\n unique() {\n return cloneWith({ unique: true, index: true });\n },\n\n vector() {\n return cloneWith({ vector: true });\n },\n\n // oxlint-disable-next-line no-explicit-any\n default(value: any) {\n // oxlint-disable-next-line no-explicit-any\n return cloneWith({ default: value }) as any;\n },\n\n hooks(hooks: Hook<FieldValue>) {\n return cloneWith({ hooks });\n },\n\n serial(config: SerialConfig) {\n return cloneWith({ serial: config });\n },\n\n clone(cloneOptions?: FieldOptions) {\n if (\n this._metadata.validate?.length &&\n cloneOptions?.array !== undefined &&\n cloneOptions.array !== (this._metadata.array === true)\n ) {\n throw new Error(\"Cannot change the array option on a field with custom validation\");\n }\n\n // Deep clone nested object fields if present\n let clonedFields = fields;\n if (fields) {\n const cloned: Record<string, TailorAnyDBField> = {};\n for (const [key, field] of Object.entries(fields)) {\n cloned[key] = field.clone();\n }\n clonedFields = cloned;\n }\n\n // Create a new field with cloned configuration\n const clonedField = createTailorDBFieldRuntime(type, options, clonedFields, values);\n\n // Deep copy metadata using cloneDeep (preserves function references)\n Object.assign(clonedField._metadata, cloneDeep(this._metadata));\n\n // Apply new options if provided\n if (cloneOptions) {\n if (cloneOptions.optional !== undefined) {\n clonedField._metadata.required = !cloneOptions.optional;\n }\n if (cloneOptions.array !== undefined) {\n clonedField._metadata.array = cloneOptions.array;\n }\n }\n\n // Copy raw relation if exists\n if (_rawRelation) {\n clonedField._setRawRelation(cloneDeep(_rawRelation));\n }\n\n return clonedField;\n },\n\n _setRawRelation(relation: RawRelationConfig) {\n _rawRelation = relation;\n },\n };\n\n return field;\n}\n\nconst createField = createTailorDBField;\n\n/**\n * Create a UUID field.\n * @param options - Field configuration options\n * @returns A UUID field\n * @example db.uuid()\n * @example db.uuid({ optional: true })\n */\nfunction uuid<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"uuid\", options);\n}\n\n/**\n * Create a string field.\n * @param options - Field configuration options\n * @returns A string field\n * @example db.string()\n * @example db.string({ optional: true })\n */\nfunction string<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"string\", options);\n}\n\n/**\n * Create a boolean field.\n * Note: The method name is `bool` but creates a `boolean` type field.\n * @param options - Field configuration options\n * @returns A boolean field\n * @example db.bool()\n * @example db.bool({ optional: true })\n */\nfunction bool<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"boolean\", options);\n}\n\n/**\n * Create an integer field.\n * @param options - Field configuration options\n * @returns An integer field\n * @example db.int()\n * @example db.int({ optional: true })\n */\nfunction int<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"integer\", options);\n}\n\n/**\n * Create a float (decimal number) field.\n * @param options - Field configuration options\n * @returns A float field\n * @example db.float()\n * @example db.float({ optional: true })\n */\nfunction float<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"float\", options);\n}\n\ninterface DecimalFieldOptions extends FieldOptions {\n scale?: number;\n}\n\n/**\n * Create a decimal field (stored as string for precision).\n * @param options - Field configuration options including optional scale (0-12)\n * @returns A decimal field\n * @example db.decimal()\n * @example db.decimal({ scale: 2 })\n * @example db.decimal({ scale: 2, optional: true })\n */\nfunction decimal<const Opt extends DecimalFieldOptions>(options?: Opt) {\n if (options?.scale !== undefined) {\n if (!Number.isInteger(options.scale) || options.scale < 0 || options.scale > 12) {\n throw new Error(\"scale must be an integer between 0 and 12\");\n }\n }\n const field = createField(\"decimal\", options);\n if (options?.scale !== undefined) {\n field._metadata.scale = options.scale;\n }\n return field;\n}\n\n/**\n * Create a date field (date only, no time component).\n * Format: \"yyyy-MM-dd\"\n * @param options - Field configuration options\n * @returns A date field\n * @example db.date()\n */\nfunction date<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"date\", options);\n}\n\n/**\n * Create a datetime field (date and time).\n * Format: ISO 8601, such as \"yyyy-MM-ddTHH:mm:ssZ\" or \"yyyy-MM-ddTHH:mm:ss+09:00\"\n * @param options - Field configuration options\n * @returns A datetime field\n * @example db.datetime()\n */\nfunction datetime<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"datetime\", options);\n}\n\n/**\n * Create a time field (time only, no date component).\n * Format: \"HH:mm\"\n * @param options - Field configuration options\n * @returns A time field\n * @example db.time()\n */\nfunction time<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"time\", options);\n}\n\n/**\n * Create an enum field with at least one allowed string value.\n * @param values - Array of allowed string values, or array of `{ value, description }` objects\n * @param options - Field configuration options\n * @returns An enum field\n * @example db.enum([\"active\", \"inactive\", \"suspended\"])\n * @example db.enum([\"small\", \"medium\", \"large\"], { optional: true })\n */\nfunction _enum<const V extends AllowedValues, const Opt extends FieldOptions>(\n values: V,\n options?: Opt,\n): TailorDBField<\n { type: \"enum\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<AllowedValuesOutput<V>, Opt>\n> {\n return createField<\"enum\", Opt, AllowedValuesOutput<V>>(\"enum\", options, undefined, values);\n}\n\n/**\n * Create a nested object field with sub-fields.\n * @param fields - Record of nested field definitions\n * @param options - Field configuration options\n * @returns A nested object field\n * @example db.object({ street: db.string(), city: db.string(), zip: db.string() })\n * @example db.object({ name: db.string() }, { optional: true })\n */\nfunction object<\n const F extends Record<string, TailorAnyDBField> &\n ExcludeNestedDBFields<F> &\n ExcludeHookedDBFields<F> &\n ExcludeDefaultedDBFields<F>,\n const Opt extends FieldOptions,\n>(fields: F, options?: Opt) {\n return createField(\"nested\", options, fields) as unknown as TailorDBField<\n { type: \"nested\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<InferFieldsOutput<F>, Opt>,\n F\n >;\n}\n\n/**\n * Creates a new TailorDBType instance.\n * @param name - Table name\n * @param fields - Field definitions\n * @param options - Table options\n * @param options.pluralForm - Optional plural form\n * @param options.description - Optional description\n * @returns A new TailorDBType\n */\nfunction createTailorDBType<\n // oxlint-disable-next-line no-explicit-any\n const Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n>(\n name: string,\n fields: Fields,\n options: { pluralForm?: string; description?: string },\n): TailorDBType<Fields, User, DefinedDBTypeMetadata> {\n let _description = options.description;\n let _settings: TypeFeatures = {};\n let _indexes: IndexDef<TailorDBType<Fields, User, DefinedDBTypeMetadata>>[] = [];\n const _permissions: RawPermissions = {};\n let _files: Record<string, string> = {};\n const _plugins: PluginAttachment[] = [];\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n let _typeHook: { create?: Function; update?: Function } | undefined;\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n let _typeValidate: Function | undefined;\n const _definedMethods = new Set<keyof DefinedDBTypeMetadata>();\n if (options.description !== undefined) {\n _definedMethods.add(\"description\");\n }\n\n function runMethodOnce<T>(method: keyof DefinedDBTypeMetadata, action: () => T): T {\n if (_definedMethods.has(method)) {\n throw new Error(`.${method}() has already been set`);\n }\n const result = action();\n _definedMethods.add(method);\n return result;\n }\n type TypeAfter<Key extends keyof DefinedDBTypeMetadata> = TailorDBType<\n Fields,\n User,\n WithDBTypeMetadata<DefinedDBTypeMetadata, Key>\n >;\n type TypeAfterUser<\n NextUser extends object,\n Key extends keyof DefinedDBTypeMetadata,\n > = TailorDBType<Fields, NextUser, WithDBTypeMetadata<DefinedDBTypeMetadata, Key>>;\n type TypeAfterPlugin<Config extends Record<string, unknown>> = TailorDBType<\n PluginExtendedFields<Fields, Config>,\n User,\n DefinedDBTypeMetadata\n >;\n\n if (options.pluralForm) {\n if (name === options.pluralForm) {\n throw new Error(`The name and the plural form must be different. name=${name}`);\n }\n _settings.pluralForm = options.pluralForm;\n }\n\n const dbType: TailorDBType<Fields, User, DefinedDBTypeMetadata> = {\n name,\n fields: { ...fields },\n _output: null as unknown as InferFieldsOutput<Fields>,\n _description,\n\n get metadata(): TailorDBTypeMetadata {\n // Convert indexes to the format expected by the manifest\n const indexes: Record<string, { fields: string[]; unique?: boolean }> = {};\n if (_indexes.length > 0) {\n _indexes.forEach((index) => {\n const fieldNames = index.fields.map((field) => String(field));\n const key = index.name || `idx_${fieldNames.join(\"_\")}`;\n indexes[key] = {\n fields: fieldNames,\n unique: index.unique,\n };\n });\n }\n\n return {\n name: this.name,\n description: _description,\n settings: _settings,\n permissions: _permissions,\n files: _files,\n ...(Object.keys(indexes).length > 0 && { indexes }),\n ...(_typeHook && { typeHook: _typeHook }),\n ...(_typeValidate && { typeValidate: _typeValidate }),\n };\n },\n\n hooks(hook: TypeHook<Fields>): TypeAfter<\"hooks\"> {\n return runMethodOnce(\"hooks\", () => {\n _typeHook = hook;\n return this as TypeAfter<\"hooks\">;\n });\n },\n\n validate(fn: TypeValidateFn<Fields>): TypeAfter<\"validate\"> {\n return runMethodOnce(\"validate\", () => {\n _typeValidate = fn;\n return this as TypeAfter<\"validate\">;\n });\n },\n\n features(features: Omit<TypeFeatures, \"pluralForm\">): TypeAfter<\"features\"> {\n return runMethodOnce(\"features\", () => {\n _settings = {\n ..._settings,\n ...features,\n };\n return this as TypeAfter<\"features\">;\n });\n },\n\n indexes(\n ...indexes: IndexDef<TailorDBType<Fields, User, DefinedDBTypeMetadata>>[]\n ): TypeAfter<\"indexes\"> {\n return runMethodOnce(\"indexes\", () => {\n _indexes = indexes;\n return this as TypeAfter<\"indexes\">;\n });\n },\n\n files<const F extends string>(\n files: Record<F, string> & FileKeyConflictError<Fields, User>,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<DefinedDBTypeMetadata, \"files\">, F> {\n return runMethodOnce(\"files\", () => {\n _files = files;\n return this as TailorDBType<\n Fields,\n User,\n WithDBTypeMetadata<DefinedDBTypeMetadata, \"files\">,\n F\n >;\n });\n },\n\n permission<\n U extends object = User,\n P extends TailorTypePermission<U, output<TailorDBType<Fields, User, DefinedDBTypeMetadata>>> =\n TailorTypePermission<U, output<TailorDBType<Fields, User, DefinedDBTypeMetadata>>>,\n >(permission: P): TypeAfterUser<U, \"permission\"> {\n return runMethodOnce(\"permission\", () => {\n const ret = this as unknown as TypeAfterUser<U, \"permission\">;\n _permissions.record = permission as RawPermissions[\"record\"];\n return ret;\n });\n },\n\n gqlPermission<\n U extends object = User,\n P extends TailorTypeGqlPermission<U> = TailorTypeGqlPermission<U>,\n >(permission: P): TypeAfterUser<U, \"gqlPermission\"> {\n return runMethodOnce(\"gqlPermission\", () => {\n const ret = this as unknown as TypeAfterUser<U, \"gqlPermission\">;\n _permissions.gql = permission as RawPermissions[\"gql\"];\n return ret;\n });\n },\n\n description(description: string): TypeAfter<\"description\"> {\n return runMethodOnce(\"description\", () => {\n _description = description;\n this._description = description;\n return this as TypeAfter<\"description\">;\n });\n },\n\n pickFields<K extends keyof Fields, const Opt extends FieldOptions>(keys: K[], options?: Opt) {\n const result = {} as Record<K, TailorAnyDBField>;\n for (const key of keys) {\n const field = this.fields[key] as TailorAnyDBField | undefined;\n if (!field) {\n // A plugin-injected field is only added to `fields` after `tailor\n // generate` runs — see the \"Injecting fields into the attached\n // table's type\" section of the plugin docs.\n throw new Error(\n `pickFields(): field \"${String(key)}\" does not exist on this table yet. If it comes from a plugin's .plugin() call, it is only added to the table after \\`tailor generate\\` runs.`,\n );\n }\n if (options) {\n result[key] = field.clone(options);\n } else {\n result[key] = field;\n }\n }\n // oxlint-disable-next-line no-explicit-any\n return result as any;\n },\n\n omitFields<K extends keyof Fields>(keys: K[]): Omit<Fields, K> {\n const keysSet = new Set(keys);\n const result = {} as Record<string, TailorAnyDBField>;\n for (const key in this.fields) {\n if (Object.hasOwn(this.fields, key) && !keysSet.has(key as unknown as K)) {\n result[key] = this.fields[key] as TailorAnyDBField;\n }\n }\n return result as Omit<Fields, K>;\n },\n\n get plugins(): PluginAttachment[] {\n return _plugins;\n },\n\n plugin<const Config extends Record<string, unknown>>(\n config: Config & PluginConfigGuard<Fields, Config, never>,\n ): TypeAfterPlugin<Config> {\n for (const [pluginId, pluginConfig] of Object.entries(config)) {\n _plugins.push({ pluginId, config: pluginConfig });\n }\n return this as unknown as TypeAfterPlugin<Config>;\n },\n };\n\n return brandValue(dbType, \"tailordb-type\");\n}\n\nconst idField = uuid();\ntype idField = typeof idField;\ntype DBTable<\n F extends { id?: never } & Record<string, TailorAnyDBField>,\n Defined extends DefinedDBTypeMetadata = DefinedDBTypeMetadata,\n> = TailorDBInstance<{ id: idField } & F, InferredAttributes, Defined>;\n\n/**\n * Creates a new database table with the specified fields.\n * An `id` field (UUID) is automatically added to every table.\n * @param name - The name of the table, or a tuple of [name, pluralForm]\n * @param fields - The field definitions for the table\n * @returns A new TailorDB table instance\n * @example\n * export const user = db.table(\"User\", {\n * name: db.string(),\n * email: db.string(),\n * age: db.int({ optional: true }),\n * role: db.enum([\"admin\", \"member\"]),\n * ...db.fields.timestamps(),\n * });\n * // Always export both the value and type:\n * export type user = typeof user;\n */\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n fields: F,\n): DBTable<F>;\n/**\n * Creates a new database table with the specified fields and description.\n * An `id` field (UUID) is automatically added to every table.\n * @param name - The name of the table, or a tuple of [name, pluralForm]\n * @param description - A description of the table\n * @param fields - The field definitions for the table\n * @returns A new TailorDB table instance\n */\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n description: string,\n fields: F,\n): DBTable<F, WithDBTypeMetadata<DefinedDBTypeMetadata, \"description\">>;\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n fieldsOrDescription: string | F,\n fields?: F,\n): DBTable<F> | DBTable<F, WithDBTypeMetadata<DefinedDBTypeMetadata, \"description\">> {\n const typeName = Array.isArray(name) ? name[0] : name;\n const pluralForm = Array.isArray(name) ? name[1] : undefined;\n\n let description: string | undefined;\n let fieldDef: F;\n if (typeof fieldsOrDescription === \"string\") {\n description = fieldsOrDescription;\n fieldDef = fields as F;\n } else {\n fieldDef = fieldsOrDescription;\n }\n return createTailorDBType<{ id: idField } & F>(\n typeName,\n {\n id: idField,\n ...fieldDef,\n },\n { pluralForm, description },\n );\n}\n\n// `Function.prototype.toString()` of this hook is embedded verbatim into deployed\n// schemas and migration diffs (see parser/service/tailordb/hooks-validate-precompiled-expr.ts\n// and field.ts). Its source text depends on how the SDK itself was built (e.g.\n// minification), so a fixed expression is pinned onto it directly here instead -\n// configure cannot import parser's `setPrecompiledScriptExpr` runtime helper across\n// the module boundary, only the symbol registry key and map types. Keep\n// this literal in sync with the \"timestamps() updatedAt hook resolves to the pinned\n// expr\" test in parser/service/tailordb/field.precompiled.test.ts, which fails if it\n// ever drifts from what this hook's own source naturally produces.\ntype TimestampsUpdatedAtHookFn = UpdateHookFn<string | Date | null, string | Date>;\nconst timestampsUpdatedAtHook: TimestampsUpdatedAtHookFn = ({ input, now }) => input ?? now;\nconst PRECOMPILED_EXPR_KEY: PrecompiledScriptExprKey =\n \"tailor-platform/sdk:precompiled-script-expr\";\nconst PRECOMPILED_EXPR_SYMBOL = Symbol.for(PRECOMPILED_EXPR_KEY);\n(timestampsUpdatedAtHook as unknown as Record<symbol, PrecompiledScriptExprMap>)[\n PRECOMPILED_EXPR_SYMBOL\n] = {\n \"hooks.update\":\n \"(({ input, now }) => input ?? now)({ input: _value, oldValue: _oldValue, invoker: _principal, now: _now })\",\n};\n\n/** TailorDB schema builder utilities for defining tables and fields. */\nexport const db = {\n table: dbTable,\n uuid,\n string,\n bool,\n int,\n float,\n decimal,\n date,\n datetime,\n time,\n enum: _enum,\n object,\n fields: {\n /**\n * Creates standard timestamp fields (createdAt, updatedAt) with automatic defaults.\n * Both fields default to the current time on create. updatedAt is also refreshed on update.\n * User-specified values are respected when provided (e.g. seeding historical records).\n * @returns An object with createdAt and updatedAt fields\n * @example\n * const model = db.table(\"Model\", {\n * name: db.string(),\n * ...db.fields.timestamps(),\n * });\n */\n timestamps: () => ({\n createdAt: datetime().default(\"now\").description(\"Record creation timestamp\"),\n updatedAt: datetime()\n .default(\"now\")\n .hooks({ update: timestampsUpdatedAtHook })\n .description(\"Record update timestamp\"),\n }),\n },\n};\n"],"mappings":"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"}
@@ -13,6 +13,11 @@ type ValidateSeedResult = {
13
13
  valid: false;
14
14
  output: string;
15
15
  error: string;
16
+ /** Where the first reported error sits, for tooling that links to source. */
17
+ location?: {
18
+ file: string;
19
+ line?: number;
20
+ };
16
21
  };
17
22
  /**
18
23
  * A JSONL seed file that received values.
@@ -1,7 +1,8 @@
1
- import{basename as e,dirname as t,join as n}from"pathe";import{pathToFileURL as r}from"node:url";import{readFile as i,readdir as a,stat as o,writeFile as s}from"node:fs/promises";import{ErrorFormatter as c,LinesDB as l,defineSchema as u,findSchemaFile as d}from"@toiroakr/lines-db";const f=[`id`];async function resolveSeedDataTarget(n){let r=await o(n);if(r.isDirectory())return{dataDir:n};if(r.isFile()&&n.endsWith(`.jsonl`))return{dataDir:t(n),tableName:e(n,`.jsonl`)};throw Error(`Invalid path: ${n}. Must be a directory or .jsonl file.`)}async function listSeedTables(t){return(await a(t)).filter(e=>e.endsWith(`.jsonl`)).map(t=>e(t,`.jsonl`)).toSorted()}function formatWarnings(e){return e.length===0?[]:[...e.map(e=>`⚠ ${e}`),``]}function formatValidationErrors(e,t){let n=new c({verbose:t}),r=[],i=new Map;for(let t of e){let e=i.get(t.file)||[];e.push(t),i.set(t.file,e)}for(let[e,t]of i){r.push(n.formatErrorHeader(t.length,e)),r.push(``);let i=t.filter(e=>e.type!==`foreignKey`||!e.foreignKeyError),a=t.filter(e=>e.type===`foreignKey`&&e.foreignKeyError);i.length>0&&r.push(n.formatValidationErrors(i.map(e=>({file:e.file,rowIndex:e.rowIndex,issues:e.issues}))));for(let e of a)e.foreignKeyError&&r.push(n.formatForeignKeyError({file:e.file,rowIndex:e.rowIndex,column:e.foreignKeyError.column,value:e.foreignKeyError.value,referencedTable:e.foreignKeyError.referencedTable,referencedColumn:e.foreignKeyError.referencedColumn}));r.push(``)}return r.join(`
1
+ import{basename as e,dirname as t,join as n}from"pathe";import{pathToFileURL as r}from"node:url";import{readFile as i,readdir as a,stat as o,writeFile as s}from"node:fs/promises";import{ErrorFormatter as c,LinesDB as l,defineSchema as u,findSchemaFile as d}from"@toiroakr/lines-db";function physicalLineOfRecord(e,t){let n=e.split(`
2
+ `),r=t;for(let[e,t]of n.entries())if(t.trim().length!==0){if(r===0)return e+1;--r}}async function firstErrorLocation(e){let t=e[0];if(t)try{let e=await i(t.file,`utf-8`);return{file:t.file,line:physicalLineOfRecord(e,t.rowIndex)}}catch{return{file:t.file}}}const f=[`id`];async function resolveSeedDataTarget(n){let r=await o(n);if(r.isDirectory())return{dataDir:n};if(r.isFile()&&n.endsWith(`.jsonl`))return{dataDir:t(n),tableName:e(n,`.jsonl`)};throw Error(`Invalid path: ${n}. Must be a directory or .jsonl file.`)}async function listSeedTables(t){return(await a(t)).filter(e=>e.endsWith(`.jsonl`)).map(t=>e(t,`.jsonl`)).toSorted()}function formatWarnings(e){return e.length===0?[]:[...e.map(e=>`⚠ ${e}`),``]}function formatValidationErrors(e,t){let n=new c({verbose:t}),r=[],i=new Map;for(let t of e){let e=i.get(t.file)||[];e.push(t),i.set(t.file,e)}for(let[e,t]of i){r.push(n.formatErrorHeader(t.length,e)),r.push(``);let i=t.filter(e=>e.type!==`foreignKey`||!e.foreignKeyError),a=t.filter(e=>e.type===`foreignKey`&&e.foreignKeyError);i.length>0&&r.push(n.formatValidationErrors(i.map(e=>({file:e.file,rowIndex:e.rowIndex,issues:e.issues}))));for(let e of a)e.foreignKeyError&&r.push(n.formatForeignKeyError({file:e.file,rowIndex:e.rowIndex,column:e.foreignKeyError.column,value:e.foreignKeyError.value,referencedTable:e.foreignKeyError.referencedTable,referencedColumn:e.foreignKeyError.referencedColumn}));r.push(``)}return r.join(`
2
3
  `)}async function validateSeedData(e){let{path:t,verbose:n=!1}=e,{dataDir:r,tableName:i}=await resolveSeedDataTarget(t),a=l.create({dataDir:r}),o;try{o=await a.initialize({tableName:i,detailedValidate:!0})}finally{await a.close()}let s=formatWarnings(o.warnings);return o.valid?(s.push(`✓ All records are valid`),{valid:!0,output:s.join(`
3
4
  `)}):{valid:!1,output:s.join(`
4
- `),error:formatValidationErrors(o.errors,n)}}function ownValue(e,t){return Object.hasOwn(e,t)?e[t]:void 0}function setField(e,t,n){Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0})}function isBlank(e){return e==null?!0:typeof e!=`object`||Array.isArray(e)?!1:Object.values(e).every(isBlank)}function splitLines(e){return e===``?[]:e.split(`
5
+ `),error:formatValidationErrors(o.errors,n),location:await firstErrorLocation(o.errors)}}function ownValue(e,t){return Object.hasOwn(e,t)?e[t]:void 0}function setField(e,t,n){Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0})}function isBlank(e){return e==null?!0:typeof e!=`object`||Array.isArray(e)?!1:Object.values(e).every(isBlank)}function splitLines(e){return e===``?[]:e.split(`
5
6
  `).map((e,t,n)=>{let r=t===n.length-1?``:`
6
7
  `,i=e.endsWith(`\r`)?e.slice(0,-1):e,a=e.endsWith(`\r`)?`\r`:``,o;if(i.trim()!==``)try{let e=JSON.parse(i);o=typeof e==`object`&&e&&!Array.isArray(e)?e:void 0}catch{o=void 0}return{text:i,eol:`${a}${r}`,row:o}})}function serializeRow(e,t){let n=new Map(t.map((e,t)=>[e,t])),rankOf=e=>n.get(e)??t.length;return JSON.stringify(Object.fromEntries(Object.entries(e).toSorted(([e],[t])=>rankOf(e)-rankOf(t))))}async function loadSeedHook(e,t){let n=await d(e,t);if(!n)return;let i=(await import(r(n).href)).hook;if(typeof i!=`function`)throw Error(`${n} does not export \`hook\`. Run \`tailor generate\` to regenerate the seed schema files.`);return i}async function fillSeedData(e){let{path:t,fields:r=f}=e;if(r.length===0)throw Error(`No fields to fill. Name at least one field.`);let{dataDir:a,tableName:o}=await resolveSeedDataTarget(t),c=o?[o]:await listSeedTables(a),l=[],u=[],d=new Set,p=[];for(let e of c){let t=await loadSeedHook(a,e);if(!t){l.push(`No schema file for ${e}, so nothing can be filled in there`);continue}p.push({table:e,hook:t})}let m=[];for(let{table:e,hook:t}of p){let o=n(a,`${e}.jsonl`),s=splitLines(await i(o,`utf-8`)),c=new Set,f=[],p=0,h=[];for(let[e,n]of s.entries()){let i=n.row;if(!i){n.text.trim()!==``&&f.push(e+1);continue}let a=t(i);h=Object.keys(a);let o=r.filter(e=>!isBlank(ownValue(a,e))&&(d.add(e),isBlank(ownValue(i,e))));if(o.length!==0){for(let e of o)setField(i,e,a[e]),c.add(e);n.text=serializeRow(i,h),p+=1}}f.length>0&&l.push(`${o}: line(s) ${f.join(`, `)} are not JSON objects, so nothing was filled in there`),p!==0&&(m.push({file:o,content:s.map(e=>`${e.text}${e.eol}`).join(``)}),u.push({table:e,file:o,fields:[...c],count:p}))}for(let{file:e,content:t}of m)await s(e,t);let h=r.filter(e=>!d.has(e));c.length>0&&h.length>0&&l.push(`No seed data produces a value for: ${h.join(`, `)}`);let g=formatWarnings(l);return g.push(u.length===0?`✓ Nothing to fill`:u.map(({file:e,fields:t,count:n})=>`\u2713 ${e}: filled ${t.join(`, `)} in ${n} row(s)`).join(`
7
8
  `)),{output:g.join(`
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/seed/index.ts"],"sourcesContent":["/**\n * Seed integration module for generated seed code.\n *\n * Re-exports `@toiroakr/lines-db` through a single import path\n * to avoid phantom dependency issues with pnpm, and provides\n * seed-specific utility functions used by the code generator.\n */\n\nimport { readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { LinesDB, ErrorFormatter, findSchemaFile } from \"@toiroakr/lines-db\";\n// `pathe`, not `node:path`: the file paths reported back are printed and returned\n// to the caller, and these stay separator-stable across platforms.\nimport { basename, dirname, join } from \"pathe\";\nimport type { JsonObject, ValidationErrorDetail } from \"@toiroakr/lines-db\";\n\nexport { defineSchema } from \"@toiroakr/lines-db\";\nexport type { ForeignKeyDefinition, IndexDefinition } from \"@toiroakr/lines-db\";\n\n/** Fields `fillSeedData` writes when the caller names none. */\nconst DEFAULT_FILL_FIELDS = [\"id\"];\n\ntype SeedDataTarget = {\n dataDir: string;\n tableName?: string;\n};\n\ntype ValidateSeedDataOptions = {\n /** Resolved absolute path to a data directory or a .jsonl file */\n path: string;\n /** Show verbose error output */\n verbose?: boolean;\n};\n\ntype ValidateSeedResult =\n | { valid: true; output: string }\n | { valid: false; output: string; error: string };\n\n/**\n * A JSONL seed file that received values.\n */\ntype FilledSeedFile = {\n /** Name of the seeded table, matching the JSONL file name */\n table: string;\n /** Absolute path to the updated JSONL file */\n file: string;\n /** Fields that were written back to the file */\n fields: string[];\n /** How many rows were missing at least one of those fields */\n count: number;\n};\n\ntype FillSeedDataOptions = {\n /** Resolved absolute path to a data directory or a .jsonl file */\n path: string;\n /** Fields to fill. Defaults to `id`. */\n fields?: readonly string[];\n};\n\ntype FillSeedDataResult = {\n output: string;\n filled: FilledSeedFile[];\n};\n\nasync function resolveSeedDataTarget(resolvedPath: string): Promise<SeedDataTarget> {\n const stats = await stat(resolvedPath);\n if (stats.isDirectory()) {\n return { dataDir: resolvedPath };\n }\n if (stats.isFile() && resolvedPath.endsWith(\".jsonl\")) {\n return { dataDir: dirname(resolvedPath), tableName: basename(resolvedPath, \".jsonl\") };\n }\n throw new Error(`Invalid path: ${resolvedPath}. Must be a directory or .jsonl file.`);\n}\n\nasync function listSeedTables(dataDir: string): Promise<string[]> {\n const entries = await readdir(dataDir);\n return entries\n .filter((entry) => entry.endsWith(\".jsonl\"))\n .map((entry) => basename(entry, \".jsonl\"))\n .toSorted();\n}\n\nfunction formatWarnings(warnings: string[]): string[] {\n if (warnings.length === 0) {\n return [];\n }\n return [...warnings.map((warning) => `⚠ ${warning}`), \"\"];\n}\n\nfunction formatValidationErrors(errors: ValidationErrorDetail[], verbose: boolean): string {\n const formatter = new ErrorFormatter({ verbose });\n const errorLines: string[] = [];\n const errorsByFile = new Map<string, ValidationErrorDetail[]>();\n for (const error of errors) {\n const fileErrors = errorsByFile.get(error.file) || [];\n fileErrors.push(error);\n errorsByFile.set(error.file, fileErrors);\n }\n for (const [file, fileErrors] of errorsByFile) {\n errorLines.push(formatter.formatErrorHeader(fileErrors.length, file));\n errorLines.push(\"\");\n const validationErrors = fileErrors.filter(\n (e) => e.type !== \"foreignKey\" || !e.foreignKeyError,\n );\n const foreignKeyErrors = fileErrors.filter((e) => e.type === \"foreignKey\" && e.foreignKeyError);\n if (validationErrors.length > 0) {\n errorLines.push(\n formatter.formatValidationErrors(\n validationErrors.map((e) => ({\n file: e.file,\n rowIndex: e.rowIndex,\n issues: e.issues,\n })),\n ),\n );\n }\n for (const fkError of foreignKeyErrors) {\n if (fkError.foreignKeyError) {\n errorLines.push(\n formatter.formatForeignKeyError({\n file: fkError.file,\n rowIndex: fkError.rowIndex,\n column: fkError.foreignKeyError.column,\n value: fkError.foreignKeyError.value,\n referencedTable: fkError.foreignKeyError.referencedTable,\n referencedColumn: fkError.foreignKeyError.referencedColumn,\n }),\n );\n }\n }\n errorLines.push(\"\");\n }\n\n return errorLines.join(\"\\n\");\n}\n\n/**\n * Validate JSONL seed data against schema definitions.\n * Resolves the given path (directory or `.jsonl` file), validates the rows it\n * holds, and returns formatted output and error messages.\n * @param options - Validation options including path and verbose flag\n * @returns Validation result with output messages and optional error details\n */\nexport async function validateSeedData(\n options: ValidateSeedDataOptions,\n): Promise<ValidateSeedResult> {\n const { path: resolvedPath, verbose = false } = options;\n const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n\n const db = LinesDB.create({ dataDir });\n let result;\n try {\n result = await db.initialize({ tableName, detailedValidate: true });\n } finally {\n await db.close();\n }\n\n const outputLines = formatWarnings(result.warnings);\n\n if (result.valid) {\n outputLines.push(\"✓ All records are valid\");\n return { valid: true, output: outputLines.join(\"\\n\") };\n }\n\n return {\n valid: false,\n output: outputLines.join(\"\\n\"),\n error: formatValidationErrors(result.errors, verbose),\n };\n}\n\n// A row's own value for a field, or undefined when the row has none.\nfunction ownValue(row: Record<string, unknown>, field: string): unknown {\n return Object.hasOwn(row, field) ? row[field] : undefined;\n}\n\n// Not `row[field] = value`: a field named `__proto__` goes through the inherited\n// setter, which leaves no own property for the serializer to read back.\nfunction setField(row: JsonObject, field: string, value: JsonObject[string]): void {\n Object.defineProperty(row, field, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n}\n\nfunction isBlank(value: unknown): boolean {\n if (value === undefined || value === null) {\n return true;\n }\n if (typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n // A nested field the row never had comes back as an object whose keys carry no\n // value of their own, and writing that into the line fills nothing in.\n return Object.values(value).every(isBlank);\n}\n\n/** A JSONL line, kept as text so an untouched line is written back verbatim. */\ntype SeedLine = {\n text: string;\n /** Line separator the file used after this line, kept so CRLF survives. */\n eol: string;\n row: JsonObject | undefined;\n};\n\nfunction splitLines(content: string): SeedLine[] {\n if (content === \"\") {\n return [];\n }\n return content.split(\"\\n\").map((raw, index, all) => {\n const eol = index === all.length - 1 ? \"\" : \"\\n\";\n const text = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n const carriage = raw.endsWith(\"\\r\") ? \"\\r\" : \"\";\n let row: JsonObject | undefined;\n if (text.trim() !== \"\") {\n try {\n const parsed: unknown = JSON.parse(text);\n row =\n parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as JsonObject)\n : undefined;\n } catch {\n row = undefined;\n }\n }\n return { text, eol: `${carriage}${eol}`, row };\n });\n}\n\n// Keys go in the order the hook produced them, which is the order the table\n// declares its fields. Keys the table does not declare follow the declared ones.\nfunction serializeRow(row: JsonObject, fieldOrder: string[]): string {\n const rank = new Map(fieldOrder.map((field, index) => [field, index]));\n const rankOf = (key: string): number => rank.get(key) ?? fieldOrder.length;\n return JSON.stringify(\n Object.fromEntries(Object.entries(row).toSorted(([a], [b]) => rankOf(a) - rankOf(b))),\n );\n}\n\ntype SeedHook = (row: unknown) => Record<string, unknown>;\n\nasync function loadSeedHook(dataDir: string, table: string): Promise<SeedHook | undefined> {\n const schemaPath = await findSchemaFile(dataDir, table);\n if (!schemaPath) {\n return undefined;\n }\n const loaded: unknown = await import(pathToFileURL(schemaPath).href);\n const hook = (loaded as { hook?: unknown }).hook;\n if (typeof hook !== \"function\") {\n throw new Error(\n `${schemaPath} does not export \\`hook\\`. Run \\`tailor generate\\` to regenerate the seed schema files.`,\n );\n }\n return hook as SeedHook;\n}\n\n/**\n * Fill in the values a record gets on create for the JSONL seed data rows that\n * are missing them, so a row can be referenced by `id` or carry a timestamp\n * before it is ever seeded.\n *\n * The values come from the table's own create-time behavior — its `id`, its field\n * defaults, and its create hooks — applied to each row on its own. Nothing is\n * validated, so a row can be filled while the data around it is still\n * incomplete: that is what lets you get the ids you need in order to write the\n * rows that reference them. Run `validateSeedData` when the data is ready.\n *\n * Only the named fields are written, and only into a row that has no value for\n * them, so a value already in the file is never replaced. A line that gains\n * nothing is written back exactly as it was, byte for byte; a line that does get\n * a value is re-serialized with its keys in the order the table declares its\n * fields, so a filled-in `id` lands at the front. A field the table gives no\n * value to — one it does not declare, or one the platform assigns such as a\n * serial field — is skipped, so one field list covers a whole data directory.\n *\n * The values are read from the schema files generated next to the data, and all\n * of them are read before anything is written: a file that predates the current\n * generator stops the run with nothing filled in anywhere.\n * @param options - Fill options including path and fields\n * @returns Which files received which fields\n */\nexport async function fillSeedData(options: FillSeedDataOptions): Promise<FillSeedDataResult> {\n const { path: resolvedPath, fields = DEFAULT_FILL_FIELDS } = options;\n if (fields.length === 0) {\n throw new Error(\"No fields to fill. Name at least one field.\");\n }\n const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n const tables = tableName ? [tableName] : await listSeedTables(dataDir);\n\n const warnings: string[] = [];\n const filled: FilledSeedFile[] = [];\n const producedFields = new Set<string>();\n\n // Every hook loads before anything is written, so a schema file that predates\n // `tailor generate` stops the run instead of leaving half the files filled.\n const hooks: { table: string; hook: SeedHook }[] = [];\n for (const table of tables) {\n const hook = await loadSeedHook(dataDir, table);\n if (!hook) {\n warnings.push(`No schema file for ${table}, so nothing can be filled in there`);\n continue;\n }\n hooks.push({ table, hook });\n }\n\n // Every line is decided before any file is written, so a hook that throws on\n // one table cannot leave another one already rewritten.\n const writes: { file: string; content: string }[] = [];\n for (const { table, hook } of hooks) {\n const file = join(dataDir, `${table}.jsonl`);\n const lines = splitLines(await readFile(file, \"utf-8\"));\n\n const written = new Set<string>();\n const unreadable: number[] = [];\n let count = 0;\n let fieldOrder: string[] = [];\n for (const [index, line] of lines.entries()) {\n const row = line.row;\n if (!row) {\n if (line.text.trim() !== \"\") {\n unreadable.push(index + 1);\n }\n continue;\n }\n const hooked = hook(row);\n fieldOrder = Object.keys(hooked);\n const gained = fields.filter((field) => {\n const value = ownValue(hooked, field);\n if (isBlank(value)) {\n return false;\n }\n producedFields.add(field);\n return isBlank(ownValue(row, field));\n });\n if (gained.length === 0) {\n continue;\n }\n for (const field of gained) {\n setField(row, field, hooked[field] as JsonObject[string]);\n written.add(field);\n }\n line.text = serializeRow(row, fieldOrder);\n count += 1;\n }\n\n if (unreadable.length > 0) {\n warnings.push(\n `${file}: line(s) ${unreadable.join(\", \")} are not JSON objects, so nothing was filled in there`,\n );\n }\n\n if (count === 0) {\n continue;\n }\n writes.push({ file, content: lines.map((line) => `${line.text}${line.eol}`).join(\"\") });\n filled.push({ table, file, fields: [...written], count });\n }\n\n for (const { file, content } of writes) {\n await writeFile(file, content);\n }\n\n const unproducedFields = fields.filter((field) => !producedFields.has(field));\n if (tables.length > 0 && unproducedFields.length > 0) {\n warnings.push(`No seed data produces a value for: ${unproducedFields.join(\", \")}`);\n }\n\n const outputLines = formatWarnings(warnings);\n outputLines.push(\n filled.length === 0\n ? \"\\u2713 Nothing to fill\"\n : filled\n .map(\n ({ file, fields: tableFields, count }) =>\n `\\u2713 ${file}: filled ${tableFields.join(\", \")} in ${count} row(s)`,\n )\n .join(\"\\n\"),\n );\n\n return { output: outputLines.join(\"\\n\"), filled };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/seed/record-lines.ts","../../src/seed/index.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { ValidationErrorDetail } from \"@toiroakr/lines-db\";\n\n/**\n * Resolve the 1-based physical line a JSONL record occupies.\n *\n * The seed reader drops blank and whitespace-only lines before indexing its\n * records, so a record's index is not its line number once a file contains\n * one. Reported locations have to point at the line a reader would open.\n * @param content - Raw JSONL file contents\n * @param recordIndex - 0-based index among the file's non-blank lines\n * @returns 1-based physical line, or undefined when the file holds no such record\n */\nexport function physicalLineOfRecord(content: string, recordIndex: number): number | undefined {\n const lines = content.split(\"\\n\");\n let remaining = recordIndex;\n for (const [index, line] of lines.entries()) {\n if (line.trim().length === 0) continue;\n if (remaining === 0) return index + 1;\n remaining -= 1;\n }\n return undefined;\n}\n\n/**\n * Locate the first reported error in its own file.\n *\n * Errors are reported per record, so the index is mapped back to a physical\n * line; a file that cannot be re-read yields the file alone rather than a\n * guessed line.\n * @param errors - Validation errors in the order the validator reported them\n * @returns Location of the first error, or undefined when there is none\n */\nexport async function firstErrorLocation(\n errors: readonly ValidationErrorDetail[],\n): Promise<{ file: string; line?: number } | undefined> {\n const first = errors[0];\n if (!first) return undefined;\n try {\n const content = await readFile(first.file, \"utf-8\");\n return { file: first.file, line: physicalLineOfRecord(content, first.rowIndex) };\n } catch {\n return { file: first.file };\n }\n}\n","/**\n * Seed integration module for generated seed code.\n *\n * Re-exports `@toiroakr/lines-db` through a single import path\n * to avoid phantom dependency issues with pnpm, and provides\n * seed-specific utility functions used by the code generator.\n */\n\nimport { readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { LinesDB, ErrorFormatter, findSchemaFile } from \"@toiroakr/lines-db\";\n// `pathe`, not `node:path`: the file paths reported back are printed and returned\n// to the caller, and these stay separator-stable across platforms.\nimport { basename, dirname, join } from \"pathe\";\nimport { firstErrorLocation } from \"./record-lines\";\nimport type { JsonObject, ValidationErrorDetail } from \"@toiroakr/lines-db\";\n\nexport { defineSchema } from \"@toiroakr/lines-db\";\nexport type { ForeignKeyDefinition, IndexDefinition } from \"@toiroakr/lines-db\";\n\n/** Fields `fillSeedData` writes when the caller names none. */\nconst DEFAULT_FILL_FIELDS = [\"id\"];\n\ntype SeedDataTarget = {\n dataDir: string;\n tableName?: string;\n};\n\ntype ValidateSeedDataOptions = {\n /** Resolved absolute path to a data directory or a .jsonl file */\n path: string;\n /** Show verbose error output */\n verbose?: boolean;\n};\n\ntype ValidateSeedResult =\n | { valid: true; output: string }\n | {\n valid: false;\n output: string;\n error: string;\n /** Where the first reported error sits, for tooling that links to source. */\n location?: { file: string; line?: number };\n };\n\n/**\n * A JSONL seed file that received values.\n */\ntype FilledSeedFile = {\n /** Name of the seeded table, matching the JSONL file name */\n table: string;\n /** Absolute path to the updated JSONL file */\n file: string;\n /** Fields that were written back to the file */\n fields: string[];\n /** How many rows were missing at least one of those fields */\n count: number;\n};\n\ntype FillSeedDataOptions = {\n /** Resolved absolute path to a data directory or a .jsonl file */\n path: string;\n /** Fields to fill. Defaults to `id`. */\n fields?: readonly string[];\n};\n\ntype FillSeedDataResult = {\n output: string;\n filled: FilledSeedFile[];\n};\n\nasync function resolveSeedDataTarget(resolvedPath: string): Promise<SeedDataTarget> {\n const stats = await stat(resolvedPath);\n if (stats.isDirectory()) {\n return { dataDir: resolvedPath };\n }\n if (stats.isFile() && resolvedPath.endsWith(\".jsonl\")) {\n return { dataDir: dirname(resolvedPath), tableName: basename(resolvedPath, \".jsonl\") };\n }\n throw new Error(`Invalid path: ${resolvedPath}. Must be a directory or .jsonl file.`);\n}\n\nasync function listSeedTables(dataDir: string): Promise<string[]> {\n const entries = await readdir(dataDir);\n return entries\n .filter((entry) => entry.endsWith(\".jsonl\"))\n .map((entry) => basename(entry, \".jsonl\"))\n .toSorted();\n}\n\nfunction formatWarnings(warnings: string[]): string[] {\n if (warnings.length === 0) {\n return [];\n }\n return [...warnings.map((warning) => `⚠ ${warning}`), \"\"];\n}\n\nfunction formatValidationErrors(errors: ValidationErrorDetail[], verbose: boolean): string {\n const formatter = new ErrorFormatter({ verbose });\n const errorLines: string[] = [];\n const errorsByFile = new Map<string, ValidationErrorDetail[]>();\n for (const error of errors) {\n const fileErrors = errorsByFile.get(error.file) || [];\n fileErrors.push(error);\n errorsByFile.set(error.file, fileErrors);\n }\n for (const [file, fileErrors] of errorsByFile) {\n errorLines.push(formatter.formatErrorHeader(fileErrors.length, file));\n errorLines.push(\"\");\n const validationErrors = fileErrors.filter(\n (e) => e.type !== \"foreignKey\" || !e.foreignKeyError,\n );\n const foreignKeyErrors = fileErrors.filter((e) => e.type === \"foreignKey\" && e.foreignKeyError);\n if (validationErrors.length > 0) {\n errorLines.push(\n formatter.formatValidationErrors(\n validationErrors.map((e) => ({\n file: e.file,\n rowIndex: e.rowIndex,\n issues: e.issues,\n })),\n ),\n );\n }\n for (const fkError of foreignKeyErrors) {\n if (fkError.foreignKeyError) {\n errorLines.push(\n formatter.formatForeignKeyError({\n file: fkError.file,\n rowIndex: fkError.rowIndex,\n column: fkError.foreignKeyError.column,\n value: fkError.foreignKeyError.value,\n referencedTable: fkError.foreignKeyError.referencedTable,\n referencedColumn: fkError.foreignKeyError.referencedColumn,\n }),\n );\n }\n }\n errorLines.push(\"\");\n }\n\n return errorLines.join(\"\\n\");\n}\n\n/**\n * Validate JSONL seed data against schema definitions.\n * Resolves the given path (directory or `.jsonl` file), validates the rows it\n * holds, and returns formatted output and error messages.\n * @param options - Validation options including path and verbose flag\n * @returns Validation result with output messages and optional error details\n */\nexport async function validateSeedData(\n options: ValidateSeedDataOptions,\n): Promise<ValidateSeedResult> {\n const { path: resolvedPath, verbose = false } = options;\n const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n\n const db = LinesDB.create({ dataDir });\n let result;\n try {\n result = await db.initialize({ tableName, detailedValidate: true });\n } finally {\n await db.close();\n }\n\n const outputLines = formatWarnings(result.warnings);\n\n if (result.valid) {\n outputLines.push(\"✓ All records are valid\");\n return { valid: true, output: outputLines.join(\"\\n\") };\n }\n\n return {\n valid: false,\n output: outputLines.join(\"\\n\"),\n error: formatValidationErrors(result.errors, verbose),\n location: await firstErrorLocation(result.errors),\n };\n}\n\n// A row's own value for a field, or undefined when the row has none.\nfunction ownValue(row: Record<string, unknown>, field: string): unknown {\n return Object.hasOwn(row, field) ? row[field] : undefined;\n}\n\n// Not `row[field] = value`: a field named `__proto__` goes through the inherited\n// setter, which leaves no own property for the serializer to read back.\nfunction setField(row: JsonObject, field: string, value: JsonObject[string]): void {\n Object.defineProperty(row, field, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n}\n\nfunction isBlank(value: unknown): boolean {\n if (value === undefined || value === null) {\n return true;\n }\n if (typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n // A nested field the row never had comes back as an object whose keys carry no\n // value of their own, and writing that into the line fills nothing in.\n return Object.values(value).every(isBlank);\n}\n\n/** A JSONL line, kept as text so an untouched line is written back verbatim. */\ntype SeedLine = {\n text: string;\n /** Line separator the file used after this line, kept so CRLF survives. */\n eol: string;\n row: JsonObject | undefined;\n};\n\nfunction splitLines(content: string): SeedLine[] {\n if (content === \"\") {\n return [];\n }\n return content.split(\"\\n\").map((raw, index, all) => {\n const eol = index === all.length - 1 ? \"\" : \"\\n\";\n const text = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n const carriage = raw.endsWith(\"\\r\") ? \"\\r\" : \"\";\n let row: JsonObject | undefined;\n if (text.trim() !== \"\") {\n try {\n const parsed: unknown = JSON.parse(text);\n row =\n parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as JsonObject)\n : undefined;\n } catch {\n row = undefined;\n }\n }\n return { text, eol: `${carriage}${eol}`, row };\n });\n}\n\n// Keys go in the order the hook produced them, which is the order the table\n// declares its fields. Keys the table does not declare follow the declared ones.\nfunction serializeRow(row: JsonObject, fieldOrder: string[]): string {\n const rank = new Map(fieldOrder.map((field, index) => [field, index]));\n const rankOf = (key: string): number => rank.get(key) ?? fieldOrder.length;\n return JSON.stringify(\n Object.fromEntries(Object.entries(row).toSorted(([a], [b]) => rankOf(a) - rankOf(b))),\n );\n}\n\ntype SeedHook = (row: unknown) => Record<string, unknown>;\n\nasync function loadSeedHook(dataDir: string, table: string): Promise<SeedHook | undefined> {\n const schemaPath = await findSchemaFile(dataDir, table);\n if (!schemaPath) {\n return undefined;\n }\n const loaded: unknown = await import(pathToFileURL(schemaPath).href);\n const hook = (loaded as { hook?: unknown }).hook;\n if (typeof hook !== \"function\") {\n throw new Error(\n `${schemaPath} does not export \\`hook\\`. Run \\`tailor generate\\` to regenerate the seed schema files.`,\n );\n }\n return hook as SeedHook;\n}\n\n/**\n * Fill in the values a record gets on create for the JSONL seed data rows that\n * are missing them, so a row can be referenced by `id` or carry a timestamp\n * before it is ever seeded.\n *\n * The values come from the table's own create-time behavior — its `id`, its field\n * defaults, and its create hooks — applied to each row on its own. Nothing is\n * validated, so a row can be filled while the data around it is still\n * incomplete: that is what lets you get the ids you need in order to write the\n * rows that reference them. Run `validateSeedData` when the data is ready.\n *\n * Only the named fields are written, and only into a row that has no value for\n * them, so a value already in the file is never replaced. A line that gains\n * nothing is written back exactly as it was, byte for byte; a line that does get\n * a value is re-serialized with its keys in the order the table declares its\n * fields, so a filled-in `id` lands at the front. A field the table gives no\n * value to — one it does not declare, or one the platform assigns such as a\n * serial field — is skipped, so one field list covers a whole data directory.\n *\n * The values are read from the schema files generated next to the data, and all\n * of them are read before anything is written: a file that predates the current\n * generator stops the run with nothing filled in anywhere.\n * @param options - Fill options including path and fields\n * @returns Which files received which fields\n */\nexport async function fillSeedData(options: FillSeedDataOptions): Promise<FillSeedDataResult> {\n const { path: resolvedPath, fields = DEFAULT_FILL_FIELDS } = options;\n if (fields.length === 0) {\n throw new Error(\"No fields to fill. Name at least one field.\");\n }\n const { dataDir, tableName } = await resolveSeedDataTarget(resolvedPath);\n const tables = tableName ? [tableName] : await listSeedTables(dataDir);\n\n const warnings: string[] = [];\n const filled: FilledSeedFile[] = [];\n const producedFields = new Set<string>();\n\n // Every hook loads before anything is written, so a schema file that predates\n // `tailor generate` stops the run instead of leaving half the files filled.\n const hooks: { table: string; hook: SeedHook }[] = [];\n for (const table of tables) {\n const hook = await loadSeedHook(dataDir, table);\n if (!hook) {\n warnings.push(`No schema file for ${table}, so nothing can be filled in there`);\n continue;\n }\n hooks.push({ table, hook });\n }\n\n // Every line is decided before any file is written, so a hook that throws on\n // one table cannot leave another one already rewritten.\n const writes: { file: string; content: string }[] = [];\n for (const { table, hook } of hooks) {\n const file = join(dataDir, `${table}.jsonl`);\n const lines = splitLines(await readFile(file, \"utf-8\"));\n\n const written = new Set<string>();\n const unreadable: number[] = [];\n let count = 0;\n let fieldOrder: string[] = [];\n for (const [index, line] of lines.entries()) {\n const row = line.row;\n if (!row) {\n if (line.text.trim() !== \"\") {\n unreadable.push(index + 1);\n }\n continue;\n }\n const hooked = hook(row);\n fieldOrder = Object.keys(hooked);\n const gained = fields.filter((field) => {\n const value = ownValue(hooked, field);\n if (isBlank(value)) {\n return false;\n }\n producedFields.add(field);\n return isBlank(ownValue(row, field));\n });\n if (gained.length === 0) {\n continue;\n }\n for (const field of gained) {\n setField(row, field, hooked[field] as JsonObject[string]);\n written.add(field);\n }\n line.text = serializeRow(row, fieldOrder);\n count += 1;\n }\n\n if (unreadable.length > 0) {\n warnings.push(\n `${file}: line(s) ${unreadable.join(\", \")} are not JSON objects, so nothing was filled in there`,\n );\n }\n\n if (count === 0) {\n continue;\n }\n writes.push({ file, content: lines.map((line) => `${line.text}${line.eol}`).join(\"\") });\n filled.push({ table, file, fields: [...written], count });\n }\n\n for (const { file, content } of writes) {\n await writeFile(file, content);\n }\n\n const unproducedFields = fields.filter((field) => !producedFields.has(field));\n if (tables.length > 0 && unproducedFields.length > 0) {\n warnings.push(`No seed data produces a value for: ${unproducedFields.join(\", \")}`);\n }\n\n const outputLines = formatWarnings(warnings);\n outputLines.push(\n filled.length === 0\n ? \"\\u2713 Nothing to fill\"\n : filled\n .map(\n ({ file, fields: tableFields, count }) =>\n `\\u2713 ${file}: filled ${tableFields.join(\", \")} in ${count} row(s)`,\n )\n .join(\"\\n\"),\n );\n\n return { output: outputLines.join(\"\\n\"), filled };\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import{t as e}from"./service-CuqkHP1r.mjs";export{e as createExecutorService};