@tailor-platform/sdk 1.17.1 → 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.
- package/CHANGELOG.md +16 -0
- package/dist/application-Csj7Ow5Q.mjs +8 -0
- package/dist/{application-BMDE8KqK.mjs → application-gWUyKuzv.mjs} +120 -1618
- package/dist/application-gWUyKuzv.mjs.map +1 -0
- package/dist/brand-BZJCv6UY.mjs +28 -0
- package/dist/brand-BZJCv6UY.mjs.map +1 -0
- package/dist/cli/index.mjs +38 -20
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +10 -33
- package/dist/cli/lib.mjs +10 -5
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/configure/index.d.mts +4 -4
- package/dist/configure/index.mjs +10 -19
- package/dist/configure/index.mjs.map +1 -1
- package/dist/enum-constants-Cwd4qdpa.mjs +115 -0
- package/dist/enum-constants-Cwd4qdpa.mjs.map +1 -0
- package/dist/file-utils-cqcpFk87.mjs +139 -0
- package/dist/file-utils-cqcpFk87.mjs.map +1 -0
- package/dist/index-BKXch-td.d.mts +18 -0
- package/dist/index-C3Ib7pFc.d.mts +18 -0
- package/dist/{index-CVcYqZSf.d.mts → index-DP8EB9FK.d.mts} +12 -5
- package/dist/index-SqWgrTnF.d.mts +20 -0
- package/dist/index-sSDpuVQY.d.mts +18 -0
- package/dist/{jiti-BrELlEYT.mjs → jiti-DHlauMCo.mjs} +2 -2
- package/dist/{jiti-BrELlEYT.mjs.map → jiti-DHlauMCo.mjs.map} +1 -1
- package/dist/{job-CULA2Pvf.mjs → job-2Q82qQ6N.mjs} +27 -5
- package/dist/job-2Q82qQ6N.mjs.map +1 -0
- package/dist/kysely-type-DtUUoAi3.mjs +259 -0
- package/dist/kysely-type-DtUUoAi3.mjs.map +1 -0
- package/dist/plugin/builtin/enum-constants/index.d.mts +4 -0
- package/dist/plugin/builtin/enum-constants/index.mjs +3 -0
- package/dist/plugin/builtin/file-utils/index.d.mts +4 -0
- package/dist/plugin/builtin/file-utils/index.mjs +3 -0
- package/dist/plugin/builtin/kysely-type/index.d.mts +4 -0
- package/dist/plugin/builtin/kysely-type/index.mjs +3 -0
- package/dist/plugin/builtin/seed/index.d.mts +4 -0
- package/dist/plugin/builtin/seed/index.mjs +3 -0
- package/dist/plugin/index.d.mts +3 -3
- package/dist/plugin/index.mjs +11 -11
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/{schema-R5TxC5Pn.mjs → schema-WDvc7Zel.mjs} +4 -3
- package/dist/schema-WDvc7Zel.mjs.map +1 -0
- package/dist/seed-Dm7lrGZ3.mjs +1050 -0
- package/dist/seed-Dm7lrGZ3.mjs.map +1 -0
- package/dist/{src-DMROgdcL.mjs → src-i4uqS1G4.mjs} +2 -2
- package/dist/{src-DMROgdcL.mjs.map → src-i4uqS1G4.mjs.map} +1 -1
- package/dist/types-Bhl_wAM2.d.mts +151 -0
- package/dist/{types-b-ig8nW_.mjs → types-ClK_HJ0G.mjs} +1 -1
- package/dist/{types-b-ig8nW_.mjs.map → types-ClK_HJ0G.mjs.map} +1 -1
- package/dist/{types-CZZBCaxB.d.mts → types-DdvTxFiD.d.mts} +1324 -988
- package/dist/{update-CUBVjZbL.mjs → update-BoNKMti-.mjs} +268 -97
- package/dist/update-BoNKMti-.mjs.map +1 -0
- package/dist/utils/test/index.d.mts +4 -4
- package/dist/utils/test/index.mjs +3 -2
- package/dist/utils/test/index.mjs.map +1 -1
- package/docs/cli/application.md +106 -14
- package/docs/cli/auth.md +92 -12
- package/docs/cli/completion.md +18 -2
- package/docs/cli/executor.md +122 -14
- package/docs/cli/function.md +32 -4
- package/docs/cli/secret.md +134 -18
- package/docs/cli/staticwebsite.md +60 -8
- package/docs/cli/tailordb.md +148 -20
- package/docs/cli/user.md +154 -22
- package/docs/cli/workflow.md +100 -12
- package/docs/cli/workspace.md +274 -38
- package/docs/generator/custom.md +2 -2
- package/docs/plugin/custom.md +270 -163
- package/docs/plugin/index.md +48 -2
- package/package.json +22 -2
- package/dist/application-BMDE8KqK.mjs.map +0 -1
- package/dist/application-Dni_W16P.mjs +0 -4
- package/dist/job-CULA2Pvf.mjs.map +0 -1
- package/dist/schema-R5TxC5Pn.mjs.map +0 -1
- package/dist/types-DthzUFfx.d.mts +0 -372
- package/dist/update-CUBVjZbL.mjs.map +0 -1
- /package/dist/{chunk-GMkBE123.mjs → chunk-CqAI0b6X.mjs} +0 -0
package/docs/plugin/custom.md
CHANGED
|
@@ -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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
+
### onTypeLoaded
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
**Trigger**: Called once for each TailorDB type that has `.plugin({ pluginId: config })` attached.
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
interface PluginProcessContext<TypeConfig = unknown, PluginConfig = unknown> {
|
|
71
|
-
/** The TailorDB type being processed */
|
|
72
|
-
type: TailorAnyDBType;
|
|
68
|
+
**Context** (`PluginProcessContext`):
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
78
|
-
pluginConfig: PluginConfig;
|
|
77
|
+
**Returns** (`TypePluginOutput`):
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
86
|
+
**Use cases**:
|
|
86
87
|
|
|
87
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
+
`TailorDBNamespaceData` contains:
|
|
102
145
|
|
|
103
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
111
|
-
resolvers?: PluginGeneratedResolver[];
|
|
177
|
+
### onResolverReady
|
|
112
178
|
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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 `
|
|
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
|
-
|
|
316
|
+
## Examples
|
|
171
317
|
|
|
172
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
343
|
-
|
|
464
|
+
### Generation-only Plugin (Type List)
|
|
465
|
+
|
|
466
|
+
A plugin that only uses `onTailorDBReady` to generate output files:
|
|
344
467
|
|
|
345
468
|
```typescript
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 `
|
|
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
|
-
|
|
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
|
-
```
|
package/docs/plugin/index.md
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
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.
|
|
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",
|