@karibsen/eponyme 0.8.13 → 0.9.0-nightly.20260810

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 (183) hide show
  1. package/README.md +89 -7
  2. package/dist/eponyme.d.mts +2 -2
  3. package/dist/eponyme.mjs +20 -3
  4. package/dist/module.d.mts +79 -5
  5. package/dist/module.json +1 -1
  6. package/dist/module.mjs +94 -2
  7. package/dist/runtime/assets/dashboard.css +1 -1
  8. package/dist/runtime/assets.d.ts +2 -0
  9. package/dist/runtime/components/EponymeRichText.d.ts +40 -0
  10. package/dist/runtime/components/EponymeRichText.js +11 -0
  11. package/dist/runtime/components/editor/EponymeCollectionPage.vue +76 -30
  12. package/dist/runtime/components/editor/EponymeCopy.vue +3 -2
  13. package/dist/runtime/components/editor/EponymeFolderNav.vue +5 -4
  14. package/dist/runtime/components/editor/EponymeFolderPage.vue +6 -8
  15. package/dist/runtime/components/editor/EponymeForm.vue +380 -38
  16. package/dist/runtime/components/editor/EponymeFormPage.vue +82 -40
  17. package/dist/runtime/components/editor/EponymeHistoryDialog.vue +54 -21
  18. package/dist/runtime/components/editor/EponymeNavigationLink.vue +3 -2
  19. package/dist/runtime/components/editor/EponymePageNavigation.vue +4 -3
  20. package/dist/runtime/components/editor/EponymePreviewPanel.vue +12 -11
  21. package/dist/runtime/components/editor/EponymeSidebar.vue +18 -16
  22. package/dist/runtime/components/editor/EponymeSidebarTree.vue +3 -2
  23. package/dist/runtime/components/fields/ArrayField.vue +39 -10
  24. package/dist/runtime/components/fields/DateTimeField.d.vue.ts +18 -0
  25. package/dist/runtime/components/fields/DateTimeField.vue +64 -0
  26. package/dist/runtime/components/fields/DateTimeField.vue.d.ts +18 -0
  27. package/dist/runtime/components/fields/DurationField.d.vue.ts +18 -0
  28. package/dist/runtime/components/fields/DurationField.vue +103 -0
  29. package/dist/runtime/components/fields/DurationField.vue.d.ts +18 -0
  30. package/dist/runtime/components/fields/FieldRenderer.vue +14 -3
  31. package/dist/runtime/components/fields/MediaPlayerField.vue +6 -5
  32. package/dist/runtime/components/fields/NumberField.d.vue.ts +2 -0
  33. package/dist/runtime/components/fields/NumberField.vue +40 -13
  34. package/dist/runtime/components/fields/NumberField.vue.d.ts +2 -0
  35. package/dist/runtime/components/fields/RichTextField.vue +47 -28
  36. package/dist/runtime/components/fields/SectionField.d.vue.ts +1 -0
  37. package/dist/runtime/components/fields/SectionField.vue +4 -3
  38. package/dist/runtime/components/fields/SectionField.vue.d.ts +1 -0
  39. package/dist/runtime/components/fields/TabsField.vue +2 -1
  40. package/dist/runtime/components/fields/lazy.d.ts +66 -0
  41. package/dist/runtime/components/fields/lazy.js +3 -0
  42. package/dist/runtime/components/ui/EPAccordion.vue +2 -1
  43. package/dist/runtime/components/ui/EPAlertDialog.vue +15 -6
  44. package/dist/runtime/components/ui/EPAvatar.d.vue.ts +1 -0
  45. package/dist/runtime/components/ui/EPAvatar.vue +26 -4
  46. package/dist/runtime/components/ui/EPAvatar.vue.d.ts +1 -0
  47. package/dist/runtime/components/ui/EPColorPicker.vue +4 -3
  48. package/dist/runtime/components/ui/EPDialog.vue +3 -2
  49. package/dist/runtime/components/ui/EPImageDialog.d.vue.ts +20 -0
  50. package/dist/runtime/components/ui/EPImageDialog.vue +114 -0
  51. package/dist/runtime/components/ui/EPImageDialog.vue.d.ts +20 -0
  52. package/dist/runtime/components/ui/EPInputPassword.d.vue.ts +12 -0
  53. package/dist/runtime/components/ui/EPInputPassword.vue +41 -0
  54. package/dist/runtime/components/ui/EPInputPassword.vue.d.ts +12 -0
  55. package/dist/runtime/components/ui/EPInputText.d.vue.ts +4 -2
  56. package/dist/runtime/components/ui/EPInputText.vue +14 -5
  57. package/dist/runtime/components/ui/EPInputText.vue.d.ts +4 -2
  58. package/dist/runtime/components/ui/EPLinkDialog.vue +15 -14
  59. package/dist/runtime/components/ui/EPLinkEditor.vue +7 -6
  60. package/dist/runtime/components/ui/EPMediaPreview.vue +2 -1
  61. package/dist/runtime/components/ui/EPSelect.vue +97 -35
  62. package/dist/runtime/components/ui/EPSlider.d.vue.ts +2 -0
  63. package/dist/runtime/components/ui/EPSlider.vue +3 -1
  64. package/dist/runtime/components/ui/EPSlider.vue.d.ts +2 -0
  65. package/dist/runtime/components/ui/EPTagsInput.vue +2 -1
  66. package/dist/runtime/components/ui/EPToast.vue +3 -2
  67. package/dist/runtime/components/ui/EPTooltip.vue +1 -1
  68. package/dist/runtime/composables/useEponyme.d.ts +4 -2
  69. package/dist/runtime/composables/useEponyme.js +7 -3
  70. package/dist/runtime/fields/datetime.d.ts +2 -0
  71. package/dist/runtime/fields/datetime.js +18 -0
  72. package/dist/runtime/fields/duration.d.ts +2 -0
  73. package/dist/runtime/fields/duration.js +16 -0
  74. package/dist/runtime/fields/index.d.ts +7 -0
  75. package/dist/runtime/fields/index.js +5 -1
  76. package/dist/runtime/fields/money.d.ts +160 -0
  77. package/dist/runtime/fields/money.js +39 -0
  78. package/dist/runtime/locales/en.json +408 -0
  79. package/dist/runtime/locales/fallback.d.ts +9 -0
  80. package/dist/runtime/locales/fallback.js +4 -0
  81. package/dist/runtime/locales/index.d.ts +441 -0
  82. package/dist/runtime/locales/index.js +6 -0
  83. package/dist/runtime/middleware/eponyme-auth.d.ts +1 -1
  84. package/dist/runtime/middleware/eponyme-owner.d.ts +1 -1
  85. package/dist/runtime/pages/EponymeChangePasswordPage.d.vue.ts +1 -0
  86. package/dist/runtime/pages/EponymeChangePasswordPage.vue +18 -19
  87. package/dist/runtime/pages/EponymeChangePasswordPage.vue.d.ts +1 -0
  88. package/dist/runtime/pages/EponymeDetailPage.d.vue.ts +1 -0
  89. package/dist/runtime/pages/EponymeDetailPage.vue +8 -6
  90. package/dist/runtime/pages/EponymeDetailPage.vue.d.ts +1 -0
  91. package/dist/runtime/pages/EponymeIndexPage.d.vue.ts +1 -0
  92. package/dist/runtime/pages/EponymeIndexPage.vue +24 -22
  93. package/dist/runtime/pages/EponymeIndexPage.vue.d.ts +1 -0
  94. package/dist/runtime/pages/EponymeLoginPage.d.vue.ts +1 -0
  95. package/dist/runtime/pages/EponymeLoginPage.vue +12 -10
  96. package/dist/runtime/pages/EponymeLoginPage.vue.d.ts +1 -0
  97. package/dist/runtime/pages/EponymeUsersPage.d.vue.ts +1 -0
  98. package/dist/runtime/pages/EponymeUsersPage.vue +75 -38
  99. package/dist/runtime/pages/EponymeUsersPage.vue.d.ts +1 -0
  100. package/dist/runtime/plugins/eponyme-theme.d.ts +2 -0
  101. package/dist/runtime/plugins/eponyme-theme.js +17 -0
  102. package/dist/runtime/server/api/eponyme/[name].get.d.ts +2 -0
  103. package/dist/runtime/server/api/eponyme/[name].get.js +4 -3
  104. package/dist/runtime/server/api/eponyme/[name].patch.js +31 -8
  105. package/dist/runtime/server/api/eponyme-auth/change-password.post.js +5 -3
  106. package/dist/runtime/server/api/eponyme-auth/login.post.js +19 -4
  107. package/dist/runtime/server/api/eponyme-collections/[path].delete.js +2 -1
  108. package/dist/runtime/server/api/eponyme-collections/[path].get.js +14 -8
  109. package/dist/runtime/server/api/eponyme-collections/[path].post.js +5 -3
  110. package/dist/runtime/server/api/eponyme-forms/[path].delete.js +4 -3
  111. package/dist/runtime/server/api/eponyme-forms/[path].get.js +4 -3
  112. package/dist/runtime/server/api/eponyme-forms/[path].post.js +8 -9
  113. package/dist/runtime/server/api/eponyme-history/[path].get.js +2 -1
  114. package/dist/runtime/server/api/eponyme-history/[path].patch.js +6 -3
  115. package/dist/runtime/server/api/eponyme-import.post.js +6 -9
  116. package/dist/runtime/server/api/eponyme-trash/[path].delete.js +2 -1
  117. package/dist/runtime/server/api/eponyme-trash/[path].get.js +3 -2
  118. package/dist/runtime/server/api/eponyme-trash/[path].patch.js +2 -1
  119. package/dist/runtime/server/api/eponyme-users/[id]/reset-password.post.js +2 -1
  120. package/dist/runtime/server/api/eponyme-users/[id].patch.js +5 -3
  121. package/dist/runtime/server/api/eponyme-users/index.post.js +5 -3
  122. package/dist/runtime/server/plugins/eponyme-sync.js +30 -0
  123. package/dist/runtime/server/services/eponyme-auth-store.d.ts +1 -2
  124. package/dist/runtime/server/services/eponyme-auth-store.js +16 -32
  125. package/dist/runtime/server/services/eponyme-cache-store.d.ts +65 -0
  126. package/dist/runtime/server/services/eponyme-cache-store.js +68 -0
  127. package/dist/runtime/server/services/eponyme-form-store.d.ts +25 -2
  128. package/dist/runtime/server/services/eponyme-form-store.js +45 -3
  129. package/dist/runtime/server/services/eponyme-rate-limit-service.d.ts +2 -0
  130. package/dist/runtime/server/services/eponyme-rate-limit-service.js +27 -0
  131. package/dist/runtime/server/services/eponyme-rate-limit-store.d.ts +103 -0
  132. package/dist/runtime/server/services/eponyme-rate-limit-store.js +70 -0
  133. package/dist/runtime/server/services/eponyme-schema-service.d.ts +2 -0
  134. package/dist/runtime/server/services/eponyme-schema-service.js +6 -0
  135. package/dist/runtime/server/services/eponyme-schema-store.d.ts +57 -0
  136. package/dist/runtime/server/services/eponyme-schema-store.js +18 -0
  137. package/dist/runtime/server/services/eponyme-service.js +4 -2
  138. package/dist/runtime/server/services/eponyme-store.d.ts +72 -16
  139. package/dist/runtime/server/services/eponyme-store.js +275 -76
  140. package/dist/runtime/server/utils/auth.js +5 -4
  141. package/dist/runtime/server/utils/body.d.ts +5 -0
  142. package/dist/runtime/server/utils/body.js +50 -0
  143. package/dist/runtime/server/utils/eponyme-cache.d.ts +3 -2
  144. package/dist/runtime/server/utils/eponyme-form.d.ts +34 -0
  145. package/dist/runtime/server/utils/eponyme-form.js +18 -0
  146. package/dist/runtime/server/utils/eponyme-hooks.js +2 -1
  147. package/dist/runtime/server/utils/eponyme-schedule.d.ts +7 -0
  148. package/dist/runtime/server/utils/eponyme-schedule.js +23 -0
  149. package/dist/runtime/server/utils/eponyme-variables.d.ts +3 -0
  150. package/dist/runtime/server/utils/eponyme-variables.js +4 -1
  151. package/dist/runtime/server/utils/rate-limit.d.ts +27 -0
  152. package/dist/runtime/server/utils/rate-limit.js +25 -0
  153. package/dist/runtime/types/eponyme-config.d.ts +8 -0
  154. package/dist/runtime/types/eponyme.d.ts +19 -7
  155. package/dist/runtime/types/field.d.ts +35 -4
  156. package/dist/runtime/types/hooks.d.ts +8 -0
  157. package/dist/runtime/types/nuxt.d.ts +8 -0
  158. package/dist/runtime/utils/date-locale.d.ts +9 -7
  159. package/dist/runtime/utils/date-locale.js +2 -1
  160. package/dist/runtime/utils/datetime.d.ts +4 -0
  161. package/dist/runtime/utils/datetime.js +35 -0
  162. package/dist/runtime/utils/duration.d.ts +11 -0
  163. package/dist/runtime/utils/duration.js +50 -0
  164. package/dist/runtime/utils/eponyme-entry-index.js +2 -2
  165. package/dist/runtime/utils/eponyme-theme.d.ts +6 -0
  166. package/dist/runtime/utils/eponyme-theme.js +6 -0
  167. package/dist/runtime/utils/filter-navigation-tree.d.ts +1 -0
  168. package/dist/runtime/utils/filter-navigation-tree.js +14 -4
  169. package/dist/runtime/utils/get-eponyme-schemas.js +8 -3
  170. package/dist/runtime/utils/get-field-default-value.js +1 -0
  171. package/dist/runtime/utils/normalize-eponyme-values.d.ts +2 -2
  172. package/dist/runtime/utils/normalize-eponyme-values.js +7 -2
  173. package/dist/runtime/utils/rich-text-fields.d.ts +12 -0
  174. package/dist/runtime/utils/rich-text-fields.js +48 -0
  175. package/dist/runtime/utils/sanitize-rich-text.d.ts +20 -0
  176. package/dist/runtime/utils/sanitize-rich-text.js +81 -0
  177. package/dist/runtime/utils/translate.d.ts +18 -0
  178. package/dist/runtime/utils/translate.js +23 -0
  179. package/dist/runtime/utils/validate-eponyme-data.js +85 -65
  180. package/dist/runtime/utils/variables.d.ts +12 -0
  181. package/dist/runtime/utils/variables.js +16 -2
  182. package/dist/types.d.mts +4 -2
  183. package/package.json +9 -2
