@maravilla-labs/platform 0.2.2 → 0.2.4

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
@@ -127,11 +127,26 @@ interface DeployEvent {
127
127
  interface EventCtx {
128
128
  /** Per-tenant env vars. */
129
129
  env: Record<string, string>;
130
+ /** KV store — same shape as `getPlatform().env.KV` / `platform.kv`. */
131
+ kv?: unknown;
132
+ /** MongoDB-style database — same shape as `getPlatform().env.DB`. */
133
+ database?: unknown;
134
+ /** Object storage. */
135
+ storage?: unknown;
136
+ /** Durable queue producer (`.send(name, payload, opts?)`). */
137
+ queue?: {
138
+ send: (name: string, payload: unknown, opts?: unknown) => Promise<string>;
139
+ };
140
+ /** Auth service — register/login/logout/user CRUD/etc. */
141
+ auth?: unknown;
142
+ /** Web Push service. */
143
+ push?: unknown;
144
+ /** Full platform object — escape hatch for services not surfaced above. */
145
+ platform?: unknown;
130
146
  /** Trace correlation id — propagate through logs. */
131
147
  traceId: string;
132
148
  tenant: string;
133
149
  handlerId: string;
134
- [extra: string]: unknown;
135
150
  }
136
151
  declare const TRIGGER_SYMBOL: "__maravilla_trigger";
137
152
  interface RegisteredHandler<Event = unknown, Ctx = EventCtx> {
@@ -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 | 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\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// ============ Handler context ============\n\nexport interface EventCtx {\n /** Per-tenant env vars. */\n env: Record<string, string>;\n /** Trace correlation idpropagate through logs. */\n traceId: string;\n tenant: string;\n handlerId: string;\n // Platform services are attached by the runtime at invoke time.\n [extra: string]: unknown;\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/** 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":";AA8KO,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;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 | 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\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// ============ 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/** 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":";AA0LO,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;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.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Universal platform client for Maravilla runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/events.ts CHANGED
@@ -162,12 +162,24 @@ export interface DeployEvent {
162
162
  export interface EventCtx {
163
163
  /** Per-tenant env vars. */
164
164
  env: Record<string, string>;
165
+ /** KV store — same shape as `getPlatform().env.KV` / `platform.kv`. */
166
+ kv?: unknown;
167
+ /** MongoDB-style database — same shape as `getPlatform().env.DB`. */
168
+ database?: unknown;
169
+ /** Object storage. */
170
+ storage?: unknown;
171
+ /** Durable queue producer (`.send(name, payload, opts?)`). */
172
+ queue?: { send: (name: string, payload: unknown, opts?: unknown) => Promise<string> };
173
+ /** Auth service — register/login/logout/user CRUD/etc. */
174
+ auth?: unknown;
175
+ /** Web Push service. */
176
+ push?: unknown;
177
+ /** Full platform object — escape hatch for services not surfaced above. */
178
+ platform?: unknown;
165
179
  /** Trace correlation id — propagate through logs. */
166
180
  traceId: string;
167
181
  tenant: string;
168
182
  handlerId: string;
169
- // Platform services are attached by the runtime at invoke time.
170
- [extra: string]: unknown;
171
183
  }
172
184
 
173
185
  // ============ Registered handler marker ============