@newhomestar/sdk 0.7.20 → 0.7.23
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/next.d.ts +249 -0
- package/dist/next.js +17 -1
- package/package.json +1 -1
package/dist/next.d.ts
CHANGED
|
@@ -50,6 +50,183 @@ export interface ParamMeta {
|
|
|
50
50
|
order?: number;
|
|
51
51
|
group?: string;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* A single FGA relation definition for an object type.
|
|
55
|
+
* Declared in `defineService().fga.relations[]` and auto-registered in the
|
|
56
|
+
* IAM service's `entity_relation_definitions` table at `nova services push` time.
|
|
57
|
+
*/
|
|
58
|
+
export interface NovaFgaRelationDef {
|
|
59
|
+
/** Object type this relation applies to (e.g. 'community', 'trnVideo') */
|
|
60
|
+
object_type: string;
|
|
61
|
+
/** The relation name (e.g. 'admin', 'editor', 'member', 'viewer') */
|
|
62
|
+
relation: string;
|
|
63
|
+
/**
|
|
64
|
+
* Relations this relation implies (e.g. admin implies ['editor', 'member']).
|
|
65
|
+
* The IAM DB trigger pre-expands implied relations into entity_computed_access
|
|
66
|
+
* automatically — no application code needed for propagation.
|
|
67
|
+
*/
|
|
68
|
+
implies?: string[];
|
|
69
|
+
/** Priority for ordering (default: 0). Higher = evaluated first. */
|
|
70
|
+
priority?: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* IAM resource declaration for `defineService().iam.resources[]`.
|
|
74
|
+
* Declares how the service's permission namespace maps to the Odyssey UI resource tree.
|
|
75
|
+
* Used by `nova services push` to set resource types, hierarchy levels, and parent-child
|
|
76
|
+
* relationships between resources.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* iam: {
|
|
81
|
+
* resources: [
|
|
82
|
+
* { name: 'HRIS Admin', resource_path: 'hris_admin', resource_type: 'module', level: 50,
|
|
83
|
+
* description: 'HRIS module — unified HR and payroll data' },
|
|
84
|
+
* { name: 'Employees', resource_path: 'hris_employees', resource_type: 'block', level: 60,
|
|
85
|
+
* parent_resource_path: 'hris_admin', description: 'Employee records' },
|
|
86
|
+
* ],
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export interface NovaIamResourceDef {
|
|
91
|
+
/** Human-readable display name shown in Odyssey UI (e.g. 'Employees', 'HRIS Admin') */
|
|
92
|
+
name: string;
|
|
93
|
+
/** Unique resource path — must match the permission slug prefix (e.g. 'hris_employees') */
|
|
94
|
+
resource_path: string;
|
|
95
|
+
/** Position in the resource hierarchy */
|
|
96
|
+
resource_type: 'system' | 'module' | 'block' | 'resource';
|
|
97
|
+
/** Hierarchy level: 50=module, 60=block, 80=resource */
|
|
98
|
+
level: number;
|
|
99
|
+
/** Optional description shown in Odyssey UI */
|
|
100
|
+
description?: string;
|
|
101
|
+
/**
|
|
102
|
+
* resource_path of the parent resource.
|
|
103
|
+
* Resolved to parentResourceId at `nova services push` time by the IAM service.
|
|
104
|
+
* e.g. set to 'hris_admin' to nest this resource under the HRIS Admin module.
|
|
105
|
+
*/
|
|
106
|
+
parent_resource_path?: string;
|
|
107
|
+
/** Arbitrary metadata stored on the resource record */
|
|
108
|
+
metadata?: Record<string, unknown>;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* FGA check declared on a novaEndpoint() — runtime-only.
|
|
112
|
+
* Describes which relation on which object a request's subject must hold.
|
|
113
|
+
* The schema (relation hierarchy) is declared in `defineService().fga.relations`.
|
|
114
|
+
*
|
|
115
|
+
* Used by `nova.assertFga(req, context)` at runtime (Phase 2).
|
|
116
|
+
* Stored on the endpoint record in the platform DB for Odyssey UI display.
|
|
117
|
+
*/
|
|
118
|
+
export interface NovaFgaCheckDef {
|
|
119
|
+
/** Object type to check (e.g. 'community', 'trnVideo') */
|
|
120
|
+
object_type: string;
|
|
121
|
+
/** Relation the subject must hold (e.g. 'member', 'viewer', 'editor') */
|
|
122
|
+
relation: string;
|
|
123
|
+
/**
|
|
124
|
+
* Where to resolve object_id from in the request:
|
|
125
|
+
* 'path' → Next.js route params (e.g. params.id for /communities/:id)
|
|
126
|
+
* 'query' → URL query string param
|
|
127
|
+
*/
|
|
128
|
+
object_id_from: 'path' | 'query';
|
|
129
|
+
/** The param name to extract object_id from (e.g. 'id', 'community_id') */
|
|
130
|
+
object_id_param: string;
|
|
131
|
+
/** Optional tenant_id param name for multi-tenant scoped checks */
|
|
132
|
+
tenant_id_param?: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Service manifest definition — the code-first single source of truth for a
|
|
136
|
+
* Nova service. `nova services push` reads this to generate nova-service.yaml
|
|
137
|
+
* and sync all service metadata to the platform.
|
|
138
|
+
*
|
|
139
|
+
* Place this in `src/service.ts` (or `src/app/service.ts`) and export as `service`.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* // src/service.ts
|
|
144
|
+
* import { defineService } from '@newhomestar/sdk/next';
|
|
145
|
+
*
|
|
146
|
+
* export const service = defineService({
|
|
147
|
+
* slug: 'community',
|
|
148
|
+
* name: 'Community Service',
|
|
149
|
+
* category: 'social',
|
|
150
|
+
* runtime: 'nextjs',
|
|
151
|
+
* apiDir: './src/app',
|
|
152
|
+
*
|
|
153
|
+
* fga: {
|
|
154
|
+
* relations: [
|
|
155
|
+
* { object_type: 'community', relation: 'admin', implies: ['editor', 'member'], priority: 100 },
|
|
156
|
+
* { object_type: 'community', relation: 'editor', implies: ['viewer'], priority: 50 },
|
|
157
|
+
* { object_type: 'community', relation: 'member', implies: ['viewer'], priority: 30 },
|
|
158
|
+
* { object_type: 'community', relation: 'viewer', implies: [], priority: 10 },
|
|
159
|
+
* ],
|
|
160
|
+
* },
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export interface NovaServiceDef {
|
|
165
|
+
/** Unique service slug (e.g. 'community', 'hris', 'iam') */
|
|
166
|
+
slug: string;
|
|
167
|
+
/** Human-readable service name */
|
|
168
|
+
name: string;
|
|
169
|
+
/** Service description */
|
|
170
|
+
description?: string;
|
|
171
|
+
/** Category for grouping in Odyssey UI (e.g. 'platform', 'social', 'hris') */
|
|
172
|
+
category?: string;
|
|
173
|
+
/** Service version (default: '1.0.0') */
|
|
174
|
+
version?: string;
|
|
175
|
+
/** Runtime mode (default: 'nextjs') */
|
|
176
|
+
runtime?: 'nextjs' | 'sdk';
|
|
177
|
+
/** Path to route files directory (default: './src/app') */
|
|
178
|
+
apiDir?: string;
|
|
179
|
+
/** Public base URL for Odyssey UI API tester */
|
|
180
|
+
baseUrl?: string;
|
|
181
|
+
/** Shared Zod schemas registered in platform DB on push */
|
|
182
|
+
schemas?: Array<{
|
|
183
|
+
slug: string;
|
|
184
|
+
name: string;
|
|
185
|
+
file: string;
|
|
186
|
+
description?: string;
|
|
187
|
+
version?: string;
|
|
188
|
+
schemaType?: string;
|
|
189
|
+
}>;
|
|
190
|
+
/** Environment variable declarations */
|
|
191
|
+
envSpec?: Array<{
|
|
192
|
+
name: string;
|
|
193
|
+
secret?: boolean;
|
|
194
|
+
default?: string;
|
|
195
|
+
}>;
|
|
196
|
+
/** Container resource requests */
|
|
197
|
+
resources?: {
|
|
198
|
+
cpu?: string;
|
|
199
|
+
memory?: string;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* FGA schema — defines relation hierarchies for all object types owned by
|
|
203
|
+
* this service. Registered in IAM `entity_relation_definitions` at push time.
|
|
204
|
+
*
|
|
205
|
+
* This MUST be pushed before any `entity_grants` are assigned, as the
|
|
206
|
+
* trigger-based expansion relies on these definitions to propagate implied
|
|
207
|
+
* relations through `entity_computed_access`.
|
|
208
|
+
*/
|
|
209
|
+
fga?: {
|
|
210
|
+
relations: NovaFgaRelationDef[];
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* IAM resource hierarchy — declares how this service's permission namespace
|
|
214
|
+
* maps to the Odyssey UI resource tree (module → block → resource).
|
|
215
|
+
*
|
|
216
|
+
* Resources declared here override the auto-derived defaults (which default
|
|
217
|
+
* to resource_type: 'module', level: 50, no parent). Use this to:
|
|
218
|
+
* - Declare a parent module resource (e.g. 'hris_admin')
|
|
219
|
+
* - Nest entity resources as blocks under that module
|
|
220
|
+
* - Control display names, descriptions, and hierarchy levels
|
|
221
|
+
*
|
|
222
|
+
* At `nova services push` time the CLI reads this, merges with auto-derived
|
|
223
|
+
* resources from permission slugs, and sends `parent_resource_path` to the
|
|
224
|
+
* IAM service which resolves it to `parentResourceId` in the DB.
|
|
225
|
+
*/
|
|
226
|
+
iam?: {
|
|
227
|
+
resources: NovaIamResourceDef[];
|
|
228
|
+
};
|
|
229
|
+
}
|
|
53
230
|
/**
|
|
54
231
|
* An event type emitted by a service endpoint.
|
|
55
232
|
* Defined inline in `novaEndpoint()` and auto-registered in the platform
|
|
@@ -218,6 +395,47 @@ export interface NovaEndpointDef<I extends ZodTypeAny = ZodTypeAny, O extends Zo
|
|
|
218
395
|
* ```
|
|
219
396
|
*/
|
|
220
397
|
events?: NovaEndpointEventDef[];
|
|
398
|
+
/**
|
|
399
|
+
* FGA (Fine-Grained Authorization) check(s) required to access this endpoint.
|
|
400
|
+
*
|
|
401
|
+
* The authenticated subject (user) must hold the declared `relation` on the
|
|
402
|
+
* specified `object_type:object_id` to be granted access.
|
|
403
|
+
*
|
|
404
|
+
* The FGA schema (relation hierarchy) is declared separately in
|
|
405
|
+
* `defineService().fga.relations` and registered at `nova services push` time.
|
|
406
|
+
* The fgaCheck here is **runtime-only** — it describes how to resolve the
|
|
407
|
+
* object_id from the current request and which relation to verify.
|
|
408
|
+
*
|
|
409
|
+
* Runtime enforcement is via `nova.assertFga(req, { path: params })` (Phase 2).
|
|
410
|
+
* At push time, the declarations are stored on the endpoint record in the
|
|
411
|
+
* platform DB for Odyssey UI display.
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* ```ts
|
|
415
|
+
* // Single check: user must be a member of the community
|
|
416
|
+
* fgaCheck: {
|
|
417
|
+
* object_type: 'community',
|
|
418
|
+
* relation: 'member',
|
|
419
|
+
* object_id_from: 'path',
|
|
420
|
+
* object_id_param: 'id',
|
|
421
|
+
* }
|
|
422
|
+
*
|
|
423
|
+
* // Multiple checks: user must be member of community AND editor of the video
|
|
424
|
+
* fgaCheck: [
|
|
425
|
+
* { object_type: 'community', relation: 'member', object_id_from: 'path', object_id_param: 'community_id' },
|
|
426
|
+
* { object_type: 'trnVideo', relation: 'editor', object_id_from: 'path', object_id_param: 'video_id' },
|
|
427
|
+
* ],
|
|
428
|
+
* fgaMode: 'all',
|
|
429
|
+
* ```
|
|
430
|
+
*/
|
|
431
|
+
fgaCheck?: NovaFgaCheckDef | NovaFgaCheckDef[];
|
|
432
|
+
/**
|
|
433
|
+
* When multiple `fgaCheck` entries are declared, controls evaluation logic:
|
|
434
|
+
* 'all' → every check must pass (default — AND logic, most restrictive)
|
|
435
|
+
* 'any' → at least one check must pass (OR logic)
|
|
436
|
+
* Ignored when fgaCheck is a single object.
|
|
437
|
+
*/
|
|
438
|
+
fgaMode?: 'all' | 'any';
|
|
221
439
|
}
|
|
222
440
|
export type NovaEndpoint<I extends ZodTypeAny = ZodTypeAny, O extends ZodTypeAny = ZodTypeAny> = NovaEndpointDef<I, O> & {
|
|
223
441
|
/**
|
|
@@ -370,9 +588,23 @@ export declare function buildPrismaPage(input: CursorPageInputType): {
|
|
|
370
588
|
id: 'desc';
|
|
371
589
|
}>;
|
|
372
590
|
};
|
|
591
|
+
/**
|
|
592
|
+
* Convert a snake_case string to camelCase.
|
|
593
|
+
* Used internally to normalise sort column names from API query params
|
|
594
|
+
* (snake_case) to Prisma model field names (camelCase).
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* ```ts
|
|
598
|
+
* snakeToCamel('modified_at') // → 'modifiedAt'
|
|
599
|
+
* snakeToCamel('createdAt') // → 'createdAt' (no-op)
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
export declare function snakeToCamel(s: string): string;
|
|
373
603
|
/**
|
|
374
604
|
* Build Prisma `skip`, `take`, and `orderBy` from an `OffsetPageInput`.
|
|
375
605
|
* Handles offset pagination and sort direction.
|
|
606
|
+
* Sort column names are automatically converted from snake_case (API convention)
|
|
607
|
+
* to camelCase (Prisma convention) via `snakeToCamel()`.
|
|
376
608
|
* Does NOT handle `search` or `filters` — apply those to the `where` clause manually.
|
|
377
609
|
*
|
|
378
610
|
* @example
|
|
@@ -497,6 +729,8 @@ export declare const CURSOR_PAGE_PARAMS: Record<string, ParamMeta>;
|
|
|
497
729
|
export declare const OFFSET_PAGE_PARAMS: Record<string, ParamMeta>;
|
|
498
730
|
/**
|
|
499
731
|
* Metadata for a Nova service — passed to `defineService()`.
|
|
732
|
+
* Extends `NovaServiceDef` to include FGA relation schema and IAM resource
|
|
733
|
+
* hierarchy declarations consumed by `nova services push`.
|
|
500
734
|
*/
|
|
501
735
|
export interface ServiceDef {
|
|
502
736
|
/** Unique service slug (snake_case). e.g. "nova_ticketing_service" */
|
|
@@ -509,6 +743,21 @@ export interface ServiceDef {
|
|
|
509
743
|
category?: string;
|
|
510
744
|
/** Semver version string — defaults to "1.0.0" */
|
|
511
745
|
version?: string;
|
|
746
|
+
/**
|
|
747
|
+
* FGA schema — relation hierarchies for object types owned by this service.
|
|
748
|
+
* Registered in IAM `entity_relation_definitions` at `nova services push` time.
|
|
749
|
+
*/
|
|
750
|
+
fga?: {
|
|
751
|
+
relations: NovaFgaRelationDef[];
|
|
752
|
+
};
|
|
753
|
+
/**
|
|
754
|
+
* IAM resource hierarchy — maps permission namespace to the Odyssey UI tree.
|
|
755
|
+
* Overrides auto-derived resource names/types/levels and sets parent-child links.
|
|
756
|
+
* See `NovaIamResourceDef` for field documentation.
|
|
757
|
+
*/
|
|
758
|
+
iam?: {
|
|
759
|
+
resources: NovaIamResourceDef[];
|
|
760
|
+
};
|
|
512
761
|
}
|
|
513
762
|
/**
|
|
514
763
|
* defineService() — declare Nova service identity and auto-set NOVA_SERVICE_SLUG.
|
package/dist/next.js
CHANGED
|
@@ -191,9 +191,25 @@ export function buildPrismaPage(input) {
|
|
|
191
191
|
orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],
|
|
192
192
|
};
|
|
193
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Convert a snake_case string to camelCase.
|
|
196
|
+
* Used internally to normalise sort column names from API query params
|
|
197
|
+
* (snake_case) to Prisma model field names (camelCase).
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```ts
|
|
201
|
+
* snakeToCamel('modified_at') // → 'modifiedAt'
|
|
202
|
+
* snakeToCamel('createdAt') // → 'createdAt' (no-op)
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
export function snakeToCamel(s) {
|
|
206
|
+
return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
207
|
+
}
|
|
194
208
|
/**
|
|
195
209
|
* Build Prisma `skip`, `take`, and `orderBy` from an `OffsetPageInput`.
|
|
196
210
|
* Handles offset pagination and sort direction.
|
|
211
|
+
* Sort column names are automatically converted from snake_case (API convention)
|
|
212
|
+
* to camelCase (Prisma convention) via `snakeToCamel()`.
|
|
197
213
|
* Does NOT handle `search` or `filters` — apply those to the `where` clause manually.
|
|
198
214
|
*
|
|
199
215
|
* @example
|
|
@@ -203,7 +219,7 @@ export function buildPrismaPage(input) {
|
|
|
203
219
|
* ```
|
|
204
220
|
*/
|
|
205
221
|
export function buildPrismaOffsetPage(input, defaultSort = 'createdAt') {
|
|
206
|
-
const col = input.sort ?? defaultSort;
|
|
222
|
+
const col = snakeToCamel(input.sort ?? defaultSort);
|
|
207
223
|
return {
|
|
208
224
|
skip: input.offset,
|
|
209
225
|
take: input.limit,
|
package/package.json
CHANGED