package/README.md CHANGED
@@ -226,6 +226,43 @@ A phone field is also allowed in a public `form()`.
226
226
 
227
227
  Eponyme uses the Prisma client owned by your application. It does not create the connection or run migrations.
228
228
 
229
+ Install Prisma 7 and its PostgreSQL driver adapter:
230
+
231
+ ```bash
232
+ pnpm add @prisma/client@latest @prisma/adapter-pg@latest
233
+ pnpm add --save-dev prisma@latest dotenv@latest
234
+ ```
235
+
236
+ Generate the client outside `node_modules` and keep the connection URL in Prisma Config:
237
+
238
+ ```prisma
239
+ // prisma/schema.prisma
240
+ generator client {
241
+ provider = "prisma-client"
242
+ output = "../generated/prisma"
243
+ }
244
+
245
+ datasource db {
246
+ provider = "postgresql"
247
+ }
248
+ ```
249
+
250
+ ```ts
251
+ // prisma.config.ts
252
+ import 'dotenv/config'
253
+ import { defineConfig, env } from 'prisma/config'
254
+
255
+ export default defineConfig({
256
+ schema: 'prisma/schema.prisma',
257
+ migrations: {
258
+ path: 'prisma/migrations',
259
+ },
260
+ datasource: {
261
+ url: env('DATABASE_URL'),
262
+ },
263
+ })
264
+ ```
265
+
229
266
  Export an initialized client from the path configured in `nuxt.config.ts`. Note the
