@tailor-platform/sdk 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/application-Csj7Ow5Q.mjs +8 -0
  3. package/dist/{application-DYfVZVPT.mjs → application-gWUyKuzv.mjs} +211 -1653
  4. package/dist/{application-DYfVZVPT.mjs.map → application-gWUyKuzv.mjs.map} +1 -1
  5. package/dist/brand-BZJCv6UY.mjs +28 -0
  6. package/dist/brand-BZJCv6UY.mjs.map +1 -0
  7. package/dist/cli/index.mjs +38 -20
  8. package/dist/cli/index.mjs.map +1 -1
  9. package/dist/cli/lib.d.mts +20 -33
  10. package/dist/cli/lib.mjs +10 -5
  11. package/dist/cli/lib.mjs.map +1 -1
  12. package/dist/configure/index.d.mts +4 -4
  13. package/dist/configure/index.mjs +10 -19
  14. package/dist/configure/index.mjs.map +1 -1
  15. package/dist/enum-constants-Cwd4qdpa.mjs +115 -0
  16. package/dist/enum-constants-Cwd4qdpa.mjs.map +1 -0
  17. package/dist/file-utils-cqcpFk87.mjs +139 -0
  18. package/dist/file-utils-cqcpFk87.mjs.map +1 -0
  19. package/dist/index-BKXch-td.d.mts +18 -0
  20. package/dist/index-C3Ib7pFc.d.mts +18 -0
  21. package/dist/{index-qQXpN674.d.mts → index-DP8EB9FK.d.mts} +13 -5
  22. package/dist/index-SqWgrTnF.d.mts +20 -0
  23. package/dist/index-sSDpuVQY.d.mts +18 -0
  24. package/dist/{jiti-BrELlEYT.mjs → jiti-DHlauMCo.mjs} +2 -2
  25. package/dist/{jiti-BrELlEYT.mjs.map → jiti-DHlauMCo.mjs.map} +1 -1
  26. package/dist/{job-CULA2Pvf.mjs → job-2Q82qQ6N.mjs} +27 -5
  27. package/dist/job-2Q82qQ6N.mjs.map +1 -0
  28. package/dist/kysely-type-DtUUoAi3.mjs +259 -0
  29. package/dist/kysely-type-DtUUoAi3.mjs.map +1 -0
  30. package/dist/plugin/builtin/enum-constants/index.d.mts +4 -0
  31. package/dist/plugin/builtin/enum-constants/index.mjs +3 -0
  32. package/dist/plugin/builtin/file-utils/index.d.mts +4 -0
  33. package/dist/plugin/builtin/file-utils/index.mjs +3 -0
  34. package/dist/plugin/builtin/kysely-type/index.d.mts +4 -0
  35. package/dist/plugin/builtin/kysely-type/index.mjs +3 -0
  36. package/dist/plugin/builtin/seed/index.d.mts +4 -0
  37. package/dist/plugin/builtin/seed/index.mjs +3 -0
  38. package/dist/plugin/index.d.mts +3 -3
  39. package/dist/plugin/index.mjs +11 -11
  40. package/dist/plugin/index.mjs.map +1 -1
  41. package/dist/{schema-R5TxC5Pn.mjs → schema-WDvc7Zel.mjs} +4 -3
  42. package/dist/schema-WDvc7Zel.mjs.map +1 -0
  43. package/dist/seed-Dm7lrGZ3.mjs +1050 -0
  44. package/dist/seed-Dm7lrGZ3.mjs.map +1 -0
  45. package/dist/{src-DMROgdcL.mjs → src-i4uqS1G4.mjs} +2 -2
  46. package/dist/{src-DMROgdcL.mjs.map → src-i4uqS1G4.mjs.map} +1 -1
  47. package/dist/types-Bhl_wAM2.d.mts +151 -0
  48. package/dist/{types-b-ig8nW_.mjs → types-ClK_HJ0G.mjs} +1 -1
  49. package/dist/{types-b-ig8nW_.mjs.map → types-ClK_HJ0G.mjs.map} +1 -1
  50. package/dist/{types-DzvazVmg.d.mts → types-DdvTxFiD.d.mts} +1380 -1044
  51. package/dist/{update-DQKCUNmr.mjs → update-BoNKMti-.mjs} +285 -112
  52. package/dist/update-BoNKMti-.mjs.map +1 -0
  53. package/dist/utils/test/index.d.mts +4 -4
  54. package/dist/utils/test/index.mjs +3 -2
  55. package/dist/utils/test/index.mjs.map +1 -1
  56. package/docs/cli/application.md +106 -14
  57. package/docs/cli/auth.md +92 -12
  58. package/docs/cli/completion.md +18 -2
  59. package/docs/cli/executor.md +122 -14
  60. package/docs/cli/function.md +32 -4
  61. package/docs/cli/secret.md +134 -18
  62. package/docs/cli/staticwebsite.md +60 -8
  63. package/docs/cli/tailordb.md +148 -20
  64. package/docs/cli/user.md +154 -22
  65. package/docs/cli/workflow.md +100 -12
  66. package/docs/cli/workspace.md +274 -38
  67. package/docs/generator/custom.md +2 -2
  68. package/docs/plugin/custom.md +270 -163
  69. package/docs/plugin/index.md +48 -2
  70. package/package.json +22 -2
  71. package/dist/application-D5ZEr4zk.mjs +0 -4
  72. package/dist/job-CULA2Pvf.mjs.map +0 -1
  73. package/dist/schema-R5TxC5Pn.mjs.map +0 -1
  74. package/dist/types-BeNtD-fA.d.mts +0 -369
  75. package/dist/update-DQKCUNmr.mjs.map +0 -1
  76. /package/dist/{chunk-GMkBE123.mjs → chunk-CqAI0b6X.mjs} +0 -0
