@glw907/cairn-cms 0.5.1 → 0.6.0-rc.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.
Files changed (233) hide show
  1. package/dist/auth/crypto.d.ts +13 -0
  2. package/dist/auth/crypto.d.ts.map +1 -0
  3. package/dist/auth/crypto.js +31 -0
  4. package/dist/auth/store.d.ts +41 -0
  5. package/dist/auth/store.d.ts.map +1 -0
  6. package/dist/auth/store.js +115 -0
  7. package/dist/auth/types.d.ts +25 -0
  8. package/dist/auth/types.d.ts.map +1 -0
  9. package/dist/auth/types.js +1 -0
  10. package/dist/components/AdminLayout.svelte +58 -164
  11. package/dist/components/AdminLayout.svelte.d.ts +14 -18
  12. package/dist/components/AdminLayout.svelte.d.ts.map +1 -1
  13. package/dist/components/ComponentPalette.svelte +36 -20
  14. package/dist/components/ComponentPalette.svelte.d.ts +11 -4
  15. package/dist/components/ComponentPalette.svelte.d.ts.map +1 -1
  16. package/dist/components/ConceptList.svelte +81 -0
  17. package/dist/components/ConceptList.svelte.d.ts +13 -0
  18. package/dist/components/ConceptList.svelte.d.ts.map +1 -0
  19. package/dist/components/ConfirmPage.svelte +23 -20
  20. package/dist/components/ConfirmPage.svelte.d.ts +6 -0
  21. package/dist/components/ConfirmPage.svelte.d.ts.map +1 -1
  22. package/dist/components/EditPage.svelte +155 -136
  23. package/dist/components/EditPage.svelte.d.ts +16 -8
  24. package/dist/components/EditPage.svelte.d.ts.map +1 -1
  25. package/dist/components/LoginPage.svelte +42 -52
  26. package/dist/components/LoginPage.svelte.d.ts +12 -0
  27. package/dist/components/LoginPage.svelte.d.ts.map +1 -1
  28. package/dist/components/ManageEditors.svelte +81 -0
  29. package/dist/components/ManageEditors.svelte.d.ts +24 -0
  30. package/dist/components/ManageEditors.svelte.d.ts.map +1 -0
  31. package/dist/components/MarkdownEditor.svelte +81 -0
  32. package/dist/components/MarkdownEditor.svelte.d.ts +20 -0
  33. package/dist/components/MarkdownEditor.svelte.d.ts.map +1 -0
  34. package/dist/components/NavTree.svelte +73 -63
  35. package/dist/components/NavTree.svelte.d.ts +13 -4
  36. package/dist/components/NavTree.svelte.d.ts.map +1 -1
  37. package/dist/components/cairn-admin.css +42 -0
  38. package/dist/components/index.d.ts +3 -2
  39. package/dist/components/index.d.ts.map +1 -1
  40. package/dist/components/index.js +5 -4
  41. package/dist/content/compose.d.ts +7 -0
  42. package/dist/content/compose.d.ts.map +1 -0
  43. package/dist/content/compose.js +32 -0
  44. package/dist/content/concepts.d.ts +17 -0
  45. package/dist/content/concepts.d.ts.map +1 -0
  46. package/dist/content/concepts.js +41 -0
  47. package/dist/content/frontmatter.d.ts +18 -0
  48. package/dist/content/frontmatter.d.ts.map +1 -0
  49. package/dist/content/frontmatter.js +58 -0
  50. package/dist/content/ids.d.ts +17 -0
  51. package/dist/content/ids.d.ts.map +1 -0
  52. package/dist/content/ids.js +33 -0
  53. package/dist/content/types.d.ts +210 -0
  54. package/dist/content/types.d.ts.map +1 -0
  55. package/dist/content/types.js +1 -0
  56. package/dist/content/validate.d.ts +13 -0
  57. package/dist/content/validate.d.ts.map +1 -0
  58. package/dist/content/validate.js +45 -0
  59. package/dist/email.d.ts +25 -12
  60. package/dist/email.d.ts.map +1 -1
  61. package/dist/email.js +24 -24
  62. package/dist/env.d.ts +24 -0
  63. package/dist/env.d.ts.map +1 -0
  64. package/dist/env.js +29 -0
  65. package/dist/github/credentials.d.ts +12 -0
  66. package/dist/github/credentials.d.ts.map +1 -0
  67. package/dist/github/credentials.js +11 -0
  68. package/dist/github/repo.d.ts +49 -0
  69. package/dist/github/repo.d.ts.map +1 -0
  70. package/dist/github/repo.js +123 -0
  71. package/dist/github/signing.d.ts +17 -0
  72. package/dist/github/signing.d.ts.map +1 -0
  73. package/dist/github/signing.js +79 -0
  74. package/dist/github/types.d.ts +35 -0
  75. package/dist/github/types.d.ts.map +1 -0
  76. package/dist/github/types.js +19 -0
  77. package/dist/index.d.ts +27 -8
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +21 -10
  80. package/dist/{nav.d.ts → nav/site-config.d.ts} +16 -24
  81. package/dist/nav/site-config.d.ts.map +1 -0
  82. package/dist/{nav.js → nav/site-config.js} +27 -13
  83. package/dist/render/glyph.d.ts +1 -1
  84. package/dist/render/glyph.d.ts.map +1 -1
  85. package/dist/render/index.d.ts +5 -5
  86. package/dist/render/index.d.ts.map +1 -1
  87. package/dist/render/index.js +6 -6
  88. package/dist/render/pipeline.d.ts +3 -3
  89. package/dist/render/pipeline.d.ts.map +1 -1
  90. package/dist/render/pipeline.js +4 -4
  91. package/dist/render/registry.d.ts +6 -4
  92. package/dist/render/registry.d.ts.map +1 -1
  93. package/dist/render/registry.js +8 -6
  94. package/dist/render/rehype-dispatch.d.ts +1 -1
  95. package/dist/render/rehype-dispatch.d.ts.map +1 -1
  96. package/dist/render/remark-directives.d.ts +1 -1
  97. package/dist/render/remark-directives.d.ts.map +1 -1
  98. package/dist/render/sanitize.d.ts +8 -0
  99. package/dist/render/sanitize.d.ts.map +1 -0
  100. package/dist/render/sanitize.js +26 -0
  101. package/dist/sveltekit/auth-routes.d.ts +23 -0
  102. package/dist/sveltekit/auth-routes.d.ts.map +1 -0
  103. package/dist/sveltekit/auth-routes.js +85 -0
  104. package/dist/sveltekit/content-routes.d.ts +80 -0
  105. package/dist/sveltekit/content-routes.d.ts.map +1 -0
  106. package/dist/sveltekit/content-routes.js +183 -0
  107. package/dist/sveltekit/editors-routes.d.ts +24 -0
  108. package/dist/sveltekit/editors-routes.d.ts.map +1 -0
  109. package/dist/sveltekit/editors-routes.js +73 -0
  110. package/dist/sveltekit/guard.d.ts +9 -0
  111. package/dist/sveltekit/guard.d.ts.map +1 -0
  112. package/dist/sveltekit/guard.js +43 -0
  113. package/dist/sveltekit/health.d.ts +19 -0
  114. package/dist/sveltekit/health.d.ts.map +1 -0
  115. package/dist/sveltekit/health.js +12 -0
  116. package/dist/sveltekit/index.d.ts +9 -173
  117. package/dist/sveltekit/index.d.ts.map +1 -1
  118. package/dist/sveltekit/index.js +8 -348
  119. package/dist/sveltekit/nav-routes.d.ts +30 -0
  120. package/dist/sveltekit/nav-routes.d.ts.map +1 -0
  121. package/dist/sveltekit/nav-routes.js +103 -0
  122. package/dist/sveltekit/types.d.ts +32 -0
  123. package/dist/sveltekit/types.d.ts.map +1 -0
  124. package/dist/sveltekit/types.js +1 -0
  125. package/package.json +32 -57
  126. package/src/lib/auth/crypto.ts +37 -0
  127. package/src/lib/auth/store.ts +158 -0
  128. package/src/lib/auth/types.ts +27 -0
  129. package/src/lib/components/AdminLayout.svelte +58 -164
  130. package/src/lib/components/ComponentPalette.svelte +36 -20
  131. package/src/lib/components/ConceptList.svelte +81 -0
  132. package/src/lib/components/ConfirmPage.svelte +23 -20
  133. package/src/lib/components/EditPage.svelte +155 -136
  134. package/src/lib/components/LoginPage.svelte +42 -52
  135. package/src/lib/components/ManageEditors.svelte +81 -0
  136. package/src/lib/components/MarkdownEditor.svelte +81 -0
  137. package/src/lib/components/NavTree.svelte +73 -63
  138. package/src/lib/components/cairn-admin.css +42 -0
  139. package/src/lib/components/index.ts +5 -4
  140. package/src/lib/content/compose.ts +39 -0
  141. package/src/lib/content/concepts.ts +57 -0
  142. package/src/lib/content/frontmatter.ts +71 -0
  143. package/src/lib/content/ids.ts +38 -0
  144. package/src/lib/content/types.ts +235 -0
  145. package/src/lib/content/validate.ts +51 -0
  146. package/src/lib/email.ts +52 -38
  147. package/src/lib/env.ts +32 -0
  148. package/src/lib/github/credentials.ts +27 -0
  149. package/src/lib/github/repo.ts +138 -0
  150. package/src/lib/github/signing.ts +97 -0
  151. package/src/lib/github/types.ts +46 -0
  152. package/src/lib/index.ts +86 -10
  153. package/src/lib/{nav.ts → nav/site-config.ts} +31 -24
  154. package/src/lib/render/glyph.ts +6 -6
  155. package/src/lib/render/index.ts +6 -6
  156. package/src/lib/render/pipeline.ts +22 -22
  157. package/src/lib/render/registry.ts +33 -26
  158. package/src/lib/render/rehype-dispatch.ts +47 -47
  159. package/src/lib/render/remark-directives.ts +46 -46
  160. package/src/lib/render/sanitize.ts +27 -0
  161. package/src/lib/sveltekit/auth-routes.ts +107 -0
  162. package/src/lib/sveltekit/content-routes.ts +261 -0
  163. package/src/lib/sveltekit/editors-routes.ts +82 -0
  164. package/src/lib/sveltekit/guard.ts +47 -0
  165. package/src/lib/sveltekit/health.ts +24 -0
  166. package/src/lib/sveltekit/index.ts +19 -512
  167. package/src/lib/sveltekit/nav-routes.ts +139 -0
  168. package/src/lib/sveltekit/types.ts +33 -0
  169. package/dist/adapter.d.ts +0 -93
  170. package/dist/adapter.d.ts.map +0 -1
  171. package/dist/adapter.js +0 -30
  172. package/dist/auth/admins.d.ts +0 -33
  173. package/dist/auth/admins.d.ts.map +0 -1
  174. package/dist/auth/admins.js +0 -90
  175. package/dist/auth/capabilities.d.ts +0 -7
  176. package/dist/auth/capabilities.d.ts.map +0 -1
  177. package/dist/auth/capabilities.js +0 -26
  178. package/dist/auth/config.d.ts +0 -2097
  179. package/dist/auth/config.d.ts.map +0 -1
  180. package/dist/auth/config.js +0 -78
  181. package/dist/auth/guard.d.ts +0 -34
  182. package/dist/auth/guard.d.ts.map +0 -1
  183. package/dist/auth/guard.js +0 -47
  184. package/dist/auth/index.d.ts +0 -5
  185. package/dist/auth/index.d.ts.map +0 -1
  186. package/dist/auth/index.js +0 -7
  187. package/dist/auth/schema.d.ts +0 -750
  188. package/dist/auth/schema.d.ts.map +0 -1
  189. package/dist/auth/schema.js +0 -93
  190. package/dist/carta.d.ts +0 -39
  191. package/dist/carta.d.ts.map +0 -1
  192. package/dist/carta.js +0 -30
  193. package/dist/components/CollectionList.svelte +0 -96
  194. package/dist/components/CollectionList.svelte.d.ts +0 -8
  195. package/dist/components/CollectionList.svelte.d.ts.map +0 -1
  196. package/dist/components/ManageAdmins.svelte +0 -84
  197. package/dist/components/ManageAdmins.svelte.d.ts +0 -10
  198. package/dist/components/ManageAdmins.svelte.d.ts.map +0 -1
  199. package/dist/content.d.ts +0 -3
  200. package/dist/content.d.ts.map +0 -1
  201. package/dist/content.js +0 -10
  202. package/dist/editor.d.ts +0 -25
  203. package/dist/editor.d.ts.map +0 -1
  204. package/dist/editor.js +0 -20
  205. package/dist/frontmatter.d.ts +0 -3
  206. package/dist/frontmatter.d.ts.map +0 -1
  207. package/dist/frontmatter.js +0 -16
  208. package/dist/github.d.ts +0 -72
  209. package/dist/github.d.ts.map +0 -1
  210. package/dist/github.js +0 -171
  211. package/dist/nav.d.ts.map +0 -1
  212. package/dist/slug.d.ts +0 -7
  213. package/dist/slug.d.ts.map +0 -1
  214. package/dist/slug.js +0 -15
  215. package/dist/utils.d.ts +0 -3
  216. package/dist/utils.d.ts.map +0 -1
  217. package/dist/utils.js +0 -11
  218. package/src/lib/adapter.ts +0 -144
  219. package/src/lib/auth/admins.ts +0 -106
  220. package/src/lib/auth/capabilities.ts +0 -35
  221. package/src/lib/auth/config.ts +0 -108
  222. package/src/lib/auth/guard.ts +0 -60
  223. package/src/lib/auth/index.ts +0 -7
  224. package/src/lib/auth/schema.ts +0 -112
  225. package/src/lib/carta.ts +0 -59
  226. package/src/lib/components/CollectionList.svelte +0 -96
  227. package/src/lib/components/ManageAdmins.svelte +0 -84
  228. package/src/lib/content.ts +0 -11
  229. package/src/lib/editor.ts +0 -38
  230. package/src/lib/frontmatter.ts +0 -17
  231. package/src/lib/github.ts +0 -220
  232. package/src/lib/slug.ts +0 -16
  233. package/src/lib/utils.ts +0 -12