230
267
  double tilde: `~~/` points at the project root, where `server/` lives, while `~/` points
231
268
  at the source directory (`app/` in Nuxt 4). A relative path such as
@@ -233,14 +270,44 @@ at the source directory (`app/` in Nuxt 4). A relative path such as
233
270
 
234
271
  ```ts
235
272
  // server/utils/prisma.ts
236
- import { PrismaClient } from '@prisma/client'
273
+ import { PrismaPg } from '@prisma/adapter-pg'
274
+ import { PrismaClient } from '../../generated/prisma/client'
275
+
276
+ const prismaGlobal = globalThis as typeof globalThis & { prisma?: PrismaClient }
277
+
278
+ function createPrismaClient() {
279
+ const connectionString = process.env.DATABASE_URL
237
280
 
238
- const prisma = new PrismaClient()
281
+ if (!connectionString)
282
+ throw new Error('DATABASE_URL is required to initialise Prisma.')
283
+
284
+ return new PrismaClient({
285
+ adapter: new PrismaPg({ connectionString }),
286
+ })
287
+ }
288
+
289
+ const prisma = prismaGlobal.prisma ?? createPrismaClient()
290
+
291
+ if (process.env.NODE_ENV !== 'production')
292
+ prismaGlobal.prisma = prisma
239
293
 
240
294
  export default prisma
241
295
  ```
242
296
 
243
- Add the required models to your Prisma schema:
297
+ Install the published Eponyme CLI, then let it add the required models and immutable migration
298
+ history to your application:
299
+
300
+ ```bash
301
+ pnpm add --save-dev @eponyme/cli
302
+ pnpm exec eponyme init
303
+ pnpm prisma migrate deploy
304
+ pnpm prisma generate
305
+ pnpm exec eponyme check --client server/utils/prisma.ts
306
+ ```
307
+
308
+ `eponyme init` only writes the host Prisma schema and migration files; it never connects to the
309
+ database. `eponyme check` is read-only and verifies the generated PrismaClient, PostgreSQL tables,
310
+ columns and the Eponyme schema version. The generated models are shown below for reference:
244
311
 
245
312
  ```prisma
246
313
  model Eponyme {
@@ -344,12 +411,27 @@ model EponymeFormSubmission {
344
411
  @@index([formName, createdAt])
345
412
  @@map("eponyme_form_submissions")
346
413
  }
347
- ```
348
414
 
