includio-cms 0.1.2 → 0.1.3

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 (40) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/ROADMAP.md +4 -0
  3. package/dist/admin/api/accept-invite.d.ts +2 -0
  4. package/dist/admin/api/accept-invite.js +30 -0
  5. package/dist/admin/api/handler.d.ts +13 -0
  6. package/dist/admin/api/handler.js +36 -0
  7. package/dist/admin/api/invite.d.ts +5 -0
  8. package/dist/admin/api/invite.js +78 -0
  9. package/dist/admin/client/collection/collection-page.svelte +5 -7
  10. package/dist/admin/client/collection/collection-page.svelte.d.ts +2 -2
  11. package/dist/admin/client/entry/entry-page.svelte +5 -7
  12. package/dist/admin/client/entry/entry-page.svelte.d.ts +2 -2
  13. package/dist/admin/client/index.d.ts +1 -0
  14. package/dist/admin/client/index.js +1 -0
  15. package/dist/cli/index.d.ts +2 -0
  16. package/dist/cli/index.js +28 -0
  17. package/dist/cli/scaffold/admin.d.ts +4 -0
  18. package/dist/cli/scaffold/admin.js +185 -0
  19. package/dist/components/ui/accordion/accordion.svelte.d.ts +1 -1
  20. package/dist/components/ui/calendar/calendar.svelte.d.ts +1 -1
  21. package/dist/components/ui/command/command-dialog.svelte.d.ts +1 -1
  22. package/dist/components/ui/command/command-input.svelte.d.ts +1 -1
  23. package/dist/components/ui/command/command.svelte.d.ts +1 -1
  24. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
  25. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  26. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
  27. package/dist/components/ui/input-group/input-group-textarea.svelte.d.ts +1 -1
  28. package/dist/components/ui/radio-group/radio-group.svelte.d.ts +1 -1
  29. package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
  30. package/dist/components/ui/tabs/tabs.svelte.d.ts +1 -1
  31. package/dist/components/ui/textarea/textarea.svelte.d.ts +1 -1
  32. package/dist/components/ui/toggle-group/toggle-group-item.svelte.d.ts +1 -1
  33. package/dist/components/ui/toggle-group/toggle-group.svelte.d.ts +1 -1
  34. package/dist/core/cms.d.ts +2 -0
  35. package/dist/core/cms.js +2 -0
  36. package/dist/types/cms.d.ts +18 -0
  37. package/dist/updates/0.1.3/index.d.ts +2 -0
  38. package/dist/updates/0.1.3/index.js +16 -0
  39. package/dist/updates/index.js +2 -1
  40. package/package.json +10 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to includio-cms are documented here.
4
4
  Generated from `src/lib/updates/` — do not edit manually.
5
5
 
6
+ ## 0.1.3 — 2026-02-18
7
+
8
+ Admin DX — route scaffolding & boilerplate reduction
9
+
10
+ ### Added
11
+ - CLI: `includio scaffold admin` generates all admin route files (14 files, 0 page.server.ts)
12
+ - CollectionPage reads page.params.collection directly (no +page.server.ts needed)
13
+ - EntryPage reads page.params.entryId directly (no +page.server.ts needed)
14
+ - Catch-all API handler: createAdminApiHandler() routes upload/orphaned/replace/invite/accept-invite
15
+ - accept-invite handler factory: createAcceptInviteHandler(auth) for project-specific auth injection
16
+ - AcceptInvitePage exported from includio-cms/admin/client
17
+ - AccountPage exported via includio-cms/admin/client/account
18
+
6
19
  ## 0.1.2 — 2026-02-18
7
20
 
8
21
  User management & RBAC
package/ROADMAP.md CHANGED
@@ -44,6 +44,10 @@
44
44
  - [x] `[feature]` `[P1]` Admin session mgmt — list/revoke sesji innych userów <!-- files: src/lib/admin/client/users/user-sessions-sheet.svelte -->
45
45
  - [x] `[feature]` `[P2]` Email invitation system — invite link generation (custom, nie w better-auth) <!-- files: src/lib/admin/remote/invite.ts -->
46
46
 
47
+ ## 0.1.3 — Admin DX
48
+
49
+ - [x] `[feature]` `[P1]` Admin route scaffolding — CLI `includio scaffold admin`, catch-all API handler, page.params fallbacks for CollectionPage/EntryPage <!-- files: src/lib/cli/scaffold/admin.ts, src/lib/admin/api/handler.ts, src/lib/admin/client/collection/collection-page.svelte, src/lib/admin/client/entry/entry-page.svelte -->
50
+
47
51
  ## 0.2.0 — Plugin system
48
52
 
