@dyrected/core 2.5.13 → 2.5.16

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 (48) hide show
  1. package/dist/app-B2tg7Djj.d.cts +1575 -0
  2. package/dist/app-B2tg7Djj.d.ts +1575 -0
  3. package/dist/app-Bh4_Opv0.d.cts +1522 -0
  4. package/dist/app-Bh4_Opv0.d.ts +1522 -0
  5. package/dist/app-Bv9gaDAN.d.cts +561 -0
  6. package/dist/app-Bv9gaDAN.d.ts +561 -0
  7. package/dist/app-BvG3bRc8.d.cts +419 -0
  8. package/dist/app-BvG3bRc8.d.ts +419 -0
  9. package/dist/app-C3B9N1KR.d.cts +1522 -0
  10. package/dist/app-C3B9N1KR.d.ts +1522 -0
  11. package/dist/app-DDJJa0ep.d.cts +1621 -0
  12. package/dist/app-DDJJa0ep.d.ts +1621 -0
  13. package/dist/app-DO1s9YW1.d.cts +1621 -0
  14. package/dist/app-DO1s9YW1.d.ts +1621 -0
  15. package/dist/app-DTP3-9PJ.d.cts +561 -0
  16. package/dist/app-DTP3-9PJ.d.ts +561 -0
  17. package/dist/app-DbKDGYTI.d.cts +566 -0
  18. package/dist/app-DbKDGYTI.d.ts +566 -0
  19. package/dist/app-DqRO-CMi.d.cts +1457 -0
  20. package/dist/app-DqRO-CMi.d.ts +1457 -0
  21. package/dist/app-DvaFpOtj.d.cts +398 -0
  22. package/dist/app-DvaFpOtj.d.ts +398 -0
  23. package/dist/app-FGzip4XM.d.cts +1563 -0
  24. package/dist/app-FGzip4XM.d.ts +1563 -0
  25. package/dist/app-T0alZAE0.d.cts +383 -0
  26. package/dist/app-T0alZAE0.d.ts +383 -0
  27. package/dist/app-oQt5-9MU.d.cts +1560 -0
  28. package/dist/app-oQt5-9MU.d.ts +1560 -0
  29. package/dist/app-rZj1VFer.d.cts +1621 -0
  30. package/dist/app-rZj1VFer.d.ts +1621 -0
  31. package/dist/app-wo82JRHl.d.cts +445 -0
  32. package/dist/app-wo82JRHl.d.ts +445 -0
  33. package/dist/chunk-23URSKPI.js +2371 -0
  34. package/dist/chunk-2JMA3M5S.js +2475 -0
  35. package/dist/chunk-3FZEUK36.js +2470 -0
  36. package/dist/chunk-DOJHZ7XN.js +2394 -0
  37. package/dist/chunk-PKNFV7KE.js +2469 -0
  38. package/dist/chunk-UBTRANFX.js +2476 -0
  39. package/dist/chunk-W6KURRMW.js +2471 -0
  40. package/dist/index.cjs +457 -48
  41. package/dist/index.d.cts +117 -8
  42. package/dist/index.d.ts +117 -8
  43. package/dist/index.js +9 -3
  44. package/dist/server.cjs +449 -46
  45. package/dist/server.d.cts +57 -15
  46. package/dist/server.d.ts +57 -15
  47. package/dist/server.js +1 -1
  48. package/package.json +1 -1
