@newhomestar/sdk 0.8.9 → 0.8.10
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 +22 -0
- package/dist/events.js +45 -1
- package/dist/next.d.ts +28 -0
- package/dist/next.js +12 -0
- package/package.json +1 -1
package/dist/events.d.ts
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register a Zod payload schema for emit-time validation.
|
|
3
|
+
* Called by `novaEndpoint()` in next.ts for each event that has a `payload` field.
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerEventPayloadSchema(slug: string, schema: {
|
|
7
|
+
safeParse: (data: unknown) => any;
|
|
8
|
+
}): void;
|
|
9
|
+
/**
|
|
10
|
+
* Look up a registered payload schema by event slug.
|
|
11
|
+
* Returns `undefined` if no schema was registered (validation is skipped).
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare function getEventPayloadSchema(slug: string): {
|
|
15
|
+
safeParse: (data: unknown) => {
|
|
16
|
+
success: boolean;
|
|
17
|
+
error?: {
|
|
18
|
+
message?: string;
|
|
19
|
+
issues?: unknown[];
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
} | undefined;
|
|
1
23
|
/** Full request body for POST /events/queue */
|
|
2
24
|
export interface QueueEventPayload {
|
|
3
25
|
/**
|
package/dist/events.js
CHANGED
|
@@ -36,6 +36,34 @@ import { Agent as UndiciAgent } from 'undici';
|
|
|
36
36
|
if (!process.env.NOVA_EVENTS_SERVICE_URL) {
|
|
37
37
|
dotenv.config({ path: '.env.local', override: false });
|
|
38
38
|
}
|
|
39
|
+
// ── Event Payload Schema Registry ──────────────────────────────────────────────
|
|
40
|
+
//
|
|
41
|
+
// Module-level registry mapping event slugs to their Zod payload schemas.
|
|
42
|
+
// Populated by `novaEndpoint()` (in next.ts) when events declare a `payload`
|
|
43
|
+
// field. Used by `withServiceEventOutbox()` for emit-time validation:
|
|
44
|
+
// - development (NODE_ENV=development): validation failure throws immediately
|
|
45
|
+
// - production: validation failure logs a warning, event is still emitted
|
|
46
|
+
//
|
|
47
|
+
// This avoids coupling events.ts to Zod at the import level — we only call
|
|
48
|
+
// `.safeParse()` on whatever object was registered (duck-typed).
|
|
49
|
+
/** @internal */
|
|
50
|
+
const _eventPayloadSchemas = new Map();
|
|
51
|
+
/**
|
|
52
|
+
* Register a Zod payload schema for emit-time validation.
|
|
53
|
+
* Called by `novaEndpoint()` in next.ts for each event that has a `payload` field.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
export function registerEventPayloadSchema(slug, schema) {
|
|
57
|
+
_eventPayloadSchemas.set(slug, schema);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Look up a registered payload schema by event slug.
|
|
61
|
+
* Returns `undefined` if no schema was registered (validation is skipped).
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
export function getEventPayloadSchema(slug) {
|
|
65
|
+
return _eventPayloadSchemas.get(slug);
|
|
66
|
+
}
|
|
39
67
|
// ── Internal helpers ───────────────────────────────────────────────────────────
|
|
40
68
|
function _getEnvOrThrow(key, context) {
|
|
41
69
|
const val = process.env[key];
|
|
@@ -178,8 +206,24 @@ export async function withServiceEventOutbox(db, reqOrCallback, maybeCallback) {
|
|
|
178
206
|
const outboxRows = [];
|
|
179
207
|
// ── Atomic: callback + outbox INSERTs in one transaction ─────────────────
|
|
180
208
|
const result = await db.$transaction(async (tx) => {
|
|
181
|
-
// The emit fn is synchronous — events are staged and written after callback returns
|
|
209
|
+
// The emit fn is synchronous — events are staged and written after callback returns.
|
|
210
|
+
// If the event has a registered payload schema (from novaEndpoint), validate at emit time.
|
|
182
211
|
const emit = (topic, payload, idempotencyKey) => {
|
|
212
|
+
// ── Emit-time payload validation ──────────────────────────────────
|
|
213
|
+
const payloadSchema = getEventPayloadSchema(topic);
|
|
214
|
+
if (payloadSchema) {
|
|
215
|
+
const result = payloadSchema.safeParse(payload);
|
|
216
|
+
if (!result.success) {
|
|
217
|
+
const errorMsg = result.error?.message ?? JSON.stringify(result.error?.issues ?? 'unknown');
|
|
218
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
219
|
+
if (isDev) {
|
|
220
|
+
throw new Error(`[nova/events] Payload validation failed for event "${topic}": ${errorMsg}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
console.warn(`[nova/events] Payload validation warning for event "${topic}": ${errorMsg}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
183
227
|
stagedEmits.push({ topic, payload, idempotencyKey });
|
|
184
228
|
};
|
|
185
229
|
const callbackResult = await callback(tx, emit);
|
package/dist/next.d.ts
CHANGED
|
@@ -296,6 +296,34 @@ export interface NovaEndpointEventDef {
|
|
|
296
296
|
priority_level?: 'low' | 'medium' | 'high';
|
|
297
297
|
/** Whether this event should trigger a push notification. Defaults to false. */
|
|
298
298
|
triggers_notification?: boolean;
|
|
299
|
+
/**
|
|
300
|
+
* Optional Zod schema describing the shape of this event's payload (attributes).
|
|
301
|
+
*
|
|
302
|
+
* Used for:
|
|
303
|
+
* 1. **Emit-time validation** — `withServiceEventOutbox()` validates the payload
|
|
304
|
+
* against this schema at emit time. In development mode (`NODE_ENV=development`),
|
|
305
|
+
* validation failures throw immediately. In production, failures are logged as
|
|
306
|
+
* warnings but the event is still emitted.
|
|
307
|
+
* 2. **JSON Schema generation** — `nova services push` converts this Zod schema to
|
|
308
|
+
* JSON Schema via `zodToJsonSchema()` and stores it as `payload_schema` on the
|
|
309
|
+
* event_type record in the Events Service. The Gamification Rule Builder UI uses
|
|
310
|
+
* the JSON Schema to dynamically render filter forms.
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* events: [{
|
|
315
|
+
* slug: 'EMPLOYEE_UPDATED',
|
|
316
|
+
* name: 'Employee Updated',
|
|
317
|
+
* category: 'hris',
|
|
318
|
+
* payload: z.object({
|
|
319
|
+
* employee_id: z.string().uuid(),
|
|
320
|
+
* changed_fields: z.array(z.string()),
|
|
321
|
+
* department: z.string().optional(),
|
|
322
|
+
* }),
|
|
323
|
+
* }]
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
payload?: ZodTypeAny;
|
|
299
327
|
}
|
|
300
328
|
/**
|
|
301
329
|
* Human-readable metadata for a single permission slug.
|
package/dist/next.js
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
30
|
import { z } from 'zod';
|
|
31
|
+
import { registerEventPayloadSchema } from './events.js';
|
|
31
32
|
// ─── Factory function ─────────────────────────────────────────────────────────
|
|
32
33
|
/**
|
|
33
34
|
* novaEndpoint() — define a Nova service endpoint co-located with its route.
|
|
@@ -46,6 +47,17 @@ import { z } from 'zod';
|
|
|
46
47
|
* ```
|
|
47
48
|
*/
|
|
48
49
|
export function novaEndpoint(cfg) {
|
|
50
|
+
// Register event payload schemas for emit-time validation.
|
|
51
|
+
// When a route file is imported, novaEndpoint() runs and populates the
|
|
52
|
+
// module-level registry in events.ts. withServiceEventOutbox() then uses
|
|
53
|
+
// the registry to validate payloads at emit time (throw in dev, warn in prod).
|
|
54
|
+
if (cfg.events) {
|
|
55
|
+
for (const evt of cfg.events) {
|
|
56
|
+
if (evt.payload) {
|
|
57
|
+
registerEventPayloadSchema(evt.slug, evt.payload);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
49
61
|
return {
|
|
50
62
|
...cfg,
|
|
51
63
|
parseQuery(req) {
|
package/package.json
CHANGED