349
- Run the migration with the workflow used by your application.
415
+ model EponymeRateLimit {
416
+ key String @id
417
+ count Int
418
+ expiresAt DateTime
419
+
420
+ @@index([expiresAt])
421
+ @@map("eponyme_rate_limits")
422
+ }
423
+
424
+ model EponymeSchema {
425
+ key String @id @default("eponyme")
426
+ version Int
427
+ updatedAt DateTime @updatedAt
428
+
429
+ @@map("_eponyme_schema")
430
+ }
431
+ ```
350
432
 
351
- Upgrading from 0.1.1 requires one migration: `Eponyme.deletedAt`, which backs the trash.
352
- Deleting a collection entry no longer destroys it.
433
+ Keep the generated migrations in source control. On an upgrade, run `eponyme init` again, inspect
434
+ the newly copied migrations, deploy them, regenerate Prisma and finish with `eponyme check`.
353
435
 
354
436
  The playground includes the same models in `playground/prisma/schema.prisma`. Copy `playground/.env.example` to `playground/.env`, set `DATABASE_URL`, then run:
355
437
 
@@ -1,7 +1,7 @@
1
1
  import { EponymeConfig, EponymeSchema, EponymeCollectionOptions, CollectionSlugField, EponymeCollectionDefinition, EponymeFormSchema, EponymeFormOptions, EponymeFormDefinition } from '../dist/runtime/types/index.js';
2
- export { ArrayFieldDefinition, ArrayFieldOptions, ArrayItemDefinition, ArrayItemFieldDefinition, ArrayItemSchema, ArrayItemValue, BooleanFieldDefinition, BooleanFieldOptions, CheckboxGroupFieldDefinition, CheckboxGroupFieldOptions, CollectionSlugField, ColorFieldDefinition, ColorFieldOptions, DateFieldDefinition, DateFieldOptions, EmailFieldDefinition, EmailFieldOptions, EponymeAuthSession, EponymeAuthUser, EponymeCollectionDataByName, EponymeCollectionDefinition, EponymeCollectionDefinitionBase, EponymeCollectionName, EponymeCollectionOptions, EponymeConfig, EponymeData, EponymeDataByName, EponymeFormDataByName, EponymeFormDefinition, EponymeFormDefinitionBase, EponymeFormFieldDefinition, EponymeFormMode, EponymeFormName, EponymeFormOptions, EponymeFormSchema, EponymeManagedUserResult, EponymeName, EponymeRole, EponymeSchema, EponymeVariableDefinition, EponymeVariableSummary, EponymeVariableValue, EponymeVariables, FieldDefinition, FieldValidationResult, FieldValidator, FieldVisibilityCondition, ImageFieldDefinition, ImageFieldOptions, MediaPlayerFieldDefinition, MediaPlayerFieldOptions, MediaPlayerProvider, MediaPlayerValue, NumberFieldDefinition, NumberFieldOptions, RadioFieldDefinition, RadioFieldOptions, RichTextFieldDefinition, RichTextFieldOptions, SectionFieldDefinition, SectionFieldOptions, SectionItemFieldDefinition, SectionItemValue, SectionSchema, SectionValue, SelectFieldDefinition, SelectFieldOptions, SelectOption, SlugFieldDefinition, SlugFieldOptions, StringFieldDefinition, StringFieldOptions, TabDefinition, TabFieldDefinition, TabFieldOptions, TabsSchema, TabsValue, TextareaFieldDefinition, TextareaFieldOptions, TodayDateDefault, UrlFieldDefinition, UrlFieldOptions, UrlType, UrlValue } from '../dist/runtime/types/index.js';
2
+ export { ArrayFieldDefinition, ArrayFieldOptions, ArrayItemDefinition, ArrayItemFieldDefinition, ArrayItemSchema, ArrayItemValue, BooleanFieldDefinition, BooleanFieldOptions, CheckboxGroupFieldDefinition, CheckboxGroupFieldOptions, CollectionSlugField, ColorFieldDefinition, ColorFieldOptions, DateFieldDefinition, DateFieldOptions, DateTimeFieldDefinition, DateTimeFieldOptions, DurationFieldDefinition, DurationFieldOptions, DurationInput, EmailFieldDefinition, EmailFieldOptions, EponymeAuthSession, EponymeAuthUser, EponymeCollectionDataByName, EponymeCollectionDefinition, EponymeCollectionDefinitionBase, EponymeCollectionName, EponymeCollectionOptions, EponymeConfig, EponymeData, EponymeDataByName, EponymeFormDataByName, EponymeFormDefinition, EponymeFormDefinitionBase, EponymeFormFieldDefinition, EponymeFormMode, EponymeFormName, EponymeFormOptions, EponymeFormSchema, EponymeManagedUserResult, EponymeName, EponymeRole, EponymeSchema, EponymeVariableDefinition, EponymeVariableSummary, EponymeVariableValue, EponymeVariables, FieldDefinition, FieldValidationResult, FieldValidator, FieldVisibilityCondition, ImageFieldDefinition, ImageFieldOptions, MediaPlayerFieldDefinition, MediaPlayerFieldOptions, MediaPlayerProvider, MediaPlayerValue, NormalizedDurationFieldOptions, NumberFieldDefinition, NumberFieldOptions, RadioFieldDefinition, RadioFieldOptions, RichTextFieldDefinition, RichTextFieldOptions, SectionFieldDefinition, SectionFieldOptions, SectionItemFieldDefinition, SectionItemValue, SectionSchema, SectionValue, SelectFieldDefinition, SelectFieldOptions, SelectOption, SlugFieldDefinition, SlugFieldOptions, StringFieldDefinition, StringFieldOptions, TabDefinition, TabFieldDefinition, TabFieldOptions, TabsSchema, TabsValue, TextareaFieldDefinition, TextareaFieldOptions, TodayDateDefault, UrlFieldDefinition, UrlFieldOptions, UrlType, UrlValue } from '../dist/runtime/types/index.js';
3
3
  import { EponymeVariables } from '../dist/runtime/types/variables.js';
4
- export { field, today } from '../dist/runtime/fields/index.js';
4
+ export { field, toMs, today } from '../dist/runtime/fields/index.js';
5
5
  export { EPONYME_ROLES, canEditEponyme, isEponymeRole } from '../dist/runtime/types/auth.js';
6
6
  export { eponymeMediaEmbedUrl, eponymeMediaThumbnailUrl, parseEponymeMediaUrl } from '../dist/runtime/utils/media-player.js';
7
7
 
package/dist/eponyme.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { field, today } from '../dist/runtime/fields/index.js';
1
+ export { field, toMs, today } from '../dist/runtime/fields/index.js';
2
2
  export { EPONYME_ROLES, canEditEponyme, isEponymeRole } from '../dist/runtime/types/auth.js';
3
3
  export { eponymeMediaEmbedUrl, eponymeMediaThumbnailUrl, parseEponymeMediaUrl } from '../dist/runtime/utils/media-player.js';
4
4
 
@@ -15,14 +15,19 @@ function collection(options) {
15
15
  const PUBLIC_FIELD_TYPES = ["string", "textarea", "email", "phone", "url", "number", "boolean", "select", "radio", "checkboxGroup"];
16
16
  const DEFAULT_HONEYPOT = "_eponyme_hp";
17
17
  const DEFAULT_MAX_BODY_BYTES = 64 * 1024;
18
+ const DEFAULT_MAX_STORED_SUBMISSIONS = 1e4;
19
+ const DEFAULT_RETENTION_DAYS = 365;
18
20
  function form(options) {
19
21
  for (const [name, definition] of Object.entries(options.fields)) {
20
22
  const type = definition.type;
21
23
  if (!PUBLIC_FIELD_TYPES.includes(type))
22
24
  throw new TypeError(`[Eponyme] Form field "${name}" uses field.${type}(), which is not available in a public form. Use one of: ${PUBLIC_FIELD_TYPES.join(", ")}.`);
23
25
  }
26
+ const mode = options.submission?.mode ?? "custom";
27
+ const maxStored = positiveIntegerOrFalse(options.submission?.maxStored, DEFAULT_MAX_STORED_SUBMISSIONS, "submission.maxStored");
28
+ const retentionDays = positiveIntegerOrFalse(options.submission?.retentionDays, DEFAULT_RETENTION_DAYS, "submission.retentionDays");
24
29
  const honeypot = options.honeypot === void 0 ? DEFAULT_HONEYPOT : options.honeypot;
25
- if (honeypot && honeypot in options.fields)
30
+ if (honeypot && Object.hasOwn(options.fields, honeypot))
26
31
  throw new TypeError(`[Eponyme] Form honeypot "${honeypot}" collides with a declared field. Rename the field or set another honeypot.`);
27
32
  return {
28
33
  __eponymeForm: true,
@@ -30,11 +35,23 @@ function form(options) {
30
35
  description: options.description,
31
36
  fields: options.fields,
32
37
  // Storing a submission has to be an explicit decision, so `custom` is the default.
33
- submission: { mode: options.submission?.mode ?? "custom" },
38
+ submission: {
39
+ mode,
40
+ store: mode === "managed" || (options.submission?.store ?? false),
41
+ maxStored,
42
+ retentionDays
43
+ },
34
44
  honeypot,
35
45
  maxBodyBytes: options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES
36
46
  };
37
47
  }
48
+ function positiveIntegerOrFalse(value, fallback, name) {
49
+ const resolved = value ?? fallback;
50
+ if (resolved === false) return false;
51
+ if (!Number.isSafeInteger(resolved) || resolved < 1)
52
+ throw new TypeError(`[Eponyme] Form ${name} must be a positive integer or false.`);
53
+ return resolved;
54
+ }
38
55
 
39
56
  function defineEponymeVariables(variables) {
40
57
  return variables;
package/dist/module.d.mts CHANGED
@@ -1,9 +1,11 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { EponymeLocaleDefinition } from '../dist/runtime/locales/index.js';
3
+ export { EponymeLocaleDefinition, EponymeMessageKey } from '../dist/runtime/locales/index.js';
2
4
  export { collection, defineEponymeConfig, defineEponymeVariables, form } from './eponyme.mjs';
3
- export { field, today } from '../dist/runtime/fields/index.js';
5
+ export { field, toMs, today } from '../dist/runtime/fields/index.js';
4
6
  export { EPONYME_ROLES, canEditEponyme, isEponymeRole } from '../dist/runtime/types/auth.js';
5
7
  export { eponymeMediaEmbedUrl, eponymeMediaThumbnailUrl, parseEponymeMediaUrl } from '../dist/runtime/utils/media-player.js';
6
- export { ArrayFieldDefinition, ArrayFieldOptions, ArrayItemDefinition, ArrayItemFieldDefinition, ArrayItemSchema, ArrayItemValue, BooleanFieldDefinition, BooleanFieldOptions, CheckboxGroupFieldDefinition, CheckboxGroupFieldOptions, CollectionSlugField, ColorFieldDefinition, ColorFieldOptions, DateFieldDefinition, DateFieldOptions, EmailFieldDefinition, EmailFieldOptions, EponymeAuthSession, EponymeAuthUser, EponymeCollectionDataByName, EponymeCollectionDefinition, EponymeCollectionDefinitionBase, EponymeCollectionName, EponymeCollectionOptions, EponymeConfig, EponymeData, EponymeDataByName, EponymeFormDataByName, EponymeFormDefinition, EponymeFormDefinitionBase, EponymeFormFieldDefinition, EponymeFormMode, EponymeFormName, EponymeFormOptions, EponymeFormSchema, EponymeManagedUserResult, EponymeName, EponymeRole, EponymeSchema, EponymeVariableDefinition, EponymeVariableSummary, EponymeVariableValue, EponymeVariables, FieldDefinition, FieldValidationResult, FieldValidator, FieldVisibilityCondition, ImageFieldDefinition, ImageFieldOptions, MediaPlayerFieldDefinition, MediaPlayerFieldOptions, MediaPlayerProvider, MediaPlayerValue, NumberFieldDefinition, NumberFieldOptions, RadioFieldDefinition, RadioFieldOptions, RichTextFieldDefinition, RichTextFieldOptions, SectionFieldDefinition, SectionFieldOptions, SectionItemFieldDefinition, SectionItemValue, SectionSchema, SectionValue, SelectFieldDefinition, SelectFieldOptions, SelectOption, SlugFieldDefinition, SlugFieldOptions, StringFieldDefinition, StringFieldOptions, TabDefinition, TabFieldDefinition, TabFieldOptions, TabsSchema, TabsValue, TextareaFieldDefinition, TextareaFieldOptions, TodayDateDefault, UrlFieldDefinition, UrlFieldOptions, UrlType, UrlValue } from '../dist/runtime/types/index.js';
8
+ export { ArrayFieldDefinition, ArrayFieldOptions, ArrayItemDefinition, ArrayItemFieldDefinition, ArrayItemSchema, ArrayItemValue, BooleanFieldDefinition, BooleanFieldOptions, CheckboxGroupFieldDefinition, CheckboxGroupFieldOptions, CollectionSlugField, ColorFieldDefinition, ColorFieldOptions, DateFieldDefinition, DateFieldOptions, DateTimeFieldDefinition, DateTimeFieldOptions, DurationFieldDefinition, DurationFieldOptions, DurationInput, EmailFieldDefinition, EmailFieldOptions, EponymeAuthSession, EponymeAuthUser, EponymeCollectionDataByName, EponymeCollectionDefinition, EponymeCollectionDefinitionBase, EponymeCollectionName, EponymeCollectionOptions, EponymeConfig, EponymeData, EponymeDataByName, EponymeFormDataByName, EponymeFormDefinition, EponymeFormDefinitionBase, EponymeFormFieldDefinition, EponymeFormMode, EponymeFormName, EponymeFormOptions, EponymeFormSchema, EponymeManagedUserResult, EponymeName, EponymeRole, EponymeSchema, EponymeVariableDefinition, EponymeVariableSummary, EponymeVariableValue, EponymeVariables, FieldDefinition, FieldValidationResult, FieldValidator, FieldVisibilityCondition, ImageFieldDefinition, ImageFieldOptions, MediaPlayerFieldDefinition, MediaPlayerFieldOptions, MediaPlayerProvider, MediaPlayerValue, NormalizedDurationFieldOptions, NumberFieldDefinition, NumberFieldOptions, RadioFieldDefinition, RadioFieldOptions, RichTextFieldDefinition, RichTextFieldOptions, SectionFieldDefinition, SectionFieldOptions, SectionItemFieldDefinition, SectionItemValue, SectionSchema, SectionValue, SelectFieldDefinition, SelectFieldOptions, SelectOption, SlugFieldDefinition, SlugFieldOptions, StringFieldDefinition, StringFieldOptions, TabDefinition, TabFieldDefinition, TabFieldOptions, TabsSchema, TabsValue, TextareaFieldDefinition, TextareaFieldOptions, TodayDateDefault, UrlFieldDefinition, UrlFieldOptions, UrlType, UrlValue } from '../dist/runtime/types/index.js';
7
9
  import '../dist/runtime/types/variables.js';
8
10
 
9
11
  /**
@@ -88,16 +90,63 @@ interface ModuleOptions {
88
90
  */
89
91
  sessionDurationDays?: number;
90
92
  };
93
+ rateLimits?: {
94
+ /** Login attempts per client address and minute. @default 10 */
95
+ loginPerIp?: number;
96
+ /** Login attempts across the deployment and minute. @default 300 */
97
+ loginGlobal?: number;
98
+ /** Failed login attempts per account and 15 minutes. @default 5 */
99
+ loginAccountFailures?: number;
100
+ /** Managed-form submissions per client address, form and minute. @default 5 */
101
+ formPerIp?: number;
102
+ /** Managed-form submissions per form across the deployment and minute. @default 100 */
103
+ formGlobal?: number;
104
+ };
91
105
  /**
92
- * How long a server instance may reuse content it has already read, in seconds. `0` disables it.
106
+ * How long content already read may be reused, in seconds. `0` disables it.
93
107
  *
94
108
  * @remarks
95
- * A save only clears the instance that served it, so this bounds how long another instance can
96
- * still answer with the previous content.
109
+ * Without `cacheStorage`, the cache is per instance and a save only clears the instance that
110
+ * served it — so this also bounds how long another instance can still answer with the previous
111
+ * content. With `cacheStorage`, an invalidation reaches every instance and this is simply how
112
+ * long a cached read lives.
97
113
  *
98
114
  * @default 5
99
115
  */
100
116
  cacheSeconds?: number;
117
+ /**
118
+ * Name of a Nitro storage mount the content cache is shared through, typically Redis.
119
+ * Left out, the cache stays in the memory of each instance.
120
+ *
121
+ * @remarks
122
+ * This is what makes a publication visible everywhere at once: a shared mount is a cache a
123
+ * write can actually invalidate across instances, which an in-process map cannot be.
124
+ *
125
+ * A small in-process tier is kept in front of it either way — it coalesces the concurrent
126
+ * reads of one render and answers repeats without a network hop — so an instance can still
127
+ * miss an invalidation for up to a second.
128
+ *
129
+ * The mount is declared by the application, and the driver's `base` is what the keys are
130
+ * prefixed with:
131
+ *
132
+ * ```ts
133
+ * // nuxt.config.ts
134
+ * nitro: {
135
+ * storage: {
136
+ * eponyme: { driver: 'redis', url: process.env.REDIS_URL, base: 'eponyme' },
137
+ * },
138
+ * },
139
+ * eponyme: { cacheStorage: 'eponyme', cacheSeconds: 60 },
140
+ * ```
141
+ *
142
+ * Keys then read `eponyme:row:<name>` and `eponyme:rows:<collection>:<version>`.
143
+ *
144
+ * `unstorage` ships the Redis driver; the application adds `ioredis` itself, so a deployment
145
+ * that caches in memory does not carry a Redis client it never builds.
146
+ *
147
+ * @see https://nitro.build/guide/storage
148
+ */
149
+ cacheStorage?: string;
101
150
  /**
102
151
  * Whether the filterable index is rebuilt at startup when the configuration that produced
103
152
  * it has changed.
@@ -135,6 +184,31 @@ interface ModuleOptions {
135
184
  * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control
136
185
  */
137
186
  cdnCacheSeconds?: number;
187
+ /**
188
+ * Language of the dashboard interface, resolved once at build time.
189
+ *
190
+ * @remarks
191
+ * One language per build: the catalogue is merged over English and inlined, so a key the
192
+ * locale does not carry falls back to English rather than showing raw. This never touches
193
+ * content, schema labels, or a host's own `@nuxtjs/i18n` setup.
194
+ *
195
+ * Catalogues live in `@eponyme/locale`, one subpath per language, and each export is a
196
+ * function so it can take overrides. Passing the function itself instead of calling it is
197
+ * refused at setup with a message saying so.
198
+ *
199
+ * @default undefined — English
200
+ * @example
201
+ * ```ts
202
+ * import { fr } from '@eponyme/locale/fr'
203
+ *
204
+ * export default defineNuxtConfig({
205
+ * eponyme: {
206
+ * locale: fr({ 'array.add': 'Ajouter un bloc' }),
207
+ * },
208
+ * })
209
+ * ```
210
+ */
211
+ locale?: EponymeLocaleDefinition;
138
212
  }
139
213
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
140
214
 
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "0.8.13",
7
+ "version": "0.9.0-nightly.20260810",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,8 +1,42 @@
1
- import { defineNuxtModule, useLogger, createResolver, findPath, addImports, addImportsDir, addServerImports, addRouteMiddleware, extendViteConfig, addTypeTemplate, addServerHandler, addServerPlugin, hasNuxtModule, hasNuxtModuleCompatibility, getNuxtModuleVersion } from '@nuxt/kit';
1
+ import { defineNuxtModule, useLogger, createResolver, addTemplate, addTypeTemplate, addPlugin, findPath, addImports, addImportsDir, addComponent, addServerImports, addRouteMiddleware, extendViteConfig, addServerHandler, addServerPlugin, hasNuxtModule, hasNuxtModuleCompatibility, getNuxtModuleVersion } from '@nuxt/kit';
2
2
  import pc from 'picocolors';
3
3
  import tailwindcss from '@tailwindcss/vite';
4
+ import { EPONYME_DEFAULT_LOCALE, eponymeEnglishMessages } from '../dist/runtime/locales/index.js';
4
5
  import { resolve } from 'pathe';
5
6
  import { tagPreviewPathRoutes } from '../dist/runtime/utils/cache-tags.js';
7
+ import { EPONYME_THEME_BOOTSTRAP } from '../dist/runtime/utils/eponyme-theme.js';
8
+
9
+ function resolveEponymeLocale(locale, prefix = "[Eponyme]") {
10
+ if (locale === void 0 || locale === null) return { ...EPONYME_DEFAULT_LOCALE, missing: [] };
11
+ if (typeof locale === "function") {
12
+ throw new TypeError(
13
+ `${prefix} eponyme.locale expects the result of a call, not the function itself.
14
+ Write \`locale: fr()\` rather than \`locale: fr\`.`
15
+ );
16
+ }
17
+ const candidate = locale;
18
+ if (typeof locale !== "object" || typeof candidate.code !== "string" || !candidate.code || typeof candidate.messages !== "object" || !candidate.messages) {
19
+ throw new TypeError(
20
+ `${prefix} eponyme.locale must be a locale definition carrying a \`code\` and \`messages\`.
21
+ Import one from @eponyme/locale, for example \`import { fr } from '@eponyme/locale/fr'\`.`
22
+ );
23
+ }
24
+ const messages = candidate.messages;
25
+ return {
26
+ ...candidate,
27
+ messages: { ...eponymeEnglishMessages, ...messages },
28
+ missing: Object.keys(eponymeEnglishMessages).filter((key) => !(key in messages))
29
+ };
30
+ }
31
+ function renderEponymeLocaleModule(locale, translatorPath) {
32
+ return [
33
+ `import { createEponymeTranslator } from ${JSON.stringify(translatorPath)}`,
34
+ `export const locale = ${JSON.stringify({ code: locale.code, dir: locale.dir ?? "ltr" })}`,
35
+ `const messages = ${JSON.stringify(locale.messages, null, 2)}`,
36
+ `export const t = createEponymeTranslator(messages${locale.plural ? `, ${locale.plural.toString()}` : ""})`,
37
+ ""
38
+ ].join("\n");
39
+ }
6
40
 