@@ -0,0 +1,561 @@
1
+ import * as hono_types from 'hono/types';
2
+ import { Hono } from 'hono';
3
+
4
+ type FieldType = "text" | "textarea" | "richText" | "number" | "boolean" | "date" | "select" | "multiSelect" | "relationship" | "array" | "object" | "json" | "blocks" | "image" | "email" | "url" | "icon" | "join" | "row";
5
+ type DynamicOptionItem = string | {
6
+ label: string;
7
+ value: any;
8
+ };
9
+ /**
10
+ * Context passed to a dynamic options resolver at request time.
11
+ * All properties are optional so external-API resolvers that need none of
12
+ * them (`async () => fetch(...)`) still compile without errors.
13
+ */
14
+ interface DynamicOptionsResolverArgs {
15
+ /** The configured database adapter — available for resolvers that query internal collections. */
16
+ db?: DatabaseAdapter;
17
+ /** The currently authenticated user (or undefined for unauthenticated requests). */
18
+ user?: any;
19
+ /**
20
+ * The HTTP request context. Sibling field values selected in the Admin UI
21
+ * are forwarded as query parameters and are accessible via `req.query`.
22
+ *
23
+ * @example
24
+ * // Cascading dropdown: ?country=ca forwarded by the Admin UI
25
+ * options: async ({ req }) => {
26
+ * const country = req.query.country ?? 'us';
27
+ * ...
28
+ * }
29
+ */
30
+ req?: {
31
+ query: Record<string, string>;
32
+ headers: Record<string, string>;
33
+ raw?: Request;
34
+ };
35
+ }
36
+ type DynamicOptionsResolver = (args: DynamicOptionsResolverArgs) => Promise<DynamicOptionItem[]> | DynamicOptionItem[];
37
+ interface DynamicOptionsConfig {
38
+ resolve: DynamicOptionsResolver;
39
+ cacheTTL?: number;
40
+ }
41
+ interface Block {
42
+ slug: string;
43
+ labels?: {
44
+ singular: string;
45
+ plural: string;
46
+ };
47
+ fields: Field[];
48
+ }
49
+ interface Field {
50
+ name?: string;
51
+ type: FieldType;
52
+ label?: string;
53
+ required?: boolean;
54
+ unique?: boolean;
55
+ defaultValue?: any;
56
+ /**
57
+ * Defines the available choices for `select` and `multiSelect` fields.
58
+ *
59
+ * **Three ways to provide options — pick the one that fits your data source:**
60
+ *
61
+ * ---
62
+ *
63
+ * ### 1. Static array (simplest)
64
+ * Use when the list is fixed and known at build time.
65
+ * ```ts
66
+ * options: [
67
+ * { label: 'Draft', value: 'draft' },
68
+ * { label: 'Published', value: 'published' },
69
+ * ]
70
+ * ```
71
+ *
72
+ * ---
73
+ *
74
+ * ### 2. Async server-side resolver (for DB queries or secret API keys)
75
+ * The function runs **on the server** inside your Node.js process — never in the browser.
76
+ * Use this when your options require:
77
+ * - Querying your own database
78
+ * - Calling a third-party API that needs a secret key
79
+ * - Filtering options based on the authenticated user
80
+ *
81
+ * The Admin UI fetches results from `GET /api/dyrected/options/:collection/:field`.
82
+ * Sibling field values are forwarded as query parameters (e.g. `?country=us`)
83
+ * and are accessible via `req.query`.
84
+ *
85
+ * ```ts
86
+ * // ✅ Fetching records from your own database
87
+ * options: async ({ db, user }) => {
88
+ * const categories = await db.find({ collection: 'categories' })
89
+ * return categories.docs.map(c => ({ label: c.name, value: c.id }))
90
+ * }
91
+ *
92
+ * // ✅ Calling a third-party API with a secret key (never exposed to the browser)
93
+ * options: async () => {
94
+ * const res = await fetch('https://api.stripe.com/v1/products', {
95
+ * headers: { Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}` }
96
+ * })
97
+ * const { data } = await res.json()
98
+ * return data.map((p: any) => ({ label: p.name, value: p.id }))
99
+ * }
100
+ *
101
+ * // ✅ Cascading dropdown driven by a sibling field — server-side version
102
+ * // The Admin UI appends sibling values as query params: ?country=us
103
+ * options: async ({ req }) => {
104
+ * const country = req?.query.country
105
+ * if (!country) return []
106
+ * const regions = await db.find({ collection: 'regions', where: { country: { equals: country } } })
107
+ * return regions.docs.map(r => ({ label: r.name, value: r.code }))
108
+ * }
109
+ *
110
+ * // ✅ With caching — avoid hammering an external API on every Admin UI open
111
+ * options: {
112
+ * resolve: async () => { ... },
113
+ * cacheTTL: 300, // cache for 5 minutes
114
+ * }
115
+ * ```
116
+ *
117
+ * ---
118
+ *
119
+ * ### 3. Client-side cascading via `admin.hooks.options`
120
+ * For **instant, zero-latency** dependent dropdowns where the option list is
121
+ * already known and just needs to be filtered by a sibling field.
122
+ * See `admin.hooks.options` below.
123
+ */
124
+ options?: string[] | {
125
+ label: string;
126
+ value: any;
127
+ }[] | DynamicOptionsResolver | DynamicOptionsConfig;
128
+ relationTo?: string;
129
+ hasMany?: boolean;
130
+ fields?: Field[];
131
+ blocks?: Block[];
132
+ collection?: string;
133
+ on?: string;
134
+ access?: {
135
+ read?: AccessFunction | string;
136
+ update?: AccessFunction | string;
137
+ };
138
+ hooks?: {
139
+ beforeChange?: FieldHook[];
140
+ afterRead?: FieldHook[];
141
+ };
142
+ admin?: {
143
+ placeholder?: string;
144
+ description?: string;
145
+ hidden?: boolean;
146
+ readOnly?: boolean;
147
+ condition?: ((data: any, siblingData: any) => boolean) | string;
148
+ layout?: "radio" | "select" | string;
149
+ direction?: "horizontal" | "vertical";
150
+ tab?: string;
151
+ width?: string;
152
+ hooks?: {
153
+ /**
154
+ * Runs **client-side** in the browser whenever any sibling field value changes.
155
+ * Use this to **derive a field's value** from other fields in real time.
156
+ *
157
+ * Return a new value to update this field. Return `undefined` to leave it unchanged.
158
+ *
159
+ * ⚠️ Do NOT return an options array here — use `admin.hooks.options` for that.
160
+ *
161
+ * @example
162
+ * // Auto-generate a URL slug from the title field
163
+ * onChange: ({ siblingData }) =>
164
+ * (siblingData.title ?? '')
165
+ * .toLowerCase()
166
+ * .replace(/[^a-z0-9]+/g, '-')
167
+ * .replace(/(^-|-$)/g, '')
168
+ *
169
+ * @example
170
+ * // Calculate a derived numeric value
171
+ * onChange: ({ siblingData }) => {
172
+ * const price = Number(siblingData.price) || 0
173
+ * const tax = Number(siblingData.taxRate) || 0
174
+ * return price + price * (tax / 100)
175
+ * }
176
+ */
177
+ onChange?: (args: {
178
+ value: any;
179
+ siblingData: any;
180
+ data: any;
181
+ setValue: (value: any) => void;
182
+ }) => any;
183
+ /**
184
+ * For `select` and `multiSelect` fields only.
185
+ *
186
+ * Runs **client-side** in the browser whenever any sibling field value changes.
187
+ * Use this to **compute the available choices** for this field based on what
188
+ * the user has selected in another field (cascading / dependent dropdowns).
189
+ *
190
+ * This hook runs entirely in the browser — **no network request, instant reactivity**.
191
+ * When the returned list changes, the field's current value is automatically cleared
192
+ * if it is no longer a valid choice.
193
+ *
194
+ * Use `options: async ({ db, req })` (the top-level field property) instead when:
195
+ * - Your option list comes from a database query or a secret third-party API key
196
+ * - You need server-side caching (`cacheTTL`)
197
+ *
198
+ * @example
199
+ * // Country → State/Province cascading dropdown
200
+ * options: ({ siblingData }) => {
201
+ * if (siblingData.country === 'us') {
202
+ * return [
203
+ * { label: 'California', value: 'CA' },
204
+ * { label: 'New York', value: 'NY' },
205
+ * ]
206
+ * }
207
+ * if (siblingData.country === 'ca') {
208
+ * return [
209
+ * { label: 'Ontario', value: 'ON' },
210
+ * { label: 'British Columbia', value: 'BC' },
211
+ * ]
212
+ * }
213
+ * return []
214
+ * }
215
+ *
216
+ * @example
217
+ * // Show different sub-types depending on a parent category field
218
+ * options: ({ siblingData }) =>
219
+ * siblingData.category === 'vehicle'
220
+ * ? [{ label: 'Car', value: 'car' }, { label: 'Truck', value: 'truck' }]
221
+ * : [{ label: 'Shirt', value: 'shirt' }, { label: 'Shoes', value: 'shoes' }]
222
+ */
223
+ options?: (args: {
224
+ siblingData: Record<string, unknown>;
225
+ data: Record<string, unknown>;
226
+ }) => Array<string | {
227
+ label: string;
228
+ value: any;
229
+ }> | Promise<Array<string | {
230
+ label: string;
231
+ value: any;
232
+ }>>;
233
+ };
234
+ };
235
+ /** For database migrations: if set, data from this key will be migrated to the current field name. */
236
+ renameTo?: string;
237
+ /** For database migrations: if true, this field will be extracted to a real SQL column for performance. */
238
+ promoted?: boolean;
239
+ }
240
+ type AccessFunction = (args: {
241
+ user: any;
242
+ doc?: any;
243
+ data?: any;
244
+ req: any;
245
+ }) => boolean | object | Promise<boolean | object>;
246
+ type HookFunction = (args: {
247
+ data?: any;
248
+ doc?: any;
249
+ user?: any;
250
+ req?: any;
251
+ /** The operation that triggered this hook. */
252
+ operation?: "create" | "update" | "delete";
253
+ }) => any | Promise<any>;
254
+ type FieldHook<T = any, V = any> = (args: {
255
+ value: V;
256
+ originalDoc?: T;
257
+ data?: T;
258
+ doc?: T;
259
+ user?: any;
260
+ }) => T | Promise<T>;
261
+ interface CollectionConfig {
262
+ slug: string;
263
+ siteId?: string;
264
+ shared?: boolean;
265
+ labels?: {
266
+ singular: string;
267
+ plural: string;
268
+ };
269
+ auth?: boolean;
270
+ upload?: boolean | UploadConfig;
271
+ fields: Field[];
272
+ timestamps?: boolean;
273
+ /** Initial data to seed this collection with on first fetch if it is empty. */
274
+ initialData?: any[];
275
+ /** Enable full activity logging to the __audit collection for this collection. */
276
+ audit?: boolean;
277
+ access?: {
278
+ read?: AccessFunction | string;
279
+ create?: AccessFunction | string;
280
+ update?: AccessFunction | string;
281
+ delete?: AccessFunction | string;
282
+ };
283
+ hooks?: {
284
+ beforeRead?: HookFunction[];
285
+ afterRead?: HookFunction[];
286
+ beforeChange?: HookFunction[];
287
+ afterChange?: HookFunction[];
288
+ beforeDelete?: HookFunction[];
289
+ afterDelete?: HookFunction[];
290
+ };
291
+ admin?: {
292
+ useAsTitle?: string;
293
+ defaultColumns?: string[];
294
+ group?: string;
295
+ hidden?: boolean;
296
+ /**
297
+ * URL to open in the Live Preview pane.
298
+ * Accepts a static string or a function that receives the document and returns a URL.
299
+ */
300
+ previewUrl?: string | ((doc: any, opts: {
301
+ locale?: string;
302
+ }) => string | null);
303
+ /** Which mode to use for live preview. Defaults to 'postMessage'. */
304
+ previewMode?: "postMessage" | "token";
305
+ };
306
+ }
307
+ interface UploadConfig {
308
+ allowedMimeTypes?: string[];
309
+ maxFileSize?: number;
310
+ /** Local disk path where files are stored. Only used by LocalStorage adapter. */
311
+ staticDir?: string;
312
+ /** Public URL prefix for locally stored files. Only used by LocalStorage adapter. */
313
+ staticURL?: string;
314
+ /** Which imageSizes entry to use as the thumbnail in the Admin media grid. */
315
+ adminThumbnail?: string;
316
+ imageSizes?: {
317
+ name: string;
318
+ width?: number;
319
+ height?: number;
320
+ crop?: string;
321
+ /** sharp fit strategy: 'cover' | 'contain' | 'fill' | 'inside' | 'outside' */
322
+ fit?: string;
323
+ /** Never upscale images smaller than the target size. Default: true. */
324
+ withoutEnlargement?: boolean;
325
+ /** Additional sharp format options. */
326
+ formatOptions?: Record<string, any>;
327
+ }[];
328
+ }
329
+ interface GlobalConfig {
330
+ slug: string;
331
+ siteId?: string;
332
+ shared?: boolean;
333
+ label?: string;
334
+ fields: Field[];
335
+ access?: {
336
+ read?: AccessFunction;
337
+ update?: AccessFunction;
338
+ };
339
+ hooks?: {
340
+ beforeRead?: HookFunction[];
341
+ afterRead?: HookFunction[];
342
+ beforeChange?: HookFunction[];
343
+ afterChange?: HookFunction[];
344
+ };
345
+ admin?: {
346
+ group?: string;
347
+ hidden?: boolean;
348
+ };
349
+ /** Initial data to seed this global with on first fetch if it is empty. */
350
+ initialData?: any;
351
+ }
352
+ interface PaginatedResult<T = any> {
353
+ docs: T[];
354
+ total: number;
355
+ limit: number;
356
+ page: number;
357
+ /** Total number of pages given the current limit. */
358
+ totalPages: number;
359
+ hasNextPage: boolean;
360
+ hasPrevPage: boolean;
361
+ }
362
+ interface DatabaseAdapter {
363
+ find(args: {
364
+ collection: string;
365
+ where?: any;
366
+ limit?: number;
367
+ page?: number;
368
+ sort?: string;
369
+ }): Promise<PaginatedResult>;
370
+ findOne(args: {
371
+ collection: string;
372
+ id: string;
373
+ }): Promise<any>;
374
+ create(args: {
375
+ collection: string;
376
+ data: any;
377
+ }): Promise<any>;
378
+ update(args: {
379
+ collection: string;
380
+ id: string;
381
+ data: any;
382
+ }): Promise<any>;
383
+ delete(args: {
384
+ collection: string;
385
+ id: string;
386
+ }): Promise<any>;
387
+ getGlobal(args: {
388
+ slug: string;
389
+ }): Promise<any>;
390
+ updateGlobal(args: {
391
+ slug: string;
392
+ data: any;
393
+ }): Promise<any>;
394
+ /**
395
+ * Sync the database schema with the provided collections and globals.
396
+ * Useful for creating tables on startup.
397
+ */
398
+ sync?(collections: CollectionConfig[], globals: GlobalConfig[]): Promise<void>;
399
+ /**
400
+ * Low-level raw query execution.
401
+ * Optional as not all adapters may support raw SQL/commands.
402
+ */
403
+ execute?(query: string, params?: any[]): Promise<any>;
404
+ }
405
+ interface FileData {
406
+ filename: string;
407
+ filesize?: number;
408
+ mimeType: string;
409
+ url: string;
410
+ width?: number;
411
+ height?: number;
412
+ focalPoint?: {
413
+ x: number;
414
+ y: number;
415
+ };
416
+ blurhash?: string;
417
+ type?: "upload" | "external";
418
+ provider?: string;
419
+ provider_metadata?: any;
420
+ [key: string]: any;
421
+ }
422
+ interface StorageAdapter {
423
+ upload(args: {
424
+ filename: string;
425
+ buffer: Uint8Array;
426
+ mimeType: string;
427
+ prefix?: string;
428
+ }): Promise<FileData>;
429
+ delete(args: {
430
+ filename: string;
431
+ }): Promise<void>;
432
+ getURL(args: {
433
+ filename: string;
434
+ }): string;
435
+ /** Retrieve file content for serving via API */
436
+ resolve?(args: {
437
+ filename: string;
438
+ }): Promise<{
439
+ buffer: Uint8Array;
440
+ mimeType: string;
441
+ } | null>;
442
+ }
443
+ /** Branding and metadata configuration for the Admin UI. */
444
+ interface AdminConfig {
445
+ branding?: {
446
+ /** URL or imported image for the full logo shown in the sidebar. */
447
+ logo?: string;
448
+ /** URL or imported image for the compact logo mark used in collapsed sidebar. */
449
+ logoMark?: string;
450
+ /** Primary accent colour as a CSS value (e.g. '#6366f1' or 'hsl(240 50% 60%)') */
451
+ primaryColor?: string;
452
+ /** URL for the browser tab favicon. */
453
+ favicon?: string;
454
+ /** Default font family for body and UI elements (sans-serif). */
455
+ fontSans?: string;
456
+ /** Default font family for headings and display elements (serif). */
457
+ fontSerif?: string;
458
+ };
459
+ meta?: {
460
+ /** Appended to every Admin page title. Default: '- Dyrected' */
461
+ titleSuffix?: string;
462
+ };
463
+ }
464
+ interface ImageService {
465
+ process(args: {
466
+ buffer: Uint8Array;
467
+ mimeType: string;
468
+ config?: CollectionConfig["upload"];
469
+ focalPoint?: {
470
+ x: number;
471
+ y: number;
472
+ };
473
+ }): Promise<{
474
+ metadata: {
475
+ width?: number;
476
+ height?: number;
477
+ blurhash?: string;
478
+ };
479
+ sizes?: Record<string, {
480
+ buffer: Uint8Array;
481
+ width: number;
482
+ height: number;
483
+ filename: string;
484
+ }>;
485
+ }>;
486
+ }
487
+ interface DyrectedConfig {
488
+ collections: CollectionConfig[];
489
+ globals: GlobalConfig[];
490
+ db?: DatabaseAdapter;
491
+ storage?: StorageAdapter;
492
+ image?: ImageService;
493
+ /** Admin UI branding and meta configuration. */
494
+ admin?: AdminConfig;
495
+ email?: {
496
+ from: string;
497
+ send: (args: {
498
+ to: string;
499
+ subject: string;
500
+ html: string;
501
+ }) => Promise<void>;
502
+ templates?: {
503
+ welcome?: (args: {
504
+ email: string;
505
+ }) => {
506
+ subject?: string;
507
+ html: string;
508
+ };
509
+ invite?: (args: {
510
+ token: string;
511
+ invitedByEmail?: string;
512
+ }) => {
513
+ subject?: string;
514
+ html: string;
515
+ };
516
+ resetPassword?: (args: {
517
+ token: string;
518
+ }) => {
519
+ subject?: string;
520
+ html: string;
521
+ };
522
+ passwordChanged?: (args: {
523
+ email: string;
524
+ }) => {
525
+ subject?: string;
526
+ html: string;
527
+ };
528
+ };
529
+ };
530
+ redis?: {
531
+ url: string;
532
+ };
533
+ cors?: {
534
+ origins: string[];
535
+ };
536
+ onSchemaFetch?: (siteId: string) => Promise<{
537
+ collections?: CollectionConfig[];
538
+ globals?: GlobalConfig[];
539
+ }>;
540
+ }
541
+
542
+ interface DyrectedContext {
543
+ Variables: {
544
+ config: DyrectedConfig;
545
+ siteId?: string;
546
+ workspaceId?: string;
547
+ /** Decoded JWT payload set by requireAuth() or optionalAuth() middleware. */
548
+ user?: {
549
+ sub: string;
550
+ email: string;
551
+ collection: string;
552
+ [key: string]: any;
553
+ };
554
+ };
555
+ }
556
+ /**
557
+ * Create the main Dyrected Hono application.
558
+ */
559
+ declare function createDyrectedApp(rawConfig: DyrectedConfig): Promise<Hono<DyrectedContext, hono_types.BlankSchema, "/">>;
560
+
561
+ export { type AccessFunction as A, type Block as B, type CollectionConfig as C, type DyrectedConfig as D, type Field as F, type GlobalConfig as G, type HookFunction as H, type ImageService as I, type PaginatedResult as P, type StorageAdapter as S, type UploadConfig as U, type AdminConfig as a, type DatabaseAdapter as b, type DynamicOptionItem as c, type DynamicOptionsConfig as d, type DynamicOptionsResolver as e, type DynamicOptionsResolverArgs as f, type DyrectedContext as g, type FieldHook as h, type FieldType as i, type FileData as j, createDyrectedApp as k };