@@ -24,112 +24,255 @@ This is required so that generators can use plugin-generated TailorDB types via
24
24
 
25
25
  ```typescript
26
26
  interface Plugin<TypeConfig = unknown, PluginConfig = unknown> {
27
- /** Unique identifier for the plugin (e.g., "@my-company/soft-delete") */
28
27
  readonly id: string;
29
-
30
- /** Human-readable description */
31
28
  readonly description: string;
32
-
33
- /** Import path for generated code to reference */
34
- readonly importPath: string;
35
-
36
- /** Controls whether per-type config is required when attaching via .plugin() */
29
+ readonly importPath?: string;
37
30
  readonly typeConfigRequired?: boolean | ((pluginConfig?: PluginConfig) => boolean);
38
-
39
- /** Plugin-level config passed via definePlugins() */
40
31
  readonly pluginConfig?: PluginConfig;
41
32
 
42
- /** Process a type with this plugin attached */
43
- processType?(
33
+ // Definition-time hooks
34
+ onTypeLoaded?(
44
35
  context: PluginProcessContext<TypeConfig, PluginConfig>,
45
36
  ): TypePluginOutput | Promise<TypePluginOutput>;
46
-
47
- /** Process a namespace (plugins without a source type) */
48
- processNamespace?(
37
+ onNamespaceLoaded?(
49
38
  context: PluginNamespaceProcessContext<PluginConfig>,
50
39
  ): PluginOutput | Promise<PluginOutput>;
40
+
41
+ // Generation-time hooks
42
+ onTailorDBReady?(
43
+ context: TailorDBReadyContext<PluginConfig>,
44
+ ): GeneratorResult | Promise<GeneratorResult>;
45
+ onResolverReady?(
46
+ context: ResolverReadyContext<PluginConfig>,
47
+ ): GeneratorResult | Promise<GeneratorResult>;
48
+ onExecutorReady?(
49
+ context: ExecutorReadyContext<PluginConfig>,
50
+ ): GeneratorResult | Promise<GeneratorResult>;
51
51
  }
52
52
  ```
53
53
 
54
- Notes:
54
+ | Property | Required | Description |
55
+ | -------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------ |
56
+ | `id` | Yes | Unique plugin identifier (e.g., `"@my-company/soft-delete"`) |
57
+ | `description` | Yes | Human-readable description |
58
+ | `importPath` | When using definition-time hooks | Path resolvable from `tailor.config.ts` directory. Used for import statements in generated code. |
59
+ | `typeConfigRequired` | No | Whether per-type config is required when attaching via `.plugin()`. Default: optional. |
60
+ | `pluginConfig` | No | Plugin-level config passed via `definePlugins()`. Set via factory function. |
55
61
 
56
- - `importPath` should be resolvable from the directory containing `tailor.config.ts`. Code generators use it to import plugin APIs such as `getGeneratedType` and executor modules.
57
- - If you want to attach a plugin via `.plugin()`, implement the `processType` method.
58
- - Namespace-only plugins implement `processNamespace` instead.
59
- - `pluginConfig` stores the plugin-level config so it can be read later during processing. Set it on the plugin object (e.g., via a factory function) before passing to `definePlugins()`.
60
- - `resolve` should return a dynamic import; relative specifiers are resolved from the plugin module.
61
- - Per-type config is optional by default. Use `typeConfigRequired: true` to make it mandatory.
62
- - To toggle optional/required based on plugin config, provide a function for `typeConfigRequired`.
63
- - Use TypeScript type parameters (`TypeConfig`, `PluginConfig`) to get type-safe config in your `processType` and `processNamespace` methods.
62
+ ## Hook Reference
64
63
 
