dyrected 2.5.49

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 (51) hide show
  1. package/LICENSE.md +50 -0
  2. package/README.md +3 -0
  3. package/dist/commands/generate-ai-rules.d.ts +3 -0
  4. package/dist/commands/generate-ai-rules.d.ts.map +1 -0
  5. package/dist/commands/generate-ai-rules.js +16 -0
  6. package/dist/commands/generate-ai-rules.js.map +1 -0
  7. package/dist/commands/generate-types.d.ts +3 -0
  8. package/dist/commands/generate-types.d.ts.map +1 -0
  9. package/dist/commands/generate-types.js +34 -0
  10. package/dist/commands/generate-types.js.map +1 -0
  11. package/dist/commands/init.d.ts +3 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +397 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/commands/sync-schema.d.ts +3 -0
  16. package/dist/commands/sync-schema.d.ts.map +1 -0
  17. package/dist/commands/sync-schema.js +89 -0
  18. package/dist/commands/sync-schema.js.map +1 -0
  19. package/dist/commands/upgrade.d.ts +3 -0
  20. package/dist/commands/upgrade.d.ts.map +1 -0
  21. package/dist/commands/upgrade.js +58 -0
  22. package/dist/commands/upgrade.js.map +1 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +29 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/utils/ai-rules-template.d.ts +2 -0
  28. package/dist/utils/ai-rules-template.d.ts.map +1 -0
  29. package/dist/utils/ai-rules-template.js +411 -0
  30. package/dist/utils/ai-rules-template.js.map +1 -0
  31. package/dist/utils/config-templates.d.ts +5 -0
  32. package/dist/utils/config-templates.d.ts.map +1 -0
  33. package/dist/utils/config-templates.js +65 -0
  34. package/dist/utils/config-templates.js.map +1 -0
  35. package/dist/utils/detect.d.ts +11 -0
  36. package/dist/utils/detect.d.ts.map +1 -0
  37. package/dist/utils/detect.js +45 -0
  38. package/dist/utils/detect.js.map +1 -0
  39. package/dist/utils/env.d.ts +10 -0
  40. package/dist/utils/env.d.ts.map +1 -0
  41. package/dist/utils/env.js +52 -0
  42. package/dist/utils/env.js.map +1 -0
  43. package/dist/utils/type-generator.d.ts +8 -0
  44. package/dist/utils/type-generator.d.ts.map +1 -0
  45. package/dist/utils/type-generator.js +246 -0
  46. package/dist/utils/type-generator.js.map +1 -0
  47. package/dist/utils/writers.d.ts +6 -0
  48. package/dist/utils/writers.d.ts.map +1 -0
  49. package/dist/utils/writers.js +152 -0
  50. package/dist/utils/writers.js.map +1 -0
  51. package/package.json +43 -0
