includio-cms 0.20.0 → 0.22.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/API.md +22 -21
- package/CHANGELOG.md +147 -0
- package/DOCS.md +1 -1
- package/README.md +138 -32
- package/ROADMAP.md +11 -4
- package/dist/admin/api/rest/handler.d.ts +13 -1
- package/dist/admin/api/rest/handler.js +13 -1
- package/dist/admin/api/rest/middleware/apiKey.js +9 -1
- package/dist/admin/api/rest/middleware/generateApiKey.d.ts +16 -0
- package/dist/admin/api/rest/middleware/generateApiKey.js +19 -0
- package/dist/admin/client/collection/collection-entries.svelte +1 -1
- package/dist/admin/client/collection/empty-state.svelte +1 -1
- package/dist/admin/client/collection/row-actions.svelte +3 -3
- package/dist/admin/client/collection/table-toolbar.svelte +3 -1
- package/dist/admin/client/entry/entry-header.svelte +3 -1
- package/dist/admin/client/users/create-user-dialog.svelte +4 -4
- package/dist/admin/client/users/delete-user-dialog.svelte +4 -2
- package/dist/admin/client/users/lang.d.ts +10 -2
- package/dist/admin/client/users/lang.js +10 -4
- package/dist/admin/client/users/users-page.svelte +3 -2
- package/dist/admin/components/media/file-upload.svelte +2 -0
- package/dist/ai-claude/index.d.ts +9 -1
- package/dist/ai-claude/index.js +9 -1
- package/dist/ai-openai/index.d.ts +9 -1
- package/dist/ai-openai/index.js +9 -1
- package/dist/cli/index.js +115 -13
- package/dist/cms/runtime/schema.d.ts +2 -0
- package/dist/cms/runtime/schema.js +4 -0
- package/dist/cms/runtime/types.d.ts +1 -1
- package/dist/core/cms.d.ts +13 -1
- package/dist/core/cms.js +13 -1
- package/dist/core/errors.d.ts +71 -0
- package/dist/core/errors.js +179 -0
- package/dist/core/server/consentLogs/operations/create.d.ts +13 -1
- package/dist/core/server/consentLogs/operations/create.js +13 -1
- package/dist/core/server/entries/operations/create.js +6 -1
- package/dist/core/server/entries/operations/get.js +14 -3
- package/dist/core/server/entries/operations/resolveEntry.d.ts +32 -1
- package/dist/core/server/entries/operations/resolveEntry.js +36 -4
- package/dist/core/server/entries/operations/update.js +5 -1
- package/dist/core/server/fields/utils/resolveMedia.d.ts +18 -1
- package/dist/core/server/fields/utils/resolveMedia.js +13 -1
- package/dist/core/server/forms/submissions/operations/create.d.ts +21 -1
- package/dist/core/server/forms/submissions/operations/create.js +18 -2
- package/dist/core/server/forms/submissions/utils/parseMultipart.d.ts +15 -1
- package/dist/core/server/forms/submissions/utils/parseMultipart.js +15 -1
- package/dist/core/server/media/operations/uploadFile.js +4 -3
- package/dist/core/server/media/styles/sharp/generateImageStyle.js +3 -2
- package/dist/core/server/media/utils/generateAdminThumbnail.js +3 -2
- package/dist/core/server/media/utils/generateBlurDataUrl.js +2 -1
- package/dist/db-postgres/index.d.ts +10 -0
- package/dist/db-postgres/index.js +10 -0
- package/dist/email-nodemailer/index.d.ts +13 -1
- package/dist/email-nodemailer/index.js +13 -1
- package/dist/entity/index.d.ts +16 -1
- package/dist/entity/index.js +16 -1
- package/dist/files-local/index.d.ts +12 -1
- package/dist/files-local/index.js +12 -1
- package/dist/paraglide/messages/_index.d.ts +3 -36
- package/dist/paraglide/messages/_index.js +3 -71
- package/dist/paraglide/messages/hello_world.d.ts +5 -0
- package/dist/paraglide/messages/hello_world.js +33 -0
- package/dist/paraglide/messages/login_hello.d.ts +16 -0
- package/dist/paraglide/messages/login_hello.js +34 -0
- package/dist/paraglide/messages/login_please_login.d.ts +16 -0
- package/dist/paraglide/messages/login_please_login.js +34 -0
- package/dist/server/auth.d.ts +11 -0
- package/dist/server/auth.js +11 -0
- package/dist/server/security/csp.d.ts +16 -0
- package/dist/server/security/csp.js +33 -0
- package/dist/server/security/csrf.d.ts +13 -0
- package/dist/server/security/csrf.js +49 -0
- package/dist/server/security/index.d.ts +3 -0
- package/dist/server/security/index.js +3 -0
- package/dist/server/security/rate-limit.d.ts +44 -0
- package/dist/server/security/rate-limit.js +97 -0
- package/dist/server/utils/withTimeout.d.ts +21 -0
- package/dist/server/utils/withTimeout.js +37 -0
- package/dist/sveltekit/config.d.ts +67 -4
- package/dist/sveltekit/config.js +73 -4
- package/dist/sveltekit/server/handle.d.ts +15 -1
- package/dist/sveltekit/server/handle.js +22 -1
- package/dist/sveltekit/server/index.d.ts +1 -0
- package/dist/sveltekit/server/index.js +1 -0
- package/dist/sveltekit/server/layout.d.ts +12 -1
- package/dist/sveltekit/server/layout.js +12 -1
- package/dist/sveltekit/server/preview.d.ts +21 -1
- package/dist/sveltekit/server/preview.js +21 -1
- package/dist/types/cms.d.ts +4 -0
- package/dist/types/cms.schema.d.ts +452 -0
- package/dist/types/cms.schema.js +629 -0
- package/dist/updates/0.21.0/index.d.ts +2 -0
- package/dist/updates/0.21.0/index.js +55 -0
- package/dist/updates/0.22.0/index.d.ts +2 -0
- package/dist/updates/0.22.0/index.js +75 -0
- package/dist/updates/index.js +3 -1
- package/package.json +12 -2
- package/dist/paraglide/messages/en.d.ts +0 -5
- package/dist/paraglide/messages/en.js +0 -14
- package/dist/paraglide/messages/pl.d.ts +0 -5
- package/dist/paraglide/messages/pl.js +0 -14
|
@@ -51,18 +51,18 @@
|
|
|
51
51
|
</DropdownMenu.Item>
|
|
52
52
|
<DropdownMenu.Separator />
|
|
53
53
|
{#if onRestore}
|
|
54
|
-
<DropdownMenu.Item onclick={onRestore}>
|
|
54
|
+
<DropdownMenu.Item onclick={onRestore} data-testid="row-action-restore">
|
|
55
55
|
<ArchiveOff class="mr-2 size-4" />
|
|
56
56
|
{t.restore}
|
|
57
57
|
</DropdownMenu.Item>
|
|
58
58
|
{:else}
|
|
59
|
-
<DropdownMenu.Item onclick={onArchive}>
|
|
59
|
+
<DropdownMenu.Item onclick={onArchive} data-testid="row-action-archive">
|
|
60
60
|
<Archive class="mr-2 size-4" />
|
|
61
61
|
{t.archive}
|
|
62
62
|
</DropdownMenu.Item>
|
|
63
63
|
{/if}
|
|
64
64
|
{#if onDelete}
|
|
65
|
-
<DropdownMenu.Item class="text-destructive focus:text-destructive" onclick={onDelete}>
|
|
65
|
+
<DropdownMenu.Item class="text-destructive focus:text-destructive" onclick={onDelete} data-testid="row-action-delete">
|
|
66
66
|
<Trash class="mr-2 size-4" />
|
|
67
67
|
{t.delete}
|
|
68
68
|
</DropdownMenu.Item>
|
|
@@ -131,6 +131,7 @@
|
|
|
131
131
|
class="gap-1.5 {hasActiveFilter
|
|
132
132
|
? 'border-primary/30 bg-lavender-lighter text-primary'
|
|
133
133
|
: ''}"
|
|
134
|
+
data-testid="status-filter-trigger"
|
|
134
135
|
>
|
|
135
136
|
<Filter class="size-3.5" />
|
|
136
137
|
{t.status}{hasActiveFilter ? `: ${activeFilterLabel}` : ''}
|
|
@@ -148,6 +149,7 @@
|
|
|
148
149
|
onStatusFilterChange(option.value);
|
|
149
150
|
statusPopoverOpen = false;
|
|
150
151
|
}}
|
|
152
|
+
data-testid="status-filter-option-{option.value ?? 'all'}"
|
|
151
153
|
>
|
|
152
154
|
{option.label()}
|
|
153
155
|
</button>
|
|
@@ -228,7 +230,7 @@
|
|
|
228
230
|
|
|
229
231
|
<div class="flex-1"></div>
|
|
230
232
|
|
|
231
|
-
<Button variant="default" size="sm" onclick={onCreateEntry}>
|
|
233
|
+
<Button variant="default" size="sm" onclick={onCreateEntry} data-testid="create-entry-button">
|
|
232
234
|
<Plus class="mr-1 size-4" />
|
|
233
235
|
{createLabel}
|
|
234
236
|
</Button>
|
|
@@ -166,6 +166,7 @@
|
|
|
166
166
|
type="button"
|
|
167
167
|
class="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-l-lg px-4 py-1.5 text-[13px] font-semibold transition-colors"
|
|
168
168
|
onclick={() => onSave('published-now')}
|
|
169
|
+
data-testid="publish-button"
|
|
169
170
|
>
|
|
170
171
|
<SendIcon class="size-3.5" />
|
|
171
172
|
{primaryButtonLabel}
|
|
@@ -179,13 +180,14 @@
|
|
|
179
180
|
type="button"
|
|
180
181
|
class="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center rounded-r-lg border-l border-white/20 px-1.5 py-1.5 transition-colors"
|
|
181
182
|
aria-label={lang[interfaceLanguage.current].saveDraft}
|
|
183
|
+
data-testid="save-draft-trigger"
|
|
182
184
|
>
|
|
183
185
|
<ChevronDownIcon class="size-3.5" />
|
|
184
186
|
</button>
|
|
185
187
|
{/snippet}
|
|
186
188
|
</DropdownMenu.Trigger>
|
|
187
189
|
<DropdownMenu.Content align="end" class="w-48">
|
|
188
|
-
<DropdownMenu.Item onclick={onSaveDraft}>
|
|
190
|
+
<DropdownMenu.Item onclick={onSaveDraft} data-testid="save-draft-button">
|
|
189
191
|
{lang[interfaceLanguage.current].saveDraft}
|
|
190
192
|
<DropdownMenu.Shortcut>{shortcutLabel}</DropdownMenu.Shortcut>
|
|
191
193
|
</DropdownMenu.Item>
|
|
@@ -141,12 +141,12 @@
|
|
|
141
141
|
<div class="space-y-2">
|
|
142
142
|
<Label>{lang.role}</Label>
|
|
143
143
|
<Select.Root type="single" value={role} onValueChange={(v) => v && (role = v as UserRole)}>
|
|
144
|
-
<Select.Trigger class="w-full" aria-describedby="create-role-hint">
|
|
144
|
+
<Select.Trigger class="w-full" aria-describedby="create-role-hint" data-testid="role-select">
|
|
145
145
|
{role === 'admin' ? lang.roleAdmin : lang.roleUser}
|
|
146
146
|
</Select.Trigger>
|
|
147
147
|
<Select.Content>
|
|
148
|
-
<Select.Item value="user">{lang.roleUser}</Select.Item>
|
|
149
|
-
<Select.Item value="admin">{lang.roleAdmin}</Select.Item>
|
|
148
|
+
<Select.Item value="user" data-testid="role-option-user">{lang.roleUser}</Select.Item>
|
|
149
|
+
<Select.Item value="admin" data-testid="role-option-admin">{lang.roleAdmin}</Select.Item>
|
|
150
150
|
</Select.Content>
|
|
151
151
|
</Select.Root>
|
|
152
152
|
<p id="create-role-hint" class="text-xs" style="color: var(--text-light);">
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
{/if}
|
|
159
159
|
<Dialog.Footer>
|
|
160
160
|
<Button type="button" variant="outline" onclick={() => onOpenChange(false)}>{lang.cancel}</Button>
|
|
161
|
-
<Button type="submit" disabled={loading}>{lang.createUser}</Button>
|
|
161
|
+
<Button type="submit" disabled={loading} data-testid="create-user-submit">{lang.createUser}</Button>
|
|
162
162
|
</Dialog.Footer>
|
|
163
163
|
</form>
|
|
164
164
|
</Dialog.Content>
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
{lang.deleteWarningTitle}
|
|
83
83
|
</p>
|
|
84
84
|
<p class="mt-1 text-sm" style="color: var(--muted-foreground);">
|
|
85
|
-
{
|
|
85
|
+
{lang.deleteWarningDesc.before}<strong>{user.name}</strong>{lang.deleteWarningDesc.after}
|
|
86
86
|
</p>
|
|
87
87
|
</div>
|
|
88
88
|
</div>
|
|
@@ -90,13 +90,14 @@
|
|
|
90
90
|
<!-- Confirm input -->
|
|
91
91
|
<div class="space-y-2">
|
|
92
92
|
<Label for="delete-confirm">
|
|
93
|
-
{
|
|
93
|
+
{lang.deleteConfirmType.before}<strong>{lang.deleteConfirmWord}</strong>{lang.deleteConfirmType.after}
|
|
94
94
|
</Label>
|
|
95
95
|
<Input
|
|
96
96
|
id="delete-confirm"
|
|
97
97
|
bind:value={confirmInput}
|
|
98
98
|
placeholder={lang.deleteConfirmWord}
|
|
99
99
|
autocomplete="off"
|
|
100
|
+
data-testid="delete-confirm-input"
|
|
100
101
|
/>
|
|
101
102
|
</div>
|
|
102
103
|
|
|
@@ -108,6 +109,7 @@
|
|
|
108
109
|
variant="destructive"
|
|
109
110
|
disabled={!confirmed || loading}
|
|
110
111
|
onclick={handleDelete}
|
|
112
|
+
data-testid="delete-user-submit"
|
|
111
113
|
>
|
|
112
114
|
{lang.deleteUser}
|
|
113
115
|
</Button>
|
|
@@ -24,8 +24,16 @@ export declare const usersLang: Record<InterfaceLanguage, {
|
|
|
24
24
|
deleteConfirmTitle: string;
|
|
25
25
|
deleteConfirmDescription: string;
|
|
26
26
|
deleteWarningTitle: string;
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/** Split into `before` / `after`; the user name is rendered between them in template (avoids `{@html}`). */
|
|
28
|
+
deleteWarningDesc: {
|
|
29
|
+
before: string;
|
|
30
|
+
after: string;
|
|
31
|
+
};
|
|
32
|
+
/** Split into `before` / `after`; `deleteConfirmWord` is rendered between them. */
|
|
33
|
+
deleteConfirmType: {
|
|
34
|
+
before: string;
|
|
35
|
+
after: string;
|
|
36
|
+
};
|
|
29
37
|
deleteConfirmWord: string;
|
|
30
38
|
userCreated: string;
|
|
31
39
|
userUpdated: string;
|
|
@@ -24,8 +24,11 @@ export const usersLang = {
|
|
|
24
24
|
deleteConfirmTitle: 'Delete user?',
|
|
25
25
|
deleteConfirmDescription: 'This action cannot be undone. The user will be permanently deleted.',
|
|
26
26
|
deleteWarningTitle: 'This action cannot be undone',
|
|
27
|
-
deleteWarningDesc:
|
|
28
|
-
|
|
27
|
+
deleteWarningDesc: {
|
|
28
|
+
before: 'User ',
|
|
29
|
+
after: ' will be permanently deleted along with all their data.'
|
|
30
|
+
},
|
|
31
|
+
deleteConfirmType: { before: 'Type ', after: ' to confirm' },
|
|
29
32
|
deleteConfirmWord: 'DELETE',
|
|
30
33
|
userCreated: 'User created',
|
|
31
34
|
userUpdated: 'User updated',
|
|
@@ -107,8 +110,11 @@ export const usersLang = {
|
|
|
107
110
|
deleteConfirmTitle: 'Usunąć użytkownika?',
|
|
108
111
|
deleteConfirmDescription: 'Ta akcja jest nieodwracalna. Użytkownik zostanie trwale usunięty.',
|
|
109
112
|
deleteWarningTitle: 'Tej operacji nie można cofnąć',
|
|
110
|
-
deleteWarningDesc:
|
|
111
|
-
|
|
113
|
+
deleteWarningDesc: {
|
|
114
|
+
before: 'Użytkownik ',
|
|
115
|
+
after: ' zostanie trwale usunięty wraz ze wszystkimi danymi.'
|
|
116
|
+
},
|
|
117
|
+
deleteConfirmType: { before: 'Wpisz ', after: ', żeby potwierdzić' },
|
|
112
118
|
deleteConfirmWord: 'USUŃ',
|
|
113
119
|
userCreated: 'Użytkownik utworzony',
|
|
114
120
|
userUpdated: 'Użytkownik zaktualizowany',
|
|
@@ -360,7 +360,7 @@
|
|
|
360
360
|
{lang.invite.inviteUser}
|
|
361
361
|
</Button>
|
|
362
362
|
{/if}
|
|
363
|
-
<Button variant="default" size="sm" onclick={() => (createOpen = true)}>
|
|
363
|
+
<Button variant="default" size="sm" onclick={() => (createOpen = true)} data-testid="create-user-button">
|
|
364
364
|
<Plus class="size-4" />
|
|
365
365
|
{lang.createUser}
|
|
366
366
|
</Button>
|
|
@@ -384,7 +384,7 @@
|
|
|
384
384
|
<p class="mb-5 max-w-sm text-sm" style="color: var(--muted-foreground);">
|
|
385
385
|
{lang.emptyDescription}
|
|
386
386
|
</p>
|
|
387
|
-
<Button onclick={() => (createOpen = true)}>
|
|
387
|
+
<Button onclick={() => (createOpen = true)} data-testid="create-user-button">
|
|
388
388
|
<Plus class="size-4" />
|
|
389
389
|
{lang.addUser}
|
|
390
390
|
</Button>
|
|
@@ -518,6 +518,7 @@
|
|
|
518
518
|
class="text-destructive h-8 w-8"
|
|
519
519
|
onclick={() => openDelete(user)}
|
|
520
520
|
title={lang.deleteUser}
|
|
521
|
+
data-testid="delete-user-row-button"
|
|
521
522
|
>
|
|
522
523
|
<Trash class="size-4" />
|
|
523
524
|
</Button>
|
|
@@ -218,6 +218,7 @@
|
|
|
218
218
|
type="button"
|
|
219
219
|
class="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-1.5 text-[13px] font-semibold text-primary-foreground transition-colors hover:bg-plum-dark whitespace-nowrap"
|
|
220
220
|
onclick={() => inputElement.click()}
|
|
221
|
+
data-testid="file-upload-button"
|
|
221
222
|
>
|
|
222
223
|
<Upload class="h-4 w-4" />
|
|
223
224
|
{lang[interfaceLanguage.current].addFiles}
|
|
@@ -229,6 +230,7 @@
|
|
|
229
230
|
type="file"
|
|
230
231
|
{accept}
|
|
231
232
|
onchange={handleUpload}
|
|
233
|
+
data-testid="file-input"
|
|
232
234
|
/>
|
|
233
235
|
|
|
234
236
|
<!-- Drag overlay -->
|
|
@@ -4,8 +4,16 @@ import type { AIAdapter, AIConfig } from '../types/adapters/ai.js';
|
|
|
4
4
|
*
|
|
5
5
|
* `@anthropic-ai/sdk` is an **optional peer dependency** — install it in your
|
|
6
6
|
* project (`pnpm add @anthropic-ai/sdk`) when using this adapter. SDK loads
|
|
7
|
-
* lazily on first call; missing peer throws a clear error.
|
|
7
|
+
* lazily on first call; a missing peer throws a clear error.
|
|
8
8
|
*
|
|
9
|
+
* @param config - `apiKey` is required.
|
|
10
|
+
* @returns An `AIAdapter` ready to use in `defineConfig({ ai })`.
|
|
9
11
|
* @public
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { claudeAdapter } from 'includio-cms/ai-claude';
|
|
15
|
+
*
|
|
16
|
+
* const ai = claudeAdapter({ apiKey: process.env.ANTHROPIC_API_KEY! });
|
|
17
|
+
* ```
|
|
10
18
|
*/
|
|
11
19
|
export declare function claudeAdapter(config: AIConfig): AIAdapter;
|
package/dist/ai-claude/index.js
CHANGED
|
@@ -5,9 +5,17 @@ import sharp from 'sharp';
|
|
|
5
5
|
*
|
|
6
6
|
* `@anthropic-ai/sdk` is an **optional peer dependency** — install it in your
|
|
7
7
|
* project (`pnpm add @anthropic-ai/sdk`) when using this adapter. SDK loads
|
|
8
|
-
* lazily on first call; missing peer throws a clear error.
|
|
8
|
+
* lazily on first call; a missing peer throws a clear error.
|
|
9
9
|
*
|
|
10
|
+
* @param config - `apiKey` is required.
|
|
11
|
+
* @returns An `AIAdapter` ready to use in `defineConfig({ ai })`.
|
|
10
12
|
* @public
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { claudeAdapter } from 'includio-cms/ai-claude';
|
|
16
|
+
*
|
|
17
|
+
* const ai = claudeAdapter({ apiKey: process.env.ANTHROPIC_API_KEY! });
|
|
18
|
+
* ```
|
|
11
19
|
*/
|
|
12
20
|
export function claudeAdapter(config) {
|
|
13
21
|
let client = null;
|
|
@@ -4,8 +4,16 @@ import type { AIAdapter, AIConfig } from '../types/adapters/ai.js';
|
|
|
4
4
|
*
|
|
5
5
|
* `openai` is an **optional peer dependency** — install it in your project
|
|
6
6
|
* (`pnpm add openai`) when using this adapter. SDK loads lazily on first call;
|
|
7
|
-
* missing peer throws a clear error.
|
|
7
|
+
* a missing peer throws a clear error.
|
|
8
8
|
*
|
|
9
|
+
* @param config - `apiKey` is required.
|
|
10
|
+
* @returns An `AIAdapter` ready to use in `defineConfig({ ai })`.
|
|
9
11
|
* @public
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { openAIAdapter } from 'includio-cms/ai-openai';
|
|
15
|
+
*
|
|
16
|
+
* const ai = openAIAdapter({ apiKey: process.env.OPENAI_API_KEY! });
|
|
17
|
+
* ```
|
|
10
18
|
*/
|
|
11
19
|
export declare function openAIAdapter(config: AIConfig): AIAdapter;
|
package/dist/ai-openai/index.js
CHANGED
|
@@ -6,9 +6,17 @@ import sharp from 'sharp';
|
|
|
6
6
|
*
|
|
7
7
|
* `openai` is an **optional peer dependency** — install it in your project
|
|
8
8
|
* (`pnpm add openai`) when using this adapter. SDK loads lazily on first call;
|
|
9
|
-
* missing peer throws a clear error.
|
|
9
|
+
* a missing peer throws a clear error.
|
|
10
10
|
*
|
|
11
|
+
* @param config - `apiKey` is required.
|
|
12
|
+
* @returns An `AIAdapter` ready to use in `defineConfig({ ai })`.
|
|
11
13
|
* @public
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { openAIAdapter } from 'includio-cms/ai-openai';
|
|
17
|
+
*
|
|
18
|
+
* const ai = openAIAdapter({ apiKey: process.env.OPENAI_API_KEY! });
|
|
19
|
+
* ```
|
|
12
20
|
*/
|
|
13
21
|
export function openAIAdapter(config) {
|
|
14
22
|
let openai = null;
|
package/dist/cli/index.js
CHANGED
|
@@ -4,30 +4,111 @@ import { installPeers } from './install-peers.js';
|
|
|
4
4
|
import { createUser } from './create-user.js';
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
7
8
|
const args = process.argv.slice(2);
|
|
8
9
|
const command = args[0];
|
|
9
10
|
const subcommand = args[1];
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const HELP_FLAGS = new Set(['--help', '-h']);
|
|
12
|
+
const VERSION_FLAGS = new Set(['--version', '-v']);
|
|
13
|
+
function readPackageVersion() {
|
|
14
|
+
try {
|
|
15
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
// In dist: dist/cli/index.js → ../../package.json
|
|
17
|
+
// In src dev: src/lib/cli/index.ts → ../../../package.json
|
|
18
|
+
const candidates = [
|
|
19
|
+
path.resolve(here, '../../package.json'),
|
|
20
|
+
path.resolve(here, '../../../package.json')
|
|
21
|
+
];
|
|
22
|
+
for (const c of candidates) {
|
|
23
|
+
if (fs.existsSync(c)) {
|
|
24
|
+
const pkg = JSON.parse(fs.readFileSync(c, 'utf-8'));
|
|
25
|
+
if (pkg?.name === 'includio-cms' && typeof pkg.version === 'string') {
|
|
26
|
+
return pkg.version;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// fall through
|
|
33
|
+
}
|
|
34
|
+
return '0.0.0';
|
|
35
|
+
}
|
|
36
|
+
function printTopLevelHelp() {
|
|
37
|
+
console.log(`includio — CLI for includio-cms
|
|
38
|
+
|
|
39
|
+
Usage: includio <command> [options]
|
|
12
40
|
|
|
13
41
|
Commands:
|
|
14
|
-
scaffold admin Generate admin route files
|
|
15
|
-
install-peers Install missing peer dependencies
|
|
16
|
-
create-user Create a new admin/user account
|
|
42
|
+
scaffold admin Generate admin route files (/admin and /api/admin)
|
|
43
|
+
install-peers Install missing optional peer dependencies
|
|
44
|
+
create-user Create a new admin/user account interactively
|
|
45
|
+
|
|
46
|
+
Global options:
|
|
47
|
+
--help, -h Show this help
|
|
48
|
+
--version, -v Print the package version
|
|
49
|
+
|
|
50
|
+
Run \`includio <command> --help\` for command-specific options.
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
function printScaffoldAdminHelp() {
|
|
54
|
+
console.log(`includio scaffold admin — generate admin route files
|
|
55
|
+
|
|
56
|
+
Usage: includio scaffold admin [options]
|
|
17
57
|
|
|
18
58
|
Options:
|
|
19
|
-
--force
|
|
20
|
-
--routes-dir Path to routes
|
|
21
|
-
--cms-config
|
|
22
|
-
--shop
|
|
23
|
-
--no-shop
|
|
24
|
-
--
|
|
59
|
+
--force Overwrite existing files
|
|
60
|
+
--routes-dir <DIR> Path to SvelteKit routes (default: src/routes)
|
|
61
|
+
--cms-config <FILE> Path to CMS config (default: src/lib/cms/cms.config.ts)
|
|
62
|
+
--shop Force shop routes ON (overrides auto-detect)
|
|
63
|
+
--no-shop Force shop routes OFF
|
|
64
|
+
--help, -h Show this help
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
includio scaffold admin
|
|
68
|
+
includio scaffold admin --force --shop
|
|
69
|
+
includio scaffold admin --routes-dir apps/web/src/routes
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
function printInstallPeersHelp() {
|
|
73
|
+
console.log(`includio install-peers — install missing optional peer deps
|
|
74
|
+
|
|
75
|
+
Reads your project's installed packages and offers to install any optional
|
|
76
|
+
peers used by the parts of includio-cms you have configured (e.g. \`openai\`,
|
|
77
|
+
\`@anthropic-ai/sdk\`, \`nodemailer\`).
|
|
78
|
+
|
|
79
|
+
Usage: includio install-peers [options]
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
--dry-run List what would be installed, don't run the package manager
|
|
83
|
+
--help, -h Show this help
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
includio install-peers --dry-run
|
|
87
|
+
includio install-peers
|
|
88
|
+
`);
|
|
89
|
+
}
|
|
90
|
+
function printCreateUserHelp() {
|
|
91
|
+
console.log(`includio create-user — create an admin/user account
|
|
92
|
+
|
|
93
|
+
Interactive prompt — asks for email, password, name, and role. Requires
|
|
94
|
+
\`DATABASE_URL\` in the environment (or .env loaded by the project).
|
|
95
|
+
|
|
96
|
+
Usage: includio create-user [options]
|
|
97
|
+
|
|
98
|
+
Options:
|
|
99
|
+
--help, -h Show this help
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
includio create-user
|
|
25
103
|
`);
|
|
26
104
|
}
|
|
27
105
|
function flagValue(name) {
|
|
28
106
|
const idx = args.indexOf(name);
|
|
29
107
|
return idx !== -1 ? args[idx + 1] : undefined;
|
|
30
108
|
}
|
|
109
|
+
function hasHelpFlag() {
|
|
110
|
+
return args.some((a) => HELP_FLAGS.has(a));
|
|
111
|
+
}
|
|
31
112
|
/**
|
|
32
113
|
* Best-effort detection: scan cms config text for an active `shop: defineShop(`
|
|
33
114
|
* or `shop: <var>` property inside `defineCMS({ ... })`. Returns null if the
|
|
@@ -43,7 +124,20 @@ function detectShopUsage(cmsConfigPath) {
|
|
|
43
124
|
.replace(/\/\/[^\n]*/g, '');
|
|
44
125
|
return /\bshop\s*:\s*\S/.test(stripped);
|
|
45
126
|
}
|
|
127
|
+
// Top-level: no command, --help, or --version
|
|
128
|
+
if (!command || HELP_FLAGS.has(command)) {
|
|
129
|
+
printTopLevelHelp();
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
if (VERSION_FLAGS.has(command)) {
|
|
133
|
+
console.log(readPackageVersion());
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
46
136
|
if (command === 'scaffold' && subcommand === 'admin') {
|
|
137
|
+
if (hasHelpFlag()) {
|
|
138
|
+
printScaffoldAdminHelp();
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
47
141
|
const force = args.includes('--force');
|
|
48
142
|
const cwd = process.cwd();
|
|
49
143
|
const routesDir = flagValue('--routes-dir') ?? path.join(cwd, 'src', 'routes');
|
|
@@ -73,13 +167,21 @@ if (command === 'scaffold' && subcommand === 'admin') {
|
|
|
73
167
|
scaffoldAdmin({ routesDir, force, shop });
|
|
74
168
|
}
|
|
75
169
|
else if (command === 'install-peers') {
|
|
170
|
+
if (hasHelpFlag()) {
|
|
171
|
+
printInstallPeersHelp();
|
|
172
|
+
process.exit(0);
|
|
173
|
+
}
|
|
76
174
|
const dryRun = args.includes('--dry-run');
|
|
77
175
|
installPeers({ dryRun });
|
|
78
176
|
}
|
|
79
177
|
else if (command === 'create-user') {
|
|
178
|
+
if (hasHelpFlag()) {
|
|
179
|
+
printCreateUserHelp();
|
|
180
|
+
process.exit(0);
|
|
181
|
+
}
|
|
80
182
|
createUser();
|
|
81
183
|
}
|
|
82
184
|
else {
|
|
83
|
-
|
|
84
|
-
process.exit(
|
|
185
|
+
printTopLevelHelp();
|
|
186
|
+
process.exit(1);
|
|
85
187
|
}
|
|
@@ -44,7 +44,7 @@ export interface BlogPost {
|
|
|
44
44
|
slug?: string;
|
|
45
45
|
cover?: ImageFieldData | VideoFieldData | null;
|
|
46
46
|
rating: number;
|
|
47
|
-
category
|
|
47
|
+
category: 'technology' | 'design' | 'business' | 'tutorial';
|
|
48
48
|
publishedAt?: string;
|
|
49
49
|
thumbnail?: ImageFieldData | VideoFieldData | null;
|
|
50
50
|
content?: StructuredContentDoc;
|
package/dist/core/cms.d.ts
CHANGED
|
@@ -39,7 +39,19 @@ export declare class CMS implements ICMS {
|
|
|
39
39
|
}
|
|
40
40
|
export declare function initCMS(config: CMSConfig): CMS;
|
|
41
41
|
/**
|
|
42
|
-
* Returns the singleton CMS instance. Must be called after `includioCMS()`
|
|
42
|
+
* Returns the singleton CMS instance. Must be called after `includioCMS()`
|
|
43
|
+
* initializes the CMS in `hooks.server.ts`.
|
|
44
|
+
*
|
|
45
|
+
* @returns The active `CMS` instance (collections, adapters, plugins, ...).
|
|
46
|
+
* @throws {Error} when called before `includioCMS()` has run (e.g. in plain
|
|
47
|
+
* client code that imports `$lib/...` without going through SvelteKit hooks).
|
|
43
48
|
* @public
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { getCMS } from 'includio-cms';
|
|
52
|
+
*
|
|
53
|
+
* const cms = getCMS();
|
|
54
|
+
* await cms.databaseAdapter.getEntries({ slug: 'posts' });
|
|
55
|
+
* ```
|
|
44
56
|
*/
|
|
45
57
|
export declare function getCMS(): CMS;
|
package/dist/core/cms.js
CHANGED
|
@@ -160,8 +160,20 @@ export function initCMS(config) {
|
|
|
160
160
|
return cms;
|
|
161
161
|
}
|
|
162
162
|
/**
|
|
163
|
-
* Returns the singleton CMS instance. Must be called after `includioCMS()`
|
|
163
|
+
* Returns the singleton CMS instance. Must be called after `includioCMS()`
|
|
164
|
+
* initializes the CMS in `hooks.server.ts`.
|
|
165
|
+
*
|
|
166
|
+
* @returns The active `CMS` instance (collections, adapters, plugins, ...).
|
|
167
|
+
* @throws {Error} when called before `includioCMS()` has run (e.g. in plain
|
|
168
|
+
* client code that imports `$lib/...` without going through SvelteKit hooks).
|
|
164
169
|
* @public
|
|
170
|
+
* @example
|
|
171
|
+
* ```ts
|
|
172
|
+
* import { getCMS } from 'includio-cms';
|
|
173
|
+
*
|
|
174
|
+
* const cms = getCMS();
|
|
175
|
+
* await cms.databaseAdapter.getEntries({ slug: 'posts' });
|
|
176
|
+
* ```
|
|
165
177
|
*/
|
|
166
178
|
export function getCMS() {
|
|
167
179
|
if (!cms) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ZodError } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Base error class for CMS runtime failures. Carries a stable `code` and a
|
|
4
|
+
* `context` bag so callers (and clients) can branch on the failure mode without
|
|
5
|
+
* string-matching on `message`.
|
|
6
|
+
*
|
|
7
|
+
* `code` is a SCREAMING_SNAKE_CASE constant; see callers for the canonical list
|
|
8
|
+
* (`ENTRY_NOT_FOUND`, `ENTRY_VERSION_NOT_FOUND`, `INVALID_DATA`,
|
|
9
|
+
* `MISSING_REQUIRED_PARAM`, `CONFIG_VALIDATION_FAILED`).
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* try { await resolveEntry({ id }); }
|
|
15
|
+
* catch (e) {
|
|
16
|
+
* if (e instanceof CmsError && e.code === 'ENTRY_NOT_FOUND') { ... }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare class CmsError extends Error {
|
|
21
|
+
readonly code: string;
|
|
22
|
+
readonly context: Record<string, unknown>;
|
|
23
|
+
constructor(code: string, message: string, context?: Record<string, unknown>, options?: {
|
|
24
|
+
cause?: unknown;
|
|
25
|
+
});
|
|
26
|
+
toString(): string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* One concrete validation issue extracted from a Zod error, with a friendly
|
|
30
|
+
* hint when the path matches a known config shape.
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export interface ConfigValidationIssue {
|
|
34
|
+
/** Dot/bracket-formatted path, e.g. `languages[0].code` or `collections[2].fields[0].slug`. */
|
|
35
|
+
path: string;
|
|
36
|
+
/** The validator's message, lightly normalized. */
|
|
37
|
+
message: string;
|
|
38
|
+
/** Optional hint describing how to fix this specific issue. */
|
|
39
|
+
hint?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Thrown by {@link defineConfig} when the runtime config fails Zod validation.
|
|
43
|
+
* Aggregates ALL issues (not just the first) so the user can fix everything in
|
|
44
|
+
* one pass.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export declare class ConfigValidationError extends CmsError {
|
|
49
|
+
readonly issues: ConfigValidationIssue[];
|
|
50
|
+
constructor(issues: ConfigValidationIssue[]);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Render a Zod issue path (`(string | number)[]`) into JS-style notation:
|
|
54
|
+
* - string segments → `.foo`
|
|
55
|
+
* - numeric segments → `[3]`
|
|
56
|
+
* - leading string segment has no leading dot.
|
|
57
|
+
*/
|
|
58
|
+
export declare function formatIssuePath(path: ReadonlyArray<string | number>): string;
|
|
59
|
+
/**
|
|
60
|
+
* Render a `ZodError` from entry/form data validation as a list of
|
|
61
|
+
* `path: message` lines (one per issue). Used by data-write operations to give
|
|
62
|
+
* callers a readable dump instead of `JSON.stringify(error.flatten())`.
|
|
63
|
+
*
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
export declare function formatZodDataIssues(error: ZodError): string;
|
|
67
|
+
/**
|
|
68
|
+
* Convert a `ZodError` into a {@link ConfigValidationError} with friendly,
|
|
69
|
+
* path-prefixed messages and per-issue hints.
|
|
70
|
+
*/
|
|
71
|
+
export declare function formatConfigError(error: ZodError): ConfigValidationError;
|