65
- ## PluginProcessContext
64
+ ### onTypeLoaded
66
65
 
67
- Context passed to the `processType` method:
66
+ **Trigger**: Called once for each TailorDB type that has `.plugin({ pluginId: config })` attached.
68
67
 
69
- ```typescript
70
- interface PluginProcessContext<TypeConfig = unknown, PluginConfig = unknown> {
71
- /** The TailorDB type being processed */
72
- type: TailorAnyDBType;
68
+ **Context** (`PluginProcessContext`):
73
69
 
74
- /** Per-type configuration from .plugin({ pluginId: typeConfig }) */
75
- typeConfig: TypeConfig;
70
+ | Field | Type | Description |
71
+ | -------------- | ----------------- | ------------------------------------------ |
72
+ | `type` | `TailorAnyDBType` | The TailorDB type being processed |
73
+ | `typeConfig` | `TypeConfig` | Per-type config from `.plugin()` |
74
+ | `pluginConfig` | `PluginConfig` | Plugin-level config from `definePlugins()` |
75
+ | `namespace` | `string` | Namespace of the TailorDB type |
76
76
 
77
- /** Plugin-level configuration from definePlugins() */
78
- pluginConfig: PluginConfig;
77
+ **Returns** (`TypePluginOutput`):
79
78
 
80
- /** Namespace of the TailorDB type */
81
- namespace: string;
82
- }
83
- ```
79
+ | Field | Type | Description |
80
+ | ----------- | ----------------------------------------------- | ----------------------------------------------------- |
81
+ | `types` | `Record<string, TailorAnyDBType>` | Additional TailorDB types to generate (keyed by kind) |
82
+ | `resolvers` | `PluginGeneratedResolver[]` | Additional resolvers to generate |
83
+ | `executors` | `PluginGeneratedExecutor[]` | Additional executors to generate |
84
+ | `extends` | `{ fields?: Record<string, TailorAnyDBField> }` | Fields to add to the source type |
84
85
 
85
- ## PluginNamespaceProcessContext
86
+ **Use cases**:
86
87
 
87
- Context passed to the `processNamespace` method:
88
+ - Generate derived types (e.g., archive tables, history tables) from user-defined types
89
+ - Add fields to existing types (e.g., `deletedAt` for soft delete)
90
+ - Generate executors triggered by record events on the source type
88
91
 
89
92
  ```typescript
90
- interface PluginNamespaceProcessContext<PluginConfig = unknown> {
91
- /** Plugin-level configuration from definePlugins() */
92
- pluginConfig: PluginConfig;
93
+ onTypeLoaded(context) {
94
+ const { type, typeConfig, namespace } = context;
95
+ return {
96
+ types: { archive: db.type(`Deleted_${type.name}`, { ... }) },
97
+ extends: { fields: { deletedAt: db.datetime({ optional: true }) } },
98
+ executors: [{ name: `${type.name}-on-delete`, resolve: async () => await import("./on-delete"), context: { sourceType: type, namespace } }],
99
+ };
100
+ },
101
+ ```
93
102
 
94
- /** Target namespace for generated types */
95
- namespace: string;
96
- }
103
+ ### onNamespaceLoaded
104
+
105
+ **Trigger**: Called once per namespace for plugins that do not require a source type.
106
+
107
+ **Context** (`PluginNamespaceProcessContext`):
108
+
109
+ | Field | Type | Description |
110
+ | -------------- | -------------- | ------------------------------------------ |
111
+ | `pluginConfig` | `PluginConfig` | Plugin-level config from `definePlugins()` |
112
+ | `namespace` | `string` | Target namespace |
113
+
114
+ **Returns** (`PluginOutput`):
115
+
116
+ Same as `TypePluginOutput` but without `extends` (namespace plugins cannot extend a source type).
117
+
118
+ **Use cases**:
119
+
120
+ - Generate types that don't derive from a specific user type (e.g., audit log, settings table)
121
+
122
+ ```typescript
123
+ onNamespaceLoaded(context) {
124
+ return {
125
+ types: { auditLog: db.type("AuditLog", { action: db.string(), ... }) },
126
+ };
127
+ },
97
128
  ```