7
41
  const MINIMUM_NUXT_UI = "4.10.0";
8
42
  const CLIENT_DEPENDENCIES = [
@@ -33,6 +67,12 @@ async function warnOnIncompatibleNuxtUi(nuxt, logger) {
33
67
  build or misbehave somewhere with no obvious link back to this warning.`
34
68
  );
35
69
  }
70
+ function positiveInteger(value, fallback, name) {
71
+ const resolved = value ?? fallback;
72
+ if (!Number.isSafeInteger(resolved) || resolved < 1)
73
+ throw new TypeError(`${pc.red("[Eponyme]")} ${name} must be a positive integer.`);
74
+ return resolved;
75
+ }
36
76
  const module$1 = defineNuxtModule({
37
77
  meta: {
38
78
  name: "@karibsen/eponyme",
@@ -49,6 +89,13 @@ const module$1 = defineNuxtModule({
49
89
  auth: {
50
90
  sessionDurationDays: 7
51
91
  },
92
+ rateLimits: {
93
+ loginPerIp: 10,
94
+ loginGlobal: 300,
95
+ loginAccountFailures: 5,
96
+ formPerIp: 5,
97
+ formGlobal: 100
98
+ },
52
99
  cacheSeconds: 5,
53
100
  autoReindex: true,
54
101
  browserCacheSeconds: 30,
@@ -62,6 +109,7 @@ const module$1 = defineNuxtModule({
62
109
  icons: [
63
110
  "mingcute:history-anticlockwise-line",
64
111
  "mingcute:eye-2-line",
112
+ "mingcute:eye-close-line",
65
113
  "mingcute:layout-leftbar-close-line",
66
114
  "mingcute:layout-leftbar-open-line",
67
115
  "mingcute:down-small-line",
@@ -112,12 +160,51 @@ const module$1 = defineNuxtModule({
112
160
  nuxt.options.runtimeConfig.eponymeAuth = {
113
161
  sessionDurationDays: options.auth?.sessionDurationDays ?? 7
114
162
  };
115
- nuxt.options.runtimeConfig.eponymeContent = {
163
+ nuxt.options.runtimeConfig.eponymeRateLimits = {
164
+ loginPerIp: positiveInteger(options.rateLimits?.loginPerIp, 10, "eponyme.rateLimits.loginPerIp"),
165
+ loginGlobal: positiveInteger(options.rateLimits?.loginGlobal, 300, "eponyme.rateLimits.loginGlobal"),
166
+ loginAccountFailures: positiveInteger(options.rateLimits?.loginAccountFailures, 5, "eponyme.rateLimits.loginAccountFailures"),
167
+ formPerIp: positiveInteger(options.rateLimits?.formPerIp, 5, "eponyme.rateLimits.formPerIp"),
168
+ formGlobal: positiveInteger(options.rateLimits?.formGlobal, 100, "eponyme.rateLimits.formGlobal")
169
+ };
170
+ const eponymeContent = {
116
171
  cacheSeconds: options.cacheSeconds ?? 5,
172
+ cacheStorage: options.cacheStorage?.trim() || "",
117
173
  autoReindex: options.autoReindex ?? true,
118
174
  browserCacheSeconds: options.browserCacheSeconds ?? 30,
119
175
  cdnCacheSeconds: options.cdnCacheSeconds ?? 300
120
176
  };
177
+ nuxt.options.runtimeConfig.eponymeContent = eponymeContent;
178
+ const locale = resolveEponymeLocale(options.locale, pc.red("[Eponyme]"));
179
+ if (locale.missing.length) {
180
+ logger.warn(
181
+ `The \`${locale.code}\` catalogue is missing ${locale.missing.length} key${locale.missing.length > 1 ? "s" : ""}, which fall back to English:
182
+ ` + locale.missing.map((key) => ` - ${key}`).join("\n")
183
+ );
184
+ }
185
+ const translatorPath = await resolver.resolvePath("./runtime/utils/translate");
186
+ const localeTemplate = addTemplate({
187
+ filename: "eponyme-locale.mjs",
188
+ write: true,
189
+ getContents: () => renderEponymeLocaleModule(locale, translatorPath)
190
+ });
191
+ nuxt.options.alias["#eponyme/locale"] = localeTemplate.dst;
192
+ addTypeTemplate({
193
+ filename: "types/eponyme-locale.d.ts",
194
+ getContents: () => `declare module '#eponyme/locale' {
195
+ import type { EponymeLocaleDefinition, EponymeMessageKey } from ${JSON.stringify(resolver.resolve("./runtime/locales"))}
196
+ import type { EponymeTranslateParams } from ${JSON.stringify(resolver.resolve("./runtime/utils/translate"))}
197
+ export const locale: EponymeLocaleDefinition
198
+ export function t(key: EponymeMessageKey, params?: EponymeTranslateParams): string
199
+ }
200
+ `
201
+ });
202
+ nuxt.options.app.head.script ??= [];
203
+ nuxt.options.app.head.script.unshift({
204
+ key: "eponyme-theme",
205
+ innerHTML: EPONYME_THEME_BOOTSTRAP
206
+ });
207
+ addPlugin(resolver.resolve("./runtime/plugins/eponyme-theme"));
121
208
  const tagged = tagPreviewPathRoutes(options.previewPaths ?? {}, nuxt.options.routeRules ??= {});
