@sonicjs-cms/core 2.18.1 → 3.0.0-beta.2

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 (225) hide show
  1. package/README.md +4 -3
  2. package/dist/admin-documents-form.template-KN7JF66Q.cjs +19 -0
  3. package/dist/{admin-layout-catalyst.template-UMTIN66R.js.map → admin-documents-form.template-KN7JF66Q.cjs.map} +1 -1
  4. package/dist/admin-documents-form.template-NLSI6Z42.js +6 -0
  5. package/dist/{admin-layout-catalyst.template-HFD37TY5.cjs.map → admin-documents-form.template-NLSI6Z42.js.map} +1 -1
  6. package/dist/admin-layout-catalyst.template-WHJGSWWD.js +7 -0
  7. package/dist/admin-layout-catalyst.template-WHJGSWWD.js.map +1 -0
  8. package/dist/admin-layout-catalyst.template-ZK5HD545.cjs +17 -0
  9. package/dist/admin-layout-catalyst.template-ZK5HD545.cjs.map +1 -0
  10. package/dist/app-Bo0X1OWX.d.ts +1268 -0
  11. package/dist/app-Do66yCcV.d.cts +1268 -0
  12. package/dist/cache-DDARE4QE.js +4 -0
  13. package/dist/cache-DDARE4QE.js.map +1 -0
  14. package/dist/cache-LVYS4BPL.cjs +33 -0
  15. package/dist/cache-LVYS4BPL.cjs.map +1 -0
  16. package/dist/chunk-2CB4KY7I.cjs +771 -0
  17. package/dist/chunk-2CB4KY7I.cjs.map +1 -0
  18. package/dist/{chunk-55RDMDOP.js → chunk-3TB6AT6X.js} +148 -55
  19. package/dist/chunk-3TB6AT6X.js.map +1 -0
  20. package/dist/{chunk-ON5ZMSU4.js → chunk-6JQOUUOB.js} +3 -3
  21. package/dist/chunk-6JQOUUOB.js.map +1 -0
  22. package/dist/chunk-6OUHGKFD.js +387 -0
  23. package/dist/chunk-6OUHGKFD.js.map +1 -0
  24. package/dist/{chunk-DSUJ5YQH.cjs → chunk-AAWNRBRB.cjs} +537 -92
  25. package/dist/chunk-AAWNRBRB.cjs.map +1 -0
  26. package/dist/chunk-AI663NBO.js +821 -0
  27. package/dist/chunk-AI663NBO.js.map +1 -0
  28. package/dist/chunk-BDDABDAB.cjs +1149 -0
  29. package/dist/chunk-BDDABDAB.cjs.map +1 -0
  30. package/dist/chunk-BLMTL57B.js +767 -0
  31. package/dist/chunk-BLMTL57B.js.map +1 -0
  32. package/dist/chunk-DNQCEKUK.cjs +327 -0
  33. package/dist/chunk-DNQCEKUK.cjs.map +1 -0
  34. package/dist/chunk-DSA4UX5B.cjs +276 -0
  35. package/dist/chunk-DSA4UX5B.cjs.map +1 -0
  36. package/dist/chunk-EF2NQUIQ.js +323 -0
  37. package/dist/chunk-EF2NQUIQ.js.map +1 -0
  38. package/dist/chunk-GCDZZNIN.js +192 -0
  39. package/dist/chunk-GCDZZNIN.js.map +1 -0
  40. package/dist/{chunk-ABB34XUS.cjs → chunk-H2AXVCLS.cjs} +667 -19
  41. package/dist/chunk-H2AXVCLS.cjs.map +1 -0
  42. package/dist/{chunk-XWIA3HVX.js → chunk-HDWE5FRJ.js} +6 -1249
  43. package/dist/chunk-HDWE5FRJ.js.map +1 -0
  44. package/dist/chunk-HIKBY7MS.cjs +70 -0
  45. package/dist/chunk-HIKBY7MS.cjs.map +1 -0
  46. package/dist/chunk-IESEVHXL.js +66 -0
  47. package/dist/chunk-IESEVHXL.js.map +1 -0
  48. package/dist/chunk-IVPRUGTY.js +242 -0
  49. package/dist/chunk-IVPRUGTY.js.map +1 -0
  50. package/dist/{chunk-SQ6FNXU2.cjs → chunk-IXUHXTHW.cjs} +2 -151
  51. package/dist/chunk-IXUHXTHW.cjs.map +1 -0
  52. package/dist/chunk-J6JTWD2A.cjs +100 -0
  53. package/dist/chunk-J6JTWD2A.cjs.map +1 -0
  54. package/dist/chunk-JEQ7FLOD.cjs +199 -0
  55. package/dist/chunk-JEQ7FLOD.cjs.map +1 -0
  56. package/dist/chunk-K25XHMM3.js +566 -0
  57. package/dist/chunk-K25XHMM3.js.map +1 -0
  58. package/dist/chunk-LRZIAW7U.cjs +158 -0
  59. package/dist/chunk-LRZIAW7U.cjs.map +1 -0
  60. package/dist/{chunk-OHYBNCVL.cjs → chunk-MVIZJOO5.cjs} +10 -1256
  61. package/dist/chunk-MVIZJOO5.cjs.map +1 -0
  62. package/dist/{chunk-UYJ6TJHX.cjs → chunk-NAVPFIG5.cjs} +148 -55
  63. package/dist/chunk-NAVPFIG5.cjs.map +1 -0
  64. package/dist/chunk-NLJVSER2.js +273 -0
  65. package/dist/chunk-NLJVSER2.js.map +1 -0
  66. package/dist/chunk-NMPEMSU4.js +154 -0
  67. package/dist/chunk-NMPEMSU4.js.map +1 -0
  68. package/dist/chunk-NUKJ54GA.cjs +245 -0
  69. package/dist/chunk-NUKJ54GA.cjs.map +1 -0
  70. package/dist/{chunk-T3Q5V33G.cjs → chunk-QAYFOER6.cjs} +621 -829
  71. package/dist/chunk-QAYFOER6.cjs.map +1 -0
  72. package/dist/{chunk-MGFRZO24.js → chunk-QZGABF2M.js} +3 -149
  73. package/dist/chunk-QZGABF2M.js.map +1 -0
  74. package/dist/chunk-RNZFGN4R.js +88 -0
  75. package/dist/chunk-RNZFGN4R.js.map +1 -0
  76. package/dist/chunk-RZ6H7OZK.js +1134 -0
  77. package/dist/chunk-RZ6H7OZK.js.map +1 -0
  78. package/dist/{chunk-XXDFQERJ.js → chunk-VD2EA3WT.js} +7192 -9806
  79. package/dist/chunk-VD2EA3WT.js.map +1 -0
  80. package/dist/{chunk-SXXTQETM.cjs → chunk-VXE42MYF.cjs} +8722 -11323
  81. package/dist/chunk-VXE42MYF.cjs.map +1 -0
  82. package/dist/{chunk-4ZSNJDLS.cjs → chunk-WULONYGB.cjs} +9 -9
  83. package/dist/chunk-WULONYGB.cjs.map +1 -0
  84. package/dist/chunk-XW56B23A.cjs +408 -0
  85. package/dist/chunk-XW56B23A.cjs.map +1 -0
  86. package/dist/chunk-YA3TJ65D.cjs +575 -0
  87. package/dist/chunk-YA3TJ65D.cjs.map +1 -0
  88. package/dist/{chunk-TFNTM3OA.js → chunk-YHSQVQXX.js} +645 -15
  89. package/dist/chunk-YHSQVQXX.js.map +1 -0
  90. package/dist/chunk-YP7GW2G5.cjs +866 -0
  91. package/dist/chunk-YP7GW2G5.cjs.map +1 -0
  92. package/dist/{chunk-QFWHAFEO.js → chunk-ZEZ245PW.js} +148 -858
  93. package/dist/chunk-ZEZ245PW.js.map +1 -0
  94. package/dist/{chunk-EW5NOBVU.js → chunk-ZGGXCFR6.js} +611 -817
  95. package/dist/chunk-ZGGXCFR6.js.map +1 -0
  96. package/dist/{collection-config-B4PG-AaF.d.cts → collection-config-JgHOpFCG.d.cts} +30 -2
  97. package/dist/{collection-config-B4PG-AaF.d.ts → collection-config-JgHOpFCG.d.ts} +30 -2
  98. package/dist/config-HFXANXCC.js +6 -0
  99. package/dist/config-HFXANXCC.js.map +1 -0
  100. package/dist/config-ON6FNMYX.cjs +19 -0
  101. package/dist/config-ON6FNMYX.cjs.map +1 -0
  102. package/dist/define-plugin-BzNHc1ZI.d.ts +1321 -0
  103. package/dist/define-plugin-IWDKYaVm.d.cts +1321 -0
  104. package/dist/document-projection-TDWRJX3Z.cjs +13 -0
  105. package/dist/document-projection-TDWRJX3Z.cjs.map +1 -0
  106. package/dist/document-projection-YYMC6I4U.js +4 -0
  107. package/dist/document-projection-YYMC6I4U.js.map +1 -0
  108. package/dist/index.cjs +13735 -4329
  109. package/dist/index.cjs.map +1 -1
  110. package/dist/index.d.cts +329 -492
  111. package/dist/index.d.ts +329 -492
  112. package/dist/index.js +13386 -3999
  113. package/dist/index.js.map +1 -1
  114. package/dist/middleware.cjs +36 -32
  115. package/dist/middleware.d.cts +69 -7
  116. package/dist/middleware.d.ts +69 -7
  117. package/dist/middleware.js +7 -3
  118. package/dist/migrations-NJJWQUKK.cjs +13 -0
  119. package/dist/{migrations-IYNTWDC6.cjs.map → migrations-NJJWQUKK.cjs.map} +1 -1
  120. package/dist/migrations-WCAVBD7C.js +4 -0
  121. package/dist/{migrations-R337UD46.js.map → migrations-WCAVBD7C.js.map} +1 -1
  122. package/dist/{plugin-bootstrap-DfVerYV4.d.cts → plugin-bootstrap-B8ThJU21.d.cts} +4315 -1661
  123. package/dist/{plugin-bootstrap-P_ciLp_C.d.ts → plugin-bootstrap-qu8hJgUt.d.ts} +4315 -1661
  124. package/dist/plugins.cjs +171 -12
  125. package/dist/plugins.d.cts +36 -2
  126. package/dist/plugins.d.ts +36 -2
  127. package/dist/plugins.js +5 -2
  128. package/dist/rbac-O73MFKDA.js +5 -0
  129. package/dist/rbac-O73MFKDA.js.map +1 -0
  130. package/dist/rbac-VONLJJKB.cjs +14 -0
  131. package/dist/rbac-VONLJJKB.cjs.map +1 -0
  132. package/dist/routes.cjs +41 -45
  133. package/dist/routes.d.cts +56 -146
  134. package/dist/routes.d.ts +56 -146
  135. package/dist/routes.js +17 -9
  136. package/dist/services.cjs +39 -72
  137. package/dist/services.d.cts +79 -54
  138. package/dist/services.d.ts +79 -54
  139. package/dist/services.js +6 -3
  140. package/dist/templates.cjs +17 -29
  141. package/dist/templates.d.cts +1 -66
  142. package/dist/templates.d.ts +1 -66
  143. package/dist/templates.js +3 -3
  144. package/dist/types-Dea1eNxU.d.cts +286 -0
  145. package/dist/types-Dea1eNxU.d.ts +286 -0
  146. package/dist/types.d.cts +1 -1
  147. package/dist/types.d.ts +1 -1
  148. package/dist/utils.cjs +18 -17
  149. package/dist/utils.d.cts +1 -1
  150. package/dist/utils.d.ts +1 -1
  151. package/dist/utils.js +2 -1
  152. package/migrations/0001_core.sql +184 -0
  153. package/migrations/0002_documents.sql +163 -0
  154. package/package.json +12 -7
  155. package/dist/admin-layout-catalyst.template-HFD37TY5.cjs +0 -17
  156. package/dist/admin-layout-catalyst.template-UMTIN66R.js +0 -7
  157. package/dist/app-C9esKLmh.d.cts +0 -112
  158. package/dist/app-C9esKLmh.d.ts +0 -112
  159. package/dist/chunk-4R3NOOL3.js +0 -2217
  160. package/dist/chunk-4R3NOOL3.js.map +0 -1
  161. package/dist/chunk-4ZSNJDLS.cjs.map +0 -1
  162. package/dist/chunk-55RDMDOP.js.map +0 -1
  163. package/dist/chunk-635JAMSE.cjs +0 -653
  164. package/dist/chunk-635JAMSE.cjs.map +0 -1
  165. package/dist/chunk-ABB34XUS.cjs.map +0 -1
  166. package/dist/chunk-C54YUA23.cjs +0 -2219
  167. package/dist/chunk-C54YUA23.cjs.map +0 -1
  168. package/dist/chunk-DSUJ5YQH.cjs.map +0 -1
  169. package/dist/chunk-EW5NOBVU.js.map +0 -1
  170. package/dist/chunk-EXNEW5US.js +0 -648
  171. package/dist/chunk-EXNEW5US.js.map +0 -1
  172. package/dist/chunk-I2H5NGJQ.js +0 -692
  173. package/dist/chunk-I2H5NGJQ.js.map +0 -1
  174. package/dist/chunk-MGFRZO24.js.map +0 -1
  175. package/dist/chunk-OHYBNCVL.cjs.map +0 -1
  176. package/dist/chunk-ON5ZMSU4.js.map +0 -1
  177. package/dist/chunk-QFWHAFEO.js.map +0 -1
  178. package/dist/chunk-SQ6FNXU2.cjs.map +0 -1
  179. package/dist/chunk-SXXTQETM.cjs.map +0 -1
  180. package/dist/chunk-T3Q5V33G.cjs.map +0 -1
  181. package/dist/chunk-TFNTM3OA.js.map +0 -1
  182. package/dist/chunk-UYJ6TJHX.cjs.map +0 -1
  183. package/dist/chunk-WAEQXGCX.cjs +0 -1898
  184. package/dist/chunk-WAEQXGCX.cjs.map +0 -1
  185. package/dist/chunk-XWIA3HVX.js.map +0 -1
  186. package/dist/chunk-XXDFQERJ.js.map +0 -1
  187. package/dist/migrations-IYNTWDC6.cjs +0 -13
  188. package/dist/migrations-R337UD46.js +0 -4
  189. package/dist/plugin-manager-BoM3Q7o7.d.cts +0 -328
  190. package/dist/plugin-manager-Efx9RyDX.d.ts +0 -328
  191. package/migrations/001_initial_schema.sql +0 -170
  192. package/migrations/002_faq_plugin.sql +0 -86
  193. package/migrations/003_stage5_enhancements.sql +0 -121
  194. package/migrations/004_stage6_user_management.sql +0 -183
  195. package/migrations/005_stage7_workflow_automation.sql +0 -294
  196. package/migrations/006_plugin_system.sql +0 -155
  197. package/migrations/007_demo_login_plugin.sql +0 -23
  198. package/migrations/008_fix_slug_validation.sql +0 -22
  199. package/migrations/009_system_logging.sql +0 -57
  200. package/migrations/011_config_managed_collections.sql +0 -15
  201. package/migrations/012_testimonials_plugin.sql +0 -80
  202. package/migrations/013_code_examples_plugin.sql +0 -177
  203. package/migrations/014_fix_plugin_registry.sql +0 -88
  204. package/migrations/015_add_remaining_plugins.sql +0 -89
  205. package/migrations/016_remove_duplicate_cache_plugin.sql +0 -17
  206. package/migrations/017_auth_configurable_fields.sql +0 -49
  207. package/migrations/018_settings_table.sql +0 -23
  208. package/migrations/019_remove_blog_posts_collection.sql +0 -15
  209. package/migrations/020_add_email_plugin.sql +0 -22
  210. package/migrations/021_add_magic_link_auth_plugin.sql +0 -42
  211. package/migrations/022_add_tinymce_plugin.sql +0 -25
  212. package/migrations/023_add_easy_mdx_plugin.sql +0 -25
  213. package/migrations/024_add_quill_editor_plugin.sql +0 -25
  214. package/migrations/025_add_easymde_plugin.sql +0 -25
  215. package/migrations/026_add_otp_login.sql +0 -42
  216. package/migrations/027_fix_slug_field_type.sql +0 -18
  217. package/migrations/028_fix_slug_field_type_in_schemas.sql +0 -30
  218. package/migrations/029_add_forms_system.sql +0 -184
  219. package/migrations/030_add_turnstile_to_forms.sql +0 -14
  220. package/migrations/031_ai_search_plugin.sql +0 -45
  221. package/migrations/032_user_profiles.sql +0 -37
  222. package/migrations/033_form_content_integration.sql +0 -19
  223. package/migrations/034_security_audit_plugin.sql +0 -27
  224. package/migrations/035_user_profiles_data_column.sql +0 -16
  225. package/migrations/036_analytics_events.sql +0 -22
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Typed hook event catalog
3
+ *
4
+ * The single source of truth for which lifecycle events a plugin may subscribe
5
+ * to, and the payload shape each one carries. Subscribing through the typed
6
+ * facade (`createTypedHooks`) gives a plugin the narrowed payload type with no
7
+ * casting — TypeScript rejects a wrong field name at the `.on()` call site.
8
+ *
9
+ * Scope note: this catalog lists the events that are (or are being) dispatched
10
+ * in production. The legacy string-keyed `HOOKS` map in `../types` declared many
11
+ * more events than were ever fired; reconciling that list down to what actually
12
+ * dispatches is tracked in the plugin-overhaul plan. Add an event here only when
13
+ * a real dispatch site exists (or is landing in the same change).
14
+ */
15
+ /**
16
+ * The acting user on a hook event. ONE canonical shape across every event —
17
+ * always `id` (never `userId`), so a plugin reading `payload.user.id` works on
18
+ * content events and auth events alike.
19
+ */
20
+ interface HookActor {
21
+ id: string;
22
+ email: string;
23
+ role?: string;
24
+ }
25
+ /** Common shape for content lifecycle events. */
26
+ interface ContentEventPayload {
27
+ /** Collection / content-type slug the event is about. */
28
+ collection: string;
29
+ /** Content row id, when known (absent for pre-create events). */
30
+ id?: string;
31
+ /** The content data being read/written. Mutable by `before` handlers in the chain. */
32
+ data: Record<string, unknown>;
33
+ /** The acting user, when the event originates from an authenticated request. */
34
+ user?: HookActor;
35
+ }
36
+ /** Emitted after a user completes self-registration. */
37
+ interface AuthRegistrationCompletedPayload {
38
+ user: HookActor;
39
+ }
40
+ /** Emitted when a password reset is requested (carries the reset token internally). */
41
+ interface AuthPasswordResetRequestedPayload {
42
+ user: HookActor;
43
+ /** Single-use reset token. Never expose this in an API response. */
44
+ resetToken: string;
45
+ }
46
+ /** Emitted after a password reset is confirmed. */
47
+ interface AuthPasswordResetCompletedPayload {
48
+ user: HookActor;
49
+ }
50
+ /** Emitted after a magic-link sign-in link is successfully consumed. */
51
+ interface AuthMagicLinkConsumedPayload {
52
+ user: HookActor;
53
+ }
54
+ /** Emitted after an OTP code is successfully verified. */
55
+ interface AuthOtpVerifiedPayload {
56
+ user: HookActor;
57
+ }
58
+ /**
59
+ * The catalog: event name → payload type.
60
+ *
61
+ * Keep keys in sync with `HookEventName` (derived below) and with the dispatch
62
+ * sites. This is an interface (not a const) so it participates in type-level
63
+ * lookups and can be augmented via declaration merging if a downstream package
64
+ * needs to extend it.
65
+ */
66
+ interface HookEventPayloads {
67
+ 'content:read': ContentEventPayload;
68
+ 'content:before:create': ContentEventPayload;
69
+ 'content:before:update': ContentEventPayload;
70
+ 'content:before:delete': ContentEventPayload;
71
+ 'content:after:create': ContentEventPayload;
72
+ 'content:after:update': ContentEventPayload;
73
+ 'content:after:delete': ContentEventPayload;
74
+ 'content:after:publish': ContentEventPayload;
75
+ 'auth:registration:completed': AuthRegistrationCompletedPayload;
76
+ 'auth:password-reset:requested': AuthPasswordResetRequestedPayload;
77
+ 'auth:password-reset:completed': AuthPasswordResetCompletedPayload;
78
+ 'auth:magic-link:consumed': AuthMagicLinkConsumedPayload;
79
+ 'auth:otp:verified': AuthOtpVerifiedPayload;
80
+ }
81
+ /** Union of all catalog event names. */
82
+ type HookEventName = keyof HookEventPayloads;
83
+ /** The payload type for a given event name. */
84
+ type HookPayload<E extends HookEventName> = HookEventPayloads[E];
85
+ /**
86
+ * Runtime list of catalog event names.
87
+ *
88
+ * Useful for validation (e.g. "is this a known event?") and for diagnostics.
89
+ * Kept as a typed tuple so it can't silently drift from the interface: any new
90
+ * key added to `HookEventPayloads` should be added here too, and the
91
+ * `satisfies` check below fails the build if the list references an unknown
92
+ * event.
93
+ */
94
+ declare const HOOK_EVENT_NAMES: readonly ["content:read", "content:before:create", "content:before:update", "content:before:delete", "content:after:create", "content:after:update", "content:after:delete", "content:after:publish", "auth:registration:completed", "auth:password-reset:requested", "auth:password-reset:completed", "auth:magic-link:consumed", "auth:otp:verified"];
95
+ /** True if `name` is a canonical catalog event. */
96
+ declare function isKnownHookEvent(name: string): name is HookEventName;
97
+ /** Deprecated event names mapped to their canonical payload type. */
98
+ interface LegacyHookEventPayloads {
99
+ 'content:create': ContentEventPayload;
100
+ 'content:update': ContentEventPayload;
101
+ 'content:delete': ContentEventPayload;
102
+ 'content:publish': ContentEventPayload;
103
+ /** No after-only successor; folded into update. */
104
+ 'content:save': ContentEventPayload;
105
+ }
106
+ /** Map each deprecated name to the canonical name it resolves to. */
107
+ declare const LEGACY_EVENT_ALIASES: {
108
+ readonly 'content:create': "content:after:create";
109
+ readonly 'content:update': "content:after:update";
110
+ readonly 'content:delete': "content:after:delete";
111
+ readonly 'content:publish': "content:after:publish";
112
+ readonly 'content:save': "content:after:update";
113
+ };
114
+ /** A deprecated event name accepted (with a warning) at subscribe time. */
115
+ type LegacyHookEventName = keyof LegacyHookEventPayloads;
116
+ /** True if `name` is a deprecated alias. */
117
+ declare function isLegacyHookEvent(name: string): name is LegacyHookEventName;
118
+ /**
119
+ * Resolve a subscribe-time event name to its canonical form: returns the name
120
+ * itself if canonical, the aliased canonical name if deprecated, or `undefined`
121
+ * if unknown.
122
+ */
123
+ declare function resolveHookEventName(name: string): HookEventName | undefined;
124
+
125
+ /**
126
+ * Typed hook facade
127
+ *
128
+ * Wraps the (string-keyed, untyped) hook system in a catalog-aware API:
129
+ *
130
+ * const hooks = createTypedHooks(hookSystem)
131
+ * hooks.on('auth:registration:completed', (payload) => {
132
+ * payload.user.email // ✓ narrowed — no cast
133
+ * payload.user.nope // ✗ type error
134
+ * })
135
+ * await hooks.dispatch('auth:registration:completed', { user: {...} })
136
+ *
137
+ * The facade is intentionally structural about the underlying hook system (see
138
+ * `HookSystemLike`) so both `HookSystemImpl` and `ScopedHookSystem` — and the
139
+ * `src`/`dist` duplicate type identities — all satisfy it without casts.
140
+ */
141
+
142
+ /** A name accepted at subscribe time: a canonical event or a deprecated alias. */
143
+ type SubscribableEvent = HookEventName | LegacyHookEventName;
144
+ /** The payload type for a subscribable name — canonical payload, even for aliases. */
145
+ type PayloadForEvent<E extends SubscribableEvent> = E extends HookEventName ? HookPayload<E> : E extends LegacyHookEventName ? LegacyHookEventPayloads[E] : never;
146
+ /**
147
+ * Minimal structural contract the typed facade needs from a hook system.
148
+ * Satisfied by `HookSystemImpl` and `ScopedHookSystem`.
149
+ */
150
+ interface HookSystemLike {
151
+ register(hookName: string, handler: (data: any, context: any) => any, priority?: number): void;
152
+ execute(hookName: string, data: any, context?: any): Promise<any>;
153
+ unregister?(hookName: string, handler: (data: any, context: any) => any): void;
154
+ }
155
+ /** Context passed to a typed hook handler (kept loose; mirrors the legacy HookContext). */
156
+ interface TypedHookContext {
157
+ /** Plugin that registered the hook, if known. */
158
+ plugin?: string;
159
+ /** Cancel the remaining hook chain. */
160
+ cancel?: () => void;
161
+ [key: string]: unknown;
162
+ }
163
+ /**
164
+ * A typed hook handler. May mutate and return the payload (threaded to the next
165
+ * handler), or return nothing (the current payload is preserved).
166
+ */
167
+ type TypedHookHandler<E extends HookEventName> = (payload: HookPayload<E>, context: TypedHookContext) => HookPayload<E> | void | Promise<HookPayload<E> | void>;
168
+ interface TypedHooks {
169
+ /**
170
+ * Subscribe to a catalog event. Accepts canonical names and (for one release)
171
+ * deprecated aliases — an alias resolves to its canonical name and emits a
172
+ * one-time deprecation warning. Lower priority runs earlier (default 10).
173
+ */
174
+ on<E extends SubscribableEvent>(event: E, handler: (payload: PayloadForEvent<E>, context: TypedHookContext) => PayloadForEvent<E> | void | Promise<PayloadForEvent<E> | void>, priority?: number): void;
175
+ /**
176
+ * Dispatch a catalog event through the handler chain. Canonical names only —
177
+ * the host owns dispatch sites. Returns the (possibly mutated) payload.
178
+ */
179
+ dispatch<E extends HookEventName>(event: E, payload: HookPayload<E>, context?: TypedHookContext): Promise<HookPayload<E>>;
180
+ }
181
+ /**
182
+ * Build a typed facade over a hook system.
183
+ *
184
+ * `on()` resolves deprecated aliases to canonical names (warning once), then
185
+ * registers under the canonical name so a legacy subscriber fires when the host
186
+ * dispatches the canonical event. Returning `void` from a handler preserves the
187
+ * current payload in the chain (the underlying `execute()` threads whatever each
188
+ * handler returns, so we coalesce `undefined` back to the incoming data).
189
+ */
190
+ declare function createTypedHooks(hookSystem: HookSystemLike): TypedHooks;
191
+
192
+ /**
193
+ * Provider-agnostic email types
194
+ *
195
+ * The whole point of this layer: a SonicJS app sends mail through one
196
+ * `EmailService` chokepoint, and the *transport* is a swappable `EmailProvider`.
197
+ * Built-ins ship for Resend and SendGrid, but a developer can drop in any
198
+ * implementation of `EmailProvider` (Postmark, SES, an internal relay, a mock in
199
+ * tests) without touching call sites. Every send is recorded in `email_log`.
200
+ */
201
+ /** A message as authored by a caller. `from` is optional (the service fills a default). */
202
+ interface EmailMessage {
203
+ to: string | string[];
204
+ subject: string;
205
+ html?: string;
206
+ text?: string;
207
+ /** Overrides the service's default from-address. */
208
+ from?: string;
209
+ replyTo?: string;
210
+ cc?: string | string[];
211
+ bcc?: string | string[];
212
+ /** Extra transport headers. */
213
+ headers?: Record<string, string>;
214
+ /**
215
+ * Logical flow this send belongs to (e.g. `'password-reset'`, `'otp'`,
216
+ * `'magic-link'`, `'welcome'`, `'test'`). Recorded in `email_log.flow` for
217
+ * observability; does not affect delivery.
218
+ */
219
+ flow?: string;
220
+ /** Free-form metadata recorded (as JSON) in `email_log.metadata`. */
221
+ metadata?: Record<string, unknown>;
222
+ }
223
+ /** A message after the service has resolved defaults — what a provider receives. */
224
+ interface NormalizedEmailMessage extends Omit<EmailMessage, 'to' | 'cc' | 'bcc'> {
225
+ to: string[];
226
+ from: string;
227
+ cc?: string[];
228
+ bcc?: string[];
229
+ }
230
+ /** Outcome of a single send attempt. */
231
+ interface SendResult {
232
+ ok: boolean;
233
+ /** Provider that handled (or attempted) the send. */
234
+ provider: string;
235
+ /** Provider-side message id, when the transport returns one. */
236
+ providerId?: string;
237
+ /** Error message when `ok` is false. */
238
+ error?: string;
239
+ /** id of the `email_log` row written for this attempt, when logging is enabled. */
240
+ logId?: string;
241
+ }
242
+ /**
243
+ * A swappable email transport.
244
+ *
245
+ * Implement this to support any provider. `isConfigured()` lets the service (and
246
+ * `resolveEmailProvider`) decide whether a provider is usable before attempting a
247
+ * send — e.g. a Resend provider with no API key reports `false`.
248
+ */
249
+ /**
250
+ * An email_log entry as seen by the reconciliation method.
251
+ * Derived from the documents row data JSON for type_id='email_log'.
252
+ * `id` is the document root_id used to identify the row.
253
+ */
254
+ interface EmailLogRow {
255
+ id: string;
256
+ provider_id?: string | null;
257
+ provider: string;
258
+ status: 'sent' | 'failed' | string;
259
+ delivery_state?: string | null;
260
+ }
261
+ interface EmailProvider {
262
+ /** Stable identifier recorded in `email_log.provider` (e.g. `'resend'`). */
263
+ readonly name: string;
264
+ /** True if the provider has everything it needs to send (credentials, etc.). */
265
+ isConfigured(): boolean;
266
+ /** Attempt delivery. Must not throw for ordinary failures — return `ok: false`. */
267
+ send(message: NormalizedEmailMessage): Promise<SendResult>;
268
+ /**
269
+ * Optional: reconcile delivery state for a batch of recently-sent messages.
270
+ *
271
+ * Called by the core `email-reconciliation` cron. Providers that expose a
272
+ * delivery/event API (e.g. Cloudflare Email with delivery webhooks) implement
273
+ * this to backfill `delivery_state`. Providers without delivery status APIs
274
+ * (e.g. Resend, SendGrid at the current integration level) leave this undefined
275
+ * — those rows stay with `delivery_state = null`.
276
+ *
277
+ * Returning an array of `{ id, delivery_state }` updates the matching rows in
278
+ * `email_log`. Errors do not propagate (cron is fire-and-log).
279
+ */
280
+ reconcile?(rows: EmailLogRow[]): Promise<Array<{
281
+ id: string;
282
+ delivery_state: string;
283
+ }>>;
284
+ }
285
+
286
+ export { type AuthMagicLinkConsumedPayload as A, type ContentEventPayload as C, type EmailProvider as E, HOOK_EVENT_NAMES as H, LEGACY_EVENT_ALIASES as L, type NormalizedEmailMessage as N, type PayloadForEvent as P, type SubscribableEvent as S, type TypedHookContext as T, type AuthOtpVerifiedPayload as a, type AuthPasswordResetCompletedPayload as b, type AuthPasswordResetRequestedPayload as c, type AuthRegistrationCompletedPayload as d, type HookActor as e, type HookEventName as f, type HookEventPayloads as g, type HookPayload as h, type HookSystemLike as i, type LegacyHookEventName as j, type LegacyHookEventPayloads as k, type TypedHookHandler as l, type TypedHooks as m, createTypedHooks as n, isKnownHookEvent as o, isLegacyHookEvent as p, type SendResult as q, resolveHookEventName as r, type EmailLogRow as s, type EmailMessage as t };
package/dist/types.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { B as BlockDefinition, a as BlockDefinitions, C as CollectionConfig, b as CollectionConfigModule, c as CollectionSchema, d as CollectionSyncResult, F as FieldConfig, e as FieldType } from './collection-config-B4PG-AaF.cjs';
1
+ export { B as BlockDefinition, a as BlockDefinitions, C as CollectionConfig, b as CollectionConfigModule, c as CollectionSchema, d as CollectionSyncResult, F as FieldConfig, e as FieldType } from './collection-config-JgHOpFCG.cjs';
2
2
  export { A as AuthService, C as ContentService, H as HOOKS, a as HookContext, b as HookHandler, c as HookName, d as HookSystem, M as MediaService, e as ModelRelationship, P as Plugin, f as PluginAdminPage, g as PluginBuilderOptions, h as PluginComponent, i as PluginConfig, j as PluginContext, k as PluginHook, l as PluginLogger, m as PluginManager, n as PluginMenuItem, o as PluginMiddleware, p as PluginModel, q as PluginRegistry, r as PluginRoutes, s as PluginService, t as PluginStatus, u as PluginValidationResult, v as PluginValidator, S as ScopedHookSystem } from './plugin-DDYetMF-.cjs';