98
129
 
99
- ## Output Types
130
+ ### onTailorDBReady
131
+
132
+ **Trigger**: Called once after all TailorDB types are loaded and auth is resolved.
133
+
134
+ **Context** (`TailorDBReadyContext`):
135
+
136
+ | Field | Type | Description |
137
+ | -------------- | ------------------------- | -------------------------------------------------------------- |
138
+ | `tailordb` | `TailorDBNamespaceData[]` | All namespaces with types, source info, and plugin attachments |
139
+ | `auth` | `GeneratorAuthInput?` | Auth configuration (machine users, OAuth2 clients, etc.) |
140
+ | `baseDir` | `string` | Output directory for generated files |
141
+ | `configPath` | `string` | Path to `tailor.config.ts` |
142
+ | `pluginConfig` | `PluginConfig` | Plugin-level config from `definePlugins()` |
100
143
 
101
- ### PluginOutput (base)
144
+ `TailorDBNamespaceData` contains:
102
145
 
103
- Base output used by both `processType` and `processNamespace`:
146
+ | Field | Type | Description |
147
+ | ------------------- | ------------------------------------------ | ------------------------------------ |
148
+ | `namespace` | `string` | Namespace name |
149
+ | `types` | `Record<string, TailorDBType>` | All finalized types in the namespace |
150
+ | `sourceInfo` | `ReadonlyMap<string, TypeSourceInfoEntry>` | Source file info for each type |
151
+ | `pluginAttachments` | `ReadonlyMap<string, PluginAttachment[]>` | Plugin configs attached to each type |
152
+
153
+ **Returns** (`GeneratorResult`):
154
+
155
+ | Field | Type | Description |
156
+ | -------- | ------------------------------------------------------ | ------------------------ |
157
+ | `files` | `Array<{ path, content, skipIfExists?, executable? }>` | Files to write to disk |
158
+ | `errors` | `string[]?` | Error messages to report |
159
+
160
+ **Use cases**:
161
+
162
+ - Generate type definitions (e.g., Kysely types, enum constants)
163
+ - Generate seed data scaffolding from type schemas
164
+ - Generate type lists or metadata files
104
165
 
105
166
  ```typescript
106
- interface PluginOutput {
107
- /** Additional TailorDB types to generate */
108
- types?: Record<string, TailorAnyDBType>;
167
+ onTailorDBReady(ctx) {
168
+ const allTypes = ctx.tailordb.flatMap((ns) =>
169
+ Object.values(ns.types).map((t) => t.name),
170
+ );
171
+ return {
172
+ files: [{ path: `${ctx.baseDir}/types.ts`, content: `export const types = ${JSON.stringify(allTypes)};\n` }],
173
+ };
174
+ },
175
+ ```
109
176
 
110
- /** Additional resolvers to generate */
111
- resolvers?: PluginGeneratedResolver[];
177
+ ### onResolverReady
112
178
 
113
- /** Additional executors to generate */
114
- executors?: PluginGeneratedExecutor[];
115
- }
179
+ **Trigger**: Called once after all resolvers are loaded, for plugins that do not implement `onExecutorReady`.
180
+
181
+ **Context** (`ResolverReadyContext`):
182
+
183
+ All fields from `TailorDBReadyContext`, plus:
184
+
185
+ | Field | Type | Description |
186
+ | ----------- | ------------------------- | ----------------------------------- |
187
+ | `resolvers` | `ResolverNamespaceData[]` | All namespaces with their resolvers |
188
+
189
+ `ResolverNamespaceData` contains:
190
+
191
+ | Field | Type | Description |
192
+ | ----------- | -------------------------- | ------------------------------ |
193
+ | `namespace` | `string` | Namespace name |
194
+ | `resolvers` | `Record<string, Resolver>` | All resolvers in the namespace |
195
+
196
+ **Returns**: `GeneratorResult` (same as `onTailorDBReady`)
197
+
198
+ **Use cases**:
199
+
200
+ - Generate API client code from resolver definitions
201
+ - Generate documentation that includes resolver endpoints
202
+
203
+ ```typescript
204
+ onResolverReady(ctx) {
205
+ const resolverNames = ctx.resolvers.flatMap((ns) =>
206
+ Object.values(ns.resolvers).map((r) => r.name),
207
+ );
208
+ return {
209
+ files: [{ path: `${ctx.baseDir}/api.ts`, content: generateApiClient(resolverNames) }],
210
+ };
211
+ },
116
212
  ```
117
213
 