122
209
  if (tagged.length) {
123
210
  const width = Math.max(...tagged.map(({ route }) => route.length));
@@ -152,10 +239,15 @@ Note that '~/' points at ${nuxt.options.srcDir}, so a server module needs '~~/'.
152
239
  { name: "eponymeMediaThumbnailUrl", from: resolver.resolve("./runtime/utils/media-player") }
153
240
  ]);
154
241
  addImportsDir(resolver.resolve("./runtime/composables"));
242
+ addComponent({ name: "EponymeRichText", filePath: resolver.resolve("./runtime/components/EponymeRichText") });
155
243
  addServerImports([
156
244
  { name: "getEponymeSitemapEntries", from: resolver.resolve("./runtime/server/utils/eponyme-sitemap") },
157
245
  { name: "reindexEponymeEntries", from: resolver.resolve("./runtime/server/utils/eponyme-reindex") },
246
+ { name: "runEponymeSchedule", from: resolver.resolve("./runtime/server/utils/eponyme-schedule") },
158
247
  { name: "validateEponymeForm", from: resolver.resolve("./runtime/server/utils/eponyme-form") },
248
+ // Lets a route Eponyme does not own feed the dashboard's submission list.
249
+ { name: "storeEponymeFormSubmission", from: resolver.resolve("./runtime/server/utils/eponyme-form") },
250
+ { name: "assertEponymeFormRateLimit", from: resolver.resolve("./runtime/server/utils/eponyme-form") },
159
251
  { name: "getEponymeCacheTags", from: resolver.resolve("./runtime/server/utils/eponyme-cache") }
160
252
  ]);