3
3
  export { P as PluginManifest } from './plugin-manifest-Dpy8wxIB.cjs';
4
4
  export { T as TelemetryConfig, a as TelemetryEvent, b as TelemetryIdentity, c as TelemetryProperties } from './telemetry-B9vIV4wh.cjs';
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { B as BlockDefinition, a as BlockDefinitions, C as CollectionConfig, b as CollectionConfigModule, c as CollectionSchema, d as CollectionSyncResult, F as FieldConfig, e as FieldType } from './collection-config-B4PG-AaF.js';
1
+ export { B as BlockDefinition, a as BlockDefinitions, C as CollectionConfig, b as CollectionConfigModule, c as CollectionSchema, d as CollectionSyncResult, F as FieldConfig, e as FieldType } from './collection-config-JgHOpFCG.js';
2
2
  export { A as AuthService, C as ContentService, H as HOOKS, a as HookContext, b as HookHandler, c as HookName, d as HookSystem, M as MediaService, e as ModelRelationship, P as Plugin, f as PluginAdminPage, g as PluginBuilderOptions, h as PluginComponent, i as PluginConfig, j as PluginContext, k as PluginHook, l as PluginLogger, m as PluginManager, n as PluginMenuItem, o as PluginMiddleware, p as PluginModel, q as PluginRegistry, r as PluginRoutes, s as PluginService, t as PluginStatus, u as PluginValidationResult, v as PluginValidator, S as ScopedHookSystem } from './plugin-DDYetMF-.js';
