@tailor-platform/sdk 2.24.0 → 2.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/dist/application-ChqHuhZW.mjs +1 -0
- package/dist/{application-CfqvzV3I.mjs → application-DS0XKBtK.mjs} +4 -4
- package/dist/application-DS0XKBtK.mjs.map +1 -0
- package/dist/cli/commands/deploy/deployment-target.d.mts +1 -0
- package/dist/cli/commands/machineuser/list.d.mts +1 -0
- package/dist/cli/commands/show.d.mts +13 -1
- package/dist/cli/commands/tailordb/migrate/bundler.d.mts +4 -1
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +6 -0
- package/dist/cli/lib.d.mts +2 -2
- package/dist/cli/lib.mjs +1 -1
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/main.mjs +47 -47
- package/dist/cli/main.mjs.map +1 -1
- package/dist/completion/zsh-worker.zsh +3 -3
- package/dist/configure/index.d.mts +2 -2
- package/dist/configure/index.mjs.map +1 -1
- package/dist/kysely/index.d.mts +47 -15
- package/dist/kysely/index.mjs +1 -1
- package/dist/kysely/index.mjs.map +1 -1
- package/dist/kysely-type-RDX59E1j.mjs +48 -0
- package/dist/kysely-type-RDX59E1j.mjs.map +1 -0
- package/dist/plugin/builtin/kysely-type/index.d.mts +11 -0
- package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
- package/dist/plugin/index.mjs +1 -1
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/plugin/types.d.mts +97 -0
- package/dist/register-ts-hook-BuwkLuxC.mjs +925 -0
- package/dist/register-ts-hook-BuwkLuxC.mjs.map +1 -0
- package/dist/runtime/index.d.mts +8 -0
- package/dist/vitest/index.d.mts +3 -2
- package/dist/vitest/index.mjs +1 -1
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mocks/tailordb-pglite.d.mts +1 -0
- package/dist/vitest/pglite-kysely.d.mts +14 -1
- package/dist/vitest/pglite-temporal.d.mts +9 -0
- package/docs/cli/application.md +23 -0
- package/docs/cli/secret.md +24 -16
- package/docs/cli-reference.md +25 -13
- package/docs/github-actions.md +69 -4
- package/docs/plugin/custom.md +76 -1
- package/docs/plugin/frontend.md +124 -0
- package/docs/plugin/index.md +24 -2
- package/docs/services/auth.md +2 -0
- package/docs/services/resolver.md +1 -1
- package/docs/services/secret.md +5 -4
- package/docs/services/staticwebsite.md +2 -0
- package/docs/services/tailordb-migration.md +5 -3
- package/docs/testing.md +2 -1
- package/package.json +5 -5
- package/dist/application-BDqze-wy.mjs +0 -1
- package/dist/application-CfqvzV3I.mjs.map +0 -1
- package/dist/kysely-type-C-iyFFH7.mjs +0 -43
- package/dist/kysely-type-C-iyFFH7.mjs.map +0 -1
- package/dist/register-ts-hook-CVlI9Jzl.mjs +0 -922
- package/dist/register-ts-hook-CVlI9Jzl.mjs.map +0 -1
- package/dist/tailordb-ddl-DqInYupv.mjs +0 -7
- package/dist/tailordb-ddl-DqInYupv.mjs.map +0 -1
package/dist/kysely/index.d.mts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { TypeLevelError, output } from "../types/helpers.mjs";
|
|
2
2
|
import { IsAutoFilledDBField, IsReadOnlyDBField, TailorAnyDBField, TailorAnyDBType } from "../configure/services/tailordb/types.mjs";
|
|
3
|
+
import { Temporal, Temporal as Temporal$1 } from "temporal-spec";
|
|
3
4
|
import { ColumnType, ColumnType as ColumnType$1, Insertable, Insertable as Insertable$1, Kysely, Kysely as Kysely$1, KyselyConfig, KyselyConfig as KyselyConfig$1, Selectable, Selectable as Selectable$1, Transaction, Transaction as Transaction$1, Updateable, Updateable as Updateable$1, sql } from "kysely";
|
|
4
5
|
import { TailordbDialect } from "@tailor-platform/function-kysely-tailordb";
|
|
5
6
|
//#region src/kysely/index.d.ts
|
|
6
7
|
export type Timestamp = ColumnType$1<Date, Date | string, Date | string>;
|
|
8
|
+
/** Column type for a `date` field read back as `Temporal.PlainDate` (`createGetDB`'s `temporal` option). */
|
|
9
|
+
export type TemporalDate = ColumnType$1<Temporal$1.PlainDate, Temporal$1.PlainDate | string, Temporal$1.PlainDate | string>;
|
|
10
|
+
/** Column type for a `datetime` field read back as `Temporal.Instant` (`createGetDB`'s `temporal` option). */
|
|
11
|
+
export type TemporalInstant = ColumnType$1<Temporal$1.Instant, Temporal$1.Instant | string, Temporal$1.Instant | string>;
|
|
12
|
+
/** Column type for a `time` field read back as `Temporal.PlainTime` (`createGetDB`'s `temporal` option). */
|
|
13
|
+
export type TemporalTime = ColumnType$1<Temporal$1.PlainTime, Temporal$1.PlainTime | string, Temporal$1.PlainTime | string>;
|
|
7
14
|
type ResolveSelect<T> = T extends ColumnType$1<infer S, unknown, unknown> ? S : T;
|
|
8
15
|
type ResolveInsert<T> = T extends ColumnType$1<unknown, infer I, unknown> ? I : T;
|
|
9
16
|
type ResolveUpdate<T> = T extends ColumnType$1<unknown, unknown, infer U> ? U : T;
|
|
@@ -15,11 +22,23 @@ export type Serial<T = string | number> = ColumnType$1<T, SerialColumnMustBeOmit
|
|
|
15
22
|
type FlattenColumns<T> = { [K in keyof T]: T[K]; } & {};
|
|
16
23
|
export type TailordbKysely<DB> = Kysely$1<DB>;
|
|
17
24
|
export type NamespaceDB<NS, N extends keyof NS = keyof NS> = TailordbKysely<NS[N]>;
|
|
25
|
+
/** Options for {@link createGetDB}. */
|
|
26
|
+
export type CreateGetDBOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* When `true`, date/datetime fields come back as `Temporal.PlainDate`/
|
|
29
|
+
* `Temporal.Instant`, and top-level time fields come back as `Temporal.PlainTime`,
|
|
30
|
+
* instead of `Date`/`string`. Nested time fields remain strings. Match the `temporal`
|
|
31
|
+
* option `kyselyTypePlugin` was configured with so the generated table types agree
|
|
32
|
+
* with this runtime behavior. Defaults to `false`.
|
|
33
|
+
*/
|
|
34
|
+
temporal?: boolean;
|
|
35
|
+
};
|
|
18
36
|
/**
|
|
19
37
|
* Create a namespace-aware getDB function for generated code.
|
|
38
|
+
* @param options - Settings applied to every Kysely instance the returned `getDB` creates
|
|
20
39
|
* @returns A getDB function that creates Kysely instances for specific namespaces
|
|
21
40
|
*/
|
|
22
|
-
export declare function createGetDB<NS>(): <const N extends keyof NS & string>(namespace: N, config?: Omit<KyselyConfig$1, "dialect">) => TailordbKysely<NS[N]>;
|
|
41
|
+
export declare function createGetDB<NS>(options?: CreateGetDBOptions): <const N extends keyof NS & string>(namespace: N, config?: Omit<KyselyConfig$1, "dialect">) => TailordbKysely<NS[N]>;
|
|
23
42
|
export type NamespaceTransaction<NS, K extends keyof NS | TailordbKysely<NS[keyof NS]> = keyof NS> = K extends TailordbKysely<infer DB> ? Transaction$1<DB> : K extends keyof NS ? Transaction$1<NS[K]> : never;
|
|
24
43
|
export type NamespaceTableName<NS> = { [N in keyof NS]: keyof NS[N]; }[keyof NS];
|
|
25
44
|
export type NamespaceTable<NS, T extends NamespaceTableName<NS>> = { [N in keyof NS]: T extends keyof NS[N] ? NS[N][T] : never; }[keyof NS];
|
|
@@ -34,21 +53,24 @@ type IsArrayDBField<F> = F extends TailorAnyDBField ? F["_defined"]["array"] ext
|
|
|
34
53
|
type Unwrapped<F> = Exclude<output<F>, null>;
|
|
35
54
|
type ElementOutput<F> = IsArrayDBField<F> extends true ? Unwrapped<F> extends readonly (infer E)[] ? E : Unwrapped<F> : Unwrapped<F>;
|
|
36
55
|
type NestedFieldsOf<F> = F extends TailorAnyDBField ? F["fields"] : never;
|
|
37
|
-
type NestedProps<Fields> = { -readonly [K in keyof Fields as null extends output<Fields[K]> ? never : K]: DBColumn<Fields[K]>; } & { -readonly [K in keyof Fields as null extends output<Fields[K]> ? K : never]?: DBColumn<Fields[K]>; };
|
|
38
|
-
type NestedNeedsColumnType<Fields> = true extends { [K in keyof Fields]: DBFieldType<Fields[K]> extends "
|
|
39
|
-
type NestedColumn<F> = NestedNeedsColumnType<NestedFieldsOf<F
|
|
40
|
-
type
|
|
41
|
-
type
|
|
42
|
-
type
|
|
43
|
-
type
|
|
44
|
-
type
|
|
56
|
+
type NestedProps<Fields, IsTemporal extends boolean> = { -readonly [K in keyof Fields as null extends output<Fields[K]> ? never : K]: DBColumn<Fields[K], IsTemporal, true>; } & { -readonly [K in keyof Fields as null extends output<Fields[K]> ? K : never]?: DBColumn<Fields[K], IsTemporal, true>; };
|
|
57
|
+
type NestedNeedsColumnType<Fields, IsTemporal extends boolean> = true extends { [K in keyof Fields]: DBFieldType<Fields[K]> extends "nested" ? NestedNeedsColumnType<NestedFieldsOf<Fields[K]>, IsTemporal> : ElementColumn<Fields[K], IsTemporal, true> extends ColumnType$1<unknown, unknown, unknown> ? true : null extends output<Fields[K]> ? true : Fields[K] extends TailorAnyDBField ? IsAutoFilledDBField<Fields[K]> : false; }[keyof Fields] ? true : false;
|
|
58
|
+
type NestedColumn<F, IsTemporal extends boolean> = NestedNeedsColumnType<NestedFieldsOf<F>, IsTemporal> extends true ? ObjectColumnType<FlattenColumns<NestedProps<NestedFieldsOf<F>, IsTemporal>>> : FlattenColumns<NestedProps<NestedFieldsOf<F>, IsTemporal>>;
|
|
59
|
+
type DateColumn<Type extends "date" | "datetime" | "time", IsTemporal extends boolean, IsNested extends boolean> = IsTemporal extends true ? Type extends "date" ? TemporalDate : Type extends "datetime" ? TemporalInstant : IsNested extends true ? string : TemporalTime : Type extends "date" | "datetime" ? Timestamp : string;
|
|
60
|
+
type ElementColumn<F, IsTemporal extends boolean, IsNested extends boolean> = DBFieldType<F> extends "nested" ? NestedColumn<F, IsTemporal> : DBFieldType<F> extends "date" | "datetime" | "time" ? DateColumn<DBFieldType<F>, IsTemporal, IsNested> : ElementOutput<F>;
|
|
61
|
+
type ArrayedColumn<F, IsTemporal extends boolean, IsNested extends boolean> = IsArrayDBField<F> extends true ? ElementColumn<F, IsTemporal, IsNested> extends ColumnType$1<unknown, unknown, unknown> ? ArrayColumnType<ElementColumn<F, IsTemporal, IsNested>> : ElementColumn<F, IsTemporal, IsNested>[] : ElementColumn<F, IsTemporal, IsNested>;
|
|
62
|
+
type NullableColumn<F, IsTemporal extends boolean, IsNested extends boolean> = null extends output<F> ? ArrayedColumn<F, IsTemporal, IsNested> | null : ArrayedColumn<F, IsTemporal, IsNested>;
|
|
63
|
+
type DBColumn<F, IsTemporal extends boolean, IsNested extends boolean = false> = F extends TailorAnyDBField ? IsReadOnlyDBField<F> extends true ? Serial<NullableColumn<F, IsTemporal, IsNested>> : IsAutoFilledDBField<F> extends true ? Generated<NullableColumn<F, IsTemporal, IsNested>> : NullableColumn<F, IsTemporal, IsNested> : never;
|
|
64
|
+
type TailorDBColumns<T extends TailorDBColumnsSource, IsTemporal extends boolean> = { [K in keyof DBFieldsOf<T>]: K extends "id" ? Generated<output<DBFieldsOf<T>[K]>> : DBColumn<DBFieldsOf<T>[K], IsTemporal>; };
|
|
45
65
|
/**
|
|
46
66
|
* Create input derived from a TailorDB table (`typeof myTable`) or a field collection.
|
|
47
67
|
*
|
|
48
68
|
* Each field resolves to the column type `kyselyTypePlugin` writes for it, so this and
|
|
49
69
|
* the generated table types agree: `.serial()` fields are never caller-supplied,
|
|
50
70
|
* `.default()` / `.hooks({ create })` fields may be omitted, optional fields stay
|
|
51
|
-
* optional, `id` is platform-generated, and
|
|
71
|
+
* optional, `id` is platform-generated, and date/datetime/time fields read back as
|
|
72
|
+
* `Date`/`string`, or as their `Temporal` counterparts when `IsTemporal` is `true`.
|
|
73
|
+
* Nested time fields remain strings in either mode.
|
|
52
74
|
*
|
|
53
75
|
* Pass a field collection when the fields are a type parameter — a shared module that
|
|
54
76
|
* lets each project extend a table with its own fields cannot name a generated table
|
|
@@ -60,15 +82,25 @@ type TailorDBColumns<T extends TailorDBColumnsSource> = { [K in keyof DBFieldsOf
|
|
|
60
82
|
* it: while `F` is still an unresolved type parameter the mapping stays deferred, so
|
|
61
83
|
* assigning to it inside the function body reports the unevaluated conditional rather
|
|
62
84
|
* than a readable shape.
|
|
85
|
+
* @param IsTemporal - Pass `true` to accept `Temporal.PlainDate`/`Temporal.Instant` for
|
|
86
|
+
* date/datetime fields and `Temporal.PlainTime` for top-level time fields (alongside a
|
|
87
|
+
* plain string), matching a `getDB` created with `createGetDB({ temporal: true })`.
|
|
88
|
+
* Nested time fields remain strings. Defaults to `false`.
|
|
63
89
|
* @example
|
|
64
90
|
* function createInput<const F extends Record<string, TailorAnyDBField>>(fields: F) {
|
|
65
91
|
* return (input: TailorDBInsertable<F>) => { ... };
|
|
66
92
|
* }
|
|
67
93
|
*/
|
|
68
|
-
export type TailorDBInsertable<T extends TailorDBColumnsSource> = FlattenColumns<Insertable$1<TailorDBColumns<T>>>;
|
|
69
|
-
/**
|
|
70
|
-
|
|
94
|
+
export type TailorDBInsertable<T extends TailorDBColumnsSource, IsTemporal extends boolean = false> = FlattenColumns<Insertable$1<TailorDBColumns<T, IsTemporal>>>;
|
|
95
|
+
/**
|
|
96
|
+
* Read shape of a TailorDB table or field collection. See {@link TailorDBInsertable}.
|
|
97
|
+
* @param IsTemporal - Pass `true` to read date/datetime fields as `Temporal.PlainDate`/
|
|
98
|
+
* `Temporal.Instant` and top-level time fields as `Temporal.PlainTime`, matching a
|
|
99
|
+
* `getDB` created with `createGetDB({ temporal: true })`. Nested time fields remain
|
|
100
|
+
* strings. Defaults to `false` (`Date` for date/datetime, `string` for time).
|
|
101
|
+
*/
|
|
102
|
+
export type TailorDBSelectable<T extends TailorDBColumnsSource, IsTemporal extends boolean = false> = FlattenColumns<Selectable$1<TailorDBColumns<T, IsTemporal>>>;
|
|
71
103
|
/** Update input for a TailorDB table or field collection. See {@link TailorDBInsertable}. */
|
|
72
|
-
export type TailorDBUpdateable<T extends TailorDBColumnsSource> = FlattenColumns<Updateable$1<TailorDBColumns<T>>>;
|
|
104
|
+
export type TailorDBUpdateable<T extends TailorDBColumnsSource, IsTemporal extends boolean = false> = FlattenColumns<Updateable$1<TailorDBColumns<T, IsTemporal>>>;
|
|
73
105
|
//#endregion
|
|
74
|
-
export { type ColumnType, type Insertable, Kysely, type KyselyConfig, type Selectable, TailordbDialect, type Transaction, type Updateable, sql };
|
|
106
|
+
export { type ColumnType, type Insertable, Kysely, type KyselyConfig, type Selectable, TailordbDialect, type Temporal, type Transaction, type Updateable, sql };
|
package/dist/kysely/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{TailordbDialect as e,TailordbDialect as t}from"@tailor-platform/function-kysely-tailordb";import{Kysely as n,Kysely as r,sql as i}from"kysely";function createGetDB(){return function(e,
|
|
1
|
+
import{TailordbDialect as e,TailordbDialect as t}from"@tailor-platform/function-kysely-tailordb";import{Kysely as n,Kysely as r,sql as i}from"kysely";function createGetDB(e={}){let n=e.temporal;return function(e,i){let a=new tailordb.Client({namespace:e,...n?{temporal:n}:{}});return new r({...i,dialect:new t(a)})}}export{n as Kysely,e as TailordbDialect,createGetDB,i as sql};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["Kysely","TailordbDialect","TailordbDialect$1","Kysely$1","client","config","Kysely","TailordbDialect","sql"],"sources":["../../src/kysely/index.ts"],"sourcesContent":["/**\n * Kysely integration module for generated TailorDB code.\n *\n * Re-exports kysely and function-kysely-tailordb types through a single import path\n * to avoid phantom dependency issues with pnpm, and provides the namespace-aware\n * utility types and factory functions the code generator emits against.\n *\n * It also holds the `TailorDB*` input types, which hand-written code uses to derive\n * create/read/update shapes straight from a table or field collection. They live here\n * rather than in the main entry point because they are built out of kysely's column\n * types, which the main entry point does not otherwise depend on.\n */\n\nimport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\nimport {\n type ColumnType,\n Kysely,\n type Insertable,\n type KyselyConfig,\n type Selectable,\n type Transaction as KyselyTransaction,\n type Updateable,\n} from \"kysely\";\nimport type {\n IsAutoFilledDBField,\n IsReadOnlyDBField,\n TailorAnyDBField,\n TailorAnyDBType,\n} from \"#/configure/services/tailordb/types\";\nimport type { output, TypeLevelError } from \"#/types/helpers\";\n\nexport {\n type ColumnType,\n Kysely,\n type KyselyConfig,\n type Transaction,\n type Insertable,\n type Selectable,\n sql,\n type Updateable,\n} from \"kysely\";\n\nexport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\n\nexport type Timestamp = ColumnType<Date, Date | string, Date | string>;\ntype ResolveSelect<T> = T extends ColumnType<infer S, unknown, unknown> ? S : T;\ntype ResolveInsert<T> = T extends ColumnType<unknown, infer I, unknown> ? I : T;\ntype ResolveUpdate<T> = T extends ColumnType<unknown, unknown, infer U> ? U : T;\nexport type ObjectColumnType<T> = ColumnType<\n { [K in keyof T]-?: Exclude<ResolveSelect<T[K]>, undefined> },\n { [K in keyof T]: ResolveInsert<T[K]> },\n { [K in keyof T]: ResolveUpdate<T[K]> }\n>;\nexport type ArrayColumnType<T> = ColumnType<\n ResolveSelect<T>[],\n ResolveInsert<T>[],\n ResolveUpdate<T>[]\n>;\nexport type Generated<T> =\n T extends ColumnType<infer S, infer I, infer U>\n ? ColumnType<S, I | undefined, U>\n : ColumnType<T, T | undefined, T>;\n// The insert/update types carry the reason so that supplying a value fails with it,\n// instead of the bare \"does not exist\" an absent key produces. The named alias is\n// what survives in nested positions (values()/set()), where tsc elides the marker's\n// type argument and only the name reaches the user.\n// `| undefined` is what makes the column omittable; Kysely drops undefined columns\n// from the statement, so passing it explicitly is the same as leaving it out.\ntype SerialColumnMustBeOmitted = TypeLevelError<\"assigned by .serial(); remove it from the input\">;\nexport type Serial<T = string | number> = ColumnType<\n T,\n SerialColumnMustBeOmitted | undefined,\n SerialColumnMustBeOmitted | undefined\n>;\n\n// Kysely composes its input types out of intersections. Flattening them keeps the\n// shape readable in assignability errors and editor tooltips.\ntype FlattenColumns<T> = { [K in keyof T]: T[K] } & {};\n\nexport type TailordbKysely<DB> = Kysely<DB>;\nexport type NamespaceDB<NS, N extends keyof NS = keyof NS> = TailordbKysely<NS[N]>;\n\n/**\n * Create a namespace-aware getDB function for generated code.\n * @returns A getDB function that creates Kysely instances for specific namespaces\n */\nexport function createGetDB<NS>() {\n return function getDB<const N extends keyof NS & string>(\n namespace: N,\n config?: Omit<KyselyConfig, \"dialect\">,\n ): TailordbKysely<NS[N]> {\n const client = new tailordb.Client({ namespace });\n return new Kysely<NS[N]>({\n dialect: new TailordbDialect(client),\n ...config,\n });\n };\n}\n\nexport type NamespaceTransaction<NS, K extends keyof NS | TailordbKysely<NS[keyof NS]> = keyof NS> =\n K extends TailordbKysely<infer DB>\n ? KyselyTransaction<DB>\n : K extends keyof NS\n ? KyselyTransaction<NS[K]>\n : never;\n\nexport type NamespaceTableName<NS> = {\n [N in keyof NS]: keyof NS[N];\n}[keyof NS];\n\nexport type NamespaceTable<NS, T extends NamespaceTableName<NS>> = {\n [N in keyof NS]: T extends keyof NS[N] ? NS[N][T] : never;\n}[keyof NS];\n\nexport type NamespaceInsertable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Insertable<NamespaceTable<NS, T>>\n>;\nexport type NamespaceSelectable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Selectable<NamespaceTable<NS, T>>\n>;\nexport type NamespaceUpdateable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Updateable<NamespaceTable<NS, T>>\n>;\n\n/** What the derived input types accept: a table (`typeof myTable`) or a field collection. */\ntype TailorDBColumnsSource = TailorAnyDBType | Record<string, TailorAnyDBField>;\n\ntype DBFieldsOf<T> = T extends TailorAnyDBType ? T[\"fields\"] : T;\n\n// The column mapping below mirrors the one `kyselyTypePlugin` applies when it writes a\n// table interface (`plugin/builtin/kysely-type/type-processor.ts`), so both surfaces\n// resolve a field to the same column type — including inside nested objects, where the\n// function runtime also hands back a Date for a date/datetime.\n// `example/tests/kysely-parity.ts` pins the two against each other on real generator output.\ntype DBFieldType<F> = F extends TailorAnyDBField ? F[\"_defined\"][\"type\"] : never;\ntype IsArrayDBField<F> = F extends TailorAnyDBField\n ? F[\"_defined\"][\"array\"] extends true\n ? true\n : false\n : false;\n\ntype Unwrapped<F> = Exclude<output<F>, null>;\ntype ElementOutput<F> =\n IsArrayDBField<F> extends true\n ? Unwrapped<F> extends readonly (infer E)[]\n ? E\n : Unwrapped<F>\n : Unwrapped<F>;\n\ntype NestedFieldsOf<F> = F extends TailorAnyDBField ? F[\"fields\"] : never;\n\n// Nested props carry the same column mapping as top-level ones, so a datetime inside an\n// object also resolves to Timestamp. The optional marker matches what the output type\n// gives them, which is what the generator emits too.\n// `-readonly` because a field collection is inferred with `const`, and the generated\n// table interfaces declare their nested props mutable.\ntype NestedProps<Fields> = {\n -readonly [K in keyof Fields as null extends output<Fields[K]> ? never : K]: DBColumn<Fields[K]>;\n} & {\n -readonly [K in keyof Fields as null extends output<Fields[K]> ? K : never]?: DBColumn<Fields[K]>;\n};\n\n// The generator reaches for ObjectColumnType only when the object holds something whose\n// select and insert types differ: a timestamp, an optional prop, or a filled-in one.\ntype NestedNeedsColumnType<Fields> = true extends {\n [K in keyof Fields]: DBFieldType<Fields[K]> extends \"date\" | \"datetime\"\n ? true\n : null extends output<Fields[K]>\n ? true\n : DBFieldType<Fields[K]> extends \"nested\"\n ? NestedNeedsColumnType<NestedFieldsOf<Fields[K]>>\n : Fields[K] extends TailorAnyDBField\n ? IsAutoFilledDBField<Fields[K]>\n : false;\n}[keyof Fields]\n ? true\n : false;\n\ntype NestedColumn<F> =\n NestedNeedsColumnType<NestedFieldsOf<F>> extends true\n ? ObjectColumnType<FlattenColumns<NestedProps<NestedFieldsOf<F>>>>\n : FlattenColumns<NestedProps<NestedFieldsOf<F>>>;\n\ntype ElementColumn<F> =\n DBFieldType<F> extends \"date\" | \"datetime\"\n ? Timestamp\n : DBFieldType<F> extends \"nested\"\n ? NestedColumn<F>\n : ElementOutput<F>;\n\n// A ColumnType cannot sit inside an array — Kysely only unwraps it at the top level of a\n// table property — so an array of them keeps the ColumnType outermost.\ntype ArrayedColumn<F> =\n IsArrayDBField<F> extends true\n ? ElementColumn<F> extends ColumnType<unknown, unknown, unknown>\n ? ArrayColumnType<ElementColumn<F>>\n : ElementColumn<F>[]\n : ElementColumn<F>;\n\ntype NullableColumn<F> = null extends output<F> ? ArrayedColumn<F> | null : ArrayedColumn<F>;\n\ntype DBColumn<F> = F extends TailorAnyDBField\n ? IsReadOnlyDBField<F> extends true\n ? Serial<NullableColumn<F>>\n : IsAutoFilledDBField<F> extends true\n ? Generated<NullableColumn<F>>\n : NullableColumn<F>\n : never;\n\n// The column map the three derived types are built from. Not exported: it is how the\n// mapping is expressed, not something callers need to name.\ntype TailorDBColumns<T extends TailorDBColumnsSource> = {\n [K in keyof DBFieldsOf<T>]: K extends \"id\"\n ? Generated<output<DBFieldsOf<T>[K]>>\n : DBColumn<DBFieldsOf<T>[K]>;\n};\n\n/**\n * Create input derived from a TailorDB table (`typeof myTable`) or a field collection.\n *\n * Each field resolves to the column type `kyselyTypePlugin` writes for it, so this and\n * the generated table types agree: `.serial()` fields are never caller-supplied,\n * `.default()` / `.hooks({ create })` fields may be omitted, optional fields stay\n * optional, `id` is platform-generated, and a date or datetime reads back as `Date`.\n *\n * Pass a field collection when the fields are a type parameter — a shared module that\n * lets each project extend a table with its own fields cannot name a generated table\n * type. Declare it as `<const F extends Record<string, TailorAnyDBField>>` so the field\n * types are inferred; a bare `Record<string, TailorAnyDBField>` erases them and nothing\n * is checked.\n *\n * Hand the result to callers rather than consuming it inside the generic that declares\n * it: while `F` is still an unresolved type parameter the mapping stays deferred, so\n * assigning to it inside the function body reports the unevaluated conditional rather\n * than a readable shape.\n * @example\n * function createInput<const F extends Record<string, TailorAnyDBField>>(fields: F) {\n * return (input: TailorDBInsertable<F>) => { ... };\n * }\n */\nexport type TailorDBInsertable<T extends TailorDBColumnsSource> = FlattenColumns<\n Insertable<TailorDBColumns<T>>\n>;\n\n/** Read shape of a TailorDB table or field collection. See {@link TailorDBInsertable}. */\nexport type TailorDBSelectable<T extends TailorDBColumnsSource> = FlattenColumns<\n Selectable<TailorDBColumns<T>>\n>;\n\n/** Update input for a TailorDB table or field collection. See {@link TailorDBInsertable}. */\nexport type TailorDBUpdateable<T extends TailorDBColumnsSource> = FlattenColumns<\n Updateable<TailorDBColumns<T>>\n>;\n"],"mappings":"sJAsFA,SAAgB,aAAkB,CAChC,OAAO,SACL,EACA,EACuB,CACvB,IAAMI,EAAS,IAAI,SAAS,OAAO,CAAE,WAAU,CAAC,EAChD,OAAO,IAAIJ,EAAc,CACvB,QAAS,IAAIC,EAAgBG,CAAM,EACnC,GAAGC,CACL,CAAC,CACH,CACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["Kysely","TailordbDialect","TailordbDialect$1","Kysely$1","temporal","options","client","config","Kysely","TailordbDialect","sql"],"sources":["../../src/kysely/index.ts"],"sourcesContent":["/**\n * Kysely integration module for generated TailorDB code.\n *\n * Re-exports kysely and function-kysely-tailordb types through a single import path\n * to avoid phantom dependency issues with pnpm, and provides the namespace-aware\n * utility types and factory functions the code generator emits against.\n *\n * It also holds the `TailorDB*` input types, which hand-written code uses to derive\n * create/read/update shapes straight from a table or field collection. They live here\n * rather than in the main entry point because they are built out of kysely's column\n * types, which the main entry point does not otherwise depend on.\n */\n\nimport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\nimport {\n type ColumnType,\n Kysely,\n type Insertable,\n type KyselyConfig,\n type Selectable,\n type Transaction as KyselyTransaction,\n type Updateable,\n} from \"kysely\";\nimport type {\n IsAutoFilledDBField,\n IsReadOnlyDBField,\n TailorAnyDBField,\n TailorAnyDBType,\n} from \"#/configure/services/tailordb/types\";\nimport type { output, TypeLevelError } from \"#/types/helpers\";\nimport type { Temporal } from \"temporal-spec\";\n\nexport {\n type ColumnType,\n Kysely,\n type KyselyConfig,\n type Transaction,\n type Insertable,\n type Selectable,\n sql,\n type Updateable,\n} from \"kysely\";\n\nexport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\n\n/** Types of the `Temporal` values that date/datetime/time columns read back as when Temporal mode is enabled. */\nexport type { Temporal } from \"temporal-spec\";\n\nexport type Timestamp = ColumnType<Date, Date | string, Date | string>;\n/** Column type for a `date` field read back as `Temporal.PlainDate` (`createGetDB`'s `temporal` option). */\nexport type TemporalDate = ColumnType<\n Temporal.PlainDate,\n Temporal.PlainDate | string,\n Temporal.PlainDate | string\n>;\n/** Column type for a `datetime` field read back as `Temporal.Instant` (`createGetDB`'s `temporal` option). */\nexport type TemporalInstant = ColumnType<\n Temporal.Instant,\n Temporal.Instant | string,\n Temporal.Instant | string\n>;\n/** Column type for a `time` field read back as `Temporal.PlainTime` (`createGetDB`'s `temporal` option). */\nexport type TemporalTime = ColumnType<\n Temporal.PlainTime,\n Temporal.PlainTime | string,\n Temporal.PlainTime | string\n>;\ntype ResolveSelect<T> = T extends ColumnType<infer S, unknown, unknown> ? S : T;\ntype ResolveInsert<T> = T extends ColumnType<unknown, infer I, unknown> ? I : T;\ntype ResolveUpdate<T> = T extends ColumnType<unknown, unknown, infer U> ? U : T;\nexport type ObjectColumnType<T> = ColumnType<\n { [K in keyof T]-?: Exclude<ResolveSelect<T[K]>, undefined> },\n { [K in keyof T]: ResolveInsert<T[K]> },\n { [K in keyof T]: ResolveUpdate<T[K]> }\n>;\nexport type ArrayColumnType<T> = ColumnType<\n ResolveSelect<T>[],\n ResolveInsert<T>[],\n ResolveUpdate<T>[]\n>;\nexport type Generated<T> =\n T extends ColumnType<infer S, infer I, infer U>\n ? ColumnType<S, I | undefined, U>\n : ColumnType<T, T | undefined, T>;\n// The insert/update types carry the reason so that supplying a value fails with it,\n// instead of the bare \"does not exist\" an absent key produces. The named alias is\n// what survives in nested positions (values()/set()), where tsc elides the marker's\n// type argument and only the name reaches the user.\n// `| undefined` is what makes the column omittable; Kysely drops undefined columns\n// from the statement, so passing it explicitly is the same as leaving it out.\ntype SerialColumnMustBeOmitted = TypeLevelError<\"assigned by .serial(); remove it from the input\">;\nexport type Serial<T = string | number> = ColumnType<\n T,\n SerialColumnMustBeOmitted | undefined,\n SerialColumnMustBeOmitted | undefined\n>;\n\n// Kysely composes its input types out of intersections. Flattening them keeps the\n// shape readable in assignability errors and editor tooltips.\ntype FlattenColumns<T> = { [K in keyof T]: T[K] } & {};\n\nexport type TailordbKysely<DB> = Kysely<DB>;\nexport type NamespaceDB<NS, N extends keyof NS = keyof NS> = TailordbKysely<NS[N]>;\n\n/** Options for {@link createGetDB}. */\nexport type CreateGetDBOptions = {\n /**\n * When `true`, date/datetime fields come back as `Temporal.PlainDate`/\n * `Temporal.Instant`, and top-level time fields come back as `Temporal.PlainTime`,\n * instead of `Date`/`string`. Nested time fields remain strings. Match the `temporal`\n * option `kyselyTypePlugin` was configured with so the generated table types agree\n * with this runtime behavior. Defaults to `false`.\n */\n temporal?: boolean;\n};\n\n/**\n * Create a namespace-aware getDB function for generated code.\n * @param options - Settings applied to every Kysely instance the returned `getDB` creates\n * @returns A getDB function that creates Kysely instances for specific namespaces\n */\nexport function createGetDB<NS>(options: CreateGetDBOptions = {}) {\n const temporal = options.temporal;\n return function getDB<const N extends keyof NS & string>(\n namespace: N,\n config?: Omit<KyselyConfig, \"dialect\">,\n ): TailordbKysely<NS[N]> {\n const client = new tailordb.Client({ namespace, ...(temporal ? { temporal } : {}) });\n return new Kysely<NS[N]>({\n ...config,\n dialect: new TailordbDialect(client),\n });\n };\n}\n\nexport type NamespaceTransaction<NS, K extends keyof NS | TailordbKysely<NS[keyof NS]> = keyof NS> =\n K extends TailordbKysely<infer DB>\n ? KyselyTransaction<DB>\n : K extends keyof NS\n ? KyselyTransaction<NS[K]>\n : never;\n\nexport type NamespaceTableName<NS> = {\n [N in keyof NS]: keyof NS[N];\n}[keyof NS];\n\nexport type NamespaceTable<NS, T extends NamespaceTableName<NS>> = {\n [N in keyof NS]: T extends keyof NS[N] ? NS[N][T] : never;\n}[keyof NS];\n\nexport type NamespaceInsertable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Insertable<NamespaceTable<NS, T>>\n>;\nexport type NamespaceSelectable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Selectable<NamespaceTable<NS, T>>\n>;\nexport type NamespaceUpdateable<NS, T extends NamespaceTableName<NS>> = FlattenColumns<\n Updateable<NamespaceTable<NS, T>>\n>;\n\n/** What the derived input types accept: a table (`typeof myTable`) or a field collection. */\ntype TailorDBColumnsSource = TailorAnyDBType | Record<string, TailorAnyDBField>;\n\ntype DBFieldsOf<T> = T extends TailorAnyDBType ? T[\"fields\"] : T;\n\n// The column mapping below mirrors the one `kyselyTypePlugin` applies when it writes a\n// table interface (`plugin/builtin/kysely-type/type-processor.ts`), so both surfaces\n// resolve a field to the same column type — including inside nested objects, where the\n// function runtime also hands back a Date for a date/datetime.\n// `example/tests/kysely-parity.ts` pins the two against each other on real generator output.\ntype DBFieldType<F> = F extends TailorAnyDBField ? F[\"_defined\"][\"type\"] : never;\ntype IsArrayDBField<F> = F extends TailorAnyDBField\n ? F[\"_defined\"][\"array\"] extends true\n ? true\n : false\n : false;\n\ntype Unwrapped<F> = Exclude<output<F>, null>;\ntype ElementOutput<F> =\n IsArrayDBField<F> extends true\n ? Unwrapped<F> extends readonly (infer E)[]\n ? E\n : Unwrapped<F>\n : Unwrapped<F>;\n\ntype NestedFieldsOf<F> = F extends TailorAnyDBField ? F[\"fields\"] : never;\n\n// Nested props carry the same column mapping as top-level ones, so a datetime inside an\n// object also resolves to Timestamp (or its Temporal counterpart under `IsTemporal`). The\n// optional marker matches what the output type gives them, which is what the generator\n// emits too.\n// `-readonly` because a field collection is inferred with `const`, and the generated\n// table interfaces declare their nested props mutable.\ntype NestedProps<Fields, IsTemporal extends boolean> = {\n -readonly [K in keyof Fields as null extends output<Fields[K]> ? never : K]: DBColumn<\n Fields[K],\n IsTemporal,\n true\n >;\n} & {\n -readonly [K in keyof Fields as null extends output<Fields[K]> ? K : never]?: DBColumn<\n Fields[K],\n IsTemporal,\n true\n >;\n};\n\n// The generator reaches for ObjectColumnType only when the object holds something whose\n// select and insert types differ: a date/datetime field, an optional prop, or a filled-in\n// one. Temporal time fields only differ at the top level.\ntype NestedNeedsColumnType<Fields, IsTemporal extends boolean> = true extends {\n [K in keyof Fields]: DBFieldType<Fields[K]> extends \"nested\"\n ? NestedNeedsColumnType<NestedFieldsOf<Fields[K]>, IsTemporal>\n : ElementColumn<Fields[K], IsTemporal, true> extends ColumnType<unknown, unknown, unknown>\n ? true\n : null extends output<Fields[K]>\n ? true\n : Fields[K] extends TailorAnyDBField\n ? IsAutoFilledDBField<Fields[K]>\n : false;\n}[keyof Fields]\n ? true\n : false;\n\ntype NestedColumn<F, IsTemporal extends boolean> =\n NestedNeedsColumnType<NestedFieldsOf<F>, IsTemporal> extends true\n ? ObjectColumnType<FlattenColumns<NestedProps<NestedFieldsOf<F>, IsTemporal>>>\n : FlattenColumns<NestedProps<NestedFieldsOf<F>, IsTemporal>>;\n\ntype DateColumn<\n Type extends \"date\" | \"datetime\" | \"time\",\n IsTemporal extends boolean,\n IsNested extends boolean,\n> = IsTemporal extends true\n ? Type extends \"date\"\n ? TemporalDate\n : Type extends \"datetime\"\n ? TemporalInstant\n : IsNested extends true\n ? string\n : TemporalTime\n : Type extends \"date\" | \"datetime\"\n ? Timestamp\n : string;\n\ntype ElementColumn<F, IsTemporal extends boolean, IsNested extends boolean> =\n DBFieldType<F> extends \"nested\"\n ? NestedColumn<F, IsTemporal>\n : DBFieldType<F> extends \"date\" | \"datetime\" | \"time\"\n ? DateColumn<DBFieldType<F>, IsTemporal, IsNested>\n : ElementOutput<F>;\n\n// A ColumnType cannot sit inside an array — Kysely only unwraps it at the top level of a\n// table property — so an array of them keeps the ColumnType outermost.\ntype ArrayedColumn<F, IsTemporal extends boolean, IsNested extends boolean> =\n IsArrayDBField<F> extends true\n ? ElementColumn<F, IsTemporal, IsNested> extends ColumnType<unknown, unknown, unknown>\n ? ArrayColumnType<ElementColumn<F, IsTemporal, IsNested>>\n : ElementColumn<F, IsTemporal, IsNested>[]\n : ElementColumn<F, IsTemporal, IsNested>;\n\ntype NullableColumn<F, IsTemporal extends boolean, IsNested extends boolean> =\n null extends output<F>\n ? ArrayedColumn<F, IsTemporal, IsNested> | null\n : ArrayedColumn<F, IsTemporal, IsNested>;\n\ntype DBColumn<\n F,\n IsTemporal extends boolean,\n IsNested extends boolean = false,\n> = F extends TailorAnyDBField\n ? IsReadOnlyDBField<F> extends true\n ? Serial<NullableColumn<F, IsTemporal, IsNested>>\n : IsAutoFilledDBField<F> extends true\n ? Generated<NullableColumn<F, IsTemporal, IsNested>>\n : NullableColumn<F, IsTemporal, IsNested>\n : never;\n\n// The column map the three derived types are built from. Not exported: it is how the\n// mapping is expressed, not something callers need to name.\ntype TailorDBColumns<T extends TailorDBColumnsSource, IsTemporal extends boolean> = {\n [K in keyof DBFieldsOf<T>]: K extends \"id\"\n ? Generated<output<DBFieldsOf<T>[K]>>\n : DBColumn<DBFieldsOf<T>[K], IsTemporal>;\n};\n\n/**\n * Create input derived from a TailorDB table (`typeof myTable`) or a field collection.\n *\n * Each field resolves to the column type `kyselyTypePlugin` writes for it, so this and\n * the generated table types agree: `.serial()` fields are never caller-supplied,\n * `.default()` / `.hooks({ create })` fields may be omitted, optional fields stay\n * optional, `id` is platform-generated, and date/datetime/time fields read back as\n * `Date`/`string`, or as their `Temporal` counterparts when `IsTemporal` is `true`.\n * Nested time fields remain strings in either mode.\n *\n * Pass a field collection when the fields are a type parameter — a shared module that\n * lets each project extend a table with its own fields cannot name a generated table\n * type. Declare it as `<const F extends Record<string, TailorAnyDBField>>` so the field\n * types are inferred; a bare `Record<string, TailorAnyDBField>` erases them and nothing\n * is checked.\n *\n * Hand the result to callers rather than consuming it inside the generic that declares\n * it: while `F` is still an unresolved type parameter the mapping stays deferred, so\n * assigning to it inside the function body reports the unevaluated conditional rather\n * than a readable shape.\n * @param IsTemporal - Pass `true` to accept `Temporal.PlainDate`/`Temporal.Instant` for\n * date/datetime fields and `Temporal.PlainTime` for top-level time fields (alongside a\n * plain string), matching a `getDB` created with `createGetDB({ temporal: true })`.\n * Nested time fields remain strings. Defaults to `false`.\n * @example\n * function createInput<const F extends Record<string, TailorAnyDBField>>(fields: F) {\n * return (input: TailorDBInsertable<F>) => { ... };\n * }\n */\nexport type TailorDBInsertable<\n T extends TailorDBColumnsSource,\n IsTemporal extends boolean = false,\n> = FlattenColumns<Insertable<TailorDBColumns<T, IsTemporal>>>;\n\n/**\n * Read shape of a TailorDB table or field collection. See {@link TailorDBInsertable}.\n * @param IsTemporal - Pass `true` to read date/datetime fields as `Temporal.PlainDate`/\n * `Temporal.Instant` and top-level time fields as `Temporal.PlainTime`, matching a\n * `getDB` created with `createGetDB({ temporal: true })`. Nested time fields remain\n * strings. Defaults to `false` (`Date` for date/datetime, `string` for time).\n */\nexport type TailorDBSelectable<\n T extends TailorDBColumnsSource,\n IsTemporal extends boolean = false,\n> = FlattenColumns<Selectable<TailorDBColumns<T, IsTemporal>>>;\n\n/** Update input for a TailorDB table or field collection. See {@link TailorDBInsertable}. */\nexport type TailorDBUpdateable<\n T extends TailorDBColumnsSource,\n IsTemporal extends boolean = false,\n> = FlattenColumns<Updateable<TailorDBColumns<T, IsTemporal>>>;\n"],"mappings":"sJAyHA,SAAgB,YAAgB,EAA8B,CAAC,EAAG,CAChE,IAAMI,EAAWC,EAAQ,SACzB,OAAO,SACL,EACA,EACuB,CACvB,IAAMC,EAAS,IAAI,SAAS,OAAO,CAAE,YAAW,GAAIF,EAAW,CAAE,UAAS,EAAI,CAAC,CAAG,CAAC,EACnF,OAAO,IAAIJ,EAAc,CACvB,GAAGO,EACH,QAAS,IAAIN,EAAgBK,CAAM,CACrC,CAAC,CACH,CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import{t as e}from"./multiline-EyzjEwn9.mjs";import{resolve as t}from"pathe";function mapFieldTypeToColumnType(e,t=!1){switch(e){case`uuid`:case`string`:case`decimal`:return`string`;case`integer`:case`float`:return`number`;case`date`:return t?`TemporalDate`:`Timestamp`;case`datetime`:return t?`TemporalInstant`:`Timestamp`;case`time`:return t?`TemporalTime`:`string`;case`bool`:case`boolean`:return`boolean`;case`enum`:case`nested`:throw Error(`Field type "${e}" has no scalar column type; resolve it before mapping.`);default:return`string`}}const n=/* @__PURE__ */ new Map([[`Timestamp`,{select:`Date`,write:`Date | string`}],[`TemporalDate`,{select:`Temporal.PlainDate`,write:`Temporal.PlainDate | string`}],[`TemporalInstant`,{select:`Temporal.Instant`,write:`Temporal.Instant | string`}],[`TemporalTime`,{select:`Temporal.PlainTime`,write:`Temporal.PlainTime | string`}]]);function emptyUsedUtilityTypes(){return{Timestamp:!1,TemporalDate:!1,TemporalInstant:!1,TemporalTime:!1,Serial:!1,ObjectColumnType:!1,ArrayColumnType:!1}}function mergeUsedUtilityTypes(e,t){return{Timestamp:e.Timestamp||t.Timestamp,TemporalDate:e.TemporalDate||t.TemporalDate,TemporalInstant:e.TemporalInstant||t.TemporalInstant,TemporalTime:e.TemporalTime||t.TemporalTime,Serial:e.Serial||t.Serial,ObjectColumnType:e.ObjectColumnType||t.ObjectColumnType,ArrayColumnType:e.ArrayColumnType||t.ArrayColumnType}}function usesColumnTypeAlias(e){return e.Timestamp||e.TemporalDate||e.TemporalInstant||e.TemporalTime}function getEnumType(e){let t=e.allowedValues;return t&&Array.isArray(t)?t.map(e=>`"${typeof e==`string`?e:e.value}"`).join(` | `):`string`}function getNestedType(e,t){let n=e.fields;if(!n||typeof n!=`object`)return{type:`string`,usedUtilityTypes:emptyUsedUtilityTypes()};let r=Object.entries(n).map(([e,n])=>{let r=generateFieldType(n,t,!0);return{fieldType:`${e}${n.required===!0?``:`?`}: ${r.type}`,usedUtilityTypes:r.usedUtilityTypes}}),i=r.reduce((e,t)=>mergeUsedUtilityTypes(e,t.usedUtilityTypes),emptyUsedUtilityTypes()),a=r.map(e=>e.fieldType),o=`{\n ${a.join(`;
|
|
2
|
+
`)}${a.length>0?`;`:``}\n}`,s=Object.values(n).some(e=>e.required!==!0),c=Object.values(n).some(e=>e.hooks?.create||e.default!==void 0||e.optionalOnCreate===!0);return usesColumnTypeAlias(i)||s||c?{type:`ObjectColumnType<${o}>`,usedUtilityTypes:{...i,ObjectColumnType:!0}}:{type:o,usedUtilityTypes:i}}function getBaseType(e,t,n){let r=e.type,i=emptyUsedUtilityTypes();if(r===`enum`)return{type:getEnumType(e),usedUtilityTypes:i};if(r===`nested`)return getNestedType(e,t);let a=mapFieldTypeToColumnType(r,t&&!(n&&r===`time`));switch(a){case`Timestamp`:i.Timestamp=!0;break;case`TemporalDate`:i.TemporalDate=!0;break;case`TemporalInstant`:i.TemporalInstant=!0;break;case`TemporalTime`:i.TemporalTime=!0}return{type:a,usedUtilityTypes:i}}function generateFieldType(e,t,r=!1){let i=getBaseType(e,t,r),a={...i.usedUtilityTypes},o=e.array===!0,s=e.required!==!0,c=n.has(i.type),l=i.type;return o&&(c||l.startsWith(`ObjectColumnType<`)?(l=`ArrayColumnType<${i.type}>`,a.ArrayColumnType=!0):l=e.type===`enum`?`(${i.type})[]`:`${i.type}[]`),s&&(l=`${l} | null`),e.serial&&(a.Serial=!0,l=`Serial<${l}>`),(e.hooks?.create||e.default!==void 0||e.optionalOnCreate===!0)&&(l=`Generated<${l}>`),{type:l,usedUtilityTypes:a}}function generateTableInterface(t,n,r){let i=Object.entries(n).filter(([e])=>e!==`id`).map(([e,t])=>({fieldName:e,...generateFieldType(t,r)})),a=[`id: Generated<string>;`,...i.map(e=>`${e.fieldName}: ${e.type};`)],o=i.reduce((e,t)=>mergeUsedUtilityTypes(e,t.usedUtilityTypes),emptyUsedUtilityTypes());return{typeDef:e`
|
|
3
|
+
${t}: {
|
|
4
|
+
${a.join(`
|
|
5
|
+
`)}
|
|
6
|
+
}
|
|
7
|
+
`,usedUtilityTypes:o}}function processKyselyFields(e,t,n=!1){let r=generateTableInterface(e,t,n);return{name:e,typeDef:r.typeDef,usedUtilityTypes:r.usedUtilityTypes}}async function processKyselyType(e,t=!1){return processKyselyFields(e.name,Object.fromEntries(Object.entries(e.fields).map(([e,t])=>[e,t.config])),t)}function generateUnifiedKyselyTypes(t,n=!1){if(t.length===0)return``;let r=t.flatMap(e=>e.types).reduce((e,t)=>mergeUsedUtilityTypes(e,t.usedUtilityTypes),emptyUsedUtilityTypes()),i=[`type Generated`];return r.Timestamp&&i.push(`type Timestamp`),r.TemporalDate&&i.push(`type TemporalDate`),r.TemporalInstant&&i.push(`type TemporalInstant`),r.TemporalTime&&i.push(`type TemporalTime`),r.ObjectColumnType&&i.push(`type ObjectColumnType`),r.ArrayColumnType&&i.push(`type ArrayColumnType`),r.Serial&&i.push(`type Serial`),[e`
|
|
8
|
+
import {
|
|
9
|
+
createGetDB,
|
|
10
|
+
${i.join(`,
|
|
11
|
+
`)},
|
|
12
|
+
type NamespaceDB,
|
|
13
|
+
type NamespaceInsertable,
|
|
14
|
+
type NamespaceSelectable,
|
|
15
|
+
type NamespaceTable,
|
|
16
|
+
type NamespaceTableName,
|
|
17
|
+
type NamespaceTransaction,
|
|
18
|
+
type NamespaceUpdateable,
|
|
19
|
+
} from "@tailor-platform/sdk/kysely";
|
|
20
|
+
`,`export interface Namespace {\n${t.map(({namespace:e,types:t})=>` "${e}": {\n${t.map(e=>e.typeDef.split(`
|
|
21
|
+
`).map(e=>e.trim()?` ${e}`:``).join(`
|
|
22
|
+
`)).join(`
|
|
23
|
+
|
|
24
|
+
`)}\n }`).join(`,
|
|
25
|
+
`)}\n}`,e`
|
|
26
|
+
export const getDB = createGetDB<Namespace>(${n?`{ temporal: true }`:``});
|
|
27
|
+
|
|
28
|
+
export type DB<N extends keyof Namespace = keyof Namespace> = NamespaceDB<Namespace, N>;
|
|
29
|
+
`,e`
|
|
30
|
+
export type Transaction<K extends keyof Namespace | DB = keyof Namespace> =
|
|
31
|
+
NamespaceTransaction<Namespace, K>;
|
|
32
|
+
|
|
33
|
+
type TableName = NamespaceTableName<Namespace>;
|
|
34
|
+
export type Table<T extends TableName> = NamespaceTable<Namespace, T>;
|
|
35
|
+
|
|
36
|
+
export type Insertable<T extends TableName> = NamespaceInsertable<Namespace, T>;
|
|
37
|
+
export type Selectable<T extends TableName> = NamespaceSelectable<Namespace, T>;
|
|
38
|
+
export type Updateable<T extends TableName> = NamespaceUpdateable<Namespace, T>;
|
|
39
|
+
`].join(`
|
|
40
|
+
|
|
41
|
+
`)+`
|
|
42
|
+
`}const r=new TextEncoder;function mapFieldTypeToPostgresType(e){switch(e){case`uuid`:return`uuid`;case`string`:case`enum`:return`text`;case`boolean`:case`bool`:return`boolean`;case`integer`:return`integer`;case`float`:return`double precision`;case`decimal`:return`numeric`;case`date`:return`date`;case`datetime`:return`timestamptz`;case`time`:return`time`;case`nested`:return`jsonb`;default:throw Error(`Field type "${e}" has no PostgreSQL column type.`)}}function identifier(e){return`"${e.replaceAll(`"`,`""`)}"`}function fnv1aHex(e){let t=2166136261;for(let n of r.encode(e))t=Math.imul(t^n,16777619)>>>0;return t.toString(16).padStart(8,`0`)}function derivedIdentifier(e,t,n){let i=`_${fnv1aHex(JSON.stringify([e,t,n]))}_${n}`,a=`${e}_${t}`;for(;r.encode(a+i).length>63;)a=a.slice(0,-1);return identifier(a+i)}function stringLiteral(e){return`'${e.replaceAll(`'`,`''`)}'`}function columnType(e){let t=e.type===`decimal`?`numeric(1000, ${e.scale??6})`:mapFieldTypeToPostgresType(e.type);return e.array&&e.type!==`nested`?`${t}[]`:t}function scalarLiteral(e,t,n){let r=mapFieldTypeToPostgresType(t.type);if(e instanceof Date){if(Number.isNaN(e.getTime()))throw Error(`Default of field ${n} is an invalid Date.`);return`${stringLiteral(e.toISOString())}::${r}`}switch(typeof e){case`string`:return r===`text`?stringLiteral(e):`${stringLiteral(e)}::${r}`;case`number`:if(!Number.isFinite(e))throw Error(`Default of field ${n} is not a finite number.`);return String(e);case`boolean`:return e?`TRUE`:`FALSE`;default:throw Error(`Default of field ${n} cannot be rendered as a SQL literal.`)}}const i=/* @__PURE__ */ new Map([[`datetime`,`now()`],[`date`,`CURRENT_DATE`],[`time`,`LOCALTIME`]]);function defaultExpression(e,t){let n=e.default,r=i.get(e.type);if(n===`now`&&r!==void 0)return r;if(e.array){if(!Array.isArray(n))throw Error(`Default of array field ${t} must be an array.`);let r=columnType(e);return n.length===0?`'{}'::${r}`:`ARRAY[${n.map(n=>scalarLiteral(n,e,t)).join(`, `)}]::${r}`}return scalarLiteral(n,e,t)}function parseSerialFormat(e,t){let n=/^(?<prefix>[^%]*)%(?<zero>0?)(?<width>\d*)(?<conversion>[a-zA-Z])(?<suffix>[^%]*)$/.exec(e)?.groups,r=n?.conversion;if(!n||r!==`d`&&r!==`x`&&r!==`X`)throw Error(`Serial format "${e}" of field ${t} is not supported; use a single %d, %x, or %X specifier with an optional width (e.g. "INV-%05d").`);return{prefix:n.prefix??``,zeroPad:n.zero===`0`,width:n.width?Number(n.width):0,conversion:r,suffix:n.suffix??``}}function sequenceName(e,t){return derivedIdentifier(e,t,`seq`)}function sequenceRange(e){let t=[`START WITH ${e.start}`,`MINVALUE ${e.start}`];return e.maxValue!==void 0&&t.push(`MAXVALUE ${e.maxValue}`),t.join(` `)}const a={d:e=>e,x:e=>`to_hex(${e})`,X:e=>`upper(to_hex(${e}))`};function serialStringDefault(e,t,n){let r=`nextval(${stringLiteral(e)})`;if(t===void 0)return`(${r}::text)`;let{prefix:i,width:o,zeroPad:s,conversion:c,suffix:l}=parseSerialFormat(t,n),u=a[c](r),d=c===`d`?`${u}::text`:u;return o>0&&(d=`format('%${o}s', ${u})`,s&&(d=`translate(${d}, ' ', '0')`)),`(${[...i?[stringLiteral(i)]:[],d,...l?[stringLiteral(l)]:[]].join(` || `)})`}function columnDefinition(e,t,n){let r=identifier(t),i=`${identifier(e)}.${r}`,a=[r,columnType(n)];if(n.serial)return n.type===`integer`?(a.push(`GENERATED BY DEFAULT AS IDENTITY (${sequenceRange(n.serial)})`),n.unique&&a.push(`UNIQUE`),a.join(` `)):(n.required&&a.push(`NOT NULL`),n.unique&&a.push(`UNIQUE`),a.push(`DEFAULT ${serialStringDefault(sequenceName(e,t),n.serial.format,i)}`),a.join(` `));let o=n.default!==void 0,s=n.hooks?.create!==void 0||n.optionalOnCreate===!0;return n.required&&(o||!s)&&a.push(`NOT NULL`),n.unique&&a.push(`UNIQUE`),o&&a.push(`DEFAULT ${defaultExpression(n,i)}`),a.join(` `)}function generateTableDDL(e){let t=identifier(e.name),n=[],r=[],i=[` "id" uuid PRIMARY KEY DEFAULT gen_random_uuid()`];for(let[a,o]of Object.entries(e.fields))if(a!==`id`){if(o.serial&&o.type!==`integer`){let i=sequenceName(e.name,a);n.push(`CREATE SEQUENCE IF NOT EXISTS ${i} ${sequenceRange(o.serial)}`),r.push(`ALTER SEQUENCE ${i} OWNED BY ${t}.${identifier(a)}`)}i.push(` ${columnDefinition(e.name,a,o)}`)}let a=Object.entries(e.indexes??{}).filter(([,e])=>e.unique).map(([n,r])=>`CREATE UNIQUE INDEX IF NOT EXISTS ${derivedIdentifier(e.name,n,`idx`)} ON ${t} (${r.fields.map(identifier).join(`, `)})`);return[...n,`CREATE TABLE IF NOT EXISTS ${t} (\n${i.join(`,
|
|
43
|
+
`)}\n)`,...r,...a]}function generateSchemaDDL(e){return e.map(e=>generateTableDDL(e).map(e=>`${e};`).join(`
|
|
44
|
+
`)).join(`
|
|
45
|
+
|
|
46
|
+
`)}function templateLiteral(e){return`\`${e.replaceAll(`\\`,`\\\\`).replaceAll("`","\\`").replaceAll("${","\\${").replaceAll(`\r`,`\\r`)}\``}function generatePgliteSchemaModule(e,t){let n=e.map(({namespace:e,tables:t})=>` ${JSON.stringify(e)}: ${templateLiteral(generateSchemaDDL(t))},`);return[`/**`,` * Auto-generated PGlite schema for TailorDB tables.`," * Create a namespace's tables in a test with `pglite.exec(pgliteSchema[namespace])`.",` *`,` * DO NOT EDIT - This file is auto-generated by ${t.generatedBy}.`,` */`,``,`export const pgliteSchema = {`,...n,`} as const;`,``].join(`
|
|
47
|
+
`)}function toDDLTables(e){return Object.values(e).map(e=>({name:e.name,fields:Object.fromEntries(Object.entries(e.fields).map(([e,t])=>[e,t.config])),indexes:e.indexes}))}function generatePGliteSchemaModule(e){return generatePgliteSchemaModule(e.map(({namespace:e,tables:t})=>({namespace:e,tables:toDDLTables(t)})),{generatedBy:`the kysely-type plugin`})}const o=`@tailor-platform/kysely-type`,s=`./generated/tailordb.ts`;function kyselyTypePlugin(e){return{id:o,description:`Generates Kysely type definitions for TailorDB tables`,pluginConfig:e,async onTailorDBReady(e){let{distPath:n,pgliteSchemaPath:r,temporal:i}=e.pluginConfig;if(r&&t(n)===t(r))throw Error(`distPath and pgliteSchemaPath must resolve to different files.`);let a=[];for(let t of e.tailordb){let e=[];for(let n of Object.values(t.tables)){let t=await processKyselyType(n,i);e.push(t)}e.length!==0&&a.push({namespace:t.namespace,types:e})}let o=[];if(a.length>0){let t=generateUnifiedKyselyTypes(a,i);o.push({path:e.pluginConfig.distPath,content:t}),e.pluginConfig.pgliteSchemaPath&&o.push({path:e.pluginConfig.pgliteSchemaPath,content:generatePGliteSchemaModule(e.tailordb.filter(e=>Object.keys(e.tables).length>0))})}return{files:o}}}}export{generateUnifiedKyselyTypes as a,mapFieldTypeToColumnType as c,generatePgliteSchemaModule as i,o as n,processKyselyFields as o,kyselyTypePlugin as r,n as s,s as t};
|
|
48
|
+
//# sourceMappingURL=kysely-type-RDX59E1j.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely-type-RDX59E1j.mjs","names":["multiline","fieldType","temporal","COLUMN_TYPE_ALIASES","a","b","usedUtilityTypes","allowedValues","fieldConfig","v","fields","fieldResults","fieldName","config","result","aggregatedUtilityTypes","acc","fieldTypes","r","obj","hasOptionalFields","hasGeneratedFields","type","nested","baseTypeResult","isArray","isNullable","isColumnTypeBase","finalType","fieldLines","name","parsedField","namespaceData","globalUsedUtilityTypes","ns","utilityTypeImports","namespace","types","line","utf8","hash","byte","value","suffix","tableName","memberName","kind","head","base","field","pgType","label","CURRENT_TIME_EXPRESSIONS","currentTime","groups","format","conversion","range","serial","SERIAL_CONVERSIONS","nextval","sequence","number","width","zeroPad","prefix","column","parts","hasDefault","filledOnCreate","tableIdentifier","table","sequences","ownerships","columns","indexes","index","tables","statement","entries","namespaces","options","KyselyGeneratorID","DEFAULT_KYSELY_TYPES_DIST_PATH","ctx","pgliteSchemaPath","resolve","distPath","allNamespaceData","typeMetadataList","metadata","files","content"],"sources":["../src/utils/field-column-type.ts","../src/plugin/builtin/kysely-type/type-processor.ts","../src/utils/tailordb-ddl.ts","../src/plugin/builtin/kysely-type/pglite-schema.ts","../src/plugin/builtin/kysely-type/index.ts"],"sourcesContent":["/** The Kysely column type a TailorDB field maps to, before array/null modifiers. */\nexport type FieldColumnType =\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"Timestamp\"\n | \"TemporalDate\"\n | \"TemporalInstant\"\n | \"TemporalTime\";\n\n/**\n * Map a scalar TailorDB field type to the column type generated code uses for it.\n *\n * With `temporal: false` (the default), `date`/`datetime` resolve to `Timestamp` and\n * `time` to `string`, because that's what the function runtime hands back for them by\n * default. With `temporal: true`, `date`/`datetime`/`time` resolve to their\n * `Temporal.PlainDate`/`Temporal.Instant`/`Temporal.PlainTime` counterparts instead,\n * matching a `getDB` created with `createGetDB({ temporal: true })`.\n *\n * `enum` and `nested` carry their own shape, so each generator resolves them\n * before reaching here; passing either is a caller bug rather than a `string`\n * column.\n * @param fieldType - TailorDB scalar field type name\n * @param temporal - Whether to map date/datetime/time fields to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @returns The column type for the field, defaulting to `string`\n * @throws If given `enum` or `nested`\n */\nexport function mapFieldTypeToColumnType(fieldType: string, temporal = false): FieldColumnType {\n switch (fieldType) {\n case \"uuid\":\n case \"string\":\n case \"decimal\":\n return \"string\";\n case \"integer\":\n case \"float\":\n return \"number\";\n case \"date\":\n return temporal ? \"TemporalDate\" : \"Timestamp\";\n case \"datetime\":\n return temporal ? \"TemporalInstant\" : \"Timestamp\";\n case \"time\":\n return temporal ? \"TemporalTime\" : \"string\";\n case \"bool\":\n case \"boolean\":\n return \"boolean\";\n case \"enum\":\n case \"nested\":\n throw new Error(\n `Field type \"${fieldType}\" has no scalar column type; resolve it before mapping.`,\n );\n default:\n return \"string\";\n }\n}\n\n/** The select and write types a `ColumnType`-shaped alias expands to. */\nexport type ColumnTypeAliasExpansion = {\n /** Type the alias reads back as. */\n readonly select: string;\n /** Type the alias accepts on insert and update. */\n readonly write: string;\n};\n\n/**\n * Column types whose alias expands to a `ColumnType`, mapped to that expansion.\n *\n * Kysely only unwraps a `ColumnType` at the top level of a table property, so an\n * array of one of these stays wrapped in `ArrayColumnType<...>` rather than taking\n * a `[]` suffix, which would nest the `ColumnType` out of Kysely's reach. Where a\n * generator has to spell out the slots itself, it reads the expansion from here\n * rather than inlining the alias, which would nest just the same.\n *\n * Both type generators track alias usage per alias to decide which declarations to\n * emit, so adding an entry here also means teaching them to report the new alias.\n */\nexport const COLUMN_TYPE_ALIASES: ReadonlyMap<string, ColumnTypeAliasExpansion> = new Map<\n FieldColumnType,\n ColumnTypeAliasExpansion\n>([\n [\"Timestamp\", { select: \"Date\", write: \"Date | string\" }],\n [\"TemporalDate\", { select: \"Temporal.PlainDate\", write: \"Temporal.PlainDate | string\" }],\n [\"TemporalInstant\", { select: \"Temporal.Instant\", write: \"Temporal.Instant | string\" }],\n [\"TemporalTime\", { select: \"Temporal.PlainTime\", write: \"Temporal.PlainTime | string\" }],\n]);\n","import { COLUMN_TYPE_ALIASES, mapFieldTypeToColumnType } from \"#/utils/field-column-type\";\nimport multiline from \"#/utils/multiline\";\nimport {\n type KyselyFieldConfig,\n type KyselyNamespaceMetadata,\n type KyselyTypeMetadata,\n type UsedUtilityTypes,\n} from \"./types\";\nimport type { TailorDBType } from \"#/parser/service/tailordb/types\";\n\ntype FieldTypeResult = {\n type: string;\n usedUtilityTypes: UsedUtilityTypes;\n};\n\nfunction emptyUsedUtilityTypes(): UsedUtilityTypes {\n return {\n Timestamp: false,\n TemporalDate: false,\n TemporalInstant: false,\n TemporalTime: false,\n Serial: false,\n ObjectColumnType: false,\n ArrayColumnType: false,\n };\n}\n\nfunction mergeUsedUtilityTypes(a: UsedUtilityTypes, b: UsedUtilityTypes): UsedUtilityTypes {\n return {\n Timestamp: a.Timestamp || b.Timestamp,\n TemporalDate: a.TemporalDate || b.TemporalDate,\n TemporalInstant: a.TemporalInstant || b.TemporalInstant,\n TemporalTime: a.TemporalTime || b.TemporalTime,\n Serial: a.Serial || b.Serial,\n ObjectColumnType: a.ObjectColumnType || b.ObjectColumnType,\n ArrayColumnType: a.ArrayColumnType || b.ArrayColumnType,\n };\n}\n\n/**\n * Whether a utility-type usage record uses any `ColumnType`-shaped alias.\n * @param usedUtilityTypes - Utility-type usage record to check\n * @returns Whether any `ColumnType`-shaped alias is used\n */\nfunction usesColumnTypeAlias(usedUtilityTypes: UsedUtilityTypes): boolean {\n return (\n usedUtilityTypes.Timestamp ||\n usedUtilityTypes.TemporalDate ||\n usedUtilityTypes.TemporalInstant ||\n usedUtilityTypes.TemporalTime\n );\n}\n\n/**\n * Get the enum type definition.\n * @param fieldConfig - The field configuration\n * @returns The enum type as a string union\n */\nfunction getEnumType(fieldConfig: KyselyFieldConfig): string {\n const allowedValues = fieldConfig.allowedValues;\n\n if (allowedValues && Array.isArray(allowedValues)) {\n return allowedValues\n .map((v: string | { value: string }) => {\n const value = typeof v === \"string\" ? v : v.value;\n return `\"${value}\"`;\n })\n .join(\" | \");\n }\n return \"string\";\n}\n\n/**\n * Get the nested object type definition.\n * @param fieldConfig - The field configuration\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @returns The nested type with used utility types\n */\nfunction getNestedType(fieldConfig: KyselyFieldConfig, temporal: boolean): FieldTypeResult {\n const fields = fieldConfig.fields;\n if (!fields || typeof fields !== \"object\") {\n return {\n type: \"string\",\n usedUtilityTypes: emptyUsedUtilityTypes(),\n };\n }\n\n const fieldResults = Object.entries(fields).map(([fieldName, config]) => {\n const result = generateFieldType(config, temporal, true);\n const optional = config.required !== true ? \"?\" : \"\";\n return {\n fieldType: `${fieldName}${optional}: ${result.type}`,\n usedUtilityTypes: result.usedUtilityTypes,\n };\n });\n\n const aggregatedUtilityTypes = fieldResults.reduce(\n (acc, result) => mergeUsedUtilityTypes(acc, result.usedUtilityTypes),\n emptyUsedUtilityTypes(),\n );\n\n const fieldTypes = fieldResults.map((r) => r.fieldType);\n const obj = `{\\n ${fieldTypes.join(\";\\n \")}${fieldTypes.length > 0 ? \";\" : \"\"}\\n}`;\n\n const hasOptionalFields = Object.values(fields).some((config) => config.required !== true);\n const hasGeneratedFields = Object.values(fields).some(\n (config) =>\n config.hooks?.create || config.default !== undefined || config.optionalOnCreate === true,\n );\n if (usesColumnTypeAlias(aggregatedUtilityTypes) || hasOptionalFields || hasGeneratedFields) {\n return {\n type: `ObjectColumnType<${obj}>`,\n usedUtilityTypes: { ...aggregatedUtilityTypes, ObjectColumnType: true },\n };\n }\n return { type: obj, usedUtilityTypes: aggregatedUtilityTypes };\n}\n\n/**\n * Get the base Kysely type for a field (without array/null modifiers).\n * @param fieldConfig - The field configuration\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @param nested - Whether the field is inside an object\n * @returns The base type with used utility types\n */\nfunction getBaseType(\n fieldConfig: KyselyFieldConfig,\n temporal: boolean,\n nested: boolean,\n): FieldTypeResult {\n const fieldType = fieldConfig.type;\n const usedUtilityTypes = emptyUsedUtilityTypes();\n\n if (fieldType === \"enum\") {\n return { type: getEnumType(fieldConfig), usedUtilityTypes };\n }\n if (fieldType === \"nested\") {\n return getNestedType(fieldConfig, temporal);\n }\n\n const type = mapFieldTypeToColumnType(fieldType, temporal && !(nested && fieldType === \"time\"));\n switch (type) {\n case \"Timestamp\":\n usedUtilityTypes.Timestamp = true;\n break;\n case \"TemporalDate\":\n usedUtilityTypes.TemporalDate = true;\n break;\n case \"TemporalInstant\":\n usedUtilityTypes.TemporalInstant = true;\n break;\n case \"TemporalTime\":\n usedUtilityTypes.TemporalTime = true;\n break;\n case \"string\":\n case \"number\":\n case \"boolean\":\n break;\n }\n\n return { type, usedUtilityTypes };\n}\n\n/**\n * Generate the complete field type including array and null modifiers.\n * @param fieldConfig - The field configuration\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @param nested - Whether the field is inside an object\n * @returns The complete field type with used utility types\n */\nfunction generateFieldType(\n fieldConfig: KyselyFieldConfig,\n temporal: boolean,\n nested = false,\n): FieldTypeResult {\n const baseTypeResult = getBaseType(fieldConfig, temporal, nested);\n const usedUtilityTypes = { ...baseTypeResult.usedUtilityTypes };\n\n const isArray = fieldConfig.array === true;\n const isNullable = fieldConfig.required !== true;\n\n // A ColumnType-shaped alias and ObjectColumnType cannot be wrapped with [] for\n // arrays, because Kysely only resolves ColumnType at the top-level table\n // property. Use ArrayColumnType to keep the ColumnType at the top level.\n const isColumnTypeBase = COLUMN_TYPE_ALIASES.has(baseTypeResult.type);\n\n let finalType = baseTypeResult.type;\n if (isArray) {\n if (isColumnTypeBase || finalType.startsWith(\"ObjectColumnType<\")) {\n finalType = `ArrayColumnType<${baseTypeResult.type}>`;\n usedUtilityTypes.ArrayColumnType = true;\n } else {\n const needsParens = fieldConfig.type === \"enum\";\n finalType = needsParens ? `(${baseTypeResult.type})[]` : `${baseTypeResult.type}[]`;\n }\n }\n if (isNullable) {\n finalType = `${finalType} | null`;\n }\n\n if (fieldConfig.serial) {\n usedUtilityTypes.Serial = true;\n finalType = `Serial<${finalType}>`;\n }\n if (\n fieldConfig.hooks?.create ||\n fieldConfig.default !== undefined ||\n fieldConfig.optionalOnCreate === true\n ) {\n finalType = `Generated<${finalType}>`;\n }\n\n return { type: finalType, usedUtilityTypes };\n}\n\n/**\n * Generate the table interface.\n * @param name - Table name\n * @param fields - Field configurations keyed by field name\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults\n * @returns The type definition and used utility types\n */\nfunction generateTableInterface(\n name: string,\n fields: Record<string, KyselyFieldConfig>,\n temporal: boolean,\n): {\n typeDef: string;\n usedUtilityTypes: UsedUtilityTypes;\n} {\n const fieldEntries = Object.entries(fields).filter(([fieldName]) => fieldName !== \"id\");\n\n const fieldResults = fieldEntries.map(([fieldName, fieldConfig]) => ({\n fieldName,\n ...generateFieldType(fieldConfig, temporal),\n }));\n\n const fieldLines = [\n \"id: Generated<string>;\",\n ...fieldResults.map((result) => `${result.fieldName}: ${result.type};`),\n ];\n\n const aggregatedUtilityTypes = fieldResults.reduce(\n (acc, result) => mergeUsedUtilityTypes(acc, result.usedUtilityTypes),\n emptyUsedUtilityTypes(),\n );\n\n const typeDef = multiline /* ts */ `\n ${name}: {\n ${fieldLines.join(\"\\n\")}\n }\n `;\n\n return { typeDef, usedUtilityTypes: aggregatedUtilityTypes };\n}\n\n/**\n * Generate KyselyTypeMetadata from field configurations.\n * @param name - Table name\n * @param fields - Field configurations keyed by field name\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults. Defaults to `false`.\n * @returns Generated Kysely type metadata\n */\nexport function processKyselyFields(\n name: string,\n fields: Record<string, KyselyFieldConfig>,\n temporal = false,\n): KyselyTypeMetadata {\n const result = generateTableInterface(name, fields, temporal);\n\n return {\n name,\n typeDef: result.typeDef,\n usedUtilityTypes: result.usedUtilityTypes,\n };\n}\n\n/**\n * Convert a TailorDBType into KyselyTypeMetadata.\n * @param type - Parsed TailorDB table\n * @param temporal - Whether date/datetime/time fields resolve to their Temporal column\n * types instead of their `Date`/`string` defaults. Defaults to `false`.\n * @returns Generated Kysely type metadata\n */\nexport async function processKyselyType(\n type: TailorDBType,\n temporal = false,\n): Promise<KyselyTypeMetadata> {\n return processKyselyFields(\n type.name,\n Object.fromEntries(\n Object.entries(type.fields).map(([fieldName, parsedField]) => [\n fieldName,\n parsedField.config,\n ]),\n ),\n temporal,\n );\n}\n\n/**\n * Generate unified types file from multiple namespaces.\n * @param namespaceData - Namespace metadata\n * @param temporal - Whether `kyselyTypePlugin` was configured with `{ temporal: true }`;\n * passed to `createGetDB` so the generated `getDB` always reads back the Temporal values\n * the tables above are typed with. Defaults to `false`.\n * @returns Generated types file contents\n */\nexport function generateUnifiedKyselyTypes(\n namespaceData: KyselyNamespaceMetadata[],\n temporal = false,\n): string {\n if (namespaceData.length === 0) {\n return \"\";\n }\n\n // Aggregate used utility types from all namespaces\n const globalUsedUtilityTypes = namespaceData\n .flatMap((ns) => ns.types)\n .reduce(\n (acc, type) => mergeUsedUtilityTypes(acc, type.usedUtilityTypes),\n emptyUsedUtilityTypes(),\n );\n\n const utilityTypeImports: string[] = [\"type Generated\"];\n if (globalUsedUtilityTypes.Timestamp) {\n utilityTypeImports.push(\"type Timestamp\");\n }\n if (globalUsedUtilityTypes.TemporalDate) {\n utilityTypeImports.push(\"type TemporalDate\");\n }\n if (globalUsedUtilityTypes.TemporalInstant) {\n utilityTypeImports.push(\"type TemporalInstant\");\n }\n if (globalUsedUtilityTypes.TemporalTime) {\n utilityTypeImports.push(\"type TemporalTime\");\n }\n if (globalUsedUtilityTypes.ObjectColumnType) {\n utilityTypeImports.push(\"type ObjectColumnType\");\n }\n if (globalUsedUtilityTypes.ArrayColumnType) {\n utilityTypeImports.push(\"type ArrayColumnType\");\n }\n if (globalUsedUtilityTypes.Serial) {\n utilityTypeImports.push(\"type Serial\");\n }\n\n const importsSection = multiline /* ts */ `\n import {\n createGetDB,\n ${utilityTypeImports.join(\",\\n\")},\n type NamespaceDB,\n type NamespaceInsertable,\n type NamespaceSelectable,\n type NamespaceTable,\n type NamespaceTableName,\n type NamespaceTransaction,\n type NamespaceUpdateable,\n } from \"@tailor-platform/sdk/kysely\";\n `;\n\n // Generate Namespace interface with multiple namespaces\n const namespaceInterfaces = namespaceData\n .map(({ namespace, types }) => {\n const typeDefsWithIndent = types\n .map((type) => {\n return type.typeDef\n .split(\"\\n\")\n .map((line) => (line.trim() ? ` ${line}` : \"\"))\n .join(\"\\n\");\n })\n .join(\"\\n\\n\");\n\n return ` \"${namespace}\": {\\n${typeDefsWithIndent}\\n }`;\n })\n .join(\",\\n\");\n\n const namespaceInterface = `export interface Namespace {\\n${namespaceInterfaces}\\n}`;\n\n const getDBFunction = multiline /* ts */ `\n export const getDB = createGetDB<Namespace>(${temporal ? \"{ temporal: true }\" : \"\"});\n\n export type DB<N extends keyof Namespace = keyof Namespace> = NamespaceDB<Namespace, N>;\n `;\n\n const utilityTypeExports = multiline /* ts */ `\n export type Transaction<K extends keyof Namespace | DB = keyof Namespace> =\n NamespaceTransaction<Namespace, K>;\n\n type TableName = NamespaceTableName<Namespace>;\n export type Table<T extends TableName> = NamespaceTable<Namespace, T>;\n\n export type Insertable<T extends TableName> = NamespaceInsertable<Namespace, T>;\n export type Selectable<T extends TableName> = NamespaceSelectable<Namespace, T>;\n export type Updateable<T extends TableName> = NamespaceUpdateable<Namespace, T>;\n `;\n\n return (\n [importsSection, namespaceInterface, getDBFunction, utilityTypeExports].join(\"\\n\\n\") + \"\\n\"\n );\n}\n","/**\n * PostgreSQL DDL derived from TailorDB table definitions, for creating the\n * tables a PGlite-backed test needs. The columns match the flat shape the\n * Kysely type generators emit, not TailorDB's storage layout.\n */\n\n/** The field shape the DDL generator reads; parsed and snapshot field configs are both assignable. */\nexport interface DDLFieldConfig {\n type: string;\n required?: boolean;\n array?: boolean;\n unique?: boolean;\n serial?: { start: number; maxValue?: number; format?: string };\n scale?: number;\n default?: unknown;\n optionalOnCreate?: boolean;\n hooks?: { create?: unknown; update?: unknown };\n fields?: Record<string, DDLFieldConfig>;\n}\n\n/** A table to emit DDL for. */\nexport interface DDLTableConfig {\n name: string;\n fields: Record<string, DDLFieldConfig>;\n indexes?: Record<string, { fields: string[]; unique?: boolean }>;\n}\n\nconst MAX_IDENTIFIER_BYTES = 63;\n// The platform rounds decimals to their scale (6 unless configured); numeric\n// needs a precision to carry a scale, and the maximum leaves it unconstrained.\nconst DECIMAL_PRECISION = 1000;\nconst DEFAULT_DECIMAL_SCALE = 6;\nconst utf8 = new TextEncoder();\n\n/**\n * Map a TailorDB field type to the PostgreSQL column type PGlite tests use for it.\n * @param fieldType - TailorDB field type name\n * @returns The PostgreSQL type name\n * @throws If the type is not a TailorDB field type\n */\nexport function mapFieldTypeToPostgresType(fieldType: string): string {\n switch (fieldType) {\n case \"uuid\":\n return \"uuid\";\n case \"string\":\n case \"enum\":\n return \"text\";\n case \"boolean\":\n case \"bool\":\n return \"boolean\";\n case \"integer\":\n return \"integer\";\n case \"float\":\n return \"double precision\";\n case \"decimal\":\n return \"numeric\";\n case \"date\":\n return \"date\";\n case \"datetime\":\n return \"timestamptz\";\n case \"time\":\n return \"time\";\n case \"nested\":\n return \"jsonb\";\n default:\n throw new Error(`Field type \"${fieldType}\" has no PostgreSQL column type.`);\n }\n}\n\nfunction identifier(name: string): string {\n return `\"${name.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction fnv1aHex(value: string): string {\n let hash = 0x811c9dc5;\n for (const byte of utf8.encode(value)) {\n hash = Math.imul(hash ^ byte, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n\nfunction derivedIdentifier(tableName: string, memberName: string, kind: \"seq\" | \"idx\"): string {\n const suffix = `_${fnv1aHex(JSON.stringify([tableName, memberName, kind]))}_${kind}`;\n let head = `${tableName}_${memberName}`;\n while (utf8.encode(head + suffix).length > MAX_IDENTIFIER_BYTES) head = head.slice(0, -1);\n return identifier(head + suffix);\n}\n\nfunction stringLiteral(value: string): string {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction columnType(field: DDLFieldConfig): string {\n const base =\n field.type === \"decimal\"\n ? `numeric(${DECIMAL_PRECISION}, ${field.scale ?? DEFAULT_DECIMAL_SCALE})`\n : mapFieldTypeToPostgresType(field.type);\n return field.array && field.type !== \"nested\" ? `${base}[]` : base;\n}\n\nfunction scalarLiteral(value: unknown, field: DDLFieldConfig, label: string): string {\n const pgType = mapFieldTypeToPostgresType(field.type);\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) {\n throw new Error(`Default of field ${label} is an invalid Date.`);\n }\n return `${stringLiteral(value.toISOString())}::${pgType}`;\n }\n switch (typeof value) {\n case \"string\":\n return pgType === \"text\" ? stringLiteral(value) : `${stringLiteral(value)}::${pgType}`;\n case \"number\":\n if (!Number.isFinite(value)) {\n throw new Error(`Default of field ${label} is not a finite number.`);\n }\n return String(value);\n case \"boolean\":\n return value ? \"TRUE\" : \"FALSE\";\n default:\n throw new Error(`Default of field ${label} cannot be rendered as a SQL literal.`);\n }\n}\n\nconst CURRENT_TIME_EXPRESSIONS = new Map([\n [\"datetime\", \"now()\"],\n [\"date\", \"CURRENT_DATE\"],\n [\"time\", \"LOCALTIME\"],\n]);\n\nfunction defaultExpression(field: DDLFieldConfig, label: string): string {\n const value = field.default;\n const currentTime = CURRENT_TIME_EXPRESSIONS.get(field.type);\n if (value === \"now\" && currentTime !== undefined) {\n return currentTime;\n }\n if (field.array) {\n if (!Array.isArray(value)) {\n throw new Error(`Default of array field ${label} must be an array.`);\n }\n const pgType = columnType(field);\n if (value.length === 0) return `'{}'::${pgType}`;\n return `ARRAY[${value.map((v) => scalarLiteral(v, field, label)).join(\", \")}]::${pgType}`;\n }\n return scalarLiteral(value, field, label);\n}\n\ninterface SerialFormat {\n prefix: string;\n width: number;\n zeroPad: boolean;\n conversion: \"d\" | \"x\" | \"X\";\n suffix: string;\n}\n\n// printf-style with exactly one conversion specifier; octal has no Postgres\n// formatting function to stand in for it.\nfunction parseSerialFormat(format: string, label: string): SerialFormat {\n const match =\n /^(?<prefix>[^%]*)%(?<zero>0?)(?<width>\\d*)(?<conversion>[a-zA-Z])(?<suffix>[^%]*)$/.exec(\n format,\n );\n const groups = match?.groups;\n const conversion = groups?.conversion;\n if (!groups || (conversion !== \"d\" && conversion !== \"x\" && conversion !== \"X\")) {\n throw new Error(\n `Serial format \"${format}\" of field ${label} is not supported; use a single %d, %x, or %X specifier with an optional width (e.g. \"INV-%05d\").`,\n );\n }\n return {\n prefix: groups.prefix ?? \"\",\n zeroPad: groups.zero === \"0\",\n width: groups.width ? Number(groups.width) : 0,\n conversion,\n suffix: groups.suffix ?? \"\",\n };\n}\n\nfunction sequenceName(tableName: string, fieldName: string): string {\n return derivedIdentifier(tableName, fieldName, \"seq\");\n}\n\n// A sequence's minimum defaults to 1, which rejects a start of 0.\nfunction sequenceRange(serial: NonNullable<DDLFieldConfig[\"serial\"]>): string {\n const range = [`START WITH ${serial.start}`, `MINVALUE ${serial.start}`];\n if (serial.maxValue !== undefined) range.push(`MAXVALUE ${serial.maxValue}`);\n return range.join(\" \");\n}\n\nconst SERIAL_CONVERSIONS: Record<SerialFormat[\"conversion\"], (nextval: string) => string> = {\n d: (nextval) => nextval,\n x: (nextval) => `to_hex(${nextval})`,\n X: (nextval) => `upper(to_hex(${nextval}))`,\n};\n\nfunction serialStringDefault(sequence: string, format: string | undefined, label: string): string {\n const nextval = `nextval(${stringLiteral(sequence)})`;\n if (format === undefined) return `(${nextval}::text)`;\n const { prefix, width, zeroPad, conversion, suffix } = parseSerialFormat(format, label);\n const number = SERIAL_CONVERSIONS[conversion](nextval);\n let value = conversion === \"d\" ? `${number}::text` : number;\n if (width > 0) {\n value = `format('%${width}s', ${number})`;\n if (zeroPad) value = `translate(${value}, ' ', '0')`;\n }\n const parts = [\n ...(prefix ? [stringLiteral(prefix)] : []),\n value,\n ...(suffix ? [stringLiteral(suffix)] : []),\n ];\n return `(${parts.join(\" || \")})`;\n}\n\nfunction columnDefinition(tableName: string, fieldName: string, field: DDLFieldConfig): string {\n const column = identifier(fieldName);\n const label = `${identifier(tableName)}.${column}`;\n const parts = [column, columnType(field)];\n\n if (field.serial) {\n if (field.type === \"integer\") {\n parts.push(`GENERATED BY DEFAULT AS IDENTITY (${sequenceRange(field.serial)})`);\n if (field.unique) parts.push(\"UNIQUE\");\n return parts.join(\" \");\n }\n if (field.required) parts.push(\"NOT NULL\");\n if (field.unique) parts.push(\"UNIQUE\");\n parts.push(\n `DEFAULT ${serialStringDefault(sequenceName(tableName, fieldName), field.serial.format, label)}`,\n );\n return parts.join(\" \");\n }\n\n const hasDefault = field.default !== undefined;\n const filledOnCreate = field.hooks?.create !== undefined || field.optionalOnCreate === true;\n if (field.required && (hasDefault || !filledOnCreate)) parts.push(\"NOT NULL\");\n if (field.unique) parts.push(\"UNIQUE\");\n if (hasDefault) parts.push(`DEFAULT ${defaultExpression(field, label)}`);\n return parts.join(\" \");\n}\n\n/**\n * DDL statements that create one table: any sequences its string serial\n * fields draw from, the table, the sequences' ownership of their columns,\n * then its unique indexes. Every statement is `IF NOT EXISTS` or otherwise\n * idempotent, so re-applying the script on a database that already has the\n * table is a no-op. Owning a sequence makes `DROP TABLE` drop it and\n * `TRUNCATE ... RESTART IDENTITY` reset it to the configured start. Index\n * names end in `_idx` so they cannot take the `<table>_<column>_key` name\n * Postgres gives a UNIQUE column, which `IF NOT EXISTS` would otherwise\n * silently skip.\n * @param table - Table name, fields, and indexes\n * @returns Statements in execution order, without trailing semicolons\n * @throws If a field type, default, or serial format cannot be expressed\n */\nexport function generateTableDDL(table: DDLTableConfig): string[] {\n const tableIdentifier = identifier(table.name);\n const sequences: string[] = [];\n const ownerships: string[] = [];\n const columns = [` \"id\" uuid PRIMARY KEY DEFAULT gen_random_uuid()`];\n\n for (const [fieldName, field] of Object.entries(table.fields)) {\n if (fieldName === \"id\") continue;\n if (field.serial && field.type !== \"integer\") {\n const sequence = sequenceName(table.name, fieldName);\n sequences.push(`CREATE SEQUENCE IF NOT EXISTS ${sequence} ${sequenceRange(field.serial)}`);\n ownerships.push(\n `ALTER SEQUENCE ${sequence} OWNED BY ${tableIdentifier}.${identifier(fieldName)}`,\n );\n }\n columns.push(` ${columnDefinition(table.name, fieldName, field)}`);\n }\n\n const indexes = Object.entries(table.indexes ?? {})\n .filter(([, index]) => index.unique)\n .map(\n ([name, index]) =>\n `CREATE UNIQUE INDEX IF NOT EXISTS ${derivedIdentifier(table.name, name, \"idx\")} ON ${tableIdentifier} (${index.fields.map(identifier).join(\", \")})`,\n );\n\n return [\n ...sequences,\n `CREATE TABLE IF NOT EXISTS ${tableIdentifier} (\\n${columns.join(\",\\n\")}\\n)`,\n ...ownerships,\n ...indexes,\n ];\n}\n\n/**\n * A single SQL script creating every given table, for `pglite.exec()`.\n * @param tables - Tables in the order to create them\n * @returns The script, empty when there are no tables\n */\nexport function generateSchemaDDL(tables: readonly DDLTableConfig[]): string {\n return tables\n .map((table) =>\n generateTableDDL(table)\n .map((statement) => `${statement};`)\n .join(\"\\n\"),\n )\n .join(\"\\n\\n\");\n}\n\n/** One namespace's tables, as the schema module groups them. */\nexport interface PgliteSchemaNamespace {\n namespace: string;\n tables: readonly DDLTableConfig[];\n}\n\n/** Options for {@link generatePgliteSchemaModule}. */\nexport interface PgliteSchemaModuleOptions {\n /** Named in the generated header as the producer users must not edit around. */\n generatedBy: string;\n}\n\n// A template literal keeps line feeds but folds a carriage return into one.\nfunction templateLiteral(value: string): string {\n const escaped = value\n .replaceAll(\"\\\\\", \"\\\\\\\\\")\n .replaceAll(\"`\", \"\\\\`\")\n .replaceAll(\"${\", \"\\\\${\")\n .replaceAll(\"\\r\", \"\\\\r\");\n return `\\`${escaped}\\``;\n}\n\n/**\n * Render the module that exports each namespace's `CREATE TABLE` script for\n * `pglite.exec()`.\n * @param namespaces - Namespaces with the tables to create\n * @param options - Header wording\n * @returns TypeScript source of the schema module\n */\nexport function generatePgliteSchemaModule(\n namespaces: readonly PgliteSchemaNamespace[],\n options: PgliteSchemaModuleOptions,\n): string {\n const entries = namespaces.map(\n ({ namespace, tables }) =>\n ` ${JSON.stringify(namespace)}: ${templateLiteral(generateSchemaDDL(tables))},`,\n );\n return [\n \"/**\",\n \" * Auto-generated PGlite schema for TailorDB tables.\",\n \" * Create a namespace's tables in a test with `pglite.exec(pgliteSchema[namespace])`.\",\n \" *\",\n ` * DO NOT EDIT - This file is auto-generated by ${options.generatedBy}.`,\n \" */\",\n \"\",\n \"export const pgliteSchema = {\",\n ...entries,\n \"} as const;\",\n \"\",\n ].join(\"\\n\");\n}\n","import { generatePgliteSchemaModule, type DDLTableConfig } from \"#/utils/tailordb-ddl\";\nimport type { TailorDBType } from \"#/parser/service/tailordb/types\";\n\n/** Tables of one namespace, as the schema module groups them. */\nexport interface PGliteSchemaNamespace {\n namespace: string;\n tables: Record<string, TailorDBType>;\n}\n\n/**\n * Reduce parsed tables to what the DDL generator reads.\n * @param tables - Parsed TailorDB tables keyed by name\n * @returns DDL table configs in the same order\n */\nexport function toDDLTables(tables: Record<string, TailorDBType>): DDLTableConfig[] {\n return Object.values(tables).map((type) => ({\n name: type.name,\n fields: Object.fromEntries(\n Object.entries(type.fields).map(([fieldName, field]) => [fieldName, field.config]),\n ),\n indexes: type.indexes,\n }));\n}\n\n/**\n * Render the module that exports each namespace's `CREATE TABLE` script for\n * `pglite.exec()`.\n * @param namespaces - Namespaces with their parsed tables\n * @returns TypeScript source of the schema module\n */\nexport function generatePGliteSchemaModule(namespaces: readonly PGliteSchemaNamespace[]): string {\n return generatePgliteSchemaModule(\n namespaces.map(({ namespace, tables }) => ({ namespace, tables: toDDLTables(tables) })),\n { generatedBy: \"the kysely-type plugin\" },\n );\n}\n","import { resolve } from \"pathe\";\nimport { generatePGliteSchemaModule } from \"./pglite-schema\";\nimport { processKyselyType, generateUnifiedKyselyTypes } from \"./type-processor\";\nimport type { Plugin, GeneratorResult, TailorDBReadyContext } from \"#/plugin/types\";\nimport type { KyselyTypeMetadata, KyselyNamespaceMetadata } from \"./types\";\n\n/** Unique identifier for the Kysely type generator plugin. */\nexport const KyselyGeneratorID = \"@tailor-platform/kysely-type\";\n\ntype KyselyTypePluginOptions = {\n distPath: string;\n pgliteSchemaPath?: string;\n /**\n * When `true`, generated tables map date/datetime fields and top-level time fields to\n * `Temporal.PlainDate`/`Temporal.Instant`/`Temporal.PlainTime` columns instead of\n * their `Date`/`string` defaults, and the generated `getDB` reads them back as those\n * Temporal values. Nested time fields remain strings. Defaults to `false`.\n */\n temporal?: boolean;\n};\n\n// Register this plugin's config type under its own id, via the package's\n// real public specifier, so callers can resolve it type-safely from a\n// `Plugin[]` array (see plugin/get-plugin-config.ts's resolvePluginConfig)\n// without importing KyselyTypePluginOptions, which stays unexported.\ndeclare module \"@tailor-platform/sdk/plugin\" {\n interface PluginConfigRegistry {\n \"@tailor-platform/kysely-type\": KyselyTypePluginOptions;\n }\n}\n\n/** Conventional output path used when `kyselyTypePlugin` has no `distPath` configured. */\nexport const DEFAULT_KYSELY_TYPES_DIST_PATH = \"./generated/tailordb.ts\";\n\n/**\n * Plugin that generates Kysely type definitions for TailorDB tables.\n * @param options - Plugin options\n * @param options.distPath - Output file path for generated types\n * @param options.pgliteSchemaPath - Output file path for the PGlite `CREATE TABLE` script module; omit to skip it\n * @param options.temporal - When `true`, generated tables map date/datetime fields and\n * top-level time fields to their `Temporal.PlainDate`/`Temporal.Instant`/\n * `Temporal.PlainTime` counterparts instead of `Date`/`string`. Nested time fields\n * remain strings. Defaults to `false`.\n * @returns Plugin instance with onTailorDBReady hook\n */\nexport function kyselyTypePlugin(\n options: KyselyTypePluginOptions,\n): Plugin<unknown, KyselyTypePluginOptions> {\n return {\n id: KyselyGeneratorID,\n description: \"Generates Kysely type definitions for TailorDB tables\",\n pluginConfig: options,\n\n async onTailorDBReady(\n ctx: TailorDBReadyContext<KyselyTypePluginOptions>,\n ): Promise<GeneratorResult> {\n const { distPath, pgliteSchemaPath, temporal } = ctx.pluginConfig;\n if (pgliteSchemaPath && resolve(distPath) === resolve(pgliteSchemaPath)) {\n throw new Error(\"distPath and pgliteSchemaPath must resolve to different files.\");\n }\n\n const allNamespaceData: KyselyNamespaceMetadata[] = [];\n\n for (const ns of ctx.tailordb) {\n const typeMetadataList: KyselyTypeMetadata[] = [];\n\n for (const type of Object.values(ns.tables)) {\n const metadata = await processKyselyType(type, temporal);\n typeMetadataList.push(metadata);\n }\n\n if (typeMetadataList.length === 0) continue;\n\n allNamespaceData.push({\n namespace: ns.namespace,\n types: typeMetadataList,\n });\n }\n\n const files: GeneratorResult[\"files\"] = [];\n if (allNamespaceData.length > 0) {\n const content = generateUnifiedKyselyTypes(allNamespaceData, temporal);\n files.push({\n path: ctx.pluginConfig.distPath,\n content,\n });\n if (ctx.pluginConfig.pgliteSchemaPath) {\n files.push({\n path: ctx.pluginConfig.pgliteSchemaPath,\n content: generatePGliteSchemaModule(\n ctx.tailordb.filter((ns) => Object.keys(ns.tables).length > 0),\n ),\n });\n }\n }\n\n return { files };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -5,6 +5,13 @@ export declare const KyselyGeneratorID = "@tailor-platform/kysely-type";
|
|
|
5
5
|
type KyselyTypePluginOptions = {
|
|
6
6
|
distPath: string;
|
|
7
7
|
pgliteSchemaPath?: string;
|
|
8
|
+
/**
|
|
9
|
+
* When `true`, generated tables map date/datetime fields and top-level time fields to
|
|
10
|
+
* `Temporal.PlainDate`/`Temporal.Instant`/`Temporal.PlainTime` columns instead of
|
|
11
|
+
* their `Date`/`string` defaults, and the generated `getDB` reads them back as those
|
|
12
|
+
* Temporal values. Nested time fields remain strings. Defaults to `false`.
|
|
13
|
+
*/
|
|
14
|
+
temporal?: boolean;
|
|
8
15
|
};
|
|
9
16
|
declare module "@tailor-platform/sdk/plugin" {
|
|
10
17
|
interface PluginConfigRegistry {
|
|
@@ -18,6 +25,10 @@ export declare const DEFAULT_KYSELY_TYPES_DIST_PATH = "./generated/tailordb.ts";
|
|
|
18
25
|
* @param options - Plugin options
|
|
19
26
|
* @param options.distPath - Output file path for generated types
|
|
20
27
|
* @param options.pgliteSchemaPath - Output file path for the PGlite `CREATE TABLE` script module; omit to skip it
|
|
28
|
+
* @param options.temporal - When `true`, generated tables map date/datetime fields and
|
|
29
|
+
* top-level time fields to their `Temporal.PlainDate`/`Temporal.Instant`/
|
|
30
|
+
* `Temporal.PlainTime` counterparts instead of `Date`/`string`. Nested time fields
|
|
31
|
+
* remain strings. Defaults to `false`.
|
|
21
32
|
* @returns Plugin instance with onTailorDBReady hook
|
|
22
33
|
*/
|
|
23
34
|
export declare function kyselyTypePlugin(options: KyselyTypePluginOptions): Plugin<unknown, KyselyTypePluginOptions>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,r as t,t as n}from"../../../kysely-type-
|
|
1
|
+
import{n as e,r as t,t as n}from"../../../kysely-type-RDX59E1j.mjs";export{n as DEFAULT_KYSELY_TYPES_DIST_PATH,e as KyselyGeneratorID,t as kyselyTypePlugin};
|
package/dist/plugin/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"../manager-8-JOvuLl.mjs";import*as t from"pathe";import*as n from"node:fs";import{pathToFileURL as r}from"node:url";function withPluginContext(e){return e}const i=/* @__PURE__ */ new Map,a=[`onTableLoaded`,`onNamespaceLoaded`,`onTailorDBReady`,`onResolverReady`,`onExecutorReady`];function isPlugin(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=e;if(typeof t.id!=`string`||typeof t.description!=`string`||t.importPath!==void 0&&typeof t.importPath!=`string`||t.tableConfigRequired!==void 0&&typeof t.tableConfigRequired!=`boolean`&&typeof t.tableConfigRequired!=`function`)return!1;for(let e of a)if(t[e]!==void 0&&typeof t[e]!=`function`)return!1;return!((t.onTableLoaded||t.onNamespaceLoaded)&&!t.importPath)}async function loadAndCacheConfig(e){let a=t.resolve(e),o=i.get(a);if(o)return o;if(!n.existsSync(a))return null;let s=await import(r(a).href);if(!s?.default)throw Error(`Invalid config module at "${a}": default export not found`);let c=s.default,l=t.dirname(a),u=/* @__PURE__ */ new Map;if(Object.hasOwn(s,`plugins`)){let e=s.plugins;if(!Array.isArray(e))throw Error(`Invalid \`plugins\` export in "${a}": expected an array returned by definePlugins(), got ${typeof e}`);for(let t of e){if(!isPlugin(t))throw Error(`Invalid \`plugins\` export in "${a}": every item must be a plugin created by definePlugins()`);if(u.has(t.id))throw Error(`Duplicate plugin ID "${t.id}" detected in "${a}". Each plugin must have a unique ID.`);u.set(t.id,{plugin:t,pluginConfig:t.pluginConfig})}}let d={config:c,plugins:u,configDir:l};return i.set(a,d),d}async function resolveNamespaceForTable(e,i,a){if(!e.db)throw Error(`No db configuration found in config`);for(let[o,s]of Object.entries(e.db)){let e=s;if(!e.external&&e.files)for(let s of e.files){let e=t.resolve(i,s),c;try{c=n.globSync(e)}catch{continue}for(let e of c){let t=await import(r(e).href);for(let e of Object.values(t))if(e===a)return o}}}throw Error(`Could not resolve namespace for table "${a.name}". Ensure the table file is included in a db namespace's files pattern.`)}async function resolveNamespaceForNamespacePlugin(e,t,n,r){if(!e.db)throw Error(`No db configuration found in config`);if(!t.onNamespaceLoaded)throw Error(`Plugin "${t.id}" does not have a onNamespaceLoaded() method`);for(let i of Object.keys(e.db)){if(e.db[i].external)continue;let a=await t.onNamespaceLoaded({pluginConfig:r,namespace:i});if(a.tables?.[n])return{namespace:i,output:a}}throw Error(`Could not resolve namespace for plugin "${t.id}" with kind "${n}". No namespace produced a table with that kind.`)}const o=/* @__PURE__ */ new WeakMap,s=/* @__PURE__ */ new WeakMap;function getCacheKey(e,t){if(t===void 0)return e;try{return`${e}:${JSON.stringify(t)}`}catch{throw Error(`pluginConfig must be JSON-serializable for caching. Received non-serializable value.`)}}async function getGeneratedTable(e,t,n,r){let i=await loadAndCacheConfig(e);if(!i)return{name:`__placeholder_${r}__`,fields:{}};let{config:a,configDir:o,plugins:s}=i,c=s.get(t);if(!c)throw Error(`Plugin "${t}" not found in config at "${e}". Ensure the plugin is registered via definePlugins().`);let{plugin:l,pluginConfig:u}=c;return n===null?getGeneratedTableForNamespacePlugin(a,l,r,u):getGeneratedTableForTableAttachedPlugin(l,n,r,u,await resolveNamespaceForTable(a,o,n))}async function getGeneratedTableForTableAttachedPlugin(e,t,n,r,i){if(!e.onTableLoaded)throw Error(`Plugin "${e.id}" does not have an onTableLoaded() method`);let a=o.get(e);a||(a=/* @__PURE__ */ new Map,o.set(e,a));let s=getCacheKey(`${t.name}:ns=${i}`,r),c=a.get(s);if(!c){let n=t.plugins.find(t=>t.pluginId===e.id)?.config;c=await e.onTableLoaded({table:t,tableConfig:n??{},pluginConfig:r,namespace:i}),a.set(s,c)}let l=c.tables?.[n];if(!l)throw Error(`Generated table not found: plugin=${e.id}, sourceTable=${t.name}, kind=${n}`);return l}async function getGeneratedTableForNamespacePlugin(e,t,n,r){if(!t.onNamespaceLoaded)throw Error(`Plugin "${t.id}" does not have a onNamespaceLoaded() method`);let i=s.get(t);if(i||(i=/* @__PURE__ */ new Map,s.set(t,i)),e.db)for(let t of Object.keys(e.db)){if(e.db[t].external)continue;let a=getCacheKey(`namespace:ns=${t}`,r),o=i.get(a);if(o?.tables?.[n])return o.tables[n]}let{namespace:a,output:o}=await resolveNamespaceForNamespacePlugin(e,t,n,r),c=getCacheKey(`namespace:ns=${a}`,r);i.set(c,o);let l=o.tables?.[n];if(!l)throw Error(`Generated table not found: plugin=${t.id}, kind=${n}`);return l}const c=/* @__PURE__ */ new Map;async function getExtendedTable(e,n){if(n.plugins.length===0)return n;let r=t.resolve(e),i=c.get(r);i||(i=/* @__PURE__ */ new WeakMap,c.set(r,i));let a=i.get(n);if(a)return a;let o=applyPluginExtensions(r,n);return i.set(n,o),o.catch(()=>i.delete(n)),o}async function applyPluginExtensions(t,n){let r=await loadAndCacheConfig(t);if(!r)return n;let{config:i,configDir:a,plugins:o}=r,s=await resolveNamespaceForTable(i,a,n),{extendedTable:c}=await new e([...o.values()].map(e=>e.plugin)).processAttachmentsForTable({rawTable:n,attachments:[...n.plugins],namespace:s});return c??n}export{getExtendedTable,getGeneratedTable,withPluginContext};
|
|
1
|
+
import{t as e}from"../manager-8-JOvuLl.mjs";import*as t from"pathe";import*as n from"node:fs";import{pathToFileURL as r}from"node:url";function withPluginContext(e){return e}const i=/* @__PURE__ */ new Map,a=[`onTableLoaded`,`onNamespaceLoaded`,`onTailorDBReady`,`onResolverReady`,`onExecutorReady`,`onDeployed`];function isPlugin(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=e;if(typeof t.id!=`string`||typeof t.description!=`string`||t.importPath!==void 0&&typeof t.importPath!=`string`||t.tableConfigRequired!==void 0&&typeof t.tableConfigRequired!=`boolean`&&typeof t.tableConfigRequired!=`function`)return!1;for(let e of a)if(t[e]!==void 0&&typeof t[e]!=`function`)return!1;return!((t.onTableLoaded||t.onNamespaceLoaded)&&!t.importPath)}async function loadAndCacheConfig(e){let a=t.resolve(e),o=i.get(a);if(o)return o;if(!n.existsSync(a))return null;let s=await import(r(a).href);if(!s?.default)throw Error(`Invalid config module at "${a}": default export not found`);let c=s.default,l=t.dirname(a),u=/* @__PURE__ */ new Map;if(Object.hasOwn(s,`plugins`)){let e=s.plugins;if(!Array.isArray(e))throw Error(`Invalid \`plugins\` export in "${a}": expected an array returned by definePlugins(), got ${typeof e}`);for(let t of e){if(!isPlugin(t))throw Error(`Invalid \`plugins\` export in "${a}": every item must be a plugin created by definePlugins()`);if(u.has(t.id))throw Error(`Duplicate plugin ID "${t.id}" detected in "${a}". Each plugin must have a unique ID.`);u.set(t.id,{plugin:t,pluginConfig:t.pluginConfig})}}let d={config:c,plugins:u,configDir:l};return i.set(a,d),d}async function resolveNamespaceForTable(e,i,a){if(!e.db)throw Error(`No db configuration found in config`);for(let[o,s]of Object.entries(e.db)){let e=s;if(!e.external&&e.files)for(let s of e.files){let e=t.resolve(i,s),c;try{c=n.globSync(e)}catch{continue}for(let e of c){let t=await import(r(e).href);for(let e of Object.values(t))if(e===a)return o}}}throw Error(`Could not resolve namespace for table "${a.name}". Ensure the table file is included in a db namespace's files pattern.`)}async function resolveNamespaceForNamespacePlugin(e,t,n,r){if(!e.db)throw Error(`No db configuration found in config`);if(!t.onNamespaceLoaded)throw Error(`Plugin "${t.id}" does not have a onNamespaceLoaded() method`);for(let i of Object.keys(e.db)){if(e.db[i].external)continue;let a=await t.onNamespaceLoaded({pluginConfig:r,namespace:i});if(a.tables?.[n])return{namespace:i,output:a}}throw Error(`Could not resolve namespace for plugin "${t.id}" with kind "${n}". No namespace produced a table with that kind.`)}const o=/* @__PURE__ */ new WeakMap,s=/* @__PURE__ */ new WeakMap;function getCacheKey(e,t){if(t===void 0)return e;try{return`${e}:${JSON.stringify(t)}`}catch{throw Error(`pluginConfig must be JSON-serializable for caching. Received non-serializable value.`)}}async function getGeneratedTable(e,t,n,r){let i=await loadAndCacheConfig(e);if(!i)return{name:`__placeholder_${r}__`,fields:{}};let{config:a,configDir:o,plugins:s}=i,c=s.get(t);if(!c)throw Error(`Plugin "${t}" not found in config at "${e}". Ensure the plugin is registered via definePlugins().`);let{plugin:l,pluginConfig:u}=c;return n===null?getGeneratedTableForNamespacePlugin(a,l,r,u):getGeneratedTableForTableAttachedPlugin(l,n,r,u,await resolveNamespaceForTable(a,o,n))}async function getGeneratedTableForTableAttachedPlugin(e,t,n,r,i){if(!e.onTableLoaded)throw Error(`Plugin "${e.id}" does not have an onTableLoaded() method`);let a=o.get(e);a||(a=/* @__PURE__ */ new Map,o.set(e,a));let s=getCacheKey(`${t.name}:ns=${i}`,r),c=a.get(s);if(!c){let n=t.plugins.find(t=>t.pluginId===e.id)?.config;c=await e.onTableLoaded({table:t,tableConfig:n??{},pluginConfig:r,namespace:i}),a.set(s,c)}let l=c.tables?.[n];if(!l)throw Error(`Generated table not found: plugin=${e.id}, sourceTable=${t.name}, kind=${n}`);return l}async function getGeneratedTableForNamespacePlugin(e,t,n,r){if(!t.onNamespaceLoaded)throw Error(`Plugin "${t.id}" does not have a onNamespaceLoaded() method`);let i=s.get(t);if(i||(i=/* @__PURE__ */ new Map,s.set(t,i)),e.db)for(let t of Object.keys(e.db)){if(e.db[t].external)continue;let a=getCacheKey(`namespace:ns=${t}`,r),o=i.get(a);if(o?.tables?.[n])return o.tables[n]}let{namespace:a,output:o}=await resolveNamespaceForNamespacePlugin(e,t,n,r),c=getCacheKey(`namespace:ns=${a}`,r);i.set(c,o);let l=o.tables?.[n];if(!l)throw Error(`Generated table not found: plugin=${t.id}, kind=${n}`);return l}const c=/* @__PURE__ */ new Map;async function getExtendedTable(e,n){if(n.plugins.length===0)return n;let r=t.resolve(e),i=c.get(r);i||(i=/* @__PURE__ */ new WeakMap,c.set(r,i));let a=i.get(n);if(a)return a;let o=applyPluginExtensions(r,n);return i.set(n,o),o.catch(()=>i.delete(n)),o}async function applyPluginExtensions(t,n){let r=await loadAndCacheConfig(t);if(!r)return n;let{config:i,configDir:a,plugins:o}=r,s=await resolveNamespaceForTable(i,a,n),{extendedTable:c}=await new e([...o.values()].map(e=>e.plugin)).processAttachmentsForTable({rawTable:n,attachments:[...n.plugins],namespace:s});return c??n}export{getExtendedTable,getGeneratedTable,withPluginContext};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["PluginManager","path","fs","factory","configCacheMap","OPTIONAL_HOOK_KEYS","value","candidate","key","resolvedPath","configPath","cached","configModule","pathToFileURL","config","configDir","plugins","pluginsExport","item","result","namespace","nsConfig","dbConfig","pattern","absolutePattern","matchedFiles","file","mod","exported","sourceTable","plugin","output","kind","processCache","namespaceProcessCache","pluginConfig","baseKey","cache","pluginEntry","pluginId","pluginCache","cacheKey","tableConfig","p","generatedTable","extendedTableCache","tables","pending","entry","extendedTable"],"sources":["../../src/plugin/with-context.ts","../../src/plugin/get-generated-table.ts"],"sourcesContent":["/**\n * Plugin executor context support for defining plugin executors in separate files.\n * This module provides utilities for creating type-safe plugin executors that receive\n * context (like table references and namespace) at runtime.\n */\n\nimport type { TailorEnv, TailorPrincipal } from \"#/runtime/types\";\n\n/**\n * Plugin executor factory function type.\n * Takes context and returns an executor configuration.\n * Returns unknown since the exact return type depends on createExecutor's generic params.\n */\nexport type PluginExecutorFactory<Ctx> = (ctx: Ctx) => unknown;\n\n// ============================================================================\n// Plugin Executor Args Types\n// ============================================================================\n\n/**\n * Base args for plugin executor function operations.\n * Provides typed access to runtime context without requiring specific record types.\n */\nexport interface PluginFunctionArgs {\n /** Workspace ID where the executor runs */\n workspaceId: string;\n /** Application namespace */\n appNamespace: string;\n /** Environment variables */\n env: TailorEnv;\n /** Principal that triggered the event, null for system events */\n actor: TailorPrincipal | null;\n /** Name of the TailorDB table */\n typeName: string;\n /** TailorDB connections by namespace */\n tailordb: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record creation.\n */\nexport interface PluginRecordCreatedArgs extends PluginFunctionArgs {\n /** The newly created record */\n newRecord: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record update.\n */\nexport interface PluginRecordUpdatedArgs extends PluginFunctionArgs {\n /** The record after update */\n newRecord: Record<string, unknown>;\n /** The record before update */\n oldRecord: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record deletion.\n */\nexport interface PluginRecordDeletedArgs extends PluginFunctionArgs {\n /** The deleted record */\n oldRecord: Record<string, unknown>;\n}\n\n/**\n * Database schema type for plugins.\n * Since plugins work with dynamic types, the schema uses Record types.\n */\nexport type PluginDBSchema = Record<string, Record<string, unknown>>;\n\n/**\n * Base record type for TailorDB records.\n * All records have an id field.\n */\nexport type PluginRecord = { id: string } & Record<string, unknown>;\n\n/**\n * Define a plugin executor that receives context at runtime.\n * This allows executor definitions to be in separate files while\n * still receiving dynamic values like typeName, generated types, and namespace.\n * @param factory - Function that takes context and returns executor configuration\n * @returns The same factory function (for type inference)\n * @example\n * ```typescript\n * // executors/on-create.ts\n * import { withPluginContext } from \"@tailor-platform/sdk/plugin\";\n * import { createExecutor, recordCreatedTrigger } from \"@tailor-platform/sdk\";\n * import { getDB } from \"@tailor-platform/function-kysely-tailordb\";\n *\n * interface MyContext {\n * sourceTable: TailorAnyDBType;\n * historyType: TailorAnyDBType;\n * namespace: string;\n * }\n *\n * export default withPluginContext<MyContext>((ctx) =>\n * createExecutor({\n * name: `${ctx.sourceTable.name.toLowerCase()}-on-create`,\n * trigger: recordCreatedTrigger({ type: ctx.sourceTable }),\n * operation: {\n * kind: \"function\",\n * body: async (args) => {\n * const db = getDB(ctx.namespace);\n * await db.insertInto(ctx.historyType.name).values({\n * recordId: args.newRecord.id,\n * // ...\n * }).execute();\n * },\n * },\n * })\n * );\n * ```\n */\nexport function withPluginContext<Ctx>(\n factory: PluginExecutorFactory<Ctx>,\n): PluginExecutorFactory<Ctx> {\n return factory;\n}\n","import * as fs from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport * as path from \"pathe\";\nimport { PluginManager } from \"./manager\";\nimport type { TailorAnyDBType } from \"#/configure/services/tailordb/types\";\nimport type { Plugin, PluginOutput, TablePluginOutput } from \"#/plugin/types\";\nimport type { TailorDBTypeRaw } from \"#/types/tailordb.generated\";\n\n// ========================================\n// Config loading and caching\n// ========================================\n\ninterface PluginEntry {\n plugin: Plugin;\n pluginConfig: unknown;\n}\n\ninterface ConfigCache {\n config: { db?: Record<string, unknown> };\n plugins: Map<string, PluginEntry>;\n configDir: string;\n}\n\n/** Cache: resolved config path -> loaded config data */\nconst configCacheMap = new Map<string, ConfigCache>();\n\nconst OPTIONAL_HOOK_KEYS = [\n \"onTableLoaded\",\n \"onNamespaceLoaded\",\n \"onTailorDBReady\",\n \"onResolverReady\",\n \"onExecutorReady\",\n] as const;\n\n/**\n * Check if a value is a Plugin instance. Mirrors PluginConfigSchema's shape without\n * depending on zod, since this module must stay usable from a bundled executor.\n * @param value - Value to check\n * @returns True if value has the shape of Plugin\n */\nfunction isPlugin(value: unknown): value is Plugin {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const candidate = value as Record<string, unknown>;\n if (typeof candidate.id !== \"string\" || typeof candidate.description !== \"string\") return false;\n if (candidate.importPath !== undefined && typeof candidate.importPath !== \"string\") return false;\n if (\n candidate.tableConfigRequired !== undefined &&\n typeof candidate.tableConfigRequired !== \"boolean\" &&\n typeof candidate.tableConfigRequired !== \"function\"\n )\n return false;\n for (const key of OPTIONAL_HOOK_KEYS) {\n if (candidate[key] !== undefined && typeof candidate[key] !== \"function\") return false;\n }\n const hasDefinitionTimeHooks = candidate.onTableLoaded || candidate.onNamespaceLoaded;\n if (hasDefinitionTimeHooks && !candidate.importPath) return false;\n return true;\n}\n\n/**\n * Load and cache config module from the given path.\n * Reads plugins from the config module's `plugins` export (definePlugins() format).\n * Returns null if the config file does not exist (e.g., in bundled executor on platform server).\n * @param configPath - Absolute or relative path to tailor.config.ts\n * @returns Cached config data with plugins map, or null if config file is not available\n */\nasync function loadAndCacheConfig(configPath: string): Promise<ConfigCache | null> {\n const resolvedPath = path.resolve(configPath);\n\n const cached = configCacheMap.get(resolvedPath);\n if (cached) return cached;\n\n // Config file may not exist in bundled environments (e.g., platform server)\n if (!fs.existsSync(resolvedPath)) {\n return null;\n }\n\n const configModule = await import(pathToFileURL(resolvedPath).href);\n if (!configModule?.default) {\n throw new Error(`Invalid config module at \"${resolvedPath}\": default export not found`);\n }\n\n const config = configModule.default as { db?: Record<string, unknown> };\n const configDir = path.dirname(resolvedPath);\n const plugins = new Map<string, PluginEntry>();\n\n if (Object.hasOwn(configModule, \"plugins\")) {\n const pluginsExport: unknown = configModule.plugins;\n if (!Array.isArray(pluginsExport)) {\n throw new Error(\n `Invalid \\`plugins\\` export in \"${resolvedPath}\": expected an array returned by definePlugins(), got ${typeof pluginsExport}`,\n );\n }\n for (const item of pluginsExport) {\n if (!isPlugin(item)) {\n throw new Error(\n `Invalid \\`plugins\\` export in \"${resolvedPath}\": every item must be a plugin created by definePlugins()`,\n );\n }\n if (plugins.has(item.id)) {\n throw new Error(\n `Duplicate plugin ID \"${item.id}\" detected in \"${resolvedPath}\". Each plugin must have a unique ID.`,\n );\n }\n plugins.set(item.id, { plugin: item, pluginConfig: item.pluginConfig });\n }\n }\n\n const result: ConfigCache = { config, plugins, configDir };\n configCacheMap.set(resolvedPath, result);\n return result;\n}\n\n// ========================================\n// Namespace resolution\n// ========================================\n\ninterface DbNamespaceConfig {\n files?: string[];\n external?: boolean;\n}\n\n/**\n * Resolve the namespace for a table-attached source table by checking config.db file patterns.\n * Uses ESM module cache identity: same file path yields same object references.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param configDir - Directory containing the config file\n * @param sourceTable - The TailorDB table to look up\n * @returns The namespace name\n */\nasync function resolveNamespaceForTable(\n config: { db?: Record<string, unknown> },\n configDir: string,\n sourceTable: TailorAnyDBType,\n): Promise<string> {\n if (!config.db) {\n throw new Error(`No db configuration found in config`);\n }\n\n for (const [namespace, nsConfig] of Object.entries(config.db)) {\n const dbConfig = nsConfig as DbNamespaceConfig;\n // Skip external namespaces (no files to resolve)\n if (dbConfig.external || !dbConfig.files) continue;\n\n for (const pattern of dbConfig.files) {\n const absolutePattern = path.resolve(configDir, pattern);\n let matchedFiles: string[];\n try {\n matchedFiles = fs.globSync(absolutePattern);\n } catch {\n continue;\n }\n\n for (const file of matchedFiles) {\n const mod = await import(pathToFileURL(file).href);\n for (const exported of Object.values(mod)) {\n if (exported === sourceTable) {\n return namespace;\n }\n }\n }\n }\n }\n\n throw new Error(\n `Could not resolve namespace for table \"${sourceTable.name}\". ` +\n `Ensure the table file is included in a db namespace's files pattern.`,\n );\n}\n\n/**\n * Resolve the namespace for a namespace plugin by trying each namespace.\n * Calls onNamespaceLoaded() for each and returns the first whose output contains the requested kind.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param plugin - Plugin instance\n * @param kind - The generated table kind to look for\n * @param pluginConfig - Plugin-level configuration\n * @returns The namespace name\n */\nasync function resolveNamespaceForNamespacePlugin(\n config: { db?: Record<string, unknown> },\n plugin: Plugin,\n kind: string,\n pluginConfig: unknown,\n): Promise<{ namespace: string; output: PluginOutput }> {\n if (!config.db) {\n throw new Error(`No db configuration found in config`);\n }\n\n if (!plugin.onNamespaceLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onNamespaceLoaded() method`);\n }\n\n for (const namespace of Object.keys(config.db)) {\n const dbConfig = config.db[namespace] as DbNamespaceConfig;\n if (dbConfig.external) continue;\n\n const output = await plugin.onNamespaceLoaded({\n pluginConfig,\n namespace,\n });\n\n if (output.tables?.[kind]) {\n return { namespace, output };\n }\n }\n\n throw new Error(\n `Could not resolve namespace for plugin \"${plugin.id}\" with kind \"${kind}\". ` +\n `No namespace produced a table with that kind.`,\n );\n}\n\n// ========================================\n// Process caching\n// ========================================\n\n// Cache: plugin -> cacheKey -> TablePluginOutput\nconst processCache = new WeakMap<Plugin, Map<string, TablePluginOutput>>();\n\n// Cache for namespace plugins: plugin -> cacheKey -> PluginOutput\nconst namespaceProcessCache = new WeakMap<Plugin, Map<string, PluginOutput>>();\n\n/**\n * Generate a cache key that includes pluginConfig.\n * @param baseKey - Base key for the cache\n * @param pluginConfig - Plugin configuration to include in the key\n * @returns Cache key string\n */\nfunction getCacheKey(baseKey: string, pluginConfig: unknown): string {\n if (pluginConfig === undefined) {\n return baseKey;\n }\n try {\n return `${baseKey}:${JSON.stringify(pluginConfig)}`;\n } catch {\n throw new Error(\n `pluginConfig must be JSON-serializable for caching. Received non-serializable value.`,\n );\n }\n}\n\n// ========================================\n// Main API\n// ========================================\n\n/**\n * Get a generated table from a plugin by loading the config and resolving everything automatically.\n * For table-attached plugins, calls onTableLoaded() with the source table.\n * For namespace plugins, calls onNamespaceLoaded() with auto-resolved namespace.\n * Results are cached per config path, plugin, namespace, and pluginConfig to avoid redundant processing.\n * @param configPath - Path to tailor.config.ts (absolute or relative to cwd)\n * @param pluginId - The plugin's unique identifier\n * @param sourceTable - The source TailorDB table (null for namespace plugins)\n * @param kind - The generated table kind (e.g., \"request\", \"step\")\n * @returns The generated TailorDB table\n */\nexport async function getGeneratedTable(\n configPath: string,\n pluginId: string,\n sourceTable: TailorAnyDBType | null,\n kind: string,\n): Promise<TailorAnyDBType> {\n const cache = await loadAndCacheConfig(configPath);\n\n if (!cache) {\n // Config not available (e.g., running in bundled executor on platform server).\n // Return a placeholder. The actual table is resolved at generate/apply time.\n return { name: `__placeholder_${kind}__`, fields: {} } as TailorAnyDBType;\n }\n\n const { config, configDir, plugins } = cache;\n\n const pluginEntry = plugins.get(pluginId);\n if (!pluginEntry) {\n throw new Error(\n `Plugin \"${pluginId}\" not found in config at \"${configPath}\". ` +\n `Ensure the plugin is registered via definePlugins().`,\n );\n }\n\n const { plugin, pluginConfig } = pluginEntry;\n\n if (sourceTable === null) {\n return getGeneratedTableForNamespacePlugin(config, plugin, kind, pluginConfig);\n }\n\n const namespace = await resolveNamespaceForTable(config, configDir, sourceTable);\n return getGeneratedTableForTableAttachedPlugin(\n plugin,\n sourceTable,\n kind,\n pluginConfig,\n namespace,\n );\n}\n\n/**\n * Get a generated table from a table-attached plugin.\n * @param plugin - The plugin instance (must have onTableLoaded() method)\n * @param sourceTable - The source TailorDB table\n * @param kind - The generated table kind\n * @param pluginConfig - Plugin-level configuration\n * @param namespace - Resolved namespace\n * @returns The generated TailorDB table\n */\nasync function getGeneratedTableForTableAttachedPlugin(\n plugin: Plugin,\n sourceTable: TailorAnyDBType,\n kind: string,\n pluginConfig: unknown,\n namespace: string,\n): Promise<TailorAnyDBType> {\n if (!plugin.onTableLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have an onTableLoaded() method`);\n }\n\n // Check cache first\n let pluginCache = processCache.get(plugin);\n if (!pluginCache) {\n pluginCache = new Map();\n processCache.set(plugin, pluginCache);\n }\n\n const cacheKey = getCacheKey(`${sourceTable.name}:ns=${namespace}`, pluginConfig);\n let output = pluginCache.get(cacheKey);\n\n if (!output) {\n const tableConfig = sourceTable.plugins.find((p) => p.pluginId === plugin.id)?.config;\n output = await plugin.onTableLoaded({\n table: sourceTable,\n tableConfig: tableConfig ?? {},\n pluginConfig,\n namespace,\n });\n pluginCache.set(cacheKey, output);\n }\n\n const generatedTable = output.tables?.[kind];\n if (!generatedTable) {\n throw new Error(\n `Generated table not found: plugin=${plugin.id}, sourceTable=${sourceTable.name}, kind=${kind}`,\n );\n }\n\n return generatedTable as TailorAnyDBType;\n}\n\n/**\n * Get a generated table from a namespace plugin.\n * Auto-resolves the namespace by trying each one.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param plugin - The plugin instance (must have onNamespaceLoaded() method)\n * @param kind - The generated table kind\n * @param pluginConfig - Plugin-level configuration\n * @returns The generated TailorDB table\n */\nasync function getGeneratedTableForNamespacePlugin(\n config: { db?: Record<string, unknown> },\n plugin: Plugin,\n kind: string,\n pluginConfig: unknown,\n): Promise<TailorAnyDBType> {\n if (!plugin.onNamespaceLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onNamespaceLoaded() method`);\n }\n\n // Check cache first - try all namespaces\n let pluginCache = namespaceProcessCache.get(plugin);\n if (!pluginCache) {\n pluginCache = new Map();\n namespaceProcessCache.set(plugin, pluginCache);\n }\n\n // Try cached results first\n if (config.db) {\n for (const namespace of Object.keys(config.db)) {\n const dbConfig = config.db[namespace] as DbNamespaceConfig;\n if (dbConfig.external) continue;\n\n const cacheKey = getCacheKey(`namespace:ns=${namespace}`, pluginConfig);\n const cached = pluginCache.get(cacheKey);\n if (cached?.tables?.[kind]) {\n return cached.tables[kind] as TailorAnyDBType;\n }\n }\n }\n\n // Not in cache - resolve namespace and process\n const { namespace, output } = await resolveNamespaceForNamespacePlugin(\n config,\n plugin,\n kind,\n pluginConfig,\n );\n\n const cacheKey = getCacheKey(`namespace:ns=${namespace}`, pluginConfig);\n pluginCache.set(cacheKey, output);\n\n const generatedTable = output.tables?.[kind];\n if (!generatedTable) {\n throw new Error(`Generated table not found: plugin=${plugin.id}, kind=${kind}`);\n }\n\n return generatedTable as TailorAnyDBType;\n}\n\n// Cache: resolved config path -> source table -> the table with plugin fields applied.\n// Holds the pending promise so concurrent callers share one plugin run.\nconst extendedTableCache = new Map<string, WeakMap<TailorAnyDBType, Promise<TailorAnyDBType>>>();\n\n/**\n * Get a table with the fields its attached plugins add to it, as `tailor generate` sees it.\n * The plugins attached with `.plugin()` run in order, each seeing the fields the ones before\n * it added, and the result is cached per config path and table.\n * The table exported from the source file is not changed; the returned table is a new object.\n * Returns the source table itself when no plugin is attached to it, or when the config is\n * not available (e.g. in a bundled executor on the platform server).\n * @template T - The source table's own type, which the returned table keeps\n * @param configPath - Path to tailor.config.ts (absolute or relative to cwd)\n * @param sourceTable - The TailorDB table as exported from its source file\n * @returns The table with every plugin-added field\n */\nexport async function getExtendedTable<T extends TailorAnyDBType>(\n configPath: string,\n sourceTable: T,\n): Promise<T> {\n if (sourceTable.plugins.length === 0) {\n return sourceTable;\n }\n const resolvedPath = path.resolve(configPath);\n let tables = extendedTableCache.get(resolvedPath);\n if (!tables) {\n tables = new WeakMap();\n extendedTableCache.set(resolvedPath, tables);\n }\n const cached = tables.get(sourceTable);\n if (cached) {\n return cached as Promise<T>;\n }\n const pending = applyPluginExtensions(resolvedPath, sourceTable);\n tables.set(sourceTable, pending);\n pending.catch(() => tables.delete(sourceTable));\n return pending as Promise<T>;\n}\n\nasync function applyPluginExtensions(\n configPath: string,\n sourceTable: TailorAnyDBType,\n): Promise<TailorAnyDBType> {\n const cache = await loadAndCacheConfig(configPath);\n if (!cache) {\n return sourceTable;\n }\n const { config, configDir, plugins } = cache;\n const namespace = await resolveNamespaceForTable(config, configDir, sourceTable);\n const manager = new PluginManager([...plugins.values()].map((entry) => entry.plugin));\n const { extendedTable } = await manager.processAttachmentsForTable({\n rawTable: sourceTable as unknown as TailorDBTypeRaw,\n attachments: [...sourceTable.plugins],\n namespace,\n });\n return (extendedTable as TailorAnyDBType | undefined) ?? sourceTable;\n}\n\n/**\n * Clear all internal caches. For testing only.\n */\nexport function _clearCacheForTesting(): void {\n configCacheMap.clear();\n extendedTableCache.clear();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["PluginManager","path","fs","factory","configCacheMap","OPTIONAL_HOOK_KEYS","value","candidate","key","resolvedPath","configPath","cached","configModule","pathToFileURL","config","configDir","plugins","pluginsExport","item","result","namespace","nsConfig","dbConfig","pattern","absolutePattern","matchedFiles","file","mod","exported","sourceTable","plugin","output","kind","processCache","namespaceProcessCache","pluginConfig","baseKey","cache","pluginEntry","pluginId","pluginCache","cacheKey","tableConfig","p","generatedTable","extendedTableCache","tables","pending","entry","extendedTable"],"sources":["../../src/plugin/with-context.ts","../../src/plugin/get-generated-table.ts"],"sourcesContent":["/**\n * Plugin executor context support for defining plugin executors in separate files.\n * This module provides utilities for creating type-safe plugin executors that receive\n * context (like table references and namespace) at runtime.\n */\n\nimport type { TailorEnv, TailorPrincipal } from \"#/runtime/types\";\n\n/**\n * Plugin executor factory function type.\n * Takes context and returns an executor configuration.\n * Returns unknown since the exact return type depends on createExecutor's generic params.\n */\nexport type PluginExecutorFactory<Ctx> = (ctx: Ctx) => unknown;\n\n// ============================================================================\n// Plugin Executor Args Types\n// ============================================================================\n\n/**\n * Base args for plugin executor function operations.\n * Provides typed access to runtime context without requiring specific record types.\n */\nexport interface PluginFunctionArgs {\n /** Workspace ID where the executor runs */\n workspaceId: string;\n /** Application namespace */\n appNamespace: string;\n /** Environment variables */\n env: TailorEnv;\n /** Principal that triggered the event, null for system events */\n actor: TailorPrincipal | null;\n /** Name of the TailorDB table */\n typeName: string;\n /** TailorDB connections by namespace */\n tailordb: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record creation.\n */\nexport interface PluginRecordCreatedArgs extends PluginFunctionArgs {\n /** The newly created record */\n newRecord: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record update.\n */\nexport interface PluginRecordUpdatedArgs extends PluginFunctionArgs {\n /** The record after update */\n newRecord: Record<string, unknown>;\n /** The record before update */\n oldRecord: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record deletion.\n */\nexport interface PluginRecordDeletedArgs extends PluginFunctionArgs {\n /** The deleted record */\n oldRecord: Record<string, unknown>;\n}\n\n/**\n * Database schema type for plugins.\n * Since plugins work with dynamic types, the schema uses Record types.\n */\nexport type PluginDBSchema = Record<string, Record<string, unknown>>;\n\n/**\n * Base record type for TailorDB records.\n * All records have an id field.\n */\nexport type PluginRecord = { id: string } & Record<string, unknown>;\n\n/**\n * Define a plugin executor that receives context at runtime.\n * This allows executor definitions to be in separate files while\n * still receiving dynamic values like typeName, generated types, and namespace.\n * @param factory - Function that takes context and returns executor configuration\n * @returns The same factory function (for type inference)\n * @example\n * ```typescript\n * // executors/on-create.ts\n * import { withPluginContext } from \"@tailor-platform/sdk/plugin\";\n * import { createExecutor, recordCreatedTrigger } from \"@tailor-platform/sdk\";\n * import { getDB } from \"@tailor-platform/function-kysely-tailordb\";\n *\n * interface MyContext {\n * sourceTable: TailorAnyDBType;\n * historyType: TailorAnyDBType;\n * namespace: string;\n * }\n *\n * export default withPluginContext<MyContext>((ctx) =>\n * createExecutor({\n * name: `${ctx.sourceTable.name.toLowerCase()}-on-create`,\n * trigger: recordCreatedTrigger({ type: ctx.sourceTable }),\n * operation: {\n * kind: \"function\",\n * body: async (args) => {\n * const db = getDB(ctx.namespace);\n * await db.insertInto(ctx.historyType.name).values({\n * recordId: args.newRecord.id,\n * // ...\n * }).execute();\n * },\n * },\n * })\n * );\n * ```\n */\nexport function withPluginContext<Ctx>(\n factory: PluginExecutorFactory<Ctx>,\n): PluginExecutorFactory<Ctx> {\n return factory;\n}\n","import * as fs from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport * as path from \"pathe\";\nimport { PluginManager } from \"./manager\";\nimport type { TailorAnyDBType } from \"#/configure/services/tailordb/types\";\nimport type { Plugin, PluginOutput, TablePluginOutput } from \"#/plugin/types\";\nimport type { TailorDBTypeRaw } from \"#/types/tailordb.generated\";\n\n// ========================================\n// Config loading and caching\n// ========================================\n\ninterface PluginEntry {\n plugin: Plugin;\n pluginConfig: unknown;\n}\n\ninterface ConfigCache {\n config: { db?: Record<string, unknown> };\n plugins: Map<string, PluginEntry>;\n configDir: string;\n}\n\n/** Cache: resolved config path -> loaded config data */\nconst configCacheMap = new Map<string, ConfigCache>();\n\nconst OPTIONAL_HOOK_KEYS = [\n \"onTableLoaded\",\n \"onNamespaceLoaded\",\n \"onTailorDBReady\",\n \"onResolverReady\",\n \"onExecutorReady\",\n \"onDeployed\",\n] as const;\n\n/**\n * Check if a value is a Plugin instance. Mirrors PluginConfigSchema's shape without\n * depending on zod, since this module must stay usable from a bundled executor.\n * @param value - Value to check\n * @returns True if value has the shape of Plugin\n */\nfunction isPlugin(value: unknown): value is Plugin {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const candidate = value as Record<string, unknown>;\n if (typeof candidate.id !== \"string\" || typeof candidate.description !== \"string\") return false;\n if (candidate.importPath !== undefined && typeof candidate.importPath !== \"string\") return false;\n if (\n candidate.tableConfigRequired !== undefined &&\n typeof candidate.tableConfigRequired !== \"boolean\" &&\n typeof candidate.tableConfigRequired !== \"function\"\n )\n return false;\n for (const key of OPTIONAL_HOOK_KEYS) {\n if (candidate[key] !== undefined && typeof candidate[key] !== \"function\") return false;\n }\n const hasDefinitionTimeHooks = candidate.onTableLoaded || candidate.onNamespaceLoaded;\n if (hasDefinitionTimeHooks && !candidate.importPath) return false;\n return true;\n}\n\n/**\n * Load and cache config module from the given path.\n * Reads plugins from the config module's `plugins` export (definePlugins() format).\n * Returns null if the config file does not exist (e.g., in bundled executor on platform server).\n * @param configPath - Absolute or relative path to tailor.config.ts\n * @returns Cached config data with plugins map, or null if config file is not available\n */\nasync function loadAndCacheConfig(configPath: string): Promise<ConfigCache | null> {\n const resolvedPath = path.resolve(configPath);\n\n const cached = configCacheMap.get(resolvedPath);\n if (cached) return cached;\n\n // Config file may not exist in bundled environments (e.g., platform server)\n if (!fs.existsSync(resolvedPath)) {\n return null;\n }\n\n const configModule = await import(pathToFileURL(resolvedPath).href);\n if (!configModule?.default) {\n throw new Error(`Invalid config module at \"${resolvedPath}\": default export not found`);\n }\n\n const config = configModule.default as { db?: Record<string, unknown> };\n const configDir = path.dirname(resolvedPath);\n const plugins = new Map<string, PluginEntry>();\n\n if (Object.hasOwn(configModule, \"plugins\")) {\n const pluginsExport: unknown = configModule.plugins;\n if (!Array.isArray(pluginsExport)) {\n throw new Error(\n `Invalid \\`plugins\\` export in \"${resolvedPath}\": expected an array returned by definePlugins(), got ${typeof pluginsExport}`,\n );\n }\n for (const item of pluginsExport) {\n if (!isPlugin(item)) {\n throw new Error(\n `Invalid \\`plugins\\` export in \"${resolvedPath}\": every item must be a plugin created by definePlugins()`,\n );\n }\n if (plugins.has(item.id)) {\n throw new Error(\n `Duplicate plugin ID \"${item.id}\" detected in \"${resolvedPath}\". Each plugin must have a unique ID.`,\n );\n }\n plugins.set(item.id, { plugin: item, pluginConfig: item.pluginConfig });\n }\n }\n\n const result: ConfigCache = { config, plugins, configDir };\n configCacheMap.set(resolvedPath, result);\n return result;\n}\n\n// ========================================\n// Namespace resolution\n// ========================================\n\ninterface DbNamespaceConfig {\n files?: string[];\n external?: boolean;\n}\n\n/**\n * Resolve the namespace for a table-attached source table by checking config.db file patterns.\n * Uses ESM module cache identity: same file path yields same object references.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param configDir - Directory containing the config file\n * @param sourceTable - The TailorDB table to look up\n * @returns The namespace name\n */\nasync function resolveNamespaceForTable(\n config: { db?: Record<string, unknown> },\n configDir: string,\n sourceTable: TailorAnyDBType,\n): Promise<string> {\n if (!config.db) {\n throw new Error(`No db configuration found in config`);\n }\n\n for (const [namespace, nsConfig] of Object.entries(config.db)) {\n const dbConfig = nsConfig as DbNamespaceConfig;\n // Skip external namespaces (no files to resolve)\n if (dbConfig.external || !dbConfig.files) continue;\n\n for (const pattern of dbConfig.files) {\n const absolutePattern = path.resolve(configDir, pattern);\n let matchedFiles: string[];\n try {\n matchedFiles = fs.globSync(absolutePattern);\n } catch {\n continue;\n }\n\n for (const file of matchedFiles) {\n const mod = await import(pathToFileURL(file).href);\n for (const exported of Object.values(mod)) {\n if (exported === sourceTable) {\n return namespace;\n }\n }\n }\n }\n }\n\n throw new Error(\n `Could not resolve namespace for table \"${sourceTable.name}\". ` +\n `Ensure the table file is included in a db namespace's files pattern.`,\n );\n}\n\n/**\n * Resolve the namespace for a namespace plugin by trying each namespace.\n * Calls onNamespaceLoaded() for each and returns the first whose output contains the requested kind.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param plugin - Plugin instance\n * @param kind - The generated table kind to look for\n * @param pluginConfig - Plugin-level configuration\n * @returns The namespace name\n */\nasync function resolveNamespaceForNamespacePlugin(\n config: { db?: Record<string, unknown> },\n plugin: Plugin,\n kind: string,\n pluginConfig: unknown,\n): Promise<{ namespace: string; output: PluginOutput }> {\n if (!config.db) {\n throw new Error(`No db configuration found in config`);\n }\n\n if (!plugin.onNamespaceLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onNamespaceLoaded() method`);\n }\n\n for (const namespace of Object.keys(config.db)) {\n const dbConfig = config.db[namespace] as DbNamespaceConfig;\n if (dbConfig.external) continue;\n\n const output = await plugin.onNamespaceLoaded({\n pluginConfig,\n namespace,\n });\n\n if (output.tables?.[kind]) {\n return { namespace, output };\n }\n }\n\n throw new Error(\n `Could not resolve namespace for plugin \"${plugin.id}\" with kind \"${kind}\". ` +\n `No namespace produced a table with that kind.`,\n );\n}\n\n// ========================================\n// Process caching\n// ========================================\n\n// Cache: plugin -> cacheKey -> TablePluginOutput\nconst processCache = new WeakMap<Plugin, Map<string, TablePluginOutput>>();\n\n// Cache for namespace plugins: plugin -> cacheKey -> PluginOutput\nconst namespaceProcessCache = new WeakMap<Plugin, Map<string, PluginOutput>>();\n\n/**\n * Generate a cache key that includes pluginConfig.\n * @param baseKey - Base key for the cache\n * @param pluginConfig - Plugin configuration to include in the key\n * @returns Cache key string\n */\nfunction getCacheKey(baseKey: string, pluginConfig: unknown): string {\n if (pluginConfig === undefined) {\n return baseKey;\n }\n try {\n return `${baseKey}:${JSON.stringify(pluginConfig)}`;\n } catch {\n throw new Error(\n `pluginConfig must be JSON-serializable for caching. Received non-serializable value.`,\n );\n }\n}\n\n// ========================================\n// Main API\n// ========================================\n\n/**\n * Get a generated table from a plugin by loading the config and resolving everything automatically.\n * For table-attached plugins, calls onTableLoaded() with the source table.\n * For namespace plugins, calls onNamespaceLoaded() with auto-resolved namespace.\n * Results are cached per config path, plugin, namespace, and pluginConfig to avoid redundant processing.\n * @param configPath - Path to tailor.config.ts (absolute or relative to cwd)\n * @param pluginId - The plugin's unique identifier\n * @param sourceTable - The source TailorDB table (null for namespace plugins)\n * @param kind - The generated table kind (e.g., \"request\", \"step\")\n * @returns The generated TailorDB table\n */\nexport async function getGeneratedTable(\n configPath: string,\n pluginId: string,\n sourceTable: TailorAnyDBType | null,\n kind: string,\n): Promise<TailorAnyDBType> {\n const cache = await loadAndCacheConfig(configPath);\n\n if (!cache) {\n // Config not available (e.g., running in bundled executor on platform server).\n // Return a placeholder. The actual table is resolved at generate/apply time.\n return { name: `__placeholder_${kind}__`, fields: {} } as TailorAnyDBType;\n }\n\n const { config, configDir, plugins } = cache;\n\n const pluginEntry = plugins.get(pluginId);\n if (!pluginEntry) {\n throw new Error(\n `Plugin \"${pluginId}\" not found in config at \"${configPath}\". ` +\n `Ensure the plugin is registered via definePlugins().`,\n );\n }\n\n const { plugin, pluginConfig } = pluginEntry;\n\n if (sourceTable === null) {\n return getGeneratedTableForNamespacePlugin(config, plugin, kind, pluginConfig);\n }\n\n const namespace = await resolveNamespaceForTable(config, configDir, sourceTable);\n return getGeneratedTableForTableAttachedPlugin(\n plugin,\n sourceTable,\n kind,\n pluginConfig,\n namespace,\n );\n}\n\n/**\n * Get a generated table from a table-attached plugin.\n * @param plugin - The plugin instance (must have onTableLoaded() method)\n * @param sourceTable - The source TailorDB table\n * @param kind - The generated table kind\n * @param pluginConfig - Plugin-level configuration\n * @param namespace - Resolved namespace\n * @returns The generated TailorDB table\n */\nasync function getGeneratedTableForTableAttachedPlugin(\n plugin: Plugin,\n sourceTable: TailorAnyDBType,\n kind: string,\n pluginConfig: unknown,\n namespace: string,\n): Promise<TailorAnyDBType> {\n if (!plugin.onTableLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have an onTableLoaded() method`);\n }\n\n // Check cache first\n let pluginCache = processCache.get(plugin);\n if (!pluginCache) {\n pluginCache = new Map();\n processCache.set(plugin, pluginCache);\n }\n\n const cacheKey = getCacheKey(`${sourceTable.name}:ns=${namespace}`, pluginConfig);\n let output = pluginCache.get(cacheKey);\n\n if (!output) {\n const tableConfig = sourceTable.plugins.find((p) => p.pluginId === plugin.id)?.config;\n output = await plugin.onTableLoaded({\n table: sourceTable,\n tableConfig: tableConfig ?? {},\n pluginConfig,\n namespace,\n });\n pluginCache.set(cacheKey, output);\n }\n\n const generatedTable = output.tables?.[kind];\n if (!generatedTable) {\n throw new Error(\n `Generated table not found: plugin=${plugin.id}, sourceTable=${sourceTable.name}, kind=${kind}`,\n );\n }\n\n return generatedTable as TailorAnyDBType;\n}\n\n/**\n * Get a generated table from a namespace plugin.\n * Auto-resolves the namespace by trying each one.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param plugin - The plugin instance (must have onNamespaceLoaded() method)\n * @param kind - The generated table kind\n * @param pluginConfig - Plugin-level configuration\n * @returns The generated TailorDB table\n */\nasync function getGeneratedTableForNamespacePlugin(\n config: { db?: Record<string, unknown> },\n plugin: Plugin,\n kind: string,\n pluginConfig: unknown,\n): Promise<TailorAnyDBType> {\n if (!plugin.onNamespaceLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onNamespaceLoaded() method`);\n }\n\n // Check cache first - try all namespaces\n let pluginCache = namespaceProcessCache.get(plugin);\n if (!pluginCache) {\n pluginCache = new Map();\n namespaceProcessCache.set(plugin, pluginCache);\n }\n\n // Try cached results first\n if (config.db) {\n for (const namespace of Object.keys(config.db)) {\n const dbConfig = config.db[namespace] as DbNamespaceConfig;\n if (dbConfig.external) continue;\n\n const cacheKey = getCacheKey(`namespace:ns=${namespace}`, pluginConfig);\n const cached = pluginCache.get(cacheKey);\n if (cached?.tables?.[kind]) {\n return cached.tables[kind] as TailorAnyDBType;\n }\n }\n }\n\n // Not in cache - resolve namespace and process\n const { namespace, output } = await resolveNamespaceForNamespacePlugin(\n config,\n plugin,\n kind,\n pluginConfig,\n );\n\n const cacheKey = getCacheKey(`namespace:ns=${namespace}`, pluginConfig);\n pluginCache.set(cacheKey, output);\n\n const generatedTable = output.tables?.[kind];\n if (!generatedTable) {\n throw new Error(`Generated table not found: plugin=${plugin.id}, kind=${kind}`);\n }\n\n return generatedTable as TailorAnyDBType;\n}\n\n// Cache: resolved config path -> source table -> the table with plugin fields applied.\n// Holds the pending promise so concurrent callers share one plugin run.\nconst extendedTableCache = new Map<string, WeakMap<TailorAnyDBType, Promise<TailorAnyDBType>>>();\n\n/**\n * Get a table with the fields its attached plugins add to it, as `tailor generate` sees it.\n * The plugins attached with `.plugin()` run in order, each seeing the fields the ones before\n * it added, and the result is cached per config path and table.\n * The table exported from the source file is not changed; the returned table is a new object.\n * Returns the source table itself when no plugin is attached to it, or when the config is\n * not available (e.g. in a bundled executor on the platform server).\n * @template T - The source table's own type, which the returned table keeps\n * @param configPath - Path to tailor.config.ts (absolute or relative to cwd)\n * @param sourceTable - The TailorDB table as exported from its source file\n * @returns The table with every plugin-added field\n */\nexport async function getExtendedTable<T extends TailorAnyDBType>(\n configPath: string,\n sourceTable: T,\n): Promise<T> {\n if (sourceTable.plugins.length === 0) {\n return sourceTable;\n }\n const resolvedPath = path.resolve(configPath);\n let tables = extendedTableCache.get(resolvedPath);\n if (!tables) {\n tables = new WeakMap();\n extendedTableCache.set(resolvedPath, tables);\n }\n const cached = tables.get(sourceTable);\n if (cached) {\n return cached as Promise<T>;\n }\n const pending = applyPluginExtensions(resolvedPath, sourceTable);\n tables.set(sourceTable, pending);\n pending.catch(() => tables.delete(sourceTable));\n return pending as Promise<T>;\n}\n\nasync function applyPluginExtensions(\n configPath: string,\n sourceTable: TailorAnyDBType,\n): Promise<TailorAnyDBType> {\n const cache = await loadAndCacheConfig(configPath);\n if (!cache) {\n return sourceTable;\n }\n const { config, configDir, plugins } = cache;\n const namespace = await resolveNamespaceForTable(config, configDir, sourceTable);\n const manager = new PluginManager([...plugins.values()].map((entry) => entry.plugin));\n const { extendedTable } = await manager.processAttachmentsForTable({\n rawTable: sourceTable as unknown as TailorDBTypeRaw,\n attachments: [...sourceTable.plugins],\n namespace,\n });\n return (extendedTable as TailorAnyDBType | undefined) ?? sourceTable;\n}\n\n/**\n * Clear all internal caches. For testing only.\n */\nexport function _clearCacheForTesting(): void {\n configCacheMap.clear();\n extendedTableCache.clear();\n}\n"],"mappings":"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"}
|