161
253
  addRouteMiddleware({
@@ -1 +1 @@
1
- @import "tailwindcss/theme" layer(theme) prefix(ep);@import "tailwindcss/preflight" layer(base);@import "tailwindcss/utilities" layer(utilities) prefix(ep);@source "../components";@source "../pages";@theme{--font-sans:"SF Pro Text",Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;--color-theme-ep:#1c1c1c;--color-border-ep:#343434;--color-selected-ep:#262626;--color-text-ep:#e7e7e7;--color-muted-ep:#8d8d8d;--color-success-ep:#72d39a;--color-warning-ep:#e6b85c;--color-danger-ep:#ff8585}:root.dark{--color-eponyme-50:#fafafa;--color-eponyme-100:#f5f5f5;--color-eponyme-200:#e6e6e6;--color-eponyme-300:#d3d3d3;--color-eponyme-400:#a3a3a3;--color-eponyme-500:#727272;--color-eponyme-600:#535353;--color-eponyme-700:#404040;--color-eponyme-800:#272727;--color-eponyme-900:#1c1c1c;--color-eponyme-950:#0b0b0b}@layer base{.eponyme-portal,.eponyme-root{font-family:var(--ep-font-sans)}:where(.eponyme-root,.eponyme-portal),:where(.eponyme-root,.eponyme-portal) *,:where(.eponyme-root,.eponyme-portal) :after,:where(.eponyme-root,.eponyme-portal) :before{border:0 solid;box-sizing:border-box;margin:0;padding:0}}.eponyme-date-input{color-scheme:dark}
1
+ @import "tailwindcss/theme" layer(theme) prefix(ep);@import "tailwindcss/preflight" layer(base);@import "tailwindcss/utilities" layer(utilities) prefix(ep);@source "../components";@source "../pages";@theme{--font-sans:"SF Pro Text",Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;--color-theme-ep:#1c1c1c;--color-border-ep:#343434;--color-selected-ep:#262626;--color-text-ep:#e7e7e7;--color-muted-ep:#8d8d8d;--color-success-ep:#72d39a;--color-warning-ep:#e6b85c;--color-danger-ep:#ff8585;--color-canvas-ep:#fff;--color-inverse-ep:#fff}:root,:root.ep-dark{--ep-color-theme-ep:#1c1c1c;--ep-color-border-ep:#343434;--ep-color-selected-ep:#262626;--ep-color-text-ep:#e7e7e7;--ep-color-muted-ep:#8d8d8d;--ep-color-success-ep:#72d39a;--ep-color-warning-ep:#e6b85c;--ep-color-danger-ep:#ff8585;--ep-color-white:#fff}:root.ep-light{--ep-color-theme-ep:#f7f7f8;--ep-color-border-ep:rgba(172,176,188,.27)!important;--ep-color-selected-ep:#ededf0;--ep-color-text-ep:#1c1d20;--ep-color-muted-ep:#62656d;--ep-color-success-ep:#187443;--ep-color-warning-ep:#ffa700;--ep-color-danger-ep:#b42336;--ep-color-white:#1c1d20}@layer base{.eponyme-portal,.eponyme-root{color-scheme:dark;font-family:var(--ep-font-sans)}:root.ep-light :where(.eponyme-root,.eponyme-portal){color-scheme:light}:where(.eponyme-root,.eponyme-portal),:where(.eponyme-root,.eponyme-portal) *,:where(.eponyme-root,.eponyme-portal) :after,:where(.eponyme-root,.eponyme-portal) :before{border:0 solid;box-sizing:border-box;margin:0;padding:0}}.eponyme-date-input{color-scheme:inherit}
@@ -7,3 +7,5 @@ declare module '*.png?url' {
7
7
  const url: string
8
8
  export default url
9
9
  }
10
+
11
+ declare module '*.css' {}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Renders a `field.richText()` value on a public page.
3
+ *
4
+ * The HTML is safe by construction rather than by inspection here: every write path runs it
5
+ * through Eponyme's sanitisation policy before it is stored, and a payload carrying anything
6
+ * the editor cannot produce is refused with a `422`. Re-sanitising in the browser would ship
7
+ * a parser to every visitor to re-check a decision the server already made — and a client
8
+ * that can be skipped is not a security boundary anyway.
9
+ *
10
+ * It exists so a host application never writes `v-html` itself, which is the line between
11
+ * "Eponyme guarantees this" and "the application hopes so".
12
+ *
13
+ * A render function rather than a template: `innerHTML` is what `v-html` compiles to, and
14
+ * writing it directly keeps the one deliberate injection point in a file that explains it.
15
+ */
16
+ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
17
+ html: {
18
+ type: StringConstructor;
19
+ default: string;
20
+ };
21
+ as: {
22
+ type: StringConstructor;
23
+ default: string;
24
+ };
25
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
26
+ [key: string]: any;
27
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
28
+ html: {
29
+ type: StringConstructor;
30
+ default: string;
31
+ };
32
+ as: {
33
+ type: StringConstructor;
34
+ default: string;
35
+ };
36
+ }>> & Readonly<{}>, {
37
+ html: string;
38
+ as: string;
39
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
40
+ export default _default;
@@ -0,0 +1,11 @@
1
+ import { defineComponent, h } from "vue";
2
+ export default defineComponent({
3
+ name: "EponymeRichText",
4
+ props: {
5
+ html: { type: String, default: "" },
6
+ as: { type: String, default: "div" }
7
+ },
8
+ setup(props) {
9
+ return () => h(props.as, { class: "eponyme-rich-text", innerHTML: props.html });
10
+ }
11
+ });