3
3
  export { P as PluginManifest } from './plugin-manifest-Dpy8wxIB.js';
4
4
  export { T as TelemetryConfig, a as TelemetryEvent, b as TelemetryIdentity, c as TelemetryProperties } from './telemetry-B9vIV4wh.js';
package/dist/utils.cjs CHANGED
@@ -1,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var chunkSQ6FNXU2_cjs = require('./chunk-SQ6FNXU2.cjs');
3
+ var chunkIXUHXTHW_cjs = require('./chunk-IXUHXTHW.cjs');
4
4
  var chunkP3XDZL6Q_cjs = require('./chunk-P3XDZL6Q.cjs');
5
5
  var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
6
+ var chunkLRZIAW7U_cjs = require('./chunk-LRZIAW7U.cjs');
6
7
  var chunkMNWKYY5E_cjs = require('./chunk-MNWKYY5E.cjs');
7
8
  require('./chunk-IGJUBJBW.cjs');
8
9
 
@@ -10,43 +11,35 @@ require('./chunk-IGJUBJBW.cjs');
10
11
 
11
12
  Object.defineProperty(exports, "QueryFilterBuilder", {
12
13
  enumerable: true,
13
- get: function () { return chunkSQ6FNXU2_cjs.QueryFilterBuilder; }
14
- });
15
- Object.defineProperty(exports, "SONICJS_VERSION", {
16
- enumerable: true,
17
- get: function () { return chunkSQ6FNXU2_cjs.SONICJS_VERSION; }
14
+ get: function () { return chunkIXUHXTHW_cjs.QueryFilterBuilder; }
18
15
  });