@@ -0,0 +1,89 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import { createJiti } from "jiti";
5
+ import { runGenerateTypes } from "../utils/type-generator.js";
6
+ import { loadCommandEnv } from "../utils/env.js";
7
+ export function registerSyncSchema(program) {
8
+ program
9
+ .command("sync:schema")
10
+ .description("Sync your local Dyrected schema with the Cloud dashboard")
11
+ .option("-k, --api-key <key>", "Your Dyrected API Key")
12
+ .option("-s, --site-id <id>", "Your Dyrected Site ID")
13
+ .option("-u, --url <url>", "Cloud API URL", "https://cloud.dyrected.com")
14
+ .option("-c, --config <path>", "Path to your dyrected.config.ts", "./dyrected.config.ts")
15
+ .option("--env-path <path>", "Path to an env file to load before syncing")
16
+ .option("--skip-on-error", "Do not exit with error if sync fails (useful for CI builds)")
17
+ .option("--skip-types", "Skip automatic type generation after a successful sync")
18
+ .addHelpText("after", `
19
+ Examples:
20
+ # Sync using env vars (DYRECTED_API_KEY, DYRECTED_SITE_ID)
21
+ $ npx dyrected sync:schema
22
+
23
+ # Sync using a specific env file
24
+ $ npx dyrected sync:schema --env-path ./.env.local
25
+
26
+ # Sync with explicit credentials
27
+ $ npx dyrected sync:schema --api-key <key> --site-id <id>
28
+
29
+ # Sync in CI without failing the build on error
30
+ $ npx dyrected sync:schema --skip-on-error
31
+
32
+ # Sync without regenerating types
33
+ $ npx dyrected sync:schema --skip-types
34
+ `)
35
+ .action(async (options) => {
36
+ try {
37
+ await loadCommandEnv({ cwd: process.cwd(), envPath: options.envPath });
38
+ const apiKey = options.apiKey || process.env.DYRECTED_API_KEY;
39
+ const siteId = options.siteId || process.env.DYRECTED_SITE_ID;
40
+ const apiUrl = options.url || process.env.DYRECTED_URL || "https://cloud.dyrected.com";
41
+ const configPath = path.resolve(process.cwd(), options.config);
42
+ if (!apiKey || !siteId) {
43
+ console.warn(chalk.yellow("\n⚠ Skipping schema sync: API Key or Site ID missing. (Required for Cloud sync, but optional for self-hosted builds)\n"));
44
+ return;
45
+ }
46
+ if (!(await fs.pathExists(configPath))) {
47
+ throw new Error(`Config file not found at ${configPath}`);
48
+ }
49
+ console.log(chalk.blue(`Loading config from ${configPath}...`));
50
+ const jiti = createJiti(configPath);
51
+ const configModule = (await jiti.import(configPath));
52
+ const config = configModule.default || configModule;
53
+ if (!config.collections)
54
+ throw new Error("Invalid config: No collections found.");
55
+ console.log(chalk.blue(`Syncing schema to ${apiUrl}...`));
56
+ const response = await fetch(`${apiUrl}/cloud/workspaces/sites/${siteId}/schema/sync`, {
57
+ method: "POST",
58
+ headers: {
59
+ "Content-Type": "application/json",
60
+ Authorization: `Bearer ${apiKey}`,
61
+ "X-API-Key": apiKey,
62
+ },
63
+ body: JSON.stringify({
64
+ collections: config.collections,
65
+ globals: config.globals || [],
66
+ admin: config.admin || {},
67
+ }),
68
+ });
69
+ if (!response.ok) {
70
+ const error = await response.json().catch(() => ({ message: response.statusText }));
71
+ throw new Error(`Sync failed: ${error.message || response.statusText}`);
72
+ }
73
+ console.log(chalk.green(`✔ Schema synced successfully for site ${siteId}`));
74
+ if (!options.skipTypes) {
75
+ console.log(chalk.blue("\nGenerating types from synced schema..."));
76
+ await runGenerateTypes({ config: options.config, output: "./dyrected-types.ts" });
77
+ }
78
+ }
79
+ catch (error) {
80
+ if (options.skipOnError) {
81
+ console.warn(chalk.yellow(`\n⚠ Sync failed, but skipping error as requested: ${error.message}`));
82
+ return;
83
+ }
84
+ console.error(chalk.red(`\nError: ${error.message}`));
85
+ process.exit(1);
86
+ }
87
+ });
88
+ }
89
+ //# sourceMappingURL=sync-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-schema.js","sourceRoot":"","sources":["../../src/commands/sync-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerUpgrade(program: Command): void;
3
+ //# sourceMappingURL=upgrade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,QA0D/C"}
@@ -0,0 +1,58 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import { execSync } from "child_process";
5
+ import { detectPackageManager } from "../utils/detect.js";
6
+ export function registerUpgrade(program) {
7
+ program
8
+ .command("upgrade")
9
+ .description("Upgrade all Dyrected packages to the latest version")
10
+ .action(async () => {
11
+ const cwd = process.cwd();
12
+ const pkgPath = path.join(cwd, "package.json");
13
+ if (!(await fs.pathExists(pkgPath))) {
14
+ console.error(chalk.red("Error: No package.json found in the current directory."));
15
+ process.exit(1);
16
+ }
17
+ let pkg;
18
+ try {
19
+ pkg = await fs.readJson(pkgPath);
20
+ }
21
+ catch (err) {
22
+ console.error(chalk.red("Error: Failed to read package.json."));
23
+ process.exit(1);
24
+ }
25
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
26
+ const dyrectedDeps = Object.keys(allDeps).filter((dep) => dep.startsWith("@dyrected/") || dep === "dyrected");
27
+ if (dyrectedDeps.length === 0) {
28
+ console.log(chalk.yellow("No Dyrected dependencies found in package.json."));
29
+ return;
30
+ }
31
+ console.log(chalk.blue(`Found Dyrected packages to upgrade: ${dyrectedDeps.join(", ")}`));
32
+ const packageManager = detectPackageManager(cwd);
33
+ const installArgs = dyrectedDeps.map((dep) => `${dep}@latest`).join(" ");
34
+ let cmd = "";
35
+ if (packageManager === "yarn") {
36
+ cmd = `yarn add ${installArgs}`;
37
+ }
38
+ else if (packageManager === "pnpm") {
39
+ cmd = `pnpm add ${installArgs}`;
40
+ }
41
+ else if (packageManager === "bun") {
42
+ cmd = `bun add ${installArgs}`;
43
+ }
44
+ else {
45
+ cmd = `npm install ${installArgs}`;
46
+ }
47
+ console.log(chalk.blue(`Running: ${cmd}`));
48
+ try {
49
+ execSync(cmd, { cwd, stdio: "inherit" });
50
+ console.log(chalk.green("\n✔ All Dyrected packages successfully upgraded to the latest version!"));
51
+ }
52
+ catch (err) {
53
+ console.error(chalk.red(`\nFailed to upgrade packages. Try running manually:\n ${cmd}`));
54
+ process.exit(1);
55
+ }
56
+ });
57
+ }
58
+ //# sourceMappingURL=upgrade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { registerInit } from "./commands/init.js";
4
+ import { registerGenerateTypes } from "./commands/generate-types.js";
5
+ import { registerGenerateAiRules } from "./commands/generate-ai-rules.js";
6
+ import { registerSyncSchema } from "./commands/sync-schema.js";
7
+ import { registerUpgrade } from "./commands/upgrade.js";
8
+ const program = new Command();
9
+ program
10
+ .name("dyrected")
11
+ .description("Dyrected CMS CLI tool")
12
+ .version("0.0.1")
13
+ .addHelpText("after", `
14
+ Commands:
15
+ init Bootstrap Dyrected in your project (interactive)
16
+ upgrade Upgrade all Dyrected packages to the latest version
17
+ generate:types Generate TypeScript types from your schema
18
+ generate:ai-rules Generate canonical instructions for AI coding tools
19
+ sync:schema Push your local schema to Dyrected Cloud
20
+
21
+ Run \`npx dyrected <command> --help\` for detailed usage and examples.
22
+ `);
23
+ registerInit(program);
24
+ registerUpgrade(program);
25
+ registerGenerateTypes(program);
26
+ registerGenerateAiRules(program);
27
+ registerSyncSchema(program);
28
+ program.parse();
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,uBAAuB,CAAC;KACpC,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CACV,OAAO,EACP;;;;;;;;;CASH,CACE,CAAC;AAEJ,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAE5B,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function buildAiRules(): string;
2
+ //# sourceMappingURL=ai-rules-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-rules-template.d.ts","sourceRoot":"","sources":["../../src/utils/ai-rules-template.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,IAAI,MAAM,CAyZrC"}
@@ -0,0 +1,411 @@
1
+ export function buildAiRules() {
2
+ return `# Dyrected AI Rules
3
+
4
+ > This is the canonical instruction file for AI coding agents working with Dyrected.
5
+ > The Dyrected CLI also creates tool-specific pointer files so supported agents load it automatically.
6
+ >
7
+ > Full documentation: https://docs.dyrected.com
8
+
9
+ ---
10
+
11
+ ## Before writing any code
12
+
13
+ 1. Read https://docs.dyrected.com before writing any integration code
14
+ 2. Check the installed package version and its exported types — do not assume API shape from memory
15
+ 3. Use only APIs that exist in the installed version; if the docs and the package disagree, use the package
16
+ 4. Read the existing \`dyrected.config.ts\` before suggesting any schema change
17
+ 5. Never invent field types, hook names, config options, or API methods
18
+
19
+ ---
20
+
21
+ ## Core imports
22
+
23
+ \`\`\`ts
24
+ import { defineCollection, defineGlobal, defineConfig } from '@dyrected/core'
25
+ import { DyrectedClient } from '@dyrected/sdk'
26
+ \`\`\`
27
+
28
+ ---
29
+
30
+ ## Available field types
31
+
32
+ \`text\`, \`textarea\`, \`richText\`, \`number\`, \`boolean\`, \`date\`, \`datetime\`, \`time\`,
33
+ \`email\`, \`url\`, \`icon\`, \`json\`, \`select\`, \`multiSelect\`, \`radio\`,
34
+ \`relationship\`, \`image\`, \`object\`, \`array\`, \`blocks\`, \`row\`, \`join\`
35
+
36
+ Do not invent field types that are not in this list.
37
+
38
+ Docs: https://docs.dyrected.com/docs/concepts/fields
39
+
40
+ ---
41
+
42
+ ## Never do these things
43
+
44
+ - **Never use \`client.collections\`** — the only correct entry point is \`client.collection('slug')\`
45
+ - **Never add custom auth or session middleware on admin routes** — Dyrected handles authentication internally; adding your own breaks the dashboard
46
+ - **Never define \`email\` or \`password\` fields on an \`auth: true\` collection** — they are injected automatically
47
+ - **Never delete a field from the config** — existing documents store data under that key; removing it orphans the data permanently
48
+ - **Never rename a field directly** — use \`renameTo\` for a lazy, zero-downtime migration (see Schema Evolution below)
49
+ - **Never add a new field to an existing collection without \`defaultValue\`** — existing documents need a safe read fallback
50
+ - **Never use function callbacks in \`admin.condition\`** if the project uses Dyrected Cloud — use Jexl string expressions instead; functions are stripped on cloud sync
51
+
52
+ ---
53
+
54
+ ## Schema evolution
55
+
56
+ Full guide: https://docs.dyrected.com/docs/concepts/schema
57
+
58
+ ### Renaming a field safely
59
+
60
+ \`\`\`ts
61
+ {
62
+ name: 'fullName', // new name
63
+ type: 'text',
64
+ renameTo: 'name', // old name — fallback on read until all docs are resaved
65
+ }
66
+ \`\`\`
67
+
68
+ Remove \`renameTo\` once all documents in production have been resaved under the new name.
69
+
70
+ ### Indexing a field for fast queries
71
+
72
+ \`\`\`ts
73
+ { name: 'slug', type: 'text', unique: true, promoted: true }
74
+ \`\`\`
75
+
76
+ After adding \`promoted: true\`, run \`npx @dyrected/cli sync:schema\` (Cloud) or restart the server (self-hosted).
77
+ Supported types: \`text\`, \`number\`, \`boolean\`, \`date\`, \`datetime\`.
78
+
79
+ ---
80
+
81
+ ## Zero-state resilience
82
+
83
+ Always write frontend data fetches with \`initialData\` fallbacks so the page renders when the backend
84
+ is unreachable or the collection is empty.
85
+
86
+ \`\`\`ts
87
+ const { docs } = await client.collection('posts').find({ initialData: [] })
88
+ const settings = await client.global('site-settings').get({ initialData: { siteName: 'My Site' } })
89
+ \`\`\`
90
+
91
+ ---
92
+
93
+ ## Relationships and depth
94
+
95
+ Docs: https://docs.dyrected.com/docs/concepts/relationships | https://docs.dyrected.com/docs/concepts/depth
96
+
97
+ A \`relationship\` field stores the ID of a document in another collection (the owning side).
98
+ A \`join\` field is a virtual reverse lookup — it stores nothing; it queries the other collection at read time.
99
+
100
+ \`\`\`ts
101
+ // relationship — stores an ID
102
+ { name: 'author', type: 'relationship', relationTo: 'users' }
103
+ { name: 'tags', type: 'relationship', relationTo: 'tags', hasMany: true }
104
+
105
+ // join — virtual reverse lookup (no storage)
106
+ {
107
+ name: 'posts',
108
+ type: 'join',
109
+ collection: 'posts', // the collection to look in
110
+ on: 'author', // the relationship field on that collection
111
+ limit: 20,
112
+ }
113
+ \`\`\`
114
+
115
+ Control how many levels of relationships are hydrated with \`depth\`:
116
+
117
+ - \`depth: 0\` — return IDs only
118
+ - \`depth: 1\` (default) — hydrate direct relationships; nested ones remain IDs
119
+ - \`depth: 2\` — hydrate two levels deep
120
+
121
+ Use \`depth: 0\` on list pages to keep payloads small. Use \`depth: 1\` (the default) when you need related field values.
122
+
123
+ ---
124
+
125
+ ## Auth collections
126
+
127
+ Docs: https://docs.dyrected.com/docs/concepts/auth-model
128
+
129
+ \`auth: true\` turns a collection into an authentication provider. Dyrected automatically injects
130
+ \`email\` (required, unique) and \`password\` (hashed, never returned in responses) — do not define them yourself.
131
+
132
+ \`\`\`ts
133
+ export const Users = defineCollection({
134
+ slug: 'users',
135
+ auth: true,
136
+ fields: [
137
+ { name: 'name', type: 'text' },
138
+ { name: 'role', type: 'select', options: ['member', 'editor', 'admin'] },
139
+ ],
140
+ })
141
+ \`\`\`
142
+
143
+ Generated endpoints (all under \`/dyrected/api/{slug}/\`): \`login\`, \`logout\`, \`me\`, \`refresh-token\`,
144
+ \`forgot-password\`, \`reset-password\`, \`invite\`, \`accept-invite\`, \`first-user\`, \`init\`.
145
+
146
+ The admin dashboard uses a **separate** auth collection by convention. Define it as:
147
+
148
+ \`\`\`ts
149
+ export const Admins = defineCollection({
150
+ slug: '__admins',
151
+ auth: true,
152
+ fields: [], // email + password are automatic
153
+ })
154
+ \`\`\`
155
+
156
+ Do not mix app user auth and admin auth in the same collection unless you explicitly want editors to log into both.
157
+
158
+ ---
159
+
160
+ ## Upload collections
161
+
162
+ Docs: https://docs.dyrected.com/docs/guides/file-uploads
163
+
164
+ \`upload: true\` turns a collection into a media library. Dyrected automatically injects \`filename\`,
165
+ \`mimeType\`, \`url\`, \`filesize\`, \`width\`, \`height\`, and \`blurhash\`.
166
+
167
+ \`\`\`ts
168
+ export const Media = defineCollection({
169
+ slug: 'media',
170
+ upload: {
171
+ allowedMimeTypes: ['image/*'],
172
+ maxFileSize: 5_000_000, // bytes
173
+ imageSizes: [
174
+ { name: 'thumbnail', width: 300, height: 300, crop: 'cover' },
175
+ { name: 'card', width: 800 },
176
+ ],
177
+ },
178
+ fields: [
179
+ { name: 'alt', type: 'text' },
180
+ ],
181
+ })
182
+ \`\`\`
183
+
184
+ ---
185
+
186
+ ## Dynamic options (select / multiSelect / radio)
187
+
188
+ Docs: https://docs.dyrected.com/docs/concepts/dynamic-options
189
+
190
+ Three ways to populate options — pick the right one:
191
+
192
+ | Approach | Where it runs | Use when |
193
+ |---|---|---|
194
+ | Static array | — | Fixed list known at build time |
195
+ | Server-side resolver (async function) | Server | DB queries, API keys, user-filtered lists, caching |
196
+ | \`admin.hooks.options\` (UI hook) | Browser | Instant dependent/cascading dropdowns |
197
+
198
+ \`\`\`ts
199
+ // Server-side resolver — runs on the server, never sent to the browser
200
+ {
201
+ name: 'category',
202
+ type: 'select',
203
+ options: async ({ db }) => {
204
+ const result = await db.find({ collection: 'categories' })
205
+ return result.docs.map(c => ({ label: c.name, value: c.id }))
206
+ },
207
+ }
208
+
209
+ // Client-side UI hook — runs in the browser, instant, no network round-trip
210
+ {
211
+ name: 'region',
212
+ type: 'select',
213
+ options: [],
214
+ admin: {
215
+ hooks: {
216
+ options: ({ siblingData }) =>
217
+ siblingData.country === 'us'
218
+ ? [{ label: 'California', value: 'CA' }, { label: 'New York', value: 'NY' }]
219
+ : [],
220
+ },
221
+ },
222
+ }
223
+ \`\`\`
224
+
225
+ ---
226
+
227
+ ## Conditional fields
228
+
229
+ Docs: https://docs.dyrected.com/docs/admin/configuration#condition--conditional-fields
230
+
231
+ Use \`admin.condition\` to show or hide a field based on sibling values. Use Jexl string expressions
232
+ (not functions) for cloud compatibility:
233
+
234
+ \`\`\`ts
235
+ // Only show 'scheduledAt' when status is 'scheduled'
236
+ {
237
+ name: 'scheduledAt',
238
+ type: 'datetime',
239
+ admin: {
240
+ condition: 'status == "scheduled"',
241
+ },
242
+ }
243
+
244
+ // Complex logic
245
+ {
246
+ name: 'salePrice',
247
+ type: 'number',
248
+ admin: {
249
+ condition: 'onSale == true && price > 0',
250
+ },
251
+ }
252
+ \`\`\`
253
+
254
+ ---
255
+
256
+ ## Custom field components and component slots
257
+
258
+ Docs: https://docs.dyrected.com/docs/admin/configuration
259
+
260
+ ### Custom field input
261
+
262
+ Replace any field's default input with a custom component. Declare a unique string key in the config:
263
+
264
+ \`\`\`ts
265
+ {
266
+ name: 'brandColor',
267
+ type: 'text',
268
+ admin: {
269
+ component: 'products.brandColor',
270
+ },
271
+ }
272
+ \`\`\`
273
+
274
+ Register the component in your \`<DyrectedAdmin>\` wrapper:
275
+
276
+ \`\`\`tsx
277
+ // React / Next.js
278
+ <DyrectedAdmin components={{ fields: { 'products.brandColor': BrandColorPicker } }} />
279
+
280
+ // Vue / Nuxt
281
+ <DyrectedAdmin :components="{ fields: { 'products.brandColor': BrandColorPicker } }" />
282
+ \`\`\`
283
+
284
+ The component receives \`value\`, \`onChange\`, \`field\`, \`path\`, \`disabled\`, and \`collection\` as props.
285
+
286
+ ### Component slots (dashboard and collection pages)
287
+
288
+ Inject custom panels into the dashboard or collection list pages by declaring slot keys in the config
289
+ and providing the matching components to \`<DyrectedAdmin>\`:
290
+
291
+ \`\`\`ts
292
+ // dyrected.config.ts
293
+ defineConfig({
294
+ admin: {
295
+ components: ['dashboard.analytics'], // slot key
296
+ },
297
+ ...
298
+ })
299
+ \`\`\`
300
+
301
+ \`\`\`tsx
302
+ <DyrectedAdmin
303
+ components={{
304
+ slots: { 'dashboard.analytics': AnalyticsDashboard }
305
+ }}
306
+ />
307
+ \`\`\`
308
+
309
+ ---
310
+
311
+ ## Intent → pattern
312
+
313
+ Translate plain-language goals to the correct Dyrected pattern. Make the decision yourself
314
+ — do not ask the developer to choose between Dyrected concepts.
315
+
316
+ | If the user wants to… | Use this pattern |
317
+ |--------------------------------------------------------------------|------------------|
318
+ | Auto-generate a slug from a title | \`beforeChange\` collection hook (server) + \`admin.hooks.onChange\` on the slug field (live preview) |
319
+ | Validate data before saving | \`beforeChange\` collection hook — throw to abort |
320
+ | Run logic after a document is saved | \`afterChange\` collection hook — never \`await\` slow ops inline; use \`.catch()\` |
321
+ | Send a webhook or email when content changes | \`afterChange\` collection hook |
322
+ | Revalidate a Next.js / Nuxt page after a save | \`afterChange\` collection hook posting to the revalidation endpoint |
323
+ | Only admins can see a field | Field-level \`access: { read: ({ user }) => user?.roles?.includes('admin') ?? false }\` |
324
+ | Different roles can edit different fields | Field-level \`access.update\` |
325
+ | Restrict who can create / edit / delete | Collection-level \`access\` config |
326
+ | Track who created or changed a document | \`audit: true\` on the collection |
327
+ | Content approval / draft-publish flow | \`workflow: publishingWorkflow()\` on the collection |
328
+ | Guard a workflow transition | \`workflow.hooks.beforeTransition\` — throw to cancel |
329
+ | Notify someone after a workflow state change | \`workflow.hooks.afterTransition\` |
330
+ | Different content per site (multi-tenant) | \`siteId\` on the collection + \`beforeRead\` hook to scope queries |
331
+ | Share content across all sites | \`shared: true\` on the collection |
332
+ | Show a dependent dropdown based on another field (instant) | \`admin.hooks.options\` on the dependent select field |
333
+ | Populate a dropdown from a database query or external API | Server-side \`options\` async resolver function |
334
+ | Show a live computed value in the admin form | \`admin.hooks.onChange\` on the output field |
335
+ | Show or hide a field based on another field's value | \`admin.condition\` with a Jexl string expression |
336
+ | Query or sort a field efficiently at scale | \`promoted: true\` on the field, then run \`sync:schema\` |
337
+ | One fixed set of content (site name, logo, tagline…) | \`defineGlobal\` |
338
+ | Multiple entries the client can add or remove | \`defineCollection\` |
339
+ | Flexible page layouts (hero, cards, testimonials…) | \`blocks\` field — one block definition per layout type |
340
+ | Seed default content on first run | \`initialData\` on the collection or global |
341
+ | Reference a document in another collection | \`relationship\` field with \`relationTo: 'slug'\` |
342
+ | Store multiple references (tags, categories…) | \`relationship\` field with \`hasMany: true\` |
343
+ | Show all related documents on the other side (reverse lookup) | \`join\` field with \`collection\` and \`on\` |
344
+ | Fetch a document with its related data populated | Use \`depth: 1\` (default) on the query |
345
+ | Allow users to log in to the app | \`auth: true\` on a collection |
346
+ | Separate admin login from app user login | Define a \`__admins\` collection with \`auth: true\` |
347
+ | Allow file / image uploads | \`upload: true\` (or an upload config object) on a collection |
348
+ | Replace the default input for a field with a custom component | \`admin.component\` string key + register in \`<DyrectedAdmin>\` |
349
+ | Inject a custom panel into the dashboard or a collection page | Component slots in \`admin.components\` config + register in \`<DyrectedAdmin>\` |
350
+
351
+ ---
352
+
353
+ ## Canonical recipe: generate a slug from a title
354
+
355
+ When the developer asks for automatic URLs, friendly URLs, or a slug derived from a title, implement both layers below. The server hook guarantees API writes are correct; the Admin hook gives editors immediate feedback.
356
+
357
+ \`\`\`ts
358
+ const toSlug = (value: unknown) =>
359
+ String(value ?? '')
360
+ .toLowerCase()
361
+ .trim()
362
+ .replace(/[^a-z0-9]+/g, '-')
363
+ .replace(/(^-|-$)/g, '')
364
+
365
+ export const Posts = defineCollection({
366
+ slug: 'posts',
367
+ hooks: {
368
+ beforeChange: [({ data, operation }) => {
369
+ if (operation === 'create' || data.title !== undefined) {
370
+ return { ...data, slug: toSlug(data.title) }
371
+ }
372
+ return data
373
+ }],
374
+ },
375
+ fields: [
376
+ { name: 'title', type: 'text', required: true },
377
+ {
378
+ name: 'slug', type: 'text', required: true, unique: true, promoted: true,
379
+ admin: { hooks: { onChange: ({ siblingData }) => toSlug(siblingData.title) } },
380
+ },
381
+ ],
382
+ })
383
+ \`\`\`
384
+
385
+ Do not implement only the browser hook: API-created documents would bypass it. Do not implement only the server hook when editors need to see the resulting URL while typing.
386
+
387
+ ---
388
+
389
+ ## Access control
390
+
391
+ Docs: https://docs.dyrected.com/docs/concepts/access-control
392
+
393
+ - Create the smallest set of permissions needed for the approved use case
394
+ - Editors may access only the content they are approved to edit
395
+ - Do not grant delete or publish access unless explicitly required
396
+ - Enforce access in the server-side config — not only by hiding controls in the Admin UI
397
+
398
+ ---
399
+
400
+ ## Working approach
401
+
402
+ - Work in small verified batches — no more than three related content areas at a time
403
+ - Verify lint, types, and build pass before moving to the next batch
404
+ - Fix a failing batch before starting new content types
405
+ - Ask plain-language questions when you need clarification:
406
+ e.g. "Should the client be able to add more items, or only edit the ones already there?"
407
+ Never ask: "Should this be a collection or a global?"
408
+ - Use the intent → pattern table above to make technical decisions without asking the developer
409
+ `;
410
+ }
411
+ //# sourceMappingURL=ai-rules-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-rules-template.js","sourceRoot":"","sources":["../../src/utils/ai-rules-template.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY;IAC1B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuZR,CAAC;AACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function buildDbConfig(db: string): string;
2
+ export declare function buildStorageConfig(storage: string): string;
3
+ export declare function buildEnvTemplate(db: string, storage: string, framework: string): string;
4
+ export declare function buildViteEnvTemplate(): string;
5
+ //# sourceMappingURL=config-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-templates.d.ts","sourceRoot":"","sources":["../../src/utils/config-templates.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAahD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAa1D;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,MAAM,CA8CR;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAU7C"}