@@ -0,0 +1,33 @@
1
+ // Structural subsets of SvelteKit's RequestEvent. A site passes its real event, which has
2
+ // these and more, so the engine never imports a site's generated App.* ambient types.
3
+ import type { AuthEnv, Editor } from '../auth/types.js';
4
+
5
+ export interface CookieSetOptions {
6
+ path: string;
7
+ httpOnly?: boolean;
8
+ secure?: boolean;
9
+ sameSite?: 'lax' | 'strict' | 'none';
10
+ maxAge?: number;
11
+ }
12
+
13
+ export interface CookieJar {
14
+ get(name: string): string | undefined;
15
+ set(name: string, value: string, opts: CookieSetOptions): void;
16
+ delete(name: string, opts: { path: string }): void;
17
+ }
18
+
19
+ export interface RequestContext {
20
+ url: URL;
21
+ request: Request;
22
+ cookies: CookieJar;
23
+ locals: { editor?: Editor | null };
24
+ platform?: { env?: AuthEnv };
25
+ // Required so a site cannot silently drop the confirm page's Referrer-Policy header
26
+ // (spec 7.1). A real SvelteKit RequestEvent always supplies it.
27
+ setHeaders(headers: Record<string, string>): void;
28
+ }
29
+
30
+ export interface HandleInput {
31
+ event: RequestContext;
32
+ resolve(event: RequestContext): Promise<Response> | Response;
33
+ }
package/dist/adapter.d.ts DELETED
@@ -1,93 +0,0 @@
1
- import type { PreviewPlugins } from './carta';
2
- import type { RepoRef } from './github';
3
- import type { ComponentRegistry } from './render';
4
- interface FieldBase {
5
- /** Frontmatter key and form input name. */
6
- name: string;
7
- label: string;
8
- required?: boolean;
9
- }
10
- export interface TextField extends FieldBase {
11
- type: 'text';
12
- }
13
- export interface DateField extends FieldBase {
14
- type: 'date';
15
- }
16
- export interface TextareaField extends FieldBase {
17
- type: 'textarea';
18
- rows?: number;
19
- }
20
- export interface BooleanField extends FieldBase {
21
- type: 'boolean';
22
- }
23
- export interface TagsField extends FieldBase {
24
- type: 'tags';
25
- /** Controlled vocabulary rendered as checkboxes. */
26
- options: readonly string[];
27
- }
28
- export interface FreeTagsField extends FieldBase {
29
- type: 'freetags';
30
- /** Free-form tags, edited as one comma-separated text input (no controlled vocabulary). */
31
- placeholder?: string;
32
- }
33
- export type CairnField = TextField | DateField | TextareaField | BooleanField | TagsField | FreeTagsField;
34
- export interface CairnCollection {
35
- /** Route `[type]` segment and list key, e.g. `posts`. */
36
- type: string;
37
- label: string;
38
- /**
39
- * Editing shape. `story` (the default when absent) is a dated feed entry; `page` is a
40
- * navigation-placed entry with a path-like slug and no date emphasis. Drives the create
41
- * form and the editor header. Never gates editing capability: the palette and toolbar are
42
- * available to both. (Pass K, R4.)
43
- */
44
- kind?: 'page' | 'story';
45
- /** Repo-relative folder holding the collection's markdown files. */
46
- dir: string;
47
- /** Editor form fields, rendered in order. */
48
- fields: CairnField[];
49
- /** Validate raw frontmatter (from the form) into the on-disk object, throwing on error. */
50
- validate(data: Record<string, unknown>, source: string): object;
51
- }
52
- /** A managed navigation menu, read from and committed to the site's YAML config file. */
53
- export interface NavMenuConfig {
54
- /** Repo-relative path to the site-config YAML, e.g. 'src/lib/site.config.yaml'. */
55
- configPath: string;
56
- /** Key within the file's `menus` map, e.g. 'primary'. */
57
- menuName: string;
58
- /** Sidebar/admin label for the menu. */
59
- label: string;
60
- /** Max nesting depth allowed in the editor (1 = flat). Defaults to 2. */
61
- maxDepth?: number;
62
- }
63
- export interface CairnAdapter {
64
- /** Branding + magic-link email copy. */
65
- siteName: string;
66
- /** From: address for magic-link email (must be a domain-authenticated sender). */
67
- sender: string;
68
- /** The repository the admin reads content from and commits to. */
69
- backend: RepoRef;
70
- /** Site plugin set for the Carta preview (parity with the live render). */
71
- preview: PreviewPlugins;
72
- collections: CairnCollection[];
73
- /**
74
- * The site's component registry: the single declaration of its directive
75
- * components (R10a). Rendering parity already flows through `preview`; this
76
- * exposes the same registry so the editor's insert-component palette can read
77
- * `registry.defs`. Optional: a site with no rich components (e.g. 907.life) may
78
- * omit it or supply an empty registry.
79
- */
80
- registry?: ComponentRegistry;
81
- /**
82
- * The navigation menu this site manages from `/admin/nav` (R3/Pass L2). The menu lives in the
83
- * site's git-committed YAML config (read at build time by the layout, committed back by the
84
- * editor). Omit to hide the nav surface, the same opt-in shape as `registry`.
85
- */
86
- navMenu?: NavMenuConfig;
87
- }
88
- /** Look up a collection by its route segment, or undefined if the segment is unknown. */
89
- export declare function findCollection(adapter: CairnAdapter, type: string): CairnCollection | undefined;
90
- /** Read raw frontmatter from a submitted form, decoding each value per its field type. */
91
- export declare function frontmatterFromForm(collection: CairnCollection, form: FormData): Record<string, unknown>;
92
- export {};
93
- //# sourceMappingURL=adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/lib/adapter.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,UAAU,SAAS;IACjB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd;AACD,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd;AACD,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,IAAI,EAAE,SAAS,CAAC;CACjB;AACD,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AACD,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,aAAa,GACb,YAAY,GACZ,SAAS,GACT,aAAa,CAAC;AAElB,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CACjE;AAED,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE/F;AAED,0FAA0F;AAC1F,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,QAAQ,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA0BzB"}
package/dist/adapter.js DELETED
@@ -1,30 +0,0 @@
1
- /** Look up a collection by its route segment, or undefined if the segment is unknown. */
2
- export function findCollection(adapter, type) {
3
- return adapter.collections.find((collection) => collection.type === type);
4
- }
5
- /** Read raw frontmatter from a submitted form, decoding each value per its field type. */
6
- export function frontmatterFromForm(collection, form) {
7
- const data = {};
8
- for (const field of collection.fields) {
9
- switch (field.type) {
10
- case 'boolean':
11
- data[field.name] = form.get(field.name) === 'on';
12
- break;
13
- case 'tags':
14
- data[field.name] = form.getAll(field.name).map(String);
15
- break;
16
- case 'freetags':
17
- // One comma-separated input → trimmed, de-duplicated, non-empty tags.
18
- data[field.name] = [
19
- ...new Set(String(form.get(field.name) ?? '')
20
- .split(',')
21
- .map((tag) => tag.trim())
22
- .filter(Boolean)),
23
- ];
24
- break;
25
- default:
26
- data[field.name] = form.get(field.name);
27
- }
28
- }
29
- return data;
30
- }
@@ -1,33 +0,0 @@
1
- import type { Auth } from './config';
2
- import type { CairnUser } from './guard';
3
- export interface AdminsData {
4
- admins: CairnUser[];
5
- /** Acting owner's email, so the UI can disable self-targeted remove/demote. */
6
- self: string;
7
- saved: boolean;
8
- error: string | null;
9
- }
10
- /**
11
- * The privilege-escalation gate. better-auth's admin API also enforces this server-side (only
12
- * `owner` holds the admin statements), but checking `locals.user` here gives clean redirect/403
13
- * UX and lets the mutations guard self-lockout before calling the API. Returns the acting owner.
14
- */
15
- export declare function requireOwner(user: CairnUser | null): CairnUser;
16
- type Ev = {
17
- locals: {
18
- auth: Auth;
19
- user: CairnUser | null;
20
- };
21
- request: Request;
22
- url: URL;
23
- };
24
- /** List the allowlist for the manage-editors page. Owner-only. */
25
- export declare function adminsLoad(event: Ev): Promise<AdminsData>;
26
- /** Add an editor (create the user). Owner-only. */
27
- export declare function addAdmin(event: Ev): Promise<never>;
28
- /** Remove an editor (delete the user). Owner-only; owners can't remove themselves (anti-lockout). */
29
- export declare function removeAdmin(event: Ev): Promise<never>;
30
- /** Change an editor's role. Owner-only; owners can't demote themselves (anti-lockout). */
31
- export declare function setAdminRole(event: Ev): Promise<never>;
32
- export {};
33
- //# sourceMappingURL=admins.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"admins.d.ts","sourceRoot":"","sources":["../../src/lib/auth/admins.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAID;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAI9D;AAED,KAAK,EAAE,GAAG;IAAE,MAAM,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;KAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,CAAC;AAgBzF,kEAAkE;AAClE,wBAAsB,UAAU,CAAC,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAa/D;AAED,mDAAmD;AACnD,wBAAsB,QAAQ,CAAC,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAYxD;AAED,qGAAqG;AACrG,wBAAsB,WAAW,CAAC,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAW3D;AAED,0FAA0F;AAC1F,wBAAsB,YAAY,CAAC,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAc5D"}
@@ -1,90 +0,0 @@
1
- // cairn-core: owner-gated editor management, on better-auth's admin API. The `user` table IS
2
- // the allowlist (disableSignUp ⇒ only listed emails can sign in), so add/remove editor = create/
3
- // remove user; role flips go through the admin plugin's access-control roles (owner/editor).
4
- // These run as SvelteKit form actions; each verifies the acting user is an owner first.
5
- import { redirect, error } from '@sveltejs/kit';
6
- const EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
7
- /**
8
- * The privilege-escalation gate. better-auth's admin API also enforces this server-side (only
9
- * `owner` holds the admin statements), but checking `locals.user` here gives clean redirect/403
10
- * UX and lets the mutations guard self-lockout before calling the API. Returns the acting owner.
11
- */
12
- export function requireOwner(user) {
13
- if (!user)
14
- throw error(401, 'Not signed in');
15
- if (user.role !== 'owner')
16
- throw error(403, 'Owner access required');
17
- return user;
18
- }
19
- function asCairnUser(u) {
20
- return { id: u.id, email: u.email, name: u.name, role: u.role === 'owner' ? 'owner' : 'editor' };
21
- }
22
- /** Find an editor by exact (lowercased) email, or undefined. */
23
- async function findByEmail(event, email) {
24
- const res = await event.locals.auth.api.listUsers({
25
- query: { searchValue: email, searchField: 'email', limit: 100 },
26
- headers: event.request.headers,
27
- });
28
- const match = (res.users ?? []).find((u) => u.email.toLowerCase() === email);
29
- return match ? asCairnUser(match) : undefined;
30
- }
31
- /** List the allowlist for the manage-editors page. Owner-only. */
32
- export async function adminsLoad(event) {
33
- const owner = requireOwner(event.locals.user);
34
- const res = await event.locals.auth.api.listUsers({
35
- query: { limit: 200 },
36
- headers: event.request.headers,
37
- });
38
- const admins = (res.users ?? []).map(asCairnUser).sort((a, b) => a.email.localeCompare(b.email));
39
- return {
40
- admins,
41
- self: owner.email,
42
- saved: event.url.searchParams.get('saved') === '1',
43
- error: event.url.searchParams.get('error'),
44
- };
45
- }
46
- /** Add an editor (create the user). Owner-only. */
47
- export async function addAdmin(event) {
48
- requireOwner(event.locals.user);
49
- const form = await event.request.formData();
50
- const email = String(form.get('email') ?? '').trim().toLowerCase();
51
- const name = String(form.get('name') ?? '').trim();
52
- const role = form.get('role') === 'owner' ? 'owner' : 'editor';
53
- if (!EMAIL_RE.test(email) || !name) {
54
- throw redirect(303, `/admin/admins?error=${encodeURIComponent('Enter a valid email and name')}`);
55
- }
56
- // No password: a magic-link-only user (no credential account), per better-auth's createUser.
57
- await event.locals.auth.api.createUser({ body: { email, name, role }, headers: event.request.headers });
58
- throw redirect(303, '/admin/admins?saved=1');
59
- }
60
- /** Remove an editor (delete the user). Owner-only; owners can't remove themselves (anti-lockout). */
61
- export async function removeAdmin(event) {
62
- const owner = requireOwner(event.locals.user);
63
- const form = await event.request.formData();
64
- const email = String(form.get('email') ?? '').trim().toLowerCase();
65
- if (email === owner.email) {
66
- throw redirect(303, `/admin/admins?error=${encodeURIComponent("You can't remove yourself")}`);
67
- }
68
- const target = await findByEmail(event, email);
69
- if (!target)
70
- throw redirect(303, `/admin/admins?error=${encodeURIComponent('No such editor')}`);
71
- await event.locals.auth.api.removeUser({ body: { userId: target.id }, headers: event.request.headers });
72
- throw redirect(303, '/admin/admins?saved=1');
73
- }
74
- /** Change an editor's role. Owner-only; owners can't demote themselves (anti-lockout). */
75
- export async function setAdminRole(event) {
76
- const owner = requireOwner(event.locals.user);
77
- const form = await event.request.formData();
78
- const email = String(form.get('email') ?? '').trim().toLowerCase();
79
- const role = form.get('role') === 'owner' ? 'owner' : 'editor';
80
- if (email === owner.email && role !== 'owner') {
81
- throw redirect(303, `/admin/admins?error=${encodeURIComponent("You can't demote yourself")}`);
82
- }
83
- const target = await findByEmail(event, email);
84
- if (!target)
85
- throw redirect(303, `/admin/admins?error=${encodeURIComponent('No such editor')}`);
86
- await event.locals.auth.api.setRole({ body: { userId: target.id, role }, headers: event.request.headers });
87
- // M3: revoke a demoted editor's live sessions so the privilege drop takes effect immediately.
88
- await event.locals.auth.api.revokeUserSessions({ body: { userId: target.id }, headers: event.request.headers });
89
- throw redirect(303, '/admin/admins?saved=1');
90
- }
@@ -1,7 +0,0 @@
1
- import type { CairnUser } from './guard';
2
- export type Capability = 'story:create' | 'story:edit' | 'page:edit' | 'page:create' | 'nav:manage' | 'user:manage';
3
- /** Does this user hold the capability? A signed-out (null) user holds nothing. */
4
- export declare function can(user: CairnUser | null, cap: Capability): boolean;
5
- /** Assert the capability for a route load/action: 401 when signed out, 403 when under-privileged. */
6
- export declare function requireCapability(user: CairnUser | null, cap: Capability): CairnUser;
7
- //# sourceMappingURL=capabilities.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/lib/auth/capabilities.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,YAAY,GACZ,WAAW,GACX,aAAa,GACb,YAAY,GACZ,aAAa,CAAC;AASlB,kFAAkF;AAClF,wBAAgB,GAAG,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAIpE;AAED,qGAAqG;AACrG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,EAAE,GAAG,EAAE,UAAU,GAAG,SAAS,CAIpF"}
@@ -1,26 +0,0 @@
1
- // cairn-core: capability checks. Management surfaces gate on a capability, not on a role name,
2
- // so the two-tier owner/editor model can grow finer capabilities (and a future role) additively.
3
- // Creating a page and changing the nav are structural acts, so they sit with owner; editing a
4
- // page's content and running the story feed are everyday editor work.
5
- import { error } from '@sveltejs/kit';
6
- // One source of truth. `'all'` means every capability; otherwise the explicit grant list. A future
7
- // `manager` role is one more row here, no call-site changes.
8
- const CAPS_BY_ROLE = {
9
- owner: 'all',
10
- editor: ['story:create', 'story:edit', 'page:edit'],
11
- };
12
- /** Does this user hold the capability? A signed-out (null) user holds nothing. */
13
- export function can(user, cap) {
14
- if (!user)
15
- return false;
16
- const grants = CAPS_BY_ROLE[user.role];
17
- return grants === 'all' || grants.includes(cap);
18
- }
19
- /** Assert the capability for a route load/action: 401 when signed out, 403 when under-privileged. */
20
- export function requireCapability(user, cap) {
21
- if (!user)
22
- throw error(401, 'Not signed in');
23
- if (!can(user, cap))
24
- throw error(403, 'You do not have permission to do that');
25
- return user;
26
- }