19
16
  Object.defineProperty(exports, "TemplateRenderer", {
20
17
  enumerable: true,
21
- get: function () { return chunkSQ6FNXU2_cjs.TemplateRenderer; }
18
+ get: function () { return chunkIXUHXTHW_cjs.TemplateRenderer; }
22
19
  });
23
20
  Object.defineProperty(exports, "buildQuery", {
24
21
  enumerable: true,
25
- get: function () { return chunkSQ6FNXU2_cjs.buildQuery; }
22
+ get: function () { return chunkIXUHXTHW_cjs.buildQuery; }
26
23
  });
27
24
  Object.defineProperty(exports, "generateSlug", {
28
25
  enumerable: true,
29
- get: function () { return chunkSQ6FNXU2_cjs.generateSlug; }
26
+ get: function () { return chunkIXUHXTHW_cjs.generateSlug; }
30
27
  });
31
28
  Object.defineProperty(exports, "getBlocksFieldConfig", {
32
29
  enumerable: true,
33
- get: function () { return chunkSQ6FNXU2_cjs.getBlocksFieldConfig; }
34
- });
35
- Object.defineProperty(exports, "getCoreVersion", {
36
- enumerable: true,
37
- get: function () { return chunkSQ6FNXU2_cjs.getCoreVersion; }
30
+ get: function () { return chunkIXUHXTHW_cjs.getBlocksFieldConfig; }
38
31
  });
39
32
  Object.defineProperty(exports, "parseBlocksValue", {
40
33
  enumerable: true,
41
- get: function () { return chunkSQ6FNXU2_cjs.parseBlocksValue; }
34
+ get: function () { return chunkIXUHXTHW_cjs.parseBlocksValue; }
42
35
  });
43
36
  Object.defineProperty(exports, "renderTemplate", {
44
37
  enumerable: true,
45
- get: function () { return chunkSQ6FNXU2_cjs.renderTemplate; }
38
+ get: function () { return chunkIXUHXTHW_cjs.renderTemplate; }
46
39
  });
47
40
  Object.defineProperty(exports, "templateRenderer", {
48
41
  enumerable: true,
49
- get: function () { return chunkSQ6FNXU2_cjs.templateRenderer; }
42
+ get: function () { return chunkIXUHXTHW_cjs.templateRenderer; }
50
43
  });
51
44
  Object.defineProperty(exports, "generateInstallationId", {
52
45
  enumerable: true,
@@ -84,6 +77,14 @@ Object.defineProperty(exports, "metricsTracker", {
84
77
  enumerable: true,
85
78
  get: function () { return chunkRCQ2HIQD_cjs.metricsTracker; }
86
79
  });
80
+ Object.defineProperty(exports, "SONICJS_VERSION", {
81
+ enumerable: true,
82
+ get: function () { return chunkLRZIAW7U_cjs.SONICJS_VERSION; }
83
+ });
84
+ Object.defineProperty(exports, "getCoreVersion", {
85
+ enumerable: true,
86
+ get: function () { return chunkLRZIAW7U_cjs.getCoreVersion; }
87
+ });
87
88
  Object.defineProperty(exports, "escapeHtml", {
88
89
  enumerable: true,
89
90
  get: function () { return chunkMNWKYY5E_cjs.escapeHtml; }
package/dist/utils.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { F as FilterCondition, a as FilterGroup, b as FilterOperator, Q as QueryFilter, c as QueryFilterBuilder, d as QueryResult, S as SONICJS_VERSION, T as TemplateRenderer, e as buildQuery, f as escapeHtml, g as getCoreVersion, m as metricsTracker, r as renderTemplate, s as sanitizeInput, h as sanitizeObject, t as templateRenderer } from './version-DFTyGfIH.cjs';
2
2
  import { T as TelemetryConfig } from './telemetry-B9vIV4wh.cjs';
3
- import { a as BlockDefinitions } from './collection-config-B4PG-AaF.cjs';
3
+ import { a as BlockDefinitions } from './collection-config-JgHOpFCG.cjs';
4
4
 
5
5
  /**
6
6
  * Slug generation utilities for creating URL-friendly slugs
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { F as FilterCondition, a as FilterGroup, b as FilterOperator, Q as QueryFilter, c as QueryFilterBuilder, d as QueryResult, S as SONICJS_VERSION, T as TemplateRenderer, e as buildQuery, f as escapeHtml, g as getCoreVersion, m as metricsTracker, r as renderTemplate, s as sanitizeInput, h as sanitizeObject, t as templateRenderer } from './version-DFTyGfIH.js';
2
2
  import { T as TelemetryConfig } from './telemetry-B9vIV4wh.js';
3
- import { a as BlockDefinitions } from './collection-config-B4PG-AaF.js';
3
+ import { a as BlockDefinitions } from './collection-config-JgHOpFCG.js';
4
4
 
5
5
  /**
6
6
  * Slug generation utilities for creating URL-friendly slugs
package/dist/utils.js CHANGED
@@ -1,6 +1,7 @@
1
- export { QueryFilterBuilder, SONICJS_VERSION, TemplateRenderer, buildQuery, generateSlug, getBlocksFieldConfig, getCoreVersion, parseBlocksValue, renderTemplate, templateRenderer } from './chunk-MGFRZO24.js';
1
+ export { QueryFilterBuilder, TemplateRenderer, buildQuery, generateSlug, getBlocksFieldConfig, parseBlocksValue, renderTemplate, templateRenderer } from './chunk-QZGABF2M.js';
2
2
  export { generateInstallationId, generateProjectId, getDefaultTelemetryConfig, getTelemetryConfig, isTelemetryEnabled, sanitizeErrorMessage, sanitizeRoute, shouldSkipEvent } from './chunk-X7ZAEI5S.js';
3
3
  export { metricsTracker } from './chunk-FICTAGD4.js';
4
+ export { SONICJS_VERSION, getCoreVersion } from './chunk-NMPEMSU4.js';
4
5
  export { escapeHtml, sanitizeInput, sanitizeObject } from './chunk-TQABQWOP.js';
5
6
  import './chunk-V4OQ3NZ2.js';
6
7
  //# sourceMappingURL=utils.js.map
@@ -0,0 +1,184 @@
1
+ -- Migration 0001: Auth tables
2
+ -- auth_user, auth_session, auth_account, auth_verification + BA plugin tables + RBAC + auth support.
3
+ -- Only auth_* prefixed tables live here. All content lives in document_* tables (0002_documents.sql).
4
+
5
+ -- ── auth_user ────────────────────────────────────────────────────────────────
6
+ -- BA user model + SonicJS domain columns as BA additionalFields.
7
+ CREATE TABLE IF NOT EXISTS auth_user (
8
+ id TEXT PRIMARY KEY,
9
+ name TEXT,
10
+ email TEXT NOT NULL UNIQUE,
11
+ email_verified INTEGER NOT NULL DEFAULT 0,
12
+ image TEXT,
13
+ created_at INTEGER NOT NULL,
14
+ updated_at INTEGER NOT NULL,
15
+ -- SonicJS additionalFields
16
+ first_name TEXT NOT NULL,
17
+ last_name TEXT NOT NULL,
18
+ role TEXT NOT NULL DEFAULT 'viewer',
19
+ -- Platform super-admin: bypasses the multi-tenant membership gate, uses global roles in every
20
+ -- tenant. Opt-in (default 0); intentionally NOT derived from the 'admin' role.
21
+ is_super_admin INTEGER NOT NULL DEFAULT 0,
22
+ avatar TEXT,
23
+ password_hash TEXT,
24
+ is_active INTEGER NOT NULL DEFAULT 1,
25
+ last_login_at INTEGER,
26
+ phone TEXT,
27
+ bio TEXT,
28
+ timezone TEXT DEFAULT 'UTC',
29
+ language TEXT DEFAULT 'en',
30
+ email_notifications INTEGER DEFAULT 1,
31
+ theme TEXT DEFAULT 'dark',
32
+ invitation_token TEXT,
33
+ invited_by TEXT,
34
+ invited_at INTEGER,
35
+ accepted_invitation_at INTEGER,
36
+ failed_login_count INTEGER NOT NULL DEFAULT 0,
37
+ locked_until INTEGER
38
+ );
39
+
40
+ CREATE INDEX IF NOT EXISTS idx_auth_user_email ON auth_user(email);
41
+ CREATE INDEX IF NOT EXISTS idx_auth_user_role ON auth_user(role);
42
+ CREATE INDEX IF NOT EXISTS idx_auth_user_invitation_token ON auth_user(invitation_token);
43
+ CREATE INDEX IF NOT EXISTS idx_auth_user_locked_until ON auth_user(locked_until) WHERE locked_until IS NOT NULL;
44
+
45
+ -- ── auth_session ─────────────────────────────────────────────────────────────
46
+ CREATE TABLE IF NOT EXISTS auth_session (
47
+ id TEXT PRIMARY KEY,
48
+ user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,
49
+ token TEXT NOT NULL UNIQUE,
50
+ expires_at INTEGER NOT NULL,
51
+ ip_address TEXT,
52
+ user_agent TEXT,
53
+ created_at INTEGER NOT NULL,
54
+ updated_at INTEGER NOT NULL
55
+ );
56
+ CREATE INDEX IF NOT EXISTS idx_auth_session_user_id ON auth_session(user_id);
57
+ CREATE INDEX IF NOT EXISTS idx_auth_session_token ON auth_session(token);
58
+ CREATE INDEX IF NOT EXISTS idx_auth_session_expires_at ON auth_session(expires_at);
59
+
60
+ -- ── auth_account ─────────────────────────────────────────────────────────────
61
+ CREATE TABLE IF NOT EXISTS auth_account (
62
+ id TEXT PRIMARY KEY,
63
+ user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,
64
+ account_id TEXT NOT NULL,
65
+ provider_id TEXT NOT NULL,
66
+ access_token TEXT,
67
+ refresh_token TEXT,
68
+ access_token_expires_at INTEGER,
69
+ refresh_token_expires_at INTEGER,
70
+ scope TEXT,
71
+ id_token TEXT,
72
+ password TEXT,
73
+ created_at INTEGER NOT NULL,
74
+ updated_at INTEGER NOT NULL
75
+ );
76
+ CREATE INDEX IF NOT EXISTS idx_auth_account_user_id ON auth_account(user_id);
77
+ CREATE INDEX IF NOT EXISTS idx_auth_account_provider ON auth_account(provider_id, account_id);
78
+
79
+ -- ── auth_verification ────────────────────────────────────────────────────────
80
+ -- Covers email verification, password reset, magic-link tokens, OTP codes.
81
+ CREATE TABLE IF NOT EXISTS auth_verification (
82
+ id TEXT PRIMARY KEY,
83
+ identifier TEXT NOT NULL,
84
+ value TEXT NOT NULL,
85
+ expires_at INTEGER NOT NULL,
86
+ created_at INTEGER NOT NULL,
87
+ updated_at INTEGER NOT NULL
88
+ );
89
+ CREATE INDEX IF NOT EXISTS idx_auth_verification_identifier ON auth_verification(identifier);
90
+
91
+ -- ── BA plugin tables ──────────────────────────────────────────────────────────
92
+
93
+ CREATE TABLE IF NOT EXISTS auth_two_factor (
94
+ id TEXT PRIMARY KEY,
95
+ secret TEXT NOT NULL,
96
+ backup_codes TEXT NOT NULL,
97
+ user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,
98
+ verified INTEGER NOT NULL DEFAULT 1,
99
+ created_at INTEGER NOT NULL,
100
+ updated_at INTEGER NOT NULL
101
+ );
102
+ CREATE INDEX IF NOT EXISTS idx_auth_two_factor_user_id ON auth_two_factor(user_id);
103
+
104
+ CREATE TABLE IF NOT EXISTS auth_tenant (
105
+ id TEXT PRIMARY KEY,
106
+ name TEXT NOT NULL,
107
+ slug TEXT NOT NULL UNIQUE,
108
+ logo TEXT,
109
+ metadata TEXT,
110
+ -- SonicJS tenant-resolution fields (BA organization additionalFields):
111
+ status TEXT NOT NULL DEFAULT 'active',
112
+ domain TEXT,
113
+ notes TEXT NOT NULL DEFAULT '',
114
+ created_at INTEGER NOT NULL,
115
+ updated_at INTEGER NOT NULL
116
+ );
117
+ CREATE INDEX IF NOT EXISTS idx_auth_tenant_domain ON auth_tenant(domain);
118
+
119
+ CREATE TABLE IF NOT EXISTS auth_tenant_member (
120
+ id TEXT PRIMARY KEY,
121
+ tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,
122
+ user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,
123
+ role TEXT NOT NULL DEFAULT 'member',
124
+ email TEXT,
125
+ created_at INTEGER NOT NULL,
126
+ updated_at INTEGER NOT NULL,
127
+ UNIQUE(tenant_id, user_id)
128
+ );
129
+ CREATE INDEX IF NOT EXISTS idx_auth_tenant_member_tenant ON auth_tenant_member(tenant_id);
130
+ CREATE INDEX IF NOT EXISTS idx_auth_tenant_member_user ON auth_tenant_member(user_id);
131
+
132
+ CREATE TABLE IF NOT EXISTS auth_tenant_invitation (
133
+ id TEXT PRIMARY KEY,
134
+ tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,
135
+ email TEXT NOT NULL,
136
+ role TEXT NOT NULL DEFAULT 'member',
137
+ status TEXT NOT NULL DEFAULT 'pending',
138
+ expires_at INTEGER NOT NULL,
139
+ inviter_id TEXT REFERENCES auth_user(id) ON DELETE SET NULL,
140
+ created_at INTEGER NOT NULL,
141
+ updated_at INTEGER NOT NULL
142
+ );
143
+ CREATE INDEX IF NOT EXISTS idx_auth_tenant_invitation_tenant ON auth_tenant_invitation(tenant_id);
144
+ CREATE INDEX IF NOT EXISTS idx_auth_tenant_invitation_email ON auth_tenant_invitation(email);
145
+
146
+ CREATE TABLE IF NOT EXISTS auth_tenant_team (
147
+ id TEXT PRIMARY KEY,
148
+ name TEXT NOT NULL,
149
+ tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,
150
+ created_at INTEGER NOT NULL,
151
+ updated_at INTEGER NOT NULL
152
+ );
153
+
154
+ -- ── RBAC ─────────────────────────────────────────────────────────────────────
155
+ -- RBAC roles, verbs, and user-role assignments are document-backed (is_auth doc
156
+ -- types rbac_role / rbac_verb / rbac_user_roles — see services/rbac.ts). The
157
+ -- system roles/verbs/grants are seeded at bootstrap by RbacService.ensureSystemRbacSeed().
158
+ -- No auth_rbac_* tables.
159
+
160
+ -- ── Auth support tables ───────────────────────────────────────────────────────
161
+ CREATE TABLE IF NOT EXISTS auth_password_history (
162
+ id TEXT PRIMARY KEY,
163
+ user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,
164
+ password_hash TEXT NOT NULL,
165
+ created_at INTEGER NOT NULL
166
+ );
167
+ CREATE INDEX IF NOT EXISTS idx_auth_password_history_user_id ON auth_password_history(user_id);
168
+
169
+ CREATE TABLE IF NOT EXISTS auth_api_tokens (
170
+ id TEXT PRIMARY KEY,
171
+ name TEXT NOT NULL,
172
+ token TEXT NOT NULL UNIQUE,
173
+ user_id TEXT NOT NULL REFERENCES auth_user(id),
174
+ permissions TEXT NOT NULL,
175
+ expires_at INTEGER,
176
+ last_used_at INTEGER,
177
+ created_at INTEGER NOT NULL
178
+ );
179
+ CREATE INDEX IF NOT EXISTS idx_auth_api_tokens_user ON auth_api_tokens(user_id);
180
+ CREATE INDEX IF NOT EXISTS idx_auth_api_tokens_token ON auth_api_tokens(token);
181
+
182
+ -- User profiles moved to the document model: a `user_profile` document (is_auth type),
183
+ -- one per user, addressed by slug = userId. See services/document-types-seed.ts and
184
+ -- plugins/core-plugins/user-profiles/user-profile-document.ts. No auth_user_profiles table.