118
- ### TypePluginOutput
214
+ ### onExecutorReady
215
+
216
+ **Trigger**: Called once after all executors are loaded.
217
+
218
+ **Context** (`ExecutorReadyContext`):
219
+
220
+ All fields from `ResolverReadyContext`, plus:
221
+
222
+ | Field | Type | Description |
223
+ | ----------- | -------------------------- | -------------------------------------- |
224
+ | `executors` | `Record<string, Executor>` | All executors (keyed by executor name) |
119
225
 
120
- Return value from `processType`. Extends `PluginOutput` with the ability to add fields to the source type:
226
+ **Returns**: `GeneratorResult` (same as `onTailorDBReady`)
227
+
228
+ **Use cases**:
229
+
230
+ - Generate dashboards or reports that need the full application topology
231
+ - Generate configuration files that reference all services
121
232
 
122
233
  ```typescript
123
- interface TypePluginOutput extends PluginOutput {
124
- /** Extensions to apply to the source type */
125
- extends?: {
126
- /** Fields to add to the source type */
127
- fields?: Record<string, TailorAnyDBField>;
234
+ onExecutorReady(ctx) {
235
+ const summary = {
236
+ types: ctx.tailordb.flatMap((ns) => Object.keys(ns.types)),
237
+ resolvers: ctx.resolvers.flatMap((ns) => Object.keys(ns.resolvers)),
238
+ executors: Object.keys(ctx.executors),
128
239
  };
129
- }
240
+ return {
241
+ files: [{ path: `${ctx.baseDir}/app-summary.json`, content: JSON.stringify(summary, null, 2) }],
242
+ };
243
+ },
130
244
  ```
131
245
 
132
- `processNamespace` returns `PluginOutput` directly (namespace plugins cannot extend a source type).
246
+ ## Hook Scheduling Rules
247
+
248
+ Each generation-time hook runs at its own pipeline phase, regardless of what other hooks the same plugin implements:
249
+
250
+ | Hook | Runs after | Data provided |
251
+ | ----------------- | ---------------- | ----------------------------------------------- |
252
+ | `onTailorDBReady` | TailorDB loaded | `tailordb` + `auth` |
253
+ | `onResolverReady` | Resolvers loaded | `tailordb` + `resolvers` + `auth` |
254
+ | `onExecutorReady` | Executors loaded | `tailordb` + `resolvers` + `executors` + `auth` |
255
+
256
+ A plugin implementing multiple hooks (e.g., both `onTailorDBReady` and `onResolverReady`) will have each hook called at its natural phase. This ensures that files generated by `onTailorDBReady` are available when resolvers are loaded, before `onResolverReady` runs.
257
+
258
+ ## Import Types
259
+
260
+ All context and result types are exported from `@tailor-platform/sdk`:
261
+
262
+ ```typescript
263
+ import type {
264
+ Plugin,
265
+ PluginProcessContext,
266
+ TypePluginOutput,
267
+ PluginOutput,
268
+ TailorDBReadyContext,
269
+ ResolverReadyContext,
270
+ ExecutorReadyContext,
271
+ TailorDBNamespaceData,
272
+ ResolverNamespaceData,
273
+ GeneratorResult,
274
+ } from "@tailor-platform/sdk";
275
+ ```
133
276
 
134
277
  ## getGeneratedType Helper
135
278
 
@@ -142,13 +285,16 @@ import { customer } from "./tailordb/customer";
142
285
 
143
286
  const configPath = join(import.meta.dirname, "./tailor.config.ts");
144
287
 
145
- // Get the generated type by config path, plugin ID, source type, and kind
288
+ // Type-attached plugin
146
289
  const DeletedCustomer = await getGeneratedType(
147
290
  configPath,
148
291
  "@example/soft-delete",
149
292
  customer,
150
293
  "archive",
151
294
  );
295
+
296
+ // Namespace plugin (pass null as sourceType)
297
+ const AuditLog = await getGeneratedType(configPath, "@example/audit-log", null, "auditLog");
152
298
  ```
153
299
 
154
300
  **Parameters:**
@@ -163,36 +309,15 @@ const DeletedCustomer = await getGeneratedType(
163
309
  1. Loads and caches the config from the given path
164
310
  2. Finds the plugin by ID from `definePlugins()` exports
165
311
  3. Auto-resolves the namespace from config
166
- 4. Calls the plugin's `processType()` or `processNamespace()` method
312
+ 4. Calls the plugin's `onTypeLoaded()` or `onNamespaceLoaded()` method
167
313
  5. Caches the result to avoid redundant processing
168
314
  6. Returns the generated type matching the specified kind
169
315
 
170
- ### Example Usage
316
+ ## Examples
171
317
 
172
- ```typescript
173
- import { join } from "node:path";
174
- import { getGeneratedType } from "@tailor-platform/sdk/plugin";
175
- import { customer } from "./tailordb/customer";
318
+ ### Definition-time Plugin (Soft Delete)
176
319
 
