@maravilla-labs/platform 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/events.d.ts CHANGED
@@ -197,6 +197,22 @@ declare function onDbChange(config: Omit<EventTriggerDb, 'kind'>, handler: (even
197
197
  * ```
198
198
  */
199
199
  declare function onAuth(config: Omit<EventTriggerAuth, 'kind'>, handler: (event: AuthEvent, ctx: EventCtx) => unknown | Promise<unknown>): RegisteredHandler<AuthEvent>;
200
+ /**
201
+ * Convenience wrapper for `onAuth({ op: 'registered' }, …)` — the most common
202
+ * auth hook. Fires once when a new user signs up; the canonical place to
203
+ * **provision** them: create their app-side profile, add them to a group/role,
204
+ * seed their initial records, send a welcome. `event.data.profile` carries the
205
+ * custom fields passed to `platform.auth.register({ profile: {...} })`.
206
+ *
207
+ * ```ts
208
+ * export const provision = onRegister(async (event, ctx) => {
209
+ * const members = await ctx.auth.getGroupByName('members');
210
+ * if (members) await ctx.auth.addUserToGroup(event.userId, members.id);
211
+ * await ctx.database.insertOne('profiles', { _id: event.userId, email: event.data?.email, created_at: event.ts });
212
+ * });
213
+ * ```
214
+ */
215
+ declare function onRegister(handler: (event: AuthEvent, ctx: EventCtx) => unknown | Promise<unknown>): RegisteredHandler<AuthEvent>;
200
216
  declare function onSchedule(cron: string, handler: (event: ScheduleEvent, ctx: EventCtx) => unknown | Promise<unknown>): RegisteredHandler<ScheduleEvent>;
201
217
  declare function onQueue<T = unknown>(name: string, config: Omit<EventTriggerQueue, 'kind' | 'name'>, handler: (messages: QueueMessage<T>[], ctx: EventCtx) => unknown | Promise<unknown>): RegisteredHandler<QueueMessage<T>[]>;
202
218
  declare function onChannel<T = unknown>(config: Omit<EventTriggerChannel, 'kind'>, handler: (event: ChannelEvent<T>, ctx: EventCtx) => unknown | Promise<unknown>): RegisteredHandler<ChannelEvent<T>>;
@@ -222,4 +238,4 @@ declare function defineEvent(config: Omit<EventTriggerRen, 'kind'>, handler: (ev
222
238
  /** Type guard used by the build-time discoverer and the runtime registry. */
223
239
  declare function isRegisteredHandler(value: unknown): value is RegisteredHandler;
224
240
 
225
- export { type AuthEvent, type AuthOp, type ChannelEvent, type DbChangeEvent, type DeployEvent, type EventCtx, type EventTrigger, type EventTriggerAuth, type EventTriggerChannel, type EventTriggerDb, type EventTriggerDeploy, type EventTriggerKv, type EventTriggerQueue, type EventTriggerRen, type EventTriggerSchedule, type EventTriggerStorage, type KvChangeEvent, type QueueMessage, type RegisteredHandler, type ScheduleEvent, type StorageEvent, TRIGGER_SYMBOL, defineEvent, isRegisteredHandler, onAuth, onChannel, onDbChange, onDeploy, onKvChange, onQueue, onSchedule, onStorage };
241
+ export { type AuthEvent, type AuthOp, type ChannelEvent, type DbChangeEvent, type DeployEvent, type EventCtx, type EventTrigger, type EventTriggerAuth, type EventTriggerChannel, type EventTriggerDb, type EventTriggerDeploy, type EventTriggerKv, type EventTriggerQueue, type EventTriggerRen, type EventTriggerSchedule, type EventTriggerStorage, type KvChangeEvent, type QueueMessage, type RegisteredHandler, type ScheduleEvent, type StorageEvent, TRIGGER_SYMBOL, defineEvent, isRegisteredHandler, onAuth, onChannel, onDbChange, onDeploy, onKvChange, onQueue, onRegister, onSchedule, onStorage };
package/dist/events.js CHANGED
@@ -12,6 +12,9 @@ function onDbChange(config, handler) {
12
12
  function onAuth(config, handler) {
13
13
  return register({ kind: "auth", ...config }, handler);
14
14
  }
15
+ function onRegister(handler) {
16
+ return onAuth({ op: "registered" }, handler);
17
+ }
15
18
  function onSchedule(cron, handler) {
16
19
  return register({ kind: "schedule", cron }, handler);
17
20
  }
@@ -43,6 +46,7 @@ export {
43
46
  onDeploy,
44
47
  onKvChange,
45
48
  onQueue,
49
+ onRegister,
46
50
  onSchedule,
47
51
  onStorage
48
52
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/events.ts"],"sourcesContent":["/**\n * @fileoverview Event handler registration helpers for Maravilla.\n *\n * User apps declare event handlers in `events.ts` or `events/*.ts`:\n *\n * ```ts\n * import { onKvChange, defineEvent } from '@maravilla-labs/platform/events';\n *\n * export const invalidateCache = onKvChange(\n * { namespace: 'config', keyPattern: 'feature:*' },\n * async (event, ctx) => { await ctx.kv.delete('cache', event.key!); },\n * );\n * ```\n *\n * These helpers are pure factories. They produce a `RegisteredHandler`\n * marker object that the build pipeline (`@maravilla-labs/functions`\n * `discoverEvents`) detects by its `__maravilla_trigger` property.\n * The Rust event dispatcher uses the trigger config from the manifest\n * and invokes the bundled handler via `globalThis.handleEvent(id, event)`.\n */\n\nimport type { RenEvent } from './ren.js';\n\n// ============ Trigger descriptors ============\n// Kept structurally identical to adapter-core's `EventTrigger` — duplicated\n// here so the runtime SDK has no build-time dep on adapter-core. Keep in sync.\n\nexport type EventTrigger =\n | EventTriggerKv\n | EventTriggerDb\n | EventTriggerAuth\n | EventTriggerSchedule\n | EventTriggerQueue\n | EventTriggerChannel\n | EventTriggerDeploy\n | EventTriggerStorage\n | EventTriggerRen;\n\nexport interface EventTriggerKv {\n kind: 'kv';\n namespace?: string;\n keyPattern?: string;\n op?: 'put' | 'delete' | 'expired';\n}\n\nexport interface EventTriggerDb {\n kind: 'db';\n collection: string;\n op?: 'insert' | 'update' | 'delete';\n}\n\nexport type AuthOp =\n | 'registered'\n | 'logged_in'\n | 'logged_out'\n | 'logged_out_all'\n | 'deleted'\n | 'updated';\n\nexport interface EventTriggerAuth {\n kind: 'auth';\n op?: AuthOp;\n}\n\nexport interface EventTriggerSchedule {\n kind: 'schedule';\n cron: string;\n}\n\nexport interface EventTriggerQueue {\n kind: 'queue';\n name: string;\n batch?: number;\n maxAttempts?: number;\n}\n\nexport interface EventTriggerChannel {\n kind: 'channel';\n channel: string;\n type?: string;\n}\n\nexport interface EventTriggerDeploy {\n kind: 'deploy';\n phase: 'ready' | 'draining' | 'stopped';\n}\n\n/**\n * Object storage trigger — fires when an object is created or removed\n * under the matching `keyPattern`. Used by both hand-written `onStorage`\n * handlers and by the synthesized handlers that the adapter compiles\n * from a `transforms` block in `maravilla.config.ts`.\n */\nexport interface EventTriggerStorage {\n kind: 'storage';\n /** Glob-style storage key pattern (e.g. `\"uploads/videos/**\"`). Omit to match all objects. */\n keyPattern?: string;\n /** Operation filter; omit to match both `put` and `delete`. */\n op?: 'put' | 'delete';\n}\n\nexport interface EventTriggerRen {\n kind: 'ren';\n match: { r?: string; t?: string; ns?: string };\n}\n\n// ============ Event payload shapes ============\n\nexport interface KvChangeEvent {\n op: 'put' | 'delete' | 'expired';\n namespace: string;\n key: string;\n /** Present on `put`. */\n value?: unknown;\n ts: number;\n}\n\nexport interface DbChangeEvent {\n op: 'insert' | 'update' | 'delete';\n collection: string;\n id: string;\n doc?: unknown;\n before?: unknown;\n after?: unknown;\n ts: number;\n}\n\nexport interface AuthEvent {\n op: AuthOp;\n userId: string;\n /**\n * Op-specific payload:\n * - `registered` → `{ email, provider, profile }` where `profile` is the\n * app's custom fields passed to `platform.auth.register({ profile: {...} })`.\n * - `logged_in` → `{ email }`\n * - `logged_out` → `{ sessionId }`\n * - `logged_out_all` / `deleted` / `updated` → null or empty.\n */\n data?: {\n email?: string;\n provider?: string;\n profile?: Record<string, unknown> | null;\n sessionId?: string;\n [k: string]: unknown;\n };\n ts: number;\n}\n\nexport interface ScheduleEvent {\n cron: string;\n scheduledAt: number;\n firedAt: number;\n}\n\nexport interface QueueMessage<T = unknown> {\n id: string;\n payload: T;\n attempt: number;\n enqueuedAt: number;\n}\n\nexport interface ChannelEvent<T = unknown> {\n channel: string;\n type: string;\n data?: T;\n uid?: string;\n ts: number;\n}\n\nexport interface DeployEvent {\n phase: 'ready' | 'draining' | 'stopped';\n ts: number;\n}\n\n/**\n * Storage event payload — emitted by the dev-server / runtime when an\n * object is put or deleted. The synthesized transforms handlers consume\n * the `key` field to look up the source object.\n */\nexport interface StorageEvent {\n op: 'put' | 'delete';\n key: string;\n /** Present on `put` — content type from the upload metadata, when known. */\n contentType?: string;\n /** Present on `put` — size in bytes, when known. */\n size?: number;\n ts: number;\n}\n\n// ============ Handler context ============\n\nexport interface EventCtx {\n /** Per-tenant env vars. */\n env: Record<string, string>;\n /** KV store — same shape as `getPlatform().env.KV` / `platform.kv`. */\n kv?: unknown;\n /** MongoDB-style database — same shape as `getPlatform().env.DB`. */\n database?: unknown;\n /** Object storage. */\n storage?: unknown;\n /** Durable queue producer (`.send(name, payload, opts?)`). */\n queue?: { send: (name: string, payload: unknown, opts?: unknown) => Promise<string> };\n /** Auth service — register/login/logout/user CRUD/etc. */\n auth?: unknown;\n /** Web Push service. */\n push?: unknown;\n /** Full platform object — escape hatch for services not surfaced above. */\n platform?: unknown;\n /** Trace correlation id — propagate through logs. */\n traceId: string;\n tenant: string;\n handlerId: string;\n}\n\n// ============ Registered handler marker ============\n\nexport const TRIGGER_SYMBOL = '__maravilla_trigger' as const;\n\nexport interface RegisteredHandler<Event = unknown, Ctx = EventCtx> {\n readonly [TRIGGER_SYMBOL]: EventTrigger;\n readonly handler: (event: Event, ctx: Ctx) => unknown | Promise<unknown>;\n}\n\nfunction register<Event, Ctx = EventCtx>(\n trigger: EventTrigger,\n handler: (event: Event, ctx: Ctx) => unknown | Promise<unknown>,\n): RegisteredHandler<Event, Ctx> {\n return { [TRIGGER_SYMBOL]: trigger, handler };\n}\n\n// ============ Public factory helpers ============\n\nexport function onKvChange(\n config: Omit<EventTriggerKv, 'kind'>,\n handler: (event: KvChangeEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<KvChangeEvent> {\n return register({ kind: 'kv', ...config }, handler);\n}\n\nexport function onDbChange(\n config: Omit<EventTriggerDb, 'kind'>,\n handler: (event: DbChangeEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<DbChangeEvent> {\n return register({ kind: 'db', ...config }, handler);\n}\n\n/**\n * React to user authentication events: registration, login, logout,\n * deletion, update. Omit `op` to match all auth events; set it to\n * narrow to a specific lifecycle transition.\n *\n * ```ts\n * export const welcomeEmail = onAuth(\n * { op: 'registered' },\n * async (event, ctx) => {\n * await sendWelcomeEmail(event.data?.email);\n * },\n * );\n * ```\n */\nexport function onAuth(\n config: Omit<EventTriggerAuth, 'kind'>,\n handler: (event: AuthEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<AuthEvent> {\n return register({ kind: 'auth', ...config }, handler);\n}\n\nexport function onSchedule(\n cron: string,\n handler: (event: ScheduleEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<ScheduleEvent> {\n return register({ kind: 'schedule', cron }, handler);\n}\n\nexport function onQueue<T = unknown>(\n name: string,\n config: Omit<EventTriggerQueue, 'kind' | 'name'>,\n handler: (messages: QueueMessage<T>[], ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<QueueMessage<T>[]> {\n return register({ kind: 'queue', name, ...config }, handler);\n}\n\nexport function onChannel<T = unknown>(\n config: Omit<EventTriggerChannel, 'kind'>,\n handler: (event: ChannelEvent<T>, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<ChannelEvent<T>> {\n return register({ kind: 'channel', ...config }, handler);\n}\n\nexport function onDeploy(\n phase: EventTriggerDeploy['phase'],\n handler: (event: DeployEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<DeployEvent> {\n return register({ kind: 'deploy', phase }, handler);\n}\n\n/**\n * React to object-storage `put` / `delete` events. `keyPattern` is a\n * glob (e.g. `\"uploads/videos/**\"`). Omit `op` to match both put and\n * delete; omit `keyPattern` to match every object in the tenant's\n * bucket.\n *\n * ```ts\n * export const onUpload = onStorage(\n * { keyPattern: 'uploads/photos/**', op: 'put' },\n * async (event, ctx) => {\n * await ctx.platform.media.transforms.resize(event.key, { width: 1600, format: 'webp' });\n * },\n * );\n * ```\n */\nexport function onStorage(\n config: Omit<EventTriggerStorage, 'kind'>,\n handler: (event: StorageEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<StorageEvent> {\n return register({ kind: 'storage', ...config }, handler);\n}\n\n/** Escape hatch for arbitrary `RenEvent` matches. */\nexport function defineEvent(\n config: Omit<EventTriggerRen, 'kind'>,\n handler: (event: RenEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<RenEvent> {\n return register({ kind: 'ren', ...config }, handler);\n}\n\n/** Type guard used by the build-time discoverer and the runtime registry. */\nexport function isRegisteredHandler(value: unknown): value is RegisteredHandler {\n return (\n typeof value === 'object' &&\n value !== null &&\n TRIGGER_SYMBOL in value &&\n typeof (value as Record<string, unknown>).handler === 'function'\n );\n}\n"],"mappings":";AAwNO,IAAM,iBAAiB;AAO9B,SAAS,SACP,SACA,SAC+B;AAC/B,SAAO,EAAE,CAAC,cAAc,GAAG,SAAS,QAAQ;AAC9C;AAIO,SAAS,WACd,QACA,SACkC;AAClC,SAAO,SAAS,EAAE,MAAM,MAAM,GAAG,OAAO,GAAG,OAAO;AACpD;AAEO,SAAS,WACd,QACA,SACkC;AAClC,SAAO,SAAS,EAAE,MAAM,MAAM,GAAG,OAAO,GAAG,OAAO;AACpD;AAgBO,SAAS,OACd,QACA,SAC8B;AAC9B,SAAO,SAAS,EAAE,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO;AACtD;AAEO,SAAS,WACd,MACA,SACkC;AAClC,SAAO,SAAS,EAAE,MAAM,YAAY,KAAK,GAAG,OAAO;AACrD;AAEO,SAAS,QACd,MACA,QACA,SACsC;AACtC,SAAO,SAAS,EAAE,MAAM,SAAS,MAAM,GAAG,OAAO,GAAG,OAAO;AAC7D;AAEO,SAAS,UACd,QACA,SACoC;AACpC,SAAO,SAAS,EAAE,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO;AACzD;AAEO,SAAS,SACd,OACA,SACgC;AAChC,SAAO,SAAS,EAAE,MAAM,UAAU,MAAM,GAAG,OAAO;AACpD;AAiBO,SAAS,UACd,QACA,SACiC;AACjC,SAAO,SAAS,EAAE,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO;AACzD;AAGO,SAAS,YACd,QACA,SAC6B;AAC7B,SAAO,SAAS,EAAE,MAAM,OAAO,GAAG,OAAO,GAAG,OAAO;AACrD;AAGO,SAAS,oBAAoB,OAA4C;AAC9E,SACE,OAAO,UAAU,YACjB,UAAU,QACV,kBAAkB,SAClB,OAAQ,MAAkC,YAAY;AAE1D;","names":[]}
1
+ {"version":3,"sources":["../src/events.ts"],"sourcesContent":["/**\n * @fileoverview Event handler registration helpers for Maravilla.\n *\n * User apps declare event handlers in `events.ts` or `events/*.ts`:\n *\n * ```ts\n * import { onKvChange, defineEvent } from '@maravilla-labs/platform/events';\n *\n * export const invalidateCache = onKvChange(\n * { namespace: 'config', keyPattern: 'feature:*' },\n * async (event, ctx) => { await ctx.kv.delete('cache', event.key!); },\n * );\n * ```\n *\n * These helpers are pure factories. They produce a `RegisteredHandler`\n * marker object that the build pipeline (`@maravilla-labs/functions`\n * `discoverEvents`) detects by its `__maravilla_trigger` property.\n * The Rust event dispatcher uses the trigger config from the manifest\n * and invokes the bundled handler via `globalThis.handleEvent(id, event)`.\n */\n\nimport type { RenEvent } from './ren.js';\n\n// ============ Trigger descriptors ============\n// Kept structurally identical to adapter-core's `EventTrigger` — duplicated\n// here so the runtime SDK has no build-time dep on adapter-core. Keep in sync.\n\nexport type EventTrigger =\n | EventTriggerKv\n | EventTriggerDb\n | EventTriggerAuth\n | EventTriggerSchedule\n | EventTriggerQueue\n | EventTriggerChannel\n | EventTriggerDeploy\n | EventTriggerStorage\n | EventTriggerRen;\n\nexport interface EventTriggerKv {\n kind: 'kv';\n namespace?: string;\n keyPattern?: string;\n op?: 'put' | 'delete' | 'expired';\n}\n\nexport interface EventTriggerDb {\n kind: 'db';\n collection: string;\n op?: 'insert' | 'update' | 'delete';\n}\n\nexport type AuthOp =\n | 'registered'\n | 'logged_in'\n | 'logged_out'\n | 'logged_out_all'\n | 'deleted'\n | 'updated';\n\nexport interface EventTriggerAuth {\n kind: 'auth';\n op?: AuthOp;\n}\n\nexport interface EventTriggerSchedule {\n kind: 'schedule';\n cron: string;\n}\n\nexport interface EventTriggerQueue {\n kind: 'queue';\n name: string;\n batch?: number;\n maxAttempts?: number;\n}\n\nexport interface EventTriggerChannel {\n kind: 'channel';\n channel: string;\n type?: string;\n}\n\nexport interface EventTriggerDeploy {\n kind: 'deploy';\n phase: 'ready' | 'draining' | 'stopped';\n}\n\n/**\n * Object storage trigger — fires when an object is created or removed\n * under the matching `keyPattern`. Used by both hand-written `onStorage`\n * handlers and by the synthesized handlers that the adapter compiles\n * from a `transforms` block in `maravilla.config.ts`.\n */\nexport interface EventTriggerStorage {\n kind: 'storage';\n /** Glob-style storage key pattern (e.g. `\"uploads/videos/**\"`). Omit to match all objects. */\n keyPattern?: string;\n /** Operation filter; omit to match both `put` and `delete`. */\n op?: 'put' | 'delete';\n}\n\nexport interface EventTriggerRen {\n kind: 'ren';\n match: { r?: string; t?: string; ns?: string };\n}\n\n// ============ Event payload shapes ============\n\nexport interface KvChangeEvent {\n op: 'put' | 'delete' | 'expired';\n namespace: string;\n key: string;\n /** Present on `put`. */\n value?: unknown;\n ts: number;\n}\n\nexport interface DbChangeEvent {\n op: 'insert' | 'update' | 'delete';\n collection: string;\n id: string;\n doc?: unknown;\n before?: unknown;\n after?: unknown;\n ts: number;\n}\n\nexport interface AuthEvent {\n op: AuthOp;\n userId: string;\n /**\n * Op-specific payload:\n * - `registered` → `{ email, provider, profile }` where `profile` is the\n * app's custom fields passed to `platform.auth.register({ profile: {...} })`.\n * - `logged_in` → `{ email }`\n * - `logged_out` → `{ sessionId }`\n * - `logged_out_all` / `deleted` / `updated` → null or empty.\n */\n data?: {\n email?: string;\n provider?: string;\n profile?: Record<string, unknown> | null;\n sessionId?: string;\n [k: string]: unknown;\n };\n ts: number;\n}\n\nexport interface ScheduleEvent {\n cron: string;\n scheduledAt: number;\n firedAt: number;\n}\n\nexport interface QueueMessage<T = unknown> {\n id: string;\n payload: T;\n attempt: number;\n enqueuedAt: number;\n}\n\nexport interface ChannelEvent<T = unknown> {\n channel: string;\n type: string;\n data?: T;\n uid?: string;\n ts: number;\n}\n\nexport interface DeployEvent {\n phase: 'ready' | 'draining' | 'stopped';\n ts: number;\n}\n\n/**\n * Storage event payload — emitted by the dev-server / runtime when an\n * object is put or deleted. The synthesized transforms handlers consume\n * the `key` field to look up the source object.\n */\nexport interface StorageEvent {\n op: 'put' | 'delete';\n key: string;\n /** Present on `put` — content type from the upload metadata, when known. */\n contentType?: string;\n /** Present on `put` — size in bytes, when known. */\n size?: number;\n ts: number;\n}\n\n// ============ Handler context ============\n\nexport interface EventCtx {\n /** Per-tenant env vars. */\n env: Record<string, string>;\n /** KV store — same shape as `getPlatform().env.KV` / `platform.kv`. */\n kv?: unknown;\n /** MongoDB-style database — same shape as `getPlatform().env.DB`. */\n database?: unknown;\n /** Object storage. */\n storage?: unknown;\n /** Durable queue producer (`.send(name, payload, opts?)`). */\n queue?: { send: (name: string, payload: unknown, opts?: unknown) => Promise<string> };\n /** Auth service — register/login/logout/user CRUD/etc. */\n auth?: unknown;\n /** Web Push service. */\n push?: unknown;\n /** Full platform object — escape hatch for services not surfaced above. */\n platform?: unknown;\n /** Trace correlation id — propagate through logs. */\n traceId: string;\n tenant: string;\n handlerId: string;\n}\n\n// ============ Registered handler marker ============\n\nexport const TRIGGER_SYMBOL = '__maravilla_trigger' as const;\n\nexport interface RegisteredHandler<Event = unknown, Ctx = EventCtx> {\n readonly [TRIGGER_SYMBOL]: EventTrigger;\n readonly handler: (event: Event, ctx: Ctx) => unknown | Promise<unknown>;\n}\n\nfunction register<Event, Ctx = EventCtx>(\n trigger: EventTrigger,\n handler: (event: Event, ctx: Ctx) => unknown | Promise<unknown>,\n): RegisteredHandler<Event, Ctx> {\n return { [TRIGGER_SYMBOL]: trigger, handler };\n}\n\n// ============ Public factory helpers ============\n\nexport function onKvChange(\n config: Omit<EventTriggerKv, 'kind'>,\n handler: (event: KvChangeEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<KvChangeEvent> {\n return register({ kind: 'kv', ...config }, handler);\n}\n\nexport function onDbChange(\n config: Omit<EventTriggerDb, 'kind'>,\n handler: (event: DbChangeEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<DbChangeEvent> {\n return register({ kind: 'db', ...config }, handler);\n}\n\n/**\n * React to user authentication events: registration, login, logout,\n * deletion, update. Omit `op` to match all auth events; set it to\n * narrow to a specific lifecycle transition.\n *\n * ```ts\n * export const welcomeEmail = onAuth(\n * { op: 'registered' },\n * async (event, ctx) => {\n * await sendWelcomeEmail(event.data?.email);\n * },\n * );\n * ```\n */\nexport function onAuth(\n config: Omit<EventTriggerAuth, 'kind'>,\n handler: (event: AuthEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<AuthEvent> {\n return register({ kind: 'auth', ...config }, handler);\n}\n\n/**\n * Convenience wrapper for `onAuth({ op: 'registered' }, …)` — the most common\n * auth hook. Fires once when a new user signs up; the canonical place to\n * **provision** them: create their app-side profile, add them to a group/role,\n * seed their initial records, send a welcome. `event.data.profile` carries the\n * custom fields passed to `platform.auth.register({ profile: {...} })`.\n *\n * ```ts\n * export const provision = onRegister(async (event, ctx) => {\n * const members = await ctx.auth.getGroupByName('members');\n * if (members) await ctx.auth.addUserToGroup(event.userId, members.id);\n * await ctx.database.insertOne('profiles', { _id: event.userId, email: event.data?.email, created_at: event.ts });\n * });\n * ```\n */\nexport function onRegister(\n handler: (event: AuthEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<AuthEvent> {\n return onAuth({ op: 'registered' }, handler);\n}\n\nexport function onSchedule(\n cron: string,\n handler: (event: ScheduleEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<ScheduleEvent> {\n return register({ kind: 'schedule', cron }, handler);\n}\n\nexport function onQueue<T = unknown>(\n name: string,\n config: Omit<EventTriggerQueue, 'kind' | 'name'>,\n handler: (messages: QueueMessage<T>[], ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<QueueMessage<T>[]> {\n return register({ kind: 'queue', name, ...config }, handler);\n}\n\nexport function onChannel<T = unknown>(\n config: Omit<EventTriggerChannel, 'kind'>,\n handler: (event: ChannelEvent<T>, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<ChannelEvent<T>> {\n return register({ kind: 'channel', ...config }, handler);\n}\n\nexport function onDeploy(\n phase: EventTriggerDeploy['phase'],\n handler: (event: DeployEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<DeployEvent> {\n return register({ kind: 'deploy', phase }, handler);\n}\n\n/**\n * React to object-storage `put` / `delete` events. `keyPattern` is a\n * glob (e.g. `\"uploads/videos/**\"`). Omit `op` to match both put and\n * delete; omit `keyPattern` to match every object in the tenant's\n * bucket.\n *\n * ```ts\n * export const onUpload = onStorage(\n * { keyPattern: 'uploads/photos/**', op: 'put' },\n * async (event, ctx) => {\n * await ctx.platform.media.transforms.resize(event.key, { width: 1600, format: 'webp' });\n * },\n * );\n * ```\n */\nexport function onStorage(\n config: Omit<EventTriggerStorage, 'kind'>,\n handler: (event: StorageEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<StorageEvent> {\n return register({ kind: 'storage', ...config }, handler);\n}\n\n/** Escape hatch for arbitrary `RenEvent` matches. */\nexport function defineEvent(\n config: Omit<EventTriggerRen, 'kind'>,\n handler: (event: RenEvent, ctx: EventCtx) => unknown | Promise<unknown>,\n): RegisteredHandler<RenEvent> {\n return register({ kind: 'ren', ...config }, handler);\n}\n\n/** Type guard used by the build-time discoverer and the runtime registry. */\nexport function isRegisteredHandler(value: unknown): value is RegisteredHandler {\n return (\n typeof value === 'object' &&\n value !== null &&\n TRIGGER_SYMBOL in value &&\n typeof (value as Record<string, unknown>).handler === 'function'\n );\n}\n"],"mappings":";AAwNO,IAAM,iBAAiB;AAO9B,SAAS,SACP,SACA,SAC+B;AAC/B,SAAO,EAAE,CAAC,cAAc,GAAG,SAAS,QAAQ;AAC9C;AAIO,SAAS,WACd,QACA,SACkC;AAClC,SAAO,SAAS,EAAE,MAAM,MAAM,GAAG,OAAO,GAAG,OAAO;AACpD;AAEO,SAAS,WACd,QACA,SACkC;AAClC,SAAO,SAAS,EAAE,MAAM,MAAM,GAAG,OAAO,GAAG,OAAO;AACpD;AAgBO,SAAS,OACd,QACA,SAC8B;AAC9B,SAAO,SAAS,EAAE,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO;AACtD;AAiBO,SAAS,WACd,SAC8B;AAC9B,SAAO,OAAO,EAAE,IAAI,aAAa,GAAG,OAAO;AAC7C;AAEO,SAAS,WACd,MACA,SACkC;AAClC,SAAO,SAAS,EAAE,MAAM,YAAY,KAAK,GAAG,OAAO;AACrD;AAEO,SAAS,QACd,MACA,QACA,SACsC;AACtC,SAAO,SAAS,EAAE,MAAM,SAAS,MAAM,GAAG,OAAO,GAAG,OAAO;AAC7D;AAEO,SAAS,UACd,QACA,SACoC;AACpC,SAAO,SAAS,EAAE,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO;AACzD;AAEO,SAAS,SACd,OACA,SACgC;AAChC,SAAO,SAAS,EAAE,MAAM,UAAU,MAAM,GAAG,OAAO;AACpD;AAiBO,SAAS,UACd,QACA,SACiC;AACjC,SAAO,SAAS,EAAE,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO;AACzD;AAGO,SAAS,YACd,QACA,SAC6B;AAC7B,SAAO,SAAS,EAAE,MAAM,OAAO,GAAG,OAAO,GAAG,OAAO;AACrD;AAGO,SAAS,oBAAoB,OAA4C;AAC9E,SACE,OAAO,UAAU,YACjB,UAAU,QACV,kBAAkB,SAClB,OAAQ,MAAkC,YAAY;AAE1D;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maravilla-labs/platform",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Universal platform client for Maravilla runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/events.ts CHANGED
@@ -265,6 +265,27 @@ export function onAuth(
265
265
  return register({ kind: 'auth', ...config }, handler);
266
266
  }
267
267
 
268
+ /**
269
+ * Convenience wrapper for `onAuth({ op: 'registered' }, …)` — the most common
270
+ * auth hook. Fires once when a new user signs up; the canonical place to
271
+ * **provision** them: create their app-side profile, add them to a group/role,
272
+ * seed their initial records, send a welcome. `event.data.profile` carries the
273
+ * custom fields passed to `platform.auth.register({ profile: {...} })`.
274
+ *
275
+ * ```ts
276
+ * export const provision = onRegister(async (event, ctx) => {
277
+ * const members = await ctx.auth.getGroupByName('members');
278
+ * if (members) await ctx.auth.addUserToGroup(event.userId, members.id);
279
+ * await ctx.database.insertOne('profiles', { _id: event.userId, email: event.data?.email, created_at: event.ts });
280
+ * });
281
+ * ```
282
+ */
283
+ export function onRegister(
284
+ handler: (event: AuthEvent, ctx: EventCtx) => unknown | Promise<unknown>,
285
+ ): RegisteredHandler<AuthEvent> {
286
+ return onAuth({ op: 'registered' }, handler);
287
+ }
288
+
268
289
  export function onSchedule(
269
290
  cron: string,
270
291
  handler: (event: ScheduleEvent, ctx: EventCtx) => unknown | Promise<unknown>,