@dyrected/core 0.0.1 → 1.0.1
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/LICENSE.md +50 -0
- package/README.md +35 -1
- package/dist/chunk-FURYJ254.mjs +33 -0
- package/dist/chunk-GM4WW6IE.js +66 -0
- package/dist/index-D38_E0gf.d.cts +336 -0
- package/dist/index-D38_E0gf.d.ts +336 -0
- package/dist/index-RylhgOwj.d.cts +340 -0
- package/dist/index-RylhgOwj.d.ts +340 -0
- package/dist/index.cjs +413 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.mts +163 -11
- package/dist/index.d.ts +15 -247
- package/dist/index.js +298 -366
- package/dist/index.mjs +1692 -77
- package/dist/server.cjs +1827 -0
- package/dist/server.d.cts +243 -0
- package/dist/server.d.ts +243 -0
- package/dist/server.js +1725 -0
- package/dist/token-7QG5DBME.mjs +10 -0
- package/package.json +32 -3
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
defineCollection: () => defineCollection,
|
|
24
|
+
defineConfig: () => defineConfig,
|
|
25
|
+
defineGlobal: () => defineGlobal,
|
|
26
|
+
generateAIPrompt: () => generateAIPrompt,
|
|
27
|
+
normalizeConfig: () => normalizeConfig
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/utils/setup-prompt.ts
|
|
32
|
+
function generateAIPrompt(activeTab, config) {
|
|
33
|
+
const frameworkLabel = activeTab === "next" ? "Next.js" : activeTab === "nuxt" ? "Nuxt" : activeTab.charAt(0).toUpperCase() + activeTab.slice(1);
|
|
34
|
+
const backendPkg = activeTab === "nuxt" ? "@dyrected/nuxt" : "@dyrected/next";
|
|
35
|
+
const isSelfHosted = config.isSelfHosted ?? (config.baseUrl?.includes("localhost") || !config.apiKey);
|
|
36
|
+
const baseIntro = isSelfHosted ? `You are a Senior Content Architect. Your mission is to integrate Dyrected CMS into a ${config.siteName || "new"} project using ${frameworkLabel}. This is a SELF-HOSTED installation.
|
|
37
|
+
The backend is already configured via ${backendPkg}.
|
|
38
|
+
Your priority is DATA PRESERVATION and creating a CMS that empowers marketing teams.` : `You are a Senior Content Architect. Your mission is to integrate Dyrected CMS into a ${config.siteName || "new"} project using ${frameworkLabel}. Complete the entire setup automatically, including embedding the Admin UI and syncing the schema. Your priority is DATA PRESERVATION and marketing independence.`;
|
|
39
|
+
const credentials = `
|
|
40
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
41
|
+
SITE CREDENTIALS
|
|
42
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
43
|
+
${config.siteName ? `Site Name : ${config.siteName}
|
|
44
|
+
` : ""}${isSelfHosted ? "" : `Site ID : ${config.siteId}
|
|
45
|
+
API Key : ${config.apiKey}
|
|
46
|
+
`}API URL : ${config.baseUrl || "https://api.dyrected.cloud"}
|
|
47
|
+
`;
|
|
48
|
+
const importantNotes = `
|
|
49
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
50
|
+
IMPORTANT NOTES \u2014 READ BEFORE ACTING
|
|
51
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
52
|
+
- \`client.collection(slug)\` is the primary API entrypoint. Do NOT use \`client.collections\`.
|
|
53
|
+
- Blocks are stored as \`[{ blockType: '<slug>', ...fields }]\` \u2014 always switch on \`blockType\` when rendering.
|
|
54
|
+
- MARKETING INDEPENDENCE: Always use a dynamic \`pages\` collection with a catch-all route. Marketing should create pages without a developer. (Exceptions: Special pages like Contact or those with complex forms may use static routes).
|
|
55
|
+
- AUTO-SEEDING: Use \`initialData\` in all data fetches to ensure the site is never empty on first load.
|
|
56
|
+
- Globals use \`client.global(slug).get()\` and \`client.global(slug).update(data)\`.
|
|
57
|
+
- Relationship fields are populated to the specified \`depth\` (default: 1). Set \`depth: 0\` for IDs only.
|
|
58
|
+
- SYNC SCHEMA: After creating the content model, you MUST execute \`npx @dyrected/cli sync:schema\` immediately. This is non-negotiable for a working setup.
|
|
59
|
+
- EMBED ADMIN: You MUST create a route (e.g. \`/admin\`) and mount the Admin UI components described below.
|
|
60
|
+
- ALL-IN-ONE: Your goal is to give the user a project that is READY TO SHIP. Do the setup, do the embedding, and do the sync in one go.
|
|
61
|
+
`;
|
|
62
|
+
const strategy = `
|
|
63
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
64
|
+
PHASE 0 \u2014 DATA PRESERVATION & STRATEGY
|
|
65
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
66
|
+
1. BACKUP: If an existing site is provided, you MUST extract all current content (text, assets, structure) and save it into a \`migration/\` folder as structured .md files BEFORE modifying any code. Never lose data.
|
|
67
|
+
2. DISCOVERY: If NO existing site is provided, STOP and ask the user:
|
|
68
|
+
- "What are your core content types (e.g. Services, Team, Blog)?"
|
|
69
|
+
- "How do you want your marketing team to manage the page layouts?"
|
|
70
|
+
3. ARCHITECTURAL CREATIVITY: Design the CMS for longevity. Use \`blocks\` for flexible page builders, \`globals\` for site settings, and \`collections\` for repeated content.
|
|
71
|
+
|
|
72
|
+
STEP 1 \u2014 CONTENT MODEL (dyrected.config.ts)
|
|
73
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
74
|
+
Use \`defineCollection\`, \`defineGlobal\`, and \`defineConfig\` from '@dyrected/core'.
|
|
75
|
+
|
|
76
|
+
SUPPORTED FIELD TYPES:
|
|
77
|
+
Primitive : text | textarea | richText | number | boolean | date | email | url | json
|
|
78
|
+
Choice : select | multiSelect (requires \`options: [{ label, value }]\`)
|
|
79
|
+
Structural : array | object (requires nested \`fields: [...]\`)
|
|
80
|
+
Relation : relationship (requires \`collection: '<slug>'\`)
|
|
81
|
+
Media : image (use a relationship to an upload collection)
|
|
82
|
+
Blocks : blocks (requires \`blocks: [{ slug, labels, fields }]\`)
|
|
83
|
+
|
|
84
|
+
COLLECTION OPTIONS:
|
|
85
|
+
\`upload: true\` \u2014 turns this collection into a media library (file uploads)
|
|
86
|
+
\`auth: true\` \u2014 adds login/register/me endpoints; password field is auto-added
|
|
87
|
+
\`admin.group\` \u2014 groups this collection under a sidebar heading
|
|
88
|
+
\`admin.useAsTitle\` \u2014 field to use as the display title in the admin list view
|
|
89
|
+
\`admin.hidden\` \u2014 hide from sidebar (useful for internal/system collections)
|
|
90
|
+
|
|
91
|
+
FIELD OPTIONS:
|
|
92
|
+
\`required\` \u2014 validation
|
|
93
|
+
\`unique\` \u2014 database-level uniqueness
|
|
94
|
+
\`defaultValue\` \u2014 fallback value
|
|
95
|
+
\`admin.condition\` \u2014 "expression" \u2014 Jexl string expression to show/hide field (e.g. "status == \\"published\\"")
|
|
96
|
+
\`admin.layout\` \u2014 "radio" | "dropdown" \u2014 Visual layout for select/multiSelect
|
|
97
|
+
\`admin.direction\` \u2014 "vertical" | "horizontal" \u2014 Layout direction for radio groups
|
|
98
|
+
\`admin.readOnly\` \u2014 display-only in the form
|
|
99
|
+
\`admin.hidden\` \u2014 completely hidden from editor UI
|
|
100
|
+
\`access.read\` \u2014 ({ user }) => boolean \u2014 field-level read access
|
|
101
|
+
\`access.update\` \u2014 ({ user }) => boolean \u2014 field-level write access
|
|
102
|
+
\`hooks.beforeChange\` \u2014 [async (value) => newValue] \u2014 transform value before save
|
|
103
|
+
\`hooks.afterRead\` \u2014 [async (value) => newValue] \u2014 transform value after read
|
|
104
|
+
|
|
105
|
+
BLOCKS EXPLAINED:
|
|
106
|
+
A \`blocks\` field stores an ordered array of typed content blocks.
|
|
107
|
+
Each block has a \`blockType\` discriminator and its own set of fields.
|
|
108
|
+
The admin UI renders a drag-and-drop block editor automatically.
|
|
109
|
+
On the frontend, iterate the array and switch on \`block.blockType\`.
|
|
110
|
+
|
|
111
|
+
COMPLETE EXAMPLE:
|
|
112
|
+
\`\`\`typescript
|
|
113
|
+
import { defineCollection, defineGlobal, defineConfig } from '@dyrected/core'
|
|
114
|
+
|
|
115
|
+
// \u2500\u2500 Media \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
116
|
+
const media = defineCollection({
|
|
117
|
+
slug: 'media',
|
|
118
|
+
labels: { singular: 'Media Item', plural: 'Media' },
|
|
119
|
+
upload: true,
|
|
120
|
+
fields: [
|
|
121
|
+
{ name: 'alt', type: 'text', label: 'Alt Text' },
|
|
122
|
+
{ name: 'caption', type: 'textarea', label: 'Caption' },
|
|
123
|
+
],
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// \u2500\u2500 Authentication collection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
127
|
+
const customers = defineCollection({
|
|
128
|
+
slug: 'customers',
|
|
129
|
+
labels: { singular: 'Customer', plural: 'Customers' },
|
|
130
|
+
auth: true, // adds /customers/login, /customers/me, etc.
|
|
131
|
+
admin: { group: 'Membership' },
|
|
132
|
+
fields: [
|
|
133
|
+
{ name: 'name', type: 'text', required: true },
|
|
134
|
+
{ name: 'email', type: 'email', required: true, unique: true },
|
|
135
|
+
// 'password' is auto-added when auth: true
|
|
136
|
+
{ name: 'avatar', type: 'relationship', relationTo: 'media' },
|
|
137
|
+
{ name: 'role', type: 'select', admin: { layout: 'radio' }, options: [
|
|
138
|
+
{ label: 'Member', value: 'member' },
|
|
139
|
+
{ label: 'VIP', value: 'vip' },
|
|
140
|
+
]},
|
|
141
|
+
],
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// \u2500\u2500 Pages with blocks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
145
|
+
const pages = defineCollection({
|
|
146
|
+
slug: 'pages',
|
|
147
|
+
labels: { singular: 'Page', plural: 'Pages' },
|
|
148
|
+
admin: { useAsTitle: 'title', group: 'Content' },
|
|
149
|
+
fields: [
|
|
150
|
+
{ name: 'title', type: 'text', required: true },
|
|
151
|
+
{ name: 'slug', type: 'text', required: true, unique: true },
|
|
152
|
+
{ name: 'seo', type: 'object', fields: [
|
|
153
|
+
{ name: 'metaTitle', type: 'text' },
|
|
154
|
+
{ name: 'metaDescription', type: 'textarea' },
|
|
155
|
+
{ name: 'ogImage', type: 'relationship', relationTo: 'media' },
|
|
156
|
+
]},
|
|
157
|
+
{
|
|
158
|
+
name: 'layout',
|
|
159
|
+
type: 'blocks',
|
|
160
|
+
label: 'Page Layout',
|
|
161
|
+
blocks: [
|
|
162
|
+
{
|
|
163
|
+
slug: 'hero',
|
|
164
|
+
labels: { singular: 'Hero', plural: 'Heroes' },
|
|
165
|
+
fields: [
|
|
166
|
+
{ name: 'heading', type: 'text', required: true },
|
|
167
|
+
{ name: 'subheading', type: 'textarea' },
|
|
168
|
+
{ name: 'image', type: 'relationship', relationTo: 'media' },
|
|
169
|
+
{ name: 'ctaLabel', type: 'text' },
|
|
170
|
+
{ name: 'ctaLink', type: 'url' },
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
slug: 'richContent',
|
|
175
|
+
labels: { singular: 'Rich Content', plural: 'Rich Content Blocks' },
|
|
176
|
+
fields: [
|
|
177
|
+
{ name: 'content', type: 'richText', required: true },
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
export default defineConfig({
|
|
186
|
+
collections: [media, customers, pages],
|
|
187
|
+
admin: {
|
|
188
|
+
branding: {
|
|
189
|
+
primaryColor: '#4f46e5',
|
|
190
|
+
logo: '/logo.png',
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
\`\`\`
|
|
195
|
+
|
|
196
|
+
${isSelfHosted ? "" : `
|
|
197
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
198
|
+
STEP 2 \u2014 CHOOSE YOUR MODE
|
|
199
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
200
|
+
The developer can choose between two modes:
|
|
201
|
+
|
|
202
|
+
1. CLOUD MODE (Managed)
|
|
203
|
+
- Use the SITE CREDENTIALS above.
|
|
204
|
+
- Point baseUrl to ${config.baseUrl}.
|
|
205
|
+
- Content is stored in Dyrected Cloud.
|
|
206
|
+
|
|
207
|
+
2. SELF-HOSTED MODE (Core)
|
|
208
|
+
- Do NOT use apiKey/siteId (unless for proxying).
|
|
209
|
+
- Use a database adapter like \`SqliteAdapter\` from '@dyrected/db-sqlite'.
|
|
210
|
+
- Content is stored locally in the developer's project.
|
|
211
|
+
`}
|
|
212
|
+
|
|
213
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
214
|
+
STEP ${isSelfHosted ? "2" : "3"} \u2014 MOUNTING THE ADMIN UI
|
|
215
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
216
|
+
The Admin UI can be mounted on any path (e.g. /cms-admin).
|
|
217
|
+
Pass the \`basename\` prop to the \`<AdminUI />\` component to match your route.
|
|
218
|
+
|
|
219
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
220
|
+
STEP ${isSelfHosted ? "3" : "4"} \u2014 FRONTEND IMPLEMENTATION
|
|
221
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
222
|
+
`;
|
|
223
|
+
const frameworks = {
|
|
224
|
+
next: `Install \`@dyrected/sdk\` (or \`@dyrected/next\` if you want Next.js server helpers).
|
|
225
|
+
|
|
226
|
+
SDK CLIENT SETUP (\`lib/dyrected.ts\`):
|
|
227
|
+
\`\`\`ts
|
|
228
|
+
import { createClient } from '@dyrected/sdk'
|
|
229
|
+
|
|
230
|
+
export const dyrected = createClient({
|
|
231
|
+
baseUrl: '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
232
|
+
apiKey: '${config.apiKey}',
|
|
233
|
+
siteId: '${config.siteId}',`}
|
|
234
|
+
})
|
|
235
|
+
\`\`\``,
|
|
236
|
+
nuxt: `Install \`@dyrected/nuxt\` and add it to \`nuxt.config.ts\`:
|
|
237
|
+
\`\`\`ts
|
|
238
|
+
export default defineNuxtConfig({
|
|
239
|
+
modules: ['@dyrected/nuxt'],
|
|
240
|
+
dyrected: {
|
|
241
|
+
baseUrl: '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
242
|
+
apiKey: '${config.apiKey}',
|
|
243
|
+
siteId: '${config.siteId}',`}
|
|
244
|
+
},
|
|
245
|
+
})
|
|
246
|
+
\`\`\`
|
|
247
|
+
|
|
248
|
+
MOUNTING THE ADMIN DASHBOARD (\`pages/cms-admin.vue\`):
|
|
249
|
+
\`\`\`vue
|
|
250
|
+
<script setup lang="ts">
|
|
251
|
+
definePageMeta({ layout: false })
|
|
252
|
+
</script>
|
|
253
|
+
|
|
254
|
+
<template>
|
|
255
|
+
<ClientOnly>
|
|
256
|
+
<DyrectedAdmin basename="/cms-admin" />
|
|
257
|
+
</ClientOnly>
|
|
258
|
+
</template>
|
|
259
|
+
\`\`\`
|
|
260
|
+
`,
|
|
261
|
+
react: `Install \`@dyrected/sdk\`:
|
|
262
|
+
|
|
263
|
+
CLIENT SETUP (\`lib/dyrected.ts\`):
|
|
264
|
+
\`\`\`ts
|
|
265
|
+
import { createClient } from '@dyrected/sdk'
|
|
266
|
+
|
|
267
|
+
export const dyrected = createClient({
|
|
268
|
+
baseUrl: '${config.baseUrl || "https://api.dyrected.cloud"}',${isSelfHosted ? "" : `
|
|
269
|
+
apiKey: '${config.apiKey}',
|
|
270
|
+
siteId: '${config.siteId}',`}
|
|
271
|
+
})
|
|
272
|
+
\`\`\`
|
|
273
|
+
|
|
274
|
+
MOUNTING THE ADMIN DASHBOARD (\`pages/admin.tsx\`):
|
|
275
|
+
\`\`\`tsx
|
|
276
|
+
import { AdminUI } from '@dyrected/admin'
|
|
277
|
+
import '@dyrected/admin/styles'
|
|
278
|
+
|
|
279
|
+
export default function AdminPage() {
|
|
280
|
+
return (
|
|
281
|
+
<div style={{ height: '100vh' }}>
|
|
282
|
+
<AdminUI
|
|
283
|
+
apiKey='${config.apiKey}'
|
|
284
|
+
siteId='${config.siteId}'
|
|
285
|
+
baseUrl='${config.baseUrl || "https://api.dyrected.cloud"}'
|
|
286
|
+
/>
|
|
287
|
+
</div>
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
\`\`\`
|
|
291
|
+
`,
|
|
292
|
+
vue: `Install \`@dyrected/sdk\`:
|
|
293
|
+
|
|
294
|
+
CLIENT SETUP (\`lib/dyrected.ts\`):
|
|
295
|
+
\`\`\`ts
|
|
296
|
+
import { createClient } from '@dyrected/sdk'
|
|
297
|
+
|
|
298
|
+
export const dyrected = createClient({
|
|
299
|
+
baseUrl: '${config.baseUrl || "https://api.dyrected.cloud"}',${isSelfHosted ? "" : `
|
|
300
|
+
apiKey: '${config.apiKey}',
|
|
301
|
+
siteId: '${config.siteId}',`}
|
|
302
|
+
})
|
|
303
|
+
\`\`\`
|
|
304
|
+
|
|
305
|
+
MOUNTING THE ADMIN DASHBOARD (\`pages/admin.vue\`):
|
|
306
|
+
\`\`\`vue
|
|
307
|
+
<template>
|
|
308
|
+
<div ref="container" style="height: 100vh" />
|
|
309
|
+
</template>
|
|
310
|
+
|
|
311
|
+
<script setup>
|
|
312
|
+
import { ref, onMounted } from 'vue'
|
|
313
|
+
import { renderAdminUI } from '@dyrected/admin'
|
|
314
|
+
import '@dyrected/admin/styles'
|
|
315
|
+
|
|
316
|
+
const container = ref(null)
|
|
317
|
+
onMounted(() => {
|
|
318
|
+
renderAdminUI(container.value, {
|
|
319
|
+
apiKey: '${config.apiKey}',
|
|
320
|
+
siteId: '${config.siteId}',
|
|
321
|
+
baseUrl: '${config.baseUrl || "https://api.dyrected.cloud"}'
|
|
322
|
+
})
|
|
323
|
+
})
|
|
324
|
+
</script>
|
|
325
|
+
\`\`\`
|
|
326
|
+
`
|
|
327
|
+
};
|
|
328
|
+
return baseIntro + credentials + importantNotes + strategy + (frameworks[activeTab] || frameworks.next) + `
|
|
329
|
+
|
|
330
|
+
API Reference: ${config.baseUrl || "http://localhost:3000"}/api/docs`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/utils/config.ts
|
|
334
|
+
var AUDIT_COLLECTION_SLUG = "__audit";
|
|
335
|
+
var SYSTEM_FIELDS = [
|
|
336
|
+
{
|
|
337
|
+
name: "createdAt",
|
|
338
|
+
type: "date",
|
|
339
|
+
label: "Created At",
|
|
340
|
+
admin: { readOnly: true, hidden: true }
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "updatedAt",
|
|
344
|
+
type: "date",
|
|
345
|
+
label: "Updated At",
|
|
346
|
+
admin: { readOnly: true, hidden: true }
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: "createdBy",
|
|
350
|
+
type: "text",
|
|
351
|
+
label: "Created By",
|
|
352
|
+
admin: { readOnly: true, hidden: true }
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: "updatedBy",
|
|
356
|
+
type: "text",
|
|
357
|
+
label: "Updated By",
|
|
358
|
+
admin: { readOnly: true, hidden: true }
|
|
359
|
+
}
|
|
360
|
+
];
|
|
361
|
+
var AUDIT_COLLECTION = {
|
|
362
|
+
slug: AUDIT_COLLECTION_SLUG,
|
|
363
|
+
labels: { singular: "Audit Log", plural: "Audit Logs" },
|
|
364
|
+
fields: [
|
|
365
|
+
{ name: "collection", type: "text", label: "Collection", required: true },
|
|
366
|
+
{ name: "documentId", type: "text", label: "Document ID" },
|
|
367
|
+
{ name: "operation", type: "select", label: "Operation", options: ["create", "update", "delete"], required: true },
|
|
368
|
+
{ name: "user", type: "text", label: "User ID" },
|
|
369
|
+
{ name: "timestamp", type: "date", label: "Timestamp", required: true },
|
|
370
|
+
{ name: "changes", type: "json", label: "Changes" }
|
|
371
|
+
],
|
|
372
|
+
admin: { hidden: true }
|
|
373
|
+
};
|
|
374
|
+
function normalizeConfig(config) {
|
|
375
|
+
const needsAudit = config.collections.some((col) => col.audit);
|
|
376
|
+
const normalizedCollections = config.collections.map((col) => {
|
|
377
|
+
const existingFieldNames = new Set(col.fields.map((f) => f.name));
|
|
378
|
+
const fieldsToInject = SYSTEM_FIELDS.filter((f) => !existingFieldNames.has(f.name));
|
|
379
|
+
return {
|
|
380
|
+
...col,
|
|
381
|
+
fields: [...col.fields, ...fieldsToInject]
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
const hasAuditCollection = normalizedCollections.some(
|
|
385
|
+
(col) => col.slug === AUDIT_COLLECTION_SLUG
|
|
386
|
+
);
|
|
387
|
+
return {
|
|
388
|
+
...config,
|
|
389
|
+
collections: [
|
|
390
|
+
...normalizedCollections,
|
|
391
|
+
...needsAudit && !hasAuditCollection ? [AUDIT_COLLECTION] : []
|
|
392
|
+
]
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/index.ts
|
|
397
|
+
function defineCollection(config) {
|
|
398
|
+
return config;
|
|
399
|
+
}
|
|
400
|
+
function defineGlobal(config) {
|
|
401
|
+
return config;
|
|
402
|
+
}
|
|
403
|
+
function defineConfig(config) {
|
|
404
|
+
return config;
|
|
405
|
+
}
|
|
406
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
407
|
+
0 && (module.exports = {
|
|
408
|
+
defineCollection,
|
|
409
|
+
defineConfig,
|
|
410
|
+
defineGlobal,
|
|
411
|
+
generateAIPrompt,
|
|
412
|
+
normalizeConfig
|
|
413
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './index-RylhgOwj.cjs';
|
|
2
|
+
export { A as AccessFunction, a as AdminConfig, B as Block, b as DatabaseAdapter, F as Field, c as FieldHook, d as FieldType, e as FileData, H as HookFunction, I as ImageService, P as PaginatedResult, S as StorageAdapter, U as UploadConfig } from './index-RylhgOwj.cjs';
|
|
3
|
+
|
|
4
|
+
interface SetupPromptConfig {
|
|
5
|
+
siteName?: string;
|
|
6
|
+
siteId?: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
isSelfHosted?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare function generateAIPrompt(activeTab: "next" | "nuxt" | "react" | "vue", config: SetupPromptConfig): string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Normalizes the Dyrected configuration by injecting system fields
|
|
15
|
+
* (createdAt, updatedAt, createdBy, updatedBy) into every collection and
|
|
16
|
+
* registering the __audit collection if any collection has audit: true.
|
|
17
|
+
*/
|
|
18
|
+
declare function normalizeConfig(config: DyrectedConfig): DyrectedConfig;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Define a collection configuration with full type safety.
|
|
22
|
+
*/
|
|
23
|
+
declare function defineCollection(config: CollectionConfig): CollectionConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Define a global configuration with full type safety.
|
|
26
|
+
*/
|
|
27
|
+
declare function defineGlobal(config: GlobalConfig): GlobalConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Define the main Dyrected configuration.
|
|
30
|
+
*/
|
|
31
|
+
declare function defineConfig(config: DyrectedConfig): DyrectedConfig;
|
|
32
|
+
|
|
33
|
+
export { CollectionConfig, DyrectedConfig, GlobalConfig, type SetupPromptConfig, defineCollection, defineConfig, defineGlobal, generateAIPrompt, normalizeConfig };
|