177
- const configPath = join(import.meta.dirname, "./tailor.config.ts");
178
-
179
- // Type-attached plugin
180
- const DeletedCustomer = await getGeneratedType(
181
- configPath,
182
- "@example/soft-delete",
183
- customer,
184
- "archive",
185
- );
186
-
187
- // Namespace plugin (pass null as sourceType)
188
- const AuditLog = await getGeneratedType(configPath, "@example/audit-log", null, "auditLog");
189
- ```
190
-
191
- ## Example: Soft Delete Plugin
192
-
193
- A complete example of a plugin that adds soft delete functionality:
194
-
195
- ### Plugin Definition
320
+ A plugin that adds soft delete functionality via `onTypeLoaded`:
196
321
 
197
322
  ```typescript
198
323
  // plugins/soft-delete/plugin.ts
@@ -252,7 +377,6 @@ function processSoftDelete(
252
377
  };
253
378
  }
254
379
 
255
- // Factory function for plugins with plugin-level config
256
380
  function createSoftDeletePlugin(
257
381
  pluginConfig?: SoftDeletePluginConfig,
258
382
  ): Plugin<SoftDeleteConfig, SoftDeletePluginConfig> {
@@ -262,15 +386,14 @@ function createSoftDeletePlugin(
262
386
  importPath: "./plugins/soft-delete",
263
387
  pluginConfig,
264
388
  typeConfigRequired: (config) => config?.requireTypeConfig === true,
265
- processType: processSoftDelete,
389
+ onTypeLoaded: processSoftDelete,
266
390
  };
267
391
  }
268
392
 
269
- // Default export is required for getGeneratedType() to work
270
393
  export default createSoftDeletePlugin();
271
394
  ```
272
395
 
273
- ### Executor with Context
396
+ #### Executor with Context
274
397
 
275
398
  ```typescript
276
399
  // plugins/soft-delete/executors/on-delete.ts
@@ -311,14 +434,13 @@ export default withPluginContext((ctx: SoftDeleteContext) => {
311
434
  });
312
435
  ```
313
436
 
314
- ### Usage
437
+ #### Usage
315
438
 
316
439
  ```typescript
317
440
  // tailor.config.ts
318
441
  import { definePlugins } from "@tailor-platform/sdk";
319
442
  import softDeletePlugin from "./plugins/soft-delete";
320
443
 
