@newhomestar/sdk 0.7.16 → 0.7.18
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 +10 -2
- package/dist/events.js +3 -23
- package/dist/next.d.ts +112 -0
- package/dist/next.js +36 -0
- package/package.json +1 -1
package/dist/events.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
/** Full request body for POST /events/queue */
|
|
2
2
|
export interface QueueEventPayload {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Fully-qualified event slug — preferred way to identify the event.
|
|
5
|
+
* e.g. "nova_ticketing_service.ticket_created"
|
|
6
|
+
* When provided, entity_type and action are not required.
|
|
7
|
+
*/
|
|
8
|
+
event_slug?: string;
|
|
9
|
+
/** Legacy: entity category. Use event_slug instead. */
|
|
10
|
+
entity_type?: string;
|
|
11
|
+
/** Legacy: event action. Use event_slug instead. */
|
|
12
|
+
action?: string;
|
|
5
13
|
/** Auto-stamped from NOVA_SERVICE_SLUG if not provided */
|
|
6
14
|
source_service?: string;
|
|
7
15
|
/** ISO 8601 — when the event actually happened (defaults to now()) */
|
package/dist/events.js
CHANGED
|
@@ -185,30 +185,10 @@ export async function withServiceEventOutbox(db, reqOrCallback, maybeCallback) {
|
|
|
185
185
|
const callbackResult = await callback(tx, emit);
|
|
186
186
|
// Write one outbox row per staged emit
|
|
187
187
|
for (const staged of stagedEmits) {
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
// dotted path ("hris.employee.created" → entity_type=hris.employee, action=created)
|
|
191
|
-
// UPPER_SNAKE ("EMPLOYEE_CREATED" → entity_type=employee, action=created)
|
|
192
|
-
// bare string ("employee" → entity_type=employee, action=event)
|
|
193
|
-
let entity_type;
|
|
194
|
-
let action;
|
|
195
|
-
if (staged.topic.includes('.')) {
|
|
196
|
-
const lastDot = staged.topic.lastIndexOf('.');
|
|
197
|
-
entity_type = staged.topic.slice(0, lastDot);
|
|
198
|
-
action = staged.topic.slice(lastDot + 1);
|
|
199
|
-
}
|
|
200
|
-
else if (staged.topic.includes('_')) {
|
|
201
|
-
const firstUnderscore = staged.topic.indexOf('_');
|
|
202
|
-
entity_type = staged.topic.slice(0, firstUnderscore).toLowerCase();
|
|
203
|
-
action = staged.topic.slice(firstUnderscore + 1).toLowerCase().replace(/_/g, '.');
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
entity_type = staged.topic.toLowerCase();
|
|
207
|
-
action = 'event';
|
|
208
|
-
}
|
|
188
|
+
// Pass the topic directly as event_slug — no decomposition needed.
|
|
189
|
+
// The Events Service uses event_slug to look up the event_type and fan-out.
|
|
209
190
|
const fullPayload = {
|
|
210
|
-
|
|
211
|
-
action,
|
|
191
|
+
event_slug: staged.topic,
|
|
212
192
|
source_service: serviceSlug ?? undefined,
|
|
213
193
|
attributes: staged.payload,
|
|
214
194
|
metadata: {
|
package/dist/next.d.ts
CHANGED
|
@@ -102,6 +102,50 @@ export interface NovaEndpointEventDef {
|
|
|
102
102
|
/** Whether this event should trigger a push notification. Defaults to false. */
|
|
103
103
|
triggers_notification?: boolean;
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Human-readable metadata for a single permission slug.
|
|
107
|
+
* Defined inline in `novaEndpoint()` alongside `requiredPermissions` and
|
|
108
|
+
* auto-embedded into the IAM permission registry when `nova services push` runs.
|
|
109
|
+
*
|
|
110
|
+
* This drives hierarchical/grouped rendering in the Odyssey UI permission
|
|
111
|
+
* assignment views — permissions in the same `group` are displayed together
|
|
112
|
+
* under a shared parent label with friendly titles and descriptions.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* requiredPermissions: 'iam_admin:roles_view',
|
|
117
|
+
* permissionMeta: {
|
|
118
|
+
* slug: 'iam_admin:roles_view',
|
|
119
|
+
* title: 'View Roles',
|
|
120
|
+
* description: 'Read access to role definitions and listings',
|
|
121
|
+
* group: 'Roles',
|
|
122
|
+
* },
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export interface PermissionMeta {
|
|
126
|
+
/**
|
|
127
|
+
* The exact permission slug this metadata describes.
|
|
128
|
+
* Must match a value in `requiredPermissions`.
|
|
129
|
+
* e.g. `'iam_admin:roles_view'`
|
|
130
|
+
*/
|
|
131
|
+
slug: string;
|
|
132
|
+
/**
|
|
133
|
+
* Human-friendly label shown in the Odyssey UI.
|
|
134
|
+
* e.g. `'View Roles'`
|
|
135
|
+
*/
|
|
136
|
+
title: string;
|
|
137
|
+
/**
|
|
138
|
+
* Optional description of what this permission grants.
|
|
139
|
+
* e.g. `'Read access to role definitions and listings'`
|
|
140
|
+
*/
|
|
141
|
+
description?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Parent group label used for hierarchical rendering in the UI.
|
|
144
|
+
* All permissions with the same `group` value are displayed together.
|
|
145
|
+
* e.g. `'Roles'`
|
|
146
|
+
*/
|
|
147
|
+
group: string;
|
|
148
|
+
}
|
|
105
149
|
export interface NovaEndpointDef<I extends ZodTypeAny = ZodTypeAny, O extends ZodTypeAny = ZodTypeAny> {
|
|
106
150
|
/** HTTP method */
|
|
107
151
|
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
@@ -139,6 +183,29 @@ export interface NovaEndpointDef<I extends ZodTypeAny = ZodTypeAny, O extends Zo
|
|
|
139
183
|
* DELETE → `'hris_admin:manage'`
|
|
140
184
|
*/
|
|
141
185
|
requiredPermissions?: string | string[];
|
|
186
|
+
/**
|
|
187
|
+
* Human-readable metadata for the permission slug(s) in `requiredPermissions`.
|
|
188
|
+
* Used to generate hierarchical, grouped permission views in the Odyssey UI.
|
|
189
|
+
*
|
|
190
|
+
* Provide one entry per unique permission slug used on this endpoint.
|
|
191
|
+
* Metadata is deduplicated by slug — defining the same slug on multiple
|
|
192
|
+
* endpoints is fine; the first definition wins.
|
|
193
|
+
*
|
|
194
|
+
* `nova services push` reads this field and embeds title, description,
|
|
195
|
+
* and group into the IAM permission registry automatically.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* requiredPermissions: 'iam_admin:roles_view',
|
|
200
|
+
* permissionMeta: {
|
|
201
|
+
* slug: 'iam_admin:roles_view',
|
|
202
|
+
* title: 'View Roles',
|
|
203
|
+
* description: 'Read access to role definitions and listings',
|
|
204
|
+
* group: 'Roles',
|
|
205
|
+
* },
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
permissionMeta?: PermissionMeta | PermissionMeta[];
|
|
142
209
|
/**
|
|
143
210
|
* Event types emitted by this endpoint.
|
|
144
211
|
* Defined here and auto-registered in the platform event_types registry
|
|
@@ -428,6 +495,51 @@ export declare const CURSOR_PAGE_PARAMS: Record<string, ParamMeta>;
|
|
|
428
495
|
* Spread into `novaEndpoint({ params: { ...OFFSET_PAGE_PARAMS, ...yourParams } })`.
|
|
429
496
|
*/
|
|
430
497
|
export declare const OFFSET_PAGE_PARAMS: Record<string, ParamMeta>;
|
|
498
|
+
/**
|
|
499
|
+
* Metadata for a Nova service — passed to `defineService()`.
|
|
500
|
+
*/
|
|
501
|
+
export interface ServiceDef {
|
|
502
|
+
/** Unique service slug (snake_case). e.g. "nova_ticketing_service" */
|
|
503
|
+
slug: string;
|
|
504
|
+
/** Human-readable service name */
|
|
505
|
+
name: string;
|
|
506
|
+
/** Short service description */
|
|
507
|
+
description?: string;
|
|
508
|
+
/** Service category for Odyssey UI grouping (e.g. "support", "hris") */
|
|
509
|
+
category?: string;
|
|
510
|
+
/** Semver version string — defaults to "1.0.0" */
|
|
511
|
+
version?: string;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* defineService() — declare Nova service identity and auto-set NOVA_SERVICE_SLUG.
|
|
515
|
+
*
|
|
516
|
+
* Call this once in a shared module (e.g. `src/lib/service.ts`) and import it
|
|
517
|
+
* early in your app entrypoint. At import time it sets `process.env.NOVA_SERVICE_SLUG`
|
|
518
|
+
* so `withServiceEventOutbox()` can stamp `source_service` on all outbound events.
|
|
519
|
+
*
|
|
520
|
+
* `nova services push` also detects this export (the `__novaService` marker) to
|
|
521
|
+
* cross-validate the slug against `nova-service.yaml` during the build scan.
|
|
522
|
+
*
|
|
523
|
+
* **Usage:**
|
|
524
|
+
* ```ts
|
|
525
|
+
* // src/lib/service.ts
|
|
526
|
+
* import { defineService } from '@newhomestar/sdk/next';
|
|
527
|
+
*
|
|
528
|
+
* export const service = defineService({
|
|
529
|
+
* slug: 'nova_ticketing_service',
|
|
530
|
+
* name: 'Nova Ticketing Service',
|
|
531
|
+
* category: 'support',
|
|
532
|
+
* });
|
|
533
|
+
* ```
|
|
534
|
+
*
|
|
535
|
+
* Then import it early — e.g. in `src/instrumentation.ts` (Next.js) or your app entry:
|
|
536
|
+
* ```ts
|
|
537
|
+
* import '@/lib/service'; // ensures NOVA_SERVICE_SLUG is set before any route runs
|
|
538
|
+
* ```
|
|
539
|
+
*/
|
|
540
|
+
export declare function defineService<T extends ServiceDef>(def: T): T & {
|
|
541
|
+
readonly __novaService: true;
|
|
542
|
+
};
|
|
431
543
|
/** @deprecated Use `buildPageResponse` instead */
|
|
432
544
|
export declare function buildPaginatedResponse<T extends {
|
|
433
545
|
id: string;
|
package/dist/next.js
CHANGED
|
@@ -323,6 +323,42 @@ export const OFFSET_PAGE_PARAMS = {
|
|
|
323
323
|
sort_dir: { in: 'query', uiType: 'select', label: 'Sort Dir', description: 'asc or desc', options: [{ label: 'Ascending', value: 'asc' }, { label: 'Descending', value: 'desc' }] },
|
|
324
324
|
filters: { in: 'query', uiType: 'json', label: 'Filters', description: 'JSON array of FilterCondition objects' },
|
|
325
325
|
};
|
|
326
|
+
/**
|
|
327
|
+
* defineService() — declare Nova service identity and auto-set NOVA_SERVICE_SLUG.
|
|
328
|
+
*
|
|
329
|
+
* Call this once in a shared module (e.g. `src/lib/service.ts`) and import it
|
|
330
|
+
* early in your app entrypoint. At import time it sets `process.env.NOVA_SERVICE_SLUG`
|
|
331
|
+
* so `withServiceEventOutbox()` can stamp `source_service` on all outbound events.
|
|
332
|
+
*
|
|
333
|
+
* `nova services push` also detects this export (the `__novaService` marker) to
|
|
334
|
+
* cross-validate the slug against `nova-service.yaml` during the build scan.
|
|
335
|
+
*
|
|
336
|
+
* **Usage:**
|
|
337
|
+
* ```ts
|
|
338
|
+
* // src/lib/service.ts
|
|
339
|
+
* import { defineService } from '@newhomestar/sdk/next';
|
|
340
|
+
*
|
|
341
|
+
* export const service = defineService({
|
|
342
|
+
* slug: 'nova_ticketing_service',
|
|
343
|
+
* name: 'Nova Ticketing Service',
|
|
344
|
+
* category: 'support',
|
|
345
|
+
* });
|
|
346
|
+
* ```
|
|
347
|
+
*
|
|
348
|
+
* Then import it early — e.g. in `src/instrumentation.ts` (Next.js) or your app entry:
|
|
349
|
+
* ```ts
|
|
350
|
+
* import '@/lib/service'; // ensures NOVA_SERVICE_SLUG is set before any route runs
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
export function defineService(def) {
|
|
354
|
+
// Set NOVA_SERVICE_SLUG at import time so withServiceEventOutbox() always has
|
|
355
|
+
// access to source_service without requiring a separate env var. Only sets it
|
|
356
|
+
// if not already set — explicit env var overrides (e.g. in tests) still work.
|
|
357
|
+
if (!process.env.NOVA_SERVICE_SLUG) {
|
|
358
|
+
process.env.NOVA_SERVICE_SLUG = def.slug;
|
|
359
|
+
}
|
|
360
|
+
return { ...def, __novaService: true };
|
|
361
|
+
}
|
|
326
362
|
// ─── Legacy re-exports (backward compat) ─────────────────────────────────────
|
|
327
363
|
/** @deprecated Use `buildPageResponse` instead */
|
|
328
364
|
export function buildPaginatedResponse(data, count, pageSize) {
|
package/package.json
CHANGED