49
53
  - [ ] `[feature]` `[P0]` Wire plugin hooks into CRUD operations (before/afterCreate, Update, Delete) <!-- files: src/lib/types/plugins.ts, src/lib/core/server/entries/operations/ -->
@@ -0,0 +1,2 @@
1
+ import { type RequestHandler } from '@sveltejs/kit';
2
+ export declare const POST: RequestHandler;
@@ -0,0 +1,30 @@
1
+ import { getInvitationByToken, markInvitationUsed } from '../remote/invite.js';
2
+ import { getCMS } from '../../core/cms.js';
3
+ import { json } from '@sveltejs/kit';
4
+ export const POST = async ({ request }) => {
5
+ const auth = getCMS().auth;
6
+ if (!auth) {
7
+ return json({ error: 'Auth adapter not configured' }, { status: 500 });
8
+ }
9
+ const { token, name, password } = await request.json();
10
+ if (!token || !name || !password) {
11
+ return json({ error: 'Token, name and password are required' }, { status: 400 });
12
+ }
13
+ const inv = await getInvitationByToken(token);
14
+ if (!inv) {
15
+ return json({ error: 'Invalid or expired invitation' }, { status: 400 });
16
+ }
17
+ const response = await auth.api.createUser({
18
+ body: {
19
+ email: inv.email,
20
+ password,
21
+ name,
22
+ role: inv.role
23
+ }
24
+ });
25
+ if (!response?.user) {
26
+ return json({ error: 'Failed to create user' }, { status: 500 });
27
+ }
28
+ await markInvitationUsed(inv.id);
29
+ return json({ success: true });
30
+ };
@@ -0,0 +1,13 @@
1
+ import { type RequestHandler } from '@sveltejs/kit';
2
+ type Handlers = Partial<Record<'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE', RequestHandler>>;
3
+ type AdminApiOptions = {
4
+ extraRoutes?: Record<string, Handlers>;
5
+ };
6
+ export declare function createAdminApiHandler(options?: AdminApiOptions): {
7
+ GET: RequestHandler;
8
+ POST: RequestHandler;
9
+ PATCH: RequestHandler;
10
+ PUT: RequestHandler;
11
+ DELETE: RequestHandler;
12
+ };
13
+ export {};
@@ -0,0 +1,36 @@
1
+ import { json } from '@sveltejs/kit';
2
+ import * as uploadHandlers from './upload.js';
3
+ import * as orphanedHandlers from './orphaned.js';
4
+ import * as replaceHandlers from './replace.js';
5
+ import * as inviteHandlers from './invite.js';
6
+ import * as acceptInviteHandlers from './accept-invite.js';
7
+ export function createAdminApiHandler(options) {
8
+ const routes = {
9
+ upload: uploadHandlers,
10
+ orphaned: orphanedHandlers,
11
+ replace: replaceHandlers,
12
+ invite: inviteHandlers,
13
+ 'accept-invite': acceptInviteHandlers,
14
+ ...options?.extraRoutes
15
+ };
16
+ function handle(method) {
17
+ return (event) => {
18
+ const path = event.params.path;
19
+ if (!path) {
20
+ return json({ error: 'Not found' }, { status: 404 });
21
+ }
22
+ const handler = routes[path]?.[method];
23
+ if (!handler) {
24
+ return json({ error: 'Not found' }, { status: 404 });
25
+ }
26
+ return handler(event);
27
+ };
28
+ }
29
+ return {
30
+ GET: handle('GET'),
31
+ POST: handle('POST'),
32
+ PATCH: handle('PATCH'),
33
+ PUT: handle('PUT'),
34
+ DELETE: handle('DELETE')
35
+ };
36
+ }
@@ -0,0 +1,5 @@
1
+ import { type RequestHandler } from '@sveltejs/kit';
2
+ export declare const POST: RequestHandler;
3
+ export declare const GET: RequestHandler;
4
+ export declare const PATCH: RequestHandler;
5
+ export declare const DELETE: RequestHandler;
@@ -0,0 +1,78 @@
1
+ import { requireRole } from '../remote/middleware/auth.js';
2
+ import { createInvitation, getPendingInvitations, deleteInvitation, checkEmailExists, getInvitationById } from '../remote/invite.js';
3
+ import { getCMS } from '../../core/cms.js';
4
+ import { json } from '@sveltejs/kit';
5
+ export const POST = async ({ request, url }) => {
6
+ const { user } = requireRole('admin');
7
+ const { email, role } = await request.json();
8
+ if (!email || !role) {
9
+ return json({ error: 'Email and role are required' }, { status: 400 });
10
+ }
11
+ const emailAdapter = getCMS().emailAdapter;
12
+ if (!emailAdapter) {
13
+ return json({ error: 'Email adapter not configured' }, { status: 400 });
14
+ }
15
+ const exists = await checkEmailExists(email);
16
+ if (exists) {
17
+ return json({ error: 'Email already registered' }, { status: 409 });
18
+ }
19
+ const inv = await createInvitation(email, role, user.id);
20
+ const inviteUrl = `${url.origin}/admin/accept-invite?token=${inv.token}`;
21
+ try {
22
+ await emailAdapter.sendMail({
23
+ to: [email],
24
+ subject: 'You have been invited to includio CMS',
25
+ html: `<p>You have been invited to join the CMS as <strong>${role}</strong>.</p>
26
+ <p><a href="${inviteUrl}">Click here to accept the invitation</a></p>
27
+ <p>This link expires in 7 days.</p>`
28
+ });
29
+ }
30
+ catch (err) {
31
+ await deleteInvitation(inv.id);
32
+ return json({ error: 'Failed to send email: ' + (err instanceof Error ? err.message : 'Unknown error') }, { status: 502 });
33
+ }
34
+ return json({ success: true, invitationId: inv.id });
35
+ };
36
+ export const GET = async () => {
37
+ requireRole('admin');
38
+ const invitations = await getPendingInvitations();
39
+ return json({ invitations });
40
+ };
41
+ export const PATCH = async ({ request, url }) => {
42
+ requireRole('admin');
43
+ const { id } = await request.json();
44
+ if (!id) {
45
+ return json({ error: 'Invitation id is required' }, { status: 400 });
46
+ }
47
+ const emailAdapter = getCMS().emailAdapter;
48
+ if (!emailAdapter) {
49
+ return json({ error: 'Email adapter not configured' }, { status: 400 });
50
+ }
51
+ const inv = await getInvitationById(id);
52
+ if (!inv) {
53
+ return json({ error: 'Invitation not found or expired' }, { status: 404 });
54
+ }
55
+ const inviteUrl = `${url.origin}/admin/accept-invite?token=${inv.token}`;
56
+ try {
57
+ await emailAdapter.sendMail({
58
+ to: [inv.email],
59
+ subject: 'You have been invited to includio CMS',
60
+ html: `<p>You have been invited to join the CMS as <strong>${inv.role}</strong>.</p>
61
+ <p><a href="${inviteUrl}">Click here to accept the invitation</a></p>
62
+ <p>This link expires in 7 days.</p>`
63
+ });
64
+ }
65
+ catch (err) {
66
+ return json({ error: 'Failed to send email: ' + (err instanceof Error ? err.message : 'Unknown error') }, { status: 502 });
67
+ }
68
+ return json({ success: true });
69
+ };
70
+ export const DELETE = async ({ request }) => {
71
+ requireRole('admin');
72
+ const { id } = await request.json();
73
+ if (!id) {
74
+ return json({ error: 'Invitation id is required' }, { status: 400 });
75
+ }
76
+ await deleteInvitation(id);
77
+ return json({ success: true });
78
+ };
@@ -1,18 +1,16 @@
1
1
  <script lang="ts">
2
+ import { page } from '$app/state';
2
3
  import { getRemotes } from '../../../sveltekit/index.js';
3
4
  import Collection from './collection.svelte';
4
5
 
5
6
  const remotes = getRemotes();
6
7
 
7
- type Props = {
8
- data: {
9
- collectionSlug: string;
10
- };
11
- };
12
-
8
+ type Props = { data?: { collectionSlug?: string } };
13
9
  let { data }: Props = $props();
10
+
11
+ const collectionSlug = $derived(data?.collectionSlug || page.params.collection || '');
14
12
  </script>
15
13
 
16
- {#await remotes.getCollection(data.collectionSlug) then collection}
14
+ {#await remotes.getCollection(collectionSlug) then collection}
17
15
  <Collection {collection} />
18
16
  {/await}
@@ -1,6 +1,6 @@
1
1
  type Props = {
2
- data: {
3
- collectionSlug: string;
2
+ data?: {
3
+ collectionSlug?: string;
4
4
  };
5
5
  };
6
6
  declare const CollectionPage: import("svelte").Component<Props, {}, "">;
@@ -1,20 +1,18 @@
1
1
  <script lang="ts">
2
+ import { page } from '$app/state';
2
3
  import { getRemotes } from '../../context/remotes.js';
3
4
  import EntryVersion from './entry-version.svelte';
4
5
  import EntryPageSkeleton from './entry-page-skeleton.svelte';
5
6
 
6
7
  const remotes = getRemotes();
7
8
 
8
- type Props = {
9
- data: {
10
- entryId: string;
11
- };
12
- };
13
-
9
+ type Props = { data?: { entryId?: string } };
14
10
  let { data }: Props = $props();
11
+
12
+ const entryId = $derived(data?.entryId || page.params.entryId || '');
15
13
  </script>
16
14
 
17
- {#await remotes.getEntryForEntryPage(data.entryId)}
15
+ {#await remotes.getEntryForEntryPage(entryId)}
18
16
  <EntryPageSkeleton />
19
17
  {:then entry}
20
18
  <EntryVersion {entry} />
@@ -1,6 +1,6 @@
1
1
  type Props = {
2
- data: {
3
- entryId: string;
2
+ data?: {
3
+ entryId?: string;
4
4
  };
5
5
  };
6
6
  declare const EntryPage: import("svelte").Component<Props, {}, "">;
@@ -8,3 +8,4 @@ export { default as MediaPage } from './media/media-page.svelte';
8
8
  export { default as FormPage } from './form/form-page.svelte';
9
9
  export { default as FormSubmissionPage } from './form/form-submission/form-submission-page.svelte';
10
10
  export { default as UsersPage } from './users/users-page.svelte';
11
+ export { default as AcceptInvitePage } from './users/accept-invite-page.svelte';
@@ -8,3 +8,4 @@ export { default as MediaPage } from './media/media-page.svelte';
8
8
  export { default as FormPage } from './form/form-page.svelte';
9
9
  export { default as FormSubmissionPage } from './form/form-submission/form-submission-page.svelte';
10
10
  export { default as UsersPage } from './users/users-page.svelte';
11
+ export { default as AcceptInvitePage } from './users/accept-invite-page.svelte';
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { scaffoldAdmin } from './scaffold/admin.js';
3
+ import path from 'node:path';
4
+ const args = process.argv.slice(2);
5
+ const command = args[0];
6
+ const subcommand = args[1];
7
+ function printUsage() {
8
+ console.log(`Usage: includio <command>
9
+
10
+ Commands:
11
+ scaffold admin Generate admin route files
12
+
13
+ Options:
14
+ --force Overwrite existing files
15
+ --routes-dir Path to routes directory (default: src/routes)
16
+ `);
17
+ }
18
+ if (command === 'scaffold' && subcommand === 'admin') {
19
+ const force = args.includes('--force');
20
+ const routesDirIdx = args.indexOf('--routes-dir');
21
+ const routesDir = routesDirIdx !== -1 ? args[routesDirIdx + 1] : path.join(process.cwd(), 'src', 'routes');
22
+ console.log('Scaffolding admin routes...\n');
23
+ scaffoldAdmin({ routesDir, force });
24
+ }
25
+ else {
26
+ printUsage();
27
+ process.exit(command ? 1 : 0);
28
+ }
@@ -0,0 +1,4 @@
1
+ export declare function scaffoldAdmin(options: {
2
+ routesDir: string;
3
+ force?: boolean;
4
+ }): void;
@@ -0,0 +1,185 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ const GENERATED_COMMENT = '<!-- Generated by includio-cms -->';
4
+ const GENERATED_COMMENT_TS = '// Generated by includio-cms';
5
+ function getAdminFiles() {
6
+ return [
7
+ {
8
+ path: 'admin/+layout.svelte',
9
+ content: `${GENERATED_COMMENT}
10
+ <script lang="ts">
11
+ import { AdminLayout } from 'includio-cms/admin/client';
12
+ import * as remotes from './admin.remote';
13
+ import { setRemotes } from 'includio-cms/sveltekit';
14
+
15
+ let data = $props();
16
+
17
+ setRemotes(remotes);
18
+ </script>
19
+
20
+ <svelte:head>
21
+ <meta name="robots" content="noindex, nofollow" />
22
+ </svelte:head>
23
+
24
+ <AdminLayout {...data} />
25
+ `
26
+ },
27
+ {
28
+ path: 'admin/admin.remote.ts',
29
+ content: `${GENERATED_COMMENT_TS}
30
+ export * from 'includio-cms/admin/remote';
31
+ `
32
+ },
33
+ {
34
+ path: 'admin/login/+page.svelte',
35
+ content: `${GENERATED_COMMENT}
36
+ <script lang="ts">
37
+ import { LoginPage } from 'includio-cms/admin/client';
38
+
39
+ let data = $props();
40
+ </script>
41
+
42
+ <LoginPage {...data} />
43
+ `
44
+ },
45
+ {
46
+ path: 'admin/accept-invite/+page.svelte',
47
+ content: `${GENERATED_COMMENT}
48
+ <script lang="ts">
49
+ import { AcceptInvitePage } from 'includio-cms/admin/client';
50
+ </script>
51
+
52
+ <AcceptInvitePage />
53
+ `
54
+ },
55
+ {
56
+ path: 'admin/(afterLogin)/+layout.svelte',
57
+ content: `${GENERATED_COMMENT}
58
+ <script lang="ts">
59
+ import { AdminAfterLoginLayout } from 'includio-cms/admin/client';
60
+
61
+ let { children } = $props();
62
+ </script>
63
+
64
+ <AdminAfterLoginLayout>
65
+ {@render children()}
66
+ </AdminAfterLoginLayout>
67
+ `
68
+ },
69
+ {
70
+ path: 'admin/(afterLogin)/+page.svelte',
71
+ content: `${GENERATED_COMMENT}
72
+ <script lang="ts">
73
+ import { DashboardPage } from 'includio-cms/admin/client';
74
+
75
+ let data = $props();
76
+ </script>
77
+
78
+ <DashboardPage {...data} />
79
+ `
80
+ },
81
+ {
82
+ path: 'admin/(afterLogin)/collections/[collection]/+page.svelte',
83
+ content: `${GENERATED_COMMENT}
84
+ <script lang="ts">
85
+ import { CollectionPage } from 'includio-cms/admin/client';
86
+ </script>
87
+
88
+ <CollectionPage />
89
+ `
90
+ },
91
+ {
92
+ path: 'admin/(afterLogin)/entries/[entryId]/+page.svelte',
93
+ content: `${GENERATED_COMMENT}
94
+ <script lang="ts">
95
+ import { EntryPage } from 'includio-cms/admin/client';
96
+ </script>
97
+
98
+ <EntryPage />
99
+ `
100
+ },
101
+ {
102
+ path: 'admin/(afterLogin)/forms/[form]/+page.svelte',
103
+ content: `${GENERATED_COMMENT}
104
+ <script lang="ts">
105
+ import { FormPage } from 'includio-cms/admin/client';
106
+ </script>
107
+
108
+ <FormPage />
109
+ `
110
+ },
111
+ {
112
+ path: 'admin/(afterLogin)/form-submissions/[submissionId]/+page.svelte',
113
+ content: `${GENERATED_COMMENT}
114
+ <script lang="ts">
115
+ import { FormSubmissionPage } from 'includio-cms/admin/client';
116
+ </script>
117
+
118
+ <FormSubmissionPage />
119
+ `
120
+ },
121
+ {
122
+ path: 'admin/(afterLogin)/media/+page.svelte',
123
+ content: `${GENERATED_COMMENT}
124
+ <script lang="ts">
125
+ import { MediaPage } from 'includio-cms/admin/client';
126
+
127
+ let data = $props();
128
+ </script>
129
+
130
+ <MediaPage {...data} />
131
+ `
132
+ },
133
+ {
134
+ path: 'admin/(afterLogin)/account/+page.svelte',
135
+ content: `${GENERATED_COMMENT}
136
+ <script lang="ts">
137
+ import { AccountPage } from 'includio-cms/admin/client/account';
138
+ </script>
139
+
140
+ <AccountPage />
141
+ `
142
+ },
143
+ {
144
+ path: 'admin/(afterLogin)/users/+page.svelte',
145
+ content: `${GENERATED_COMMENT}
146
+ <script lang="ts">
147
+ import { UsersPage } from 'includio-cms/admin/client';
148
+ </script>
149
+
150
+ <UsersPage />
151
+ `
152
+ },
153
+ {
154
+ path: 'admin/api/[...path]/+server.ts',
155
+ content: `${GENERATED_COMMENT_TS}
156
+ import { createAdminApiHandler } from 'includio-cms/admin/api/handler';
157
+
158
+ export const { GET, POST, PATCH, PUT, DELETE } = createAdminApiHandler();
159
+ `
160
+ }
161
+ ];
162
+ }
163
+ export function scaffoldAdmin(options) {
164
+ const { routesDir, force = false } = options;
165
+ const files = getAdminFiles();
166
+ let created = 0;
167
+ let skipped = 0;
168
+ for (const file of files) {
169
+ const fullPath = path.join(routesDir, file.path);
170
+ const dir = path.dirname(fullPath);
171
+ if (fs.existsSync(fullPath) && !force) {
172
+ console.log(` skip ${file.path} (exists)`);
173
+ skipped++;
174
+ continue;
175
+ }
176
+ fs.mkdirSync(dir, { recursive: true });
177
+ fs.writeFileSync(fullPath, file.content);
178
+ console.log(` create ${file.path}`);
179
+ created++;
180
+ }
181
+ console.log(`\nDone: ${created} created, ${skipped} skipped`);
182
+ if (skipped > 0 && !force) {
183
+ console.log('Use --force to overwrite existing files.');
184
+ }
185
+ }
@@ -1,4 +1,4 @@
1
1
  import { Accordion as AccordionPrimitive } from "bits-ui";
2
- declare const Accordion: import("svelte").Component<AccordionPrimitive.RootProps, {}, "value" | "ref">;
2
+ declare const Accordion: import("svelte").Component<AccordionPrimitive.RootProps, {}, "ref" | "value">;
3
3
  type Accordion = ReturnType<typeof Accordion>;
4
4
  export default Accordion;
@@ -16,6 +16,6 @@ type $$ComponentProps = WithoutChildrenOrChild<CalendarPrimitive.RootProps> & {
16
16
  outsideMonth: boolean;
17
17
  }]>;
18
18
  };
19
- declare const Calendar: import("svelte").Component<$$ComponentProps, {}, "value" | "placeholder" | "ref">;
19
+ declare const Calendar: import("svelte").Component<$$ComponentProps, {}, "ref" | "value" | "placeholder">;
20
20
  type Calendar = ReturnType<typeof Calendar>;
21
21
  export default Calendar;
@@ -7,6 +7,6 @@ type $$ComponentProps = WithoutChildrenOrChild<DialogPrimitive.RootProps> & With
7
7
  title?: string;
8
8
  description?: string;
9
9
  };
10
- declare const CommandDialog: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "open">;
10
+ declare const CommandDialog: import("svelte").Component<$$ComponentProps, {}, "ref" | "value" | "open">;
11
11
  type CommandDialog = ReturnType<typeof CommandDialog>;
12
12
  export default CommandDialog;
@@ -1,4 +1,4 @@
1
1
  import { Command as CommandPrimitive } from "bits-ui";
2
- declare const CommandInput: import("svelte").Component<CommandPrimitive.InputProps, {}, "value" | "ref">;
2
+ declare const CommandInput: import("svelte").Component<CommandPrimitive.InputProps, {}, "ref" | "value">;
3
3
  type CommandInput = ReturnType<typeof CommandInput>;
4
4
  export default CommandInput;
@@ -3,6 +3,6 @@ export type CommandRootApi = CommandPrimitive.Root;
3
3
  type $$ComponentProps = CommandPrimitive.RootProps & {
4
4
  api?: CommandRootApi | null;
5
5
  };
6
- declare const Command: import("svelte").Component<$$ComponentProps, {}, "api" | "value" | "ref">;
6
+ declare const Command: import("svelte").Component<$$ComponentProps, {}, "api" | "ref" | "value">;
7
7
  type Command = ReturnType<typeof Command>;
8
8
  export default Command;
@@ -1,4 +1,4 @@
1
1
  import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
2
- declare const DropdownMenuRadioGroup: import("svelte").Component<DropdownMenuPrimitive.RadioGroupProps, {}, "value" | "ref">;
2
+ declare const DropdownMenuRadioGroup: import("svelte").Component<DropdownMenuPrimitive.RadioGroupProps, {}, "ref" | "value">;
3
3
  type DropdownMenuRadioGroup = ReturnType<typeof DropdownMenuRadioGroup>;
4
4
  export default DropdownMenuRadioGroup;
@@ -8,6 +8,6 @@ type Props = WithElementRef<Omit<HTMLInputAttributes, "type"> & ({
8
8
  type?: InputType;
9
9
  files?: undefined;
10
10
  })>;
11
- declare const Input: import("svelte").Component<Props, {}, "value" | "ref" | "files">;
11
+ declare const Input: import("svelte").Component<Props, {}, "ref" | "value" | "files">;
12
12
  type Input = ReturnType<typeof Input>;
13
13
  export default Input;
@@ -6,6 +6,6 @@ declare const InputGroupInput: import("svelte").Component<(Omit<import("svelte/e
6
6
  files?: undefined;
7
7
  })) & {
8
8
  ref?: HTMLElement | null | undefined;
9
- }, {}, "value" | "ref">;
9
+ }, {}, "ref" | "value">;
10
10
  type InputGroupInput = ReturnType<typeof InputGroupInput>;
11
11
  export default InputGroupInput;
@@ -1,3 +1,3 @@
1
- declare const InputGroupTextarea: import("svelte").Component<Omit<import("../../../utils.js").WithElementRef<import("svelte/elements").HTMLTextareaAttributes>, "children">, {}, "value" | "ref">;
1
+ declare const InputGroupTextarea: import("svelte").Component<Omit<import("../../../utils.js").WithElementRef<import("svelte/elements").HTMLTextareaAttributes>, "children">, {}, "ref" | "value">;
2
2
  type InputGroupTextarea = ReturnType<typeof InputGroupTextarea>;
3
3
  export default InputGroupTextarea;
@@ -1,4 +1,4 @@
1
1
  import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
2
- declare const RadioGroup: import("svelte").Component<RadioGroupPrimitive.RootProps, {}, "value" | "ref">;
2
+ declare const RadioGroup: import("svelte").Component<RadioGroupPrimitive.RootProps, {}, "ref" | "value">;
3
3
  type RadioGroup = ReturnType<typeof RadioGroup>;
4
4
  export default RadioGroup;
@@ -6,6 +6,6 @@ declare const SidebarInput: import("svelte").Component<(Omit<import("svelte/elem
6
6
  files?: undefined;
7
7
  })) & {
8
8
  ref?: HTMLElement | null | undefined;
9
- }, {}, "value" | "ref">;
9
+ }, {}, "ref" | "value">;
10
10
  type SidebarInput = ReturnType<typeof SidebarInput>;
11
11
  export default SidebarInput;
@@ -1,4 +1,4 @@
1
1
  import { Tabs as TabsPrimitive } from "bits-ui";
2
- declare const Tabs: import("svelte").Component<TabsPrimitive.RootProps, {}, "value" | "ref">;
2
+ declare const Tabs: import("svelte").Component<TabsPrimitive.RootProps, {}, "ref" | "value">;
3
3
  type Tabs = ReturnType<typeof Tabs>;
4
4
  export default Tabs;
@@ -1,5 +1,5 @@
1
1
  import { type WithElementRef } from "../../../utils.js";
2
2
  import type { HTMLTextareaAttributes } from "svelte/elements";
3
- declare const Textarea: import("svelte").Component<Omit<WithElementRef<HTMLTextareaAttributes>, "children">, {}, "value" | "ref">;
3
+ declare const Textarea: import("svelte").Component<Omit<WithElementRef<HTMLTextareaAttributes>, "children">, {}, "ref" | "value">;
4
4
  type Textarea = ReturnType<typeof Textarea>;
5
5
  export default Textarea;
@@ -1,6 +1,6 @@
1
1
  import { ToggleGroup as ToggleGroupPrimitive } from "bits-ui";
2
2
  import { type ToggleVariants } from "../toggle/index.js";
3
3
  type $$ComponentProps = ToggleGroupPrimitive.ItemProps & ToggleVariants;
4
- declare const ToggleGroupItem: import("svelte").Component<$$ComponentProps, {}, "value" | "ref">;
4
+ declare const ToggleGroupItem: import("svelte").Component<$$ComponentProps, {}, "ref" | "value">;
5
5
  type ToggleGroupItem = ReturnType<typeof ToggleGroupItem>;
6
6
  export default ToggleGroupItem;
@@ -3,6 +3,6 @@ export declare function setToggleGroupCtx(props: ToggleVariants): void;
3
3
  export declare function getToggleGroupCtx(): ToggleVariants;
4
4
  import { ToggleGroup as ToggleGroupPrimitive } from "bits-ui";
5
5
  type $$ComponentProps = ToggleGroupPrimitive.RootProps & ToggleVariants;
6
- declare const ToggleGroup: import("svelte").Component<$$ComponentProps, {}, "value" | "ref">;
6
+ declare const ToggleGroup: import("svelte").Component<$$ComponentProps, {}, "ref" | "value">;
7
7
  type ToggleGroup = ReturnType<typeof ToggleGroup>;
8
8
  export default ToggleGroup;
@@ -8,11 +8,13 @@ import type { PluginConfig } from '../types/plugins.js';
8
8
  import type { FormConfig } from '../types/forms.js';
9
9
  import type { AIAdapter } from '../types/adapters/ai.js';
10
10
  import type { EmailAdapter } from '../types/adapters/email.js';
11
+ import type { AuthAdapter } from '../types/cms.js';
11
12
  export declare class CMS implements ICMS {
12
13
  private config;
13
14
  databaseAdapter: DatabaseAdapter;
14
15
  filesAdapter: FilesAdapter;
15
16
  emailAdapter: EmailAdapter | null;
17
+ auth: AuthAdapter | null;
16
18
  aiAdapter: AIAdapter | null;
17
19
  collections: Record<string, CollectionConfigWithType>;
18
20
  singles: Record<string, SingleConfigWithType>;
package/dist/core/cms.js CHANGED
@@ -3,6 +3,7 @@ export class CMS {
3
3
  databaseAdapter;
4
4
  filesAdapter;
5
5
  emailAdapter;
6
+ auth;
6
7
  aiAdapter = null;
7
8
  collections;
8
9
  singles;
@@ -15,6 +16,7 @@ export class CMS {
15
16
  this.databaseAdapter = config.db;
16
17
  this.filesAdapter = config.files;
17
18
  this.emailAdapter = config.email ?? null;
19
+ this.auth = config.auth ?? null;
18
20
  this.aiAdapter = config.ai || null;
19
21
  this.mediaConfig = config.media || {};
20
22
  this.collections = {};
@@ -11,6 +11,22 @@ export interface MediaConfig {
11
11
  maxOriginalWidth?: number;
12
12
  maxOriginalHeight?: number;
13
13
  }
14
+ export interface AuthAdapter {
15
+ api: {
16
+ createUser: (opts: {
17
+ body: {
18
+ email: string;
19
+ password: string;
20
+ name: string;
21
+ role: string;
22
+ };
23
+ }) => Promise<{
24
+ user?: {
25
+ id: string;
26
+ };
27
+ } | null>;
28
+ };
29
+ }
14
30
  export interface CMSConfig {
15
31
  languages: Language[];
16
32
  collections?: CollectionConfig[];
@@ -19,6 +35,7 @@ export interface CMSConfig {
19
35
  db: DatabaseAdapter;
20
36
  files: FilesAdapter;
21
37
  email?: EmailAdapter;
38
+ auth?: AuthAdapter;
22
39
  plugins?: PluginConfig[];
23
40
  ai?: AIAdapter;
24
41
  media?: MediaConfig;
@@ -31,6 +48,7 @@ export interface ICMS {
31
48
  databaseAdapter: DatabaseAdapter;
32
49
  filesAdapter: FilesAdapter;
33
50
  emailAdapter: EmailAdapter | null;
51
+ auth: AuthAdapter | null;
34
52
  plugins: PluginConfig[];
35
53
  aiAdapter: AIAdapter | null;
36
54
  mediaConfig: MediaConfig;
@@ -0,0 +1,2 @@
1
+ import type { CmsUpdate } from '../index.js';
2
+ export declare const update: CmsUpdate;
@@ -0,0 +1,16 @@
1
+ export const update = {
2
+ version: '0.1.3',
3
+ date: '2026-02-18',
4
+ description: 'Admin DX — route scaffolding & boilerplate reduction',
5
+ features: [
6
+ 'CLI: `includio scaffold admin` generates all admin route files (14 files, 0 page.server.ts)',
7
+ 'CollectionPage reads page.params.collection directly (no +page.server.ts needed)',
8
+ 'EntryPage reads page.params.entryId directly (no +page.server.ts needed)',
9
+ 'Catch-all API handler: createAdminApiHandler() routes upload/orphaned/replace/invite/accept-invite',
10
+ 'accept-invite handler factory: createAcceptInviteHandler(auth) for project-specific auth injection',
11
+ 'AcceptInvitePage exported from includio-cms/admin/client',
12
+ 'AccountPage exported via includio-cms/admin/client/account'
13
+ ],
14
+ fixes: [],
15
+ breakingChanges: []
16
+ };
@@ -6,7 +6,8 @@ import { update as update0069 } from './0.0.69/index.js';
6
6
  import { update as update010 } from './0.1.0/index.js';
7
7
  import { update as update011 } from './0.1.1/index.js';
8
8
  import { update as update012 } from './0.1.2/index.js';
9
- export const updates = [update0065, update0066, update0067, update0068, update0069, update010, update011, update012];
9
+ import { update as update013 } from './0.1.3/index.js';
10
+ export const updates = [update0065, update0066, update0067, update0068, update0069, update010, update011, update012, update013];
10
11
  export const getUpdatesFrom = (fromVersion) => {
11
12
  const fromParts = fromVersion.split('.').map(Number);
12
13
  return updates.filter((update) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "includio-cms",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -26,6 +26,9 @@
26
26
  "changelog": "tsx scripts/generate-changelog.ts",
27
27
  "prepublishOnly": "tsx scripts/generate-changelog.ts"
28
28
  },
29
+ "bin": {
30
+ "includio": "./dist/cli/index.js"
31
+ },
29
32
  "files": [
30
33
  "dist",
31
34
  "!dist/**/*.test.*",
@@ -57,7 +60,12 @@
57
60
  },
58
61
  "./admin/api/*": {
59
62
  "types": "./dist/admin/api/*.d.ts",
60
- "svelte": "./dist/admin/api/*.js"
63
+ "svelte": "./dist/admin/api/*.js",
64
+ "node": "./dist/admin/api/*.js"
65
+ },
66
+ "./admin/client/account": {
67
+ "types": "./dist/admin/client/account/index.d.ts",
68
+ "svelte": "./dist/admin/client/account/index.js"
61
69
  },
62
70
  "./admin/remote": {
63
71
  "types": "./dist/admin/remote/index.d.ts",