321
- // Use a factory function to pass plugin-level config
322
444
  export const plugins = definePlugins(
323
445
  softDeletePlugin({
324
446
  archiveTablePrefix: "Deleted_",
@@ -339,26 +461,64 @@ export const customer = db
339
461
  });
340
462
  ```
341
463
 
342
- If your plugin uses `typeConfigRequired` as a function, you can toggle whether per-type config
343
- is required via `pluginConfig`:
464
+ ### Generation-only Plugin (Type List)
465
+
466
+ A plugin that only uses `onTailorDBReady` to generate output files:
344
467
 
345
468
  ```typescript
346
- export const plugins = definePlugins(
347
- softDeletePlugin({
348
- archiveTablePrefix: "Deleted_",
349
- requireTypeConfig: true,
350
- }),
351
- );
469
+ import type { Plugin, GeneratorResult } from "@tailor-platform/sdk";
470
+
471
+ const typeListPlugin: Plugin = {
472
+ id: "@example/type-list",
473
+ description: "Generates a list of all TailorDB type names",
474
+
475
+ onTailorDBReady(ctx): GeneratorResult {
476
+ const allTypes = ctx.tailordb.flatMap((ns) =>
477
+ Object.entries(ns.types).map(([_, type]) => ({
478
+ name: type.name,
479
+ fieldCount: Object.keys(type.fields).length,
480
+ namespace: ns.namespace,
481
+ })),
482
+ );
483
+ const content = `// Generated type list\nexport const types = ${JSON.stringify(allTypes, null, 2)} as const;\n`;
484
+ return {
485
+ files: [{ path: `${ctx.baseDir}/types.ts`, content }],
486
+ };
487
+ },
488
+ };
352
489
  ```
353
490
 
354
- ## Adding Type Safety
491
+ ### Hybrid Plugin (Definition + Generation)
492
+
493
+ A plugin that uses both definition-time and generation-time hooks:
494
+
495
+ ```typescript
496
+ const plugin: Plugin = {
497
+ id: "@example/hybrid",
498
+ description: "Generates derived types and produces output files",
499
+ importPath: "./plugins/hybrid",
500
+
501
+ // Definition-time: Generate additional types from attached source types
502
+ onTypeLoaded(context) {
503
+ return { types: { derived: createDerivedType(context.type) } };
504
+ },
505
+
506
+ // Generation-time: Generate output files from all finalized types
507
+ onTailorDBReady(ctx) {
508
+ const allTypes = ctx.tailordb.flatMap((ns) => Object.values(ns.types).map((t) => t.name));
509
+ return {
510
+ files: [{ path: `${ctx.baseDir}/output.ts`, content: generateCode(allTypes) }],
511
+ };
512
+ },
513
+ };
514
+ ```
355
515
 
356
- Plugin type safety is provided at two levels:
516
+ ## Adding Type Safety
357
517
 
358
518
  ### Plugin-level type safety (TypeConfig / PluginConfig)
359
519
 
360
520
  Use TypeScript type parameters on `Plugin<TypeConfig, PluginConfig>` to get type-safe config
361
- in `processType` and `processNamespace` methods:
521
+ in `onTypeLoaded` and `onNamespaceLoaded` methods:
362
522
 
363
523
  ```typescript
364
524
  interface MyTypeConfig {
@@ -372,7 +532,7 @@ interface MyPluginConfig {
372
532
  const plugin: Plugin<MyTypeConfig, MyPluginConfig> = {
373
533
  id: "@example/my-plugin",
374
534
  // ...
375
- processType(context) {
535
+ onTypeLoaded(context) {
376
536
  // context.typeConfig is MyTypeConfig
377
537
  // context.pluginConfig is MyPluginConfig
378
538
  },
@@ -407,56 +567,3 @@ declare module "@tailor-platform/sdk" {
407
567
  }
408
568
  }
409
569
  ```
410
-
411
- ## Plugin Types
412
-
413
- ### Type-Attached Plugins
414
-
415
- Implement `processType` to handle types with the plugin attached:
416
-
417
- ```typescript
418
- const plugin: Plugin = {
419
- id: "@example/my-plugin",
420
- // ...
421
- processType(context) {
422
- // Called for each type with .plugin({ "@example/my-plugin": config })
423
- return {
424
- types: {
425
- /* generated types */
426
- },
427
- };
428
- },
429
- };
430
- ```
431
-
432
- ### Namespace Plugins
433
-
434
- Implement `processNamespace` for plugins that generate types independently:
435
-
436
- ```typescript
437
- const plugin: Plugin = {
438
- id: "@example/audit-log",
439
- // ...
440
- processNamespace(context) {
441
- // Called once per namespace, with namespace-level types available
442
- return { types: { auditLog: /* generated type */ } };
443
- },
444
- };
445
- ```
446
-
447
- ### Hybrid Plugins
448
-
449
- Implement both methods for plugins that support both modes:
450
-
451
- ```typescript
452
- const plugin: Plugin = {
453
- id: "@example/hybrid",
454
- // ...
455
- processType(context) {
456
- // Handle type attachments
457
- },
458
- processNamespace(context) {
459
- // Handle namespace generation
460
- },
461
- };
462
- ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **Beta Feature**: The plugin system is currently in beta. APIs may change in future releases.
4
4
 
5
- Plugins extend TailorDB types by automatically generating additional types and executors based on your type definitions.
5
+ Plugins extend TailorDB types by automatically generating additional types, executors, and output files based on your type definitions.
6
6
 
7
7
  ## Overview
8
8
 
@@ -100,6 +100,7 @@ Plugins can generate:
100
100
  - **Types**: Additional TailorDB types (e.g., `CustomerHistory`, `Deleted_Customer`)
101
101
  - **Executors**: Event handlers triggered by record changes
102
102
  - **Field Extensions**: Additional fields added to the source type
103
+ - **Output Files**: TypeScript code and other files via generation-time hooks
103
104
 
104
105
  Generated files are placed under `.tailor-sdk/<plugin-id>/` (the plugin ID is sanitized,
105
106
  e.g. `@example/soft-delete` → `example-soft-delete`), such as:
@@ -107,6 +108,51 @@ e.g. `@example/soft-delete` → `example-soft-delete`), such as:
107
108
  - `.tailor-sdk/example-soft-delete/types`
108
109
  - `.tailor-sdk/example-soft-delete/executors`
109
110
 
111
+ ## Plugin Lifecycle
112
+
113
+ Plugins have 5 hooks across two lifecycle phases. Each hook fires at a specific point in the `tailor-sdk generate` pipeline:
114
+
115
+ ```
116
+ tailor-sdk generate
117
+
118
+ ├─ Load TailorDB types
119
+ │ ├─ onTypeLoaded ← per type with .plugin() attached
120
+ │ └─ onNamespaceLoaded ← once per namespace (namespace plugins)
121
+
122
+ ├─ Resolve Auth
123
+
124
+ ├─ onTailorDBReady ← all types finalized
125
+
126
+ ├─ Load Resolvers
127
+
128
+ ├─ onResolverReady ← all resolvers finalized
129
+
130
+ ├─ Load Executors
131
+
132
+ └─ onExecutorReady ← all executors finalized
133
+ ```
134
+
135
+ ### Definition-time hooks
136
+
137
+ | Hook | Trigger | Can do |
138
+ | ------------------- | ----------------------------------- | --------------------------------------------------------------- |
139
+ | `onTypeLoaded` | Each type with `.plugin()` attached | Generate types, resolvers, executors; extend source type fields |
140
+ | `onNamespaceLoaded` | Once per namespace | Generate types, resolvers, executors |
141
+
142
+ These hooks produce TailorDB types, resolvers, and executors that become part of the application. Requires `importPath` on the plugin.
143
+
144
+ ### Generation-time hooks
145
+
146
+ | Hook | Available data | Can do |
147
+ | ----------------- | ------------------------------------------ | ------------------ |
148
+ | `onTailorDBReady` | TailorDB types, Auth | Write output files |
149
+ | `onResolverReady` | TailorDB types, Resolvers, Auth | Write output files |
150
+ | `onExecutorReady` | TailorDB types, Resolvers, Executors, Auth | Write output files |
151
+
152
+ These hooks receive all finalized data and produce output files (TypeScript code, etc.). They replace the previous standalone `defineGenerators()` approach. No `importPath` required.
153
+
154
+ A plugin can implement hooks from either or both phases.
155
+
110
156
  ## Creating Custom Plugins
111
157
 
112
- See [Custom Plugins](./custom.md) for how to create your own plugins.
158
+ See [Custom Plugins](./custom.md) for the full hook reference and examples.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/sdk",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -48,6 +48,26 @@
48
48
  "types": "./dist/plugin/index.d.mts",
49
49
  "import": "./dist/plugin/index.mjs",
50
50
  "default": "./dist/plugin/index.mjs"
51
+ },
52
+ "./plugin/kysely-type": {
53
+ "types": "./dist/plugin/builtin/kysely-type/index.d.mts",
54
+ "import": "./dist/plugin/builtin/kysely-type/index.mjs",
55
+ "default": "./dist/plugin/builtin/kysely-type/index.mjs"
56
+ },
57
+ "./plugin/enum-constants": {
58
+ "types": "./dist/plugin/builtin/enum-constants/index.d.mts",
59
+ "import": "./dist/plugin/builtin/enum-constants/index.mjs",
60
+ "default": "./dist/plugin/builtin/enum-constants/index.mjs"
61
+ },
62
+ "./plugin/file-utils": {
63
+ "types": "./dist/plugin/builtin/file-utils/index.d.mts",
64
+ "import": "./dist/plugin/builtin/file-utils/index.mjs",
65
+ "default": "./dist/plugin/builtin/file-utils/index.mjs"
66
+ },
67
+ "./plugin/seed": {
68
+ "types": "./dist/plugin/builtin/seed/index.d.mts",
69
+ "import": "./dist/plugin/builtin/seed/index.mjs",
70
+ "default": "./dist/plugin/builtin/seed/index.mjs"
51
71
  }
52
72
  },
53
73
  "dependencies": {
@@ -78,7 +98,7 @@
78
98
  "p-limit": "7.2.0",
79
99
  "pathe": "2.0.3",
80
100
  "pkg-types": "2.3.0",
81
- "politty": "^0.3.0",
101
+ "politty": "^0.4.0",
82
102
  "rolldown": "1.0.0-beta.60",
83
103
  "serve": "14.2.5",
84
104
  "std-env": "3.10.0",
@@ -1,4 +0,0 @@
1
- import "./chunk-GMkBE123.mjs";
2
- import { n as generatePluginFilesIfNeeded, r as loadApplication, t as defineApplication } from "./application-DYfVZVPT.mjs";
3
-
4
- export { defineApplication };