@veloxts/router 0.7.4 → 0.7.6
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/CHANGELOG.md +18 -0
- package/GUIDE.md +9 -9
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -2
- package/dist/openapi/generator.js +9 -4
- package/dist/openapi/schema-converter.d.ts +13 -0
- package/dist/openapi/schema-converter.js +51 -0
- package/dist/procedure/builder.js +33 -21
- package/dist/procedure/types.d.ts +67 -28
- package/dist/resource/index.d.ts +7 -5
- package/dist/resource/index.js +3 -2
- package/dist/resource/instance.d.ts +38 -20
- package/dist/resource/instance.js +51 -44
- package/dist/resource/levels.d.ts +71 -0
- package/dist/resource/levels.js +109 -0
- package/dist/resource/schema.d.ts +151 -159
- package/dist/resource/schema.js +132 -124
- package/dist/resource/tags.d.ts +35 -15
- package/dist/resource/tags.js +1 -1
- package/dist/resource/types.d.ts +10 -8
- package/dist/resource/visibility.d.ts +16 -3
- package/dist/resource/visibility.js +16 -0
- package/dist/types.d.ts +28 -5
- package/package.json +3 -3
|
@@ -5,24 +5,36 @@
|
|
|
5
5
|
* field-level visibility controls. The builder tracks field types
|
|
6
6
|
* at compile time to enable type-safe projections.
|
|
7
7
|
*
|
|
8
|
+
* Supports both the default 3-level system (public/authenticated/admin)
|
|
9
|
+
* and custom access levels defined via `defineAccessLevels()`.
|
|
10
|
+
*
|
|
8
11
|
* @module resource/schema
|
|
9
12
|
*/
|
|
10
13
|
import type { ZodType } from 'zod';
|
|
11
|
-
import type {
|
|
12
|
-
import type {
|
|
14
|
+
import type { AccessLevelConfig } from './levels.js';
|
|
15
|
+
import type { ContextTag, TagToLevel } from './tags.js';
|
|
16
|
+
import type { VisibilityLevel } from './visibility.js';
|
|
13
17
|
/**
|
|
14
18
|
* A single field definition in a resource schema
|
|
15
19
|
*
|
|
20
|
+
* The `TLevel` parameter is a union of level strings representing which
|
|
21
|
+
* access levels can see this field. For the default system:
|
|
22
|
+
* - `.public()` → `'public' | 'authenticated' | 'admin'`
|
|
23
|
+
* - `.authenticated()` → `'authenticated' | 'admin'`
|
|
24
|
+
* - `.admin()` → `'admin'`
|
|
25
|
+
*
|
|
26
|
+
* For custom levels, `TLevel` is the union of levels passed to the builder method.
|
|
27
|
+
*
|
|
16
28
|
* @template TName - The field name as a literal type
|
|
17
29
|
* @template TSchema - The Zod schema type for this field
|
|
18
|
-
* @template TLevel -
|
|
30
|
+
* @template TLevel - Union of access level strings that can see this field
|
|
19
31
|
*/
|
|
20
|
-
export interface ResourceField<TName extends string = string, TSchema extends ZodType = ZodType, TLevel extends
|
|
32
|
+
export interface ResourceField<TName extends string = string, TSchema extends ZodType = ZodType, TLevel extends string = string> {
|
|
21
33
|
/** Field name */
|
|
22
34
|
readonly name: TName;
|
|
23
35
|
/** Zod schema for validation */
|
|
24
36
|
readonly schema: TSchema;
|
|
25
|
-
/** Visibility level */
|
|
37
|
+
/** Visibility level (union of levels that can see this field) */
|
|
26
38
|
readonly visibility: TLevel;
|
|
27
39
|
}
|
|
28
40
|
/**
|
|
@@ -30,10 +42,10 @@ export interface ResourceField<TName extends string = string, TSchema extends Zo
|
|
|
30
42
|
*
|
|
31
43
|
* @template TName - The field name as a literal type
|
|
32
44
|
* @template TNestedFields - The nested schema's field definitions
|
|
33
|
-
* @template TLevel -
|
|
45
|
+
* @template TLevel - Union of access level strings controlling WHETHER the relation is included
|
|
34
46
|
* @template TCardinality - 'one' for nullable object, 'many' for array
|
|
35
47
|
*/
|
|
36
|
-
export interface RelationField<TName extends string = string, TNestedFields extends readonly BuilderField[] = readonly BuilderField[], TLevel extends
|
|
48
|
+
export interface RelationField<TName extends string = string, TNestedFields extends readonly BuilderField[] = readonly BuilderField[], TLevel extends string = string, TCardinality extends 'one' | 'many' = 'one' | 'many'> {
|
|
37
49
|
readonly name: TName;
|
|
38
50
|
readonly nestedSchema: ResourceSchema<TNestedFields>;
|
|
39
51
|
readonly visibility: TLevel;
|
|
@@ -51,7 +63,10 @@ export type BuilderField = ResourceField | RelationField;
|
|
|
51
63
|
export interface RuntimeField {
|
|
52
64
|
readonly name: string;
|
|
53
65
|
readonly schema?: ZodType;
|
|
54
|
-
|
|
66
|
+
/** Set of levels that can see this field (source of truth) */
|
|
67
|
+
readonly visibleTo: ReadonlySet<string>;
|
|
68
|
+
/** Single visibility level string (backward compat, first level in set) */
|
|
69
|
+
readonly visibility: string;
|
|
55
70
|
readonly nestedSchema?: ResourceSchema;
|
|
56
71
|
readonly cardinality?: 'one' | 'many';
|
|
57
72
|
}
|
|
@@ -72,27 +87,13 @@ export interface ResourceSchema<TFields extends readonly BuilderField[] = readon
|
|
|
72
87
|
/**
|
|
73
88
|
* A resource schema tagged with an explicit access level
|
|
74
89
|
*
|
|
75
|
-
* Created by accessing `.public`, `.authenticated`, or `.admin` on a built schema
|
|
76
|
-
*
|
|
77
|
-
* projection via `resource(data, Schema.authenticated)`.
|
|
90
|
+
* Created by accessing `.public`, `.authenticated`, or `.admin` on a built schema,
|
|
91
|
+
* or by accessing any custom level property on a custom-level schema.
|
|
78
92
|
*
|
|
79
93
|
* @template TFields - The field definitions from the base schema
|
|
80
|
-
* @template TLevel - The access level for projection
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```typescript
|
|
84
|
-
* const UserSchema = resourceSchema()
|
|
85
|
-
* .public('id', z.string())
|
|
86
|
-
* .authenticated('email', z.string())
|
|
87
|
-
* .build();
|
|
88
|
-
*
|
|
89
|
-
* // Tagged views
|
|
90
|
-
* UserSchema.public // TaggedResourceSchema<..., 'public'>
|
|
91
|
-
* UserSchema.authenticated // TaggedResourceSchema<..., 'authenticated'>
|
|
92
|
-
* UserSchema.admin // TaggedResourceSchema<..., 'admin'>
|
|
93
|
-
* ```
|
|
94
|
+
* @template TLevel - The access level for projection (any string for custom levels)
|
|
94
95
|
*/
|
|
95
|
-
export interface TaggedResourceSchema<TFields extends readonly BuilderField[] = readonly BuilderField[], TLevel extends
|
|
96
|
+
export interface TaggedResourceSchema<TFields extends readonly BuilderField[] = readonly BuilderField[], TLevel extends string = string> extends ResourceSchema<TFields> {
|
|
96
97
|
readonly _level: TLevel;
|
|
97
98
|
}
|
|
98
99
|
/**
|
|
@@ -110,36 +111,25 @@ export interface ResourceSchemaWithViews<TFields extends readonly BuilderField[]
|
|
|
110
111
|
readonly admin: TaggedResourceSchema<TFields, 'admin'>;
|
|
111
112
|
}
|
|
112
113
|
/**
|
|
113
|
-
*
|
|
114
|
+
* A completed custom-level resource schema with tagged views for each level
|
|
114
115
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
116
|
+
* Returned by `resourceSchema(config).build()`. Has one property per
|
|
117
|
+
* defined level, each a TaggedResourceSchema for that level.
|
|
117
118
|
*
|
|
118
|
-
* @template
|
|
119
|
-
|
|
120
|
-
export type OutputForLevel<TSchema extends TaggedResourceSchema> = TSchema extends TaggedResourceSchema<infer TFields, infer TLevel> ? OutputForTag<ResourceSchema<TFields>, LevelToTag<TLevel>> : never;
|
|
121
|
-
/**
|
|
122
|
-
* Type guard to check if a schema is a TaggedResourceSchema (has _level)
|
|
119
|
+
* @template TFields - The field definitions
|
|
120
|
+
* @template TLevels - The tuple of level names
|
|
123
121
|
*/
|
|
124
|
-
export
|
|
122
|
+
export type CustomResourceSchemaWithViews<TFields extends readonly BuilderField[], TLevels extends readonly string[]> = ResourceSchema<TFields> & {
|
|
123
|
+
[K in TLevels[number]]: TaggedResourceSchema<TFields, K>;
|
|
124
|
+
} & {
|
|
125
|
+
readonly _levelConfig: AccessLevelConfig;
|
|
126
|
+
};
|
|
125
127
|
/**
|
|
126
128
|
* Helper to infer the output type of a Zod schema
|
|
127
129
|
*/
|
|
128
130
|
type InferZodOutput<T> = T extends {
|
|
129
131
|
parse: (data: unknown) => infer O;
|
|
130
132
|
} ? O : never;
|
|
131
|
-
/**
|
|
132
|
-
* Filters fields by visibility and extracts their types
|
|
133
|
-
*
|
|
134
|
-
* This type iterates over the fields tuple and includes only those
|
|
135
|
-
* that are visible to the given context tag. RelationField entries
|
|
136
|
-
* are recursively projected using the same tag.
|
|
137
|
-
*/
|
|
138
|
-
type FilterFieldsByTag<TFields extends readonly BuilderField[], TTag extends ContextTag> = TFields extends readonly [infer First, ...infer Rest] ? Rest extends readonly BuilderField[] ? First extends RelationField<infer Name, infer NestedFields, infer Level, infer Card> ? IsVisibleToTag<Level, TTag> extends true ? {
|
|
139
|
-
[K in Name]: Card extends 'one' ? Simplify<FilterFieldsByTag<NestedFields, TTag>> | null : Array<Simplify<FilterFieldsByTag<NestedFields, TTag>>>;
|
|
140
|
-
} & FilterFieldsByTag<Rest, TTag> : FilterFieldsByTag<Rest, TTag> : First extends ResourceField<infer Name, infer Schema, infer Level> ? IsVisibleToTag<Level, TTag> extends true ? {
|
|
141
|
-
[K in Name]: InferZodOutput<Schema>;
|
|
142
|
-
} & FilterFieldsByTag<Rest, TTag> : FilterFieldsByTag<Rest, TTag> : FilterFieldsByTag<Rest, TTag> : unknown : unknown;
|
|
143
133
|
/**
|
|
144
134
|
* Simplifies an intersection type to a cleaner object type
|
|
145
135
|
*
|
|
@@ -148,28 +138,59 @@ type FilterFieldsByTag<TFields extends readonly BuilderField[], TTag extends Con
|
|
|
148
138
|
type Simplify<T> = T extends object ? {
|
|
149
139
|
[K in keyof T]: T[K];
|
|
150
140
|
} : T;
|
|
141
|
+
/**
|
|
142
|
+
* Converts a default VisibilityLevel to its union of visible levels
|
|
143
|
+
*
|
|
144
|
+
* Used by the default builder to track which levels can see a field:
|
|
145
|
+
* - `'public'` → `'public' | 'authenticated' | 'admin'`
|
|
146
|
+
* - `'authenticated'` → `'authenticated' | 'admin'`
|
|
147
|
+
* - `'admin'` → `'admin'`
|
|
148
|
+
*/
|
|
149
|
+
export type LevelToVisibleUnion<TLevel extends VisibilityLevel> = TLevel extends 'public' ? 'public' | 'authenticated' | 'admin' : TLevel extends 'authenticated' ? 'authenticated' | 'admin' : 'admin';
|
|
150
|
+
/**
|
|
151
|
+
* Filters fields by level using set membership (union extends check)
|
|
152
|
+
*
|
|
153
|
+
* Includes a field if `TTarget extends TFieldLevels` — i.e., the target
|
|
154
|
+
* level string is a member of the field's visibility union.
|
|
155
|
+
*
|
|
156
|
+
* Works for both the default 3-level system and custom levels.
|
|
157
|
+
*/
|
|
158
|
+
export type FilterFieldsByLevel<TFields extends readonly BuilderField[], TTarget extends string> = TFields extends readonly [infer First, ...infer Rest] ? Rest extends readonly BuilderField[] ? First extends RelationField<infer Name, infer NestedFields, infer Levels, infer Card> ? TTarget extends Levels ? {
|
|
159
|
+
[K in Name]: Card extends 'one' ? Simplify<FilterFieldsByLevel<NestedFields, TTarget>> | null : Array<Simplify<FilterFieldsByLevel<NestedFields, TTarget>>>;
|
|
160
|
+
} & FilterFieldsByLevel<Rest, TTarget> : FilterFieldsByLevel<Rest, TTarget> : First extends ResourceField<infer Name, infer Schema, infer Levels> ? TTarget extends Levels ? {
|
|
161
|
+
[K in Name]: InferZodOutput<Schema>;
|
|
162
|
+
} & FilterFieldsByLevel<Rest, TTarget> : FilterFieldsByLevel<Rest, TTarget> : FilterFieldsByLevel<Rest, TTarget> : unknown : unknown;
|
|
163
|
+
/**
|
|
164
|
+
* Filters fields by visibility using tag-based access
|
|
165
|
+
*
|
|
166
|
+
* Delegates to `FilterFieldsByLevel` using `TagToLevel` to convert the
|
|
167
|
+
* phantom tag to a level string. Works with the default 3-level system.
|
|
168
|
+
*/
|
|
169
|
+
type FilterFieldsByTag<TFields extends readonly BuilderField[], TTag extends ContextTag> = FilterFieldsByLevel<TFields, TagToLevel<TTag>>;
|
|
151
170
|
/**
|
|
152
171
|
* Computes the output type for a schema at a given context tag
|
|
153
172
|
*
|
|
154
173
|
* @template TSchema - The resource schema type
|
|
155
174
|
* @template TTag - The context tag to compute output for
|
|
175
|
+
*/
|
|
176
|
+
export type OutputForTag<TSchema extends ResourceSchema, TTag extends ContextTag> = TSchema extends ResourceSchema<infer TFields> ? Simplify<FilterFieldsByTag<TFields, TTag>> : never;
|
|
177
|
+
/**
|
|
178
|
+
* Computes the output type for a tagged resource schema
|
|
156
179
|
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* type PublicOutput = OutputForTag<UserSchema, typeof ANONYMOUS>;
|
|
160
|
-
* // Result: { id: string; name: string }
|
|
180
|
+
* Uses `FilterFieldsByLevel` directly, which works for both default
|
|
181
|
+
* and custom access levels.
|
|
161
182
|
*
|
|
162
|
-
*
|
|
163
|
-
* // Result: { id: string; name: string; email: string }
|
|
164
|
-
* ```
|
|
183
|
+
* @template TSchema - A tagged resource schema
|
|
165
184
|
*/
|
|
166
|
-
export type
|
|
185
|
+
export type OutputForLevel<TSchema extends TaggedResourceSchema> = TSchema extends TaggedResourceSchema<infer TFields, infer TLevel> ? Simplify<FilterFieldsByLevel<TFields, TLevel>> : never;
|
|
167
186
|
/**
|
|
168
187
|
* Convenience type aliases for common tag outputs
|
|
169
188
|
*/
|
|
170
|
-
export type
|
|
171
|
-
|
|
172
|
-
export type
|
|
189
|
+
export type PublicOutput<TSchema extends ResourceSchema> = OutputForTag<TSchema, typeof import('./tags.js').PUBLIC>;
|
|
190
|
+
/** @deprecated Use PublicOutput */
|
|
191
|
+
export type AnonymousOutput<TSchema extends ResourceSchema> = PublicOutput<TSchema>;
|
|
192
|
+
export type AuthenticatedOutput<TSchema extends ResourceSchema> = OutputForTag<TSchema, typeof import('./tags.js').AUTHENTICATED>;
|
|
193
|
+
export type AdminOutput<TSchema extends ResourceSchema> = OutputForTag<TSchema, typeof import('./tags.js').ADMIN>;
|
|
173
194
|
/**
|
|
174
195
|
* Fluent builder for constructing resource schemas
|
|
175
196
|
*
|
|
@@ -197,141 +218,112 @@ export declare class ResourceSchemaBuilder<TFields extends readonly BuilderField
|
|
|
197
218
|
static create(): ResourceSchemaBuilder<readonly []>;
|
|
198
219
|
/**
|
|
199
220
|
* Adds a public field (visible to everyone)
|
|
200
|
-
*
|
|
201
|
-
* @param name - Field name
|
|
202
|
-
* @param schema - Zod schema for the field
|
|
203
|
-
* @returns New builder with the field added
|
|
204
221
|
*/
|
|
205
|
-
public<TName extends string, TSchema extends ZodType>(name: TName, schema: TSchema): ResourceSchemaBuilder<readonly [...TFields, ResourceField<TName, TSchema, 'public'>]>;
|
|
222
|
+
public<TName extends string, TSchema extends ZodType>(name: TName, schema: TSchema): ResourceSchemaBuilder<readonly [...TFields, ResourceField<TName, TSchema, 'public' | 'authenticated' | 'admin'>]>;
|
|
206
223
|
/**
|
|
207
224
|
* Adds an authenticated field (visible to authenticated users and admins)
|
|
208
|
-
*
|
|
209
|
-
* @param name - Field name
|
|
210
|
-
* @param schema - Zod schema for the field
|
|
211
|
-
* @returns New builder with the field added
|
|
212
225
|
*/
|
|
213
|
-
authenticated<TName extends string, TSchema extends ZodType>(name: TName, schema: TSchema): ResourceSchemaBuilder<readonly [...TFields, ResourceField<TName, TSchema, 'authenticated'>]>;
|
|
226
|
+
authenticated<TName extends string, TSchema extends ZodType>(name: TName, schema: TSchema): ResourceSchemaBuilder<readonly [...TFields, ResourceField<TName, TSchema, 'authenticated' | 'admin'>]>;
|
|
214
227
|
/**
|
|
215
228
|
* Adds an admin field (visible only to admins)
|
|
216
|
-
*
|
|
217
|
-
* @param name - Field name
|
|
218
|
-
* @param schema - Zod schema for the field
|
|
219
|
-
* @returns New builder with the field added
|
|
220
229
|
*/
|
|
221
230
|
admin<TName extends string, TSchema extends ZodType>(name: TName, schema: TSchema): ResourceSchemaBuilder<readonly [...TFields, ResourceField<TName, TSchema, 'admin'>]>;
|
|
222
231
|
/**
|
|
223
232
|
* Adds a has-one relation (nullable nested object)
|
|
224
|
-
*
|
|
225
|
-
* The relation's visibility controls WHETHER it appears in the output.
|
|
226
|
-
* The parent's projection level controls WHAT fields of the nested schema are shown.
|
|
227
|
-
*
|
|
228
|
-
* **Note:** The nested schema's generic field types are tracked at compile time
|
|
229
|
-
* for output type computation, but the runtime field stores an untyped
|
|
230
|
-
* `ResourceSchema` reference. Always pass the direct result of `.build()`
|
|
231
|
-
* to ensure the compile-time and runtime schemas stay in sync.
|
|
232
|
-
*
|
|
233
|
-
* @param name - Relation field name
|
|
234
|
-
* @param nestedSchema - The nested resource schema (result of `.build()`)
|
|
235
|
-
* @param visibility - Visibility level for this relation
|
|
236
|
-
* @returns New builder with the relation added
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* ```typescript
|
|
240
|
-
* const UserSchema = resourceSchema()
|
|
241
|
-
* .public('id', z.string())
|
|
242
|
-
* .hasOne('organization', OrgSchema, 'public')
|
|
243
|
-
* .build();
|
|
244
|
-
* ```
|
|
245
233
|
*/
|
|
246
|
-
hasOne<TName extends string, TNestedFields extends readonly BuilderField[], TLevel extends VisibilityLevel>(name: TName, nestedSchema: ResourceSchema<TNestedFields>, visibility: TLevel): ResourceSchemaBuilder<readonly [...TFields, RelationField<TName, TNestedFields, TLevel
|
|
234
|
+
hasOne<TName extends string, TNestedFields extends readonly BuilderField[], TLevel extends VisibilityLevel>(name: TName, nestedSchema: ResourceSchema<TNestedFields>, visibility: TLevel): ResourceSchemaBuilder<readonly [...TFields, RelationField<TName, TNestedFields, LevelToVisibleUnion<TLevel>, 'one'>]>;
|
|
247
235
|
/**
|
|
248
236
|
* Adds a has-many relation (array of nested objects)
|
|
249
|
-
*
|
|
250
|
-
* The relation's visibility controls WHETHER it appears in the output.
|
|
251
|
-
* The parent's projection level controls WHAT fields of the nested schema are shown.
|
|
252
|
-
*
|
|
253
|
-
* **Note:** The nested schema's generic field types are tracked at compile time
|
|
254
|
-
* for output type computation, but the runtime field stores an untyped
|
|
255
|
-
* `ResourceSchema` reference. Always pass the direct result of `.build()`
|
|
256
|
-
* to ensure the compile-time and runtime schemas stay in sync.
|
|
257
|
-
*
|
|
258
|
-
* @param name - Relation field name
|
|
259
|
-
* @param nestedSchema - The nested resource schema (result of `.build()`)
|
|
260
|
-
* @param visibility - Visibility level for this relation
|
|
261
|
-
* @returns New builder with the relation added
|
|
262
|
-
*
|
|
263
|
-
* @example
|
|
264
|
-
* ```typescript
|
|
265
|
-
* const UserSchema = resourceSchema()
|
|
266
|
-
* .public('id', z.string())
|
|
267
|
-
* .hasMany('posts', PostSchema, 'authenticated')
|
|
268
|
-
* .build();
|
|
269
|
-
* ```
|
|
270
237
|
*/
|
|
271
|
-
hasMany<TName extends string, TNestedFields extends readonly BuilderField[], TLevel extends VisibilityLevel>(name: TName, nestedSchema: ResourceSchema<TNestedFields>, visibility: TLevel): ResourceSchemaBuilder<readonly [...TFields, RelationField<TName, TNestedFields, TLevel
|
|
238
|
+
hasMany<TName extends string, TNestedFields extends readonly BuilderField[], TLevel extends VisibilityLevel>(name: TName, nestedSchema: ResourceSchema<TNestedFields>, visibility: TLevel): ResourceSchemaBuilder<readonly [...TFields, RelationField<TName, TNestedFields, LevelToVisibleUnion<TLevel>, 'many'>]>;
|
|
272
239
|
/**
|
|
273
240
|
* Adds a field with explicit visibility level
|
|
274
|
-
*
|
|
275
|
-
* @param name - Field name
|
|
276
|
-
* @param schema - Zod schema for the field
|
|
277
|
-
* @param visibility - Visibility level
|
|
278
|
-
* @returns New builder with the field added
|
|
279
241
|
*/
|
|
280
|
-
field<TName extends string, TSchema extends ZodType, TLevel extends VisibilityLevel>(name: TName, schema: TSchema, visibility: TLevel): ResourceSchemaBuilder<readonly [...TFields, ResourceField<TName, TSchema, TLevel
|
|
242
|
+
field<TName extends string, TSchema extends ZodType, TLevel extends VisibilityLevel>(name: TName, schema: TSchema, visibility: TLevel): ResourceSchemaBuilder<readonly [...TFields, ResourceField<TName, TSchema, LevelToVisibleUnion<TLevel>>]>;
|
|
281
243
|
/**
|
|
282
244
|
* Builds the final resource schema with tagged views
|
|
283
|
-
*
|
|
284
|
-
* Returns a schema with `.public`, `.authenticated`, and `.admin`
|
|
285
|
-
* properties for declarative projection in procedures.
|
|
286
|
-
*
|
|
287
|
-
* @returns Completed resource schema with tagged views
|
|
288
|
-
*
|
|
289
|
-
* @example
|
|
290
|
-
* ```typescript
|
|
291
|
-
* const UserSchema = resourceSchema()
|
|
292
|
-
* .public('id', z.string())
|
|
293
|
-
* .authenticated('email', z.string())
|
|
294
|
-
* .build();
|
|
295
|
-
*
|
|
296
|
-
* // Use tagged views in procedures
|
|
297
|
-
* procedure().resource(UserSchema.authenticated).query(handler);
|
|
298
|
-
*
|
|
299
|
-
* // Or in handlers
|
|
300
|
-
* resource(data, UserSchema.authenticated);
|
|
301
|
-
* ```
|
|
302
245
|
*/
|
|
303
246
|
build(): ResourceSchemaWithViews<TFields>;
|
|
304
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Resolves a group reference to its union of level strings
|
|
250
|
+
*/
|
|
251
|
+
type ResolveGroupToUnion<TLevels extends readonly string[], TGroups extends Record<string, '*' | readonly string[]>, K extends keyof TGroups> = TGroups[K] extends '*' ? TLevels[number] : TGroups[K] extends readonly (infer U extends string)[] ? U : never;
|
|
252
|
+
/**
|
|
253
|
+
* Resolves a group name or levels array to a union of level strings
|
|
254
|
+
*/
|
|
255
|
+
type ResolveRefToUnion<TLevels extends readonly string[], TGroups extends Record<string, '*' | readonly string[]>, TRef> = TRef extends keyof TGroups ? ResolveGroupToUnion<TLevels, TGroups, TRef> : TRef extends readonly (infer U extends string)[] ? U : never;
|
|
256
|
+
/**
|
|
257
|
+
* Methods available on the custom schema builder (non-dynamic)
|
|
258
|
+
*/
|
|
259
|
+
interface CustomSchemaBuilderMethods<TLevels extends readonly string[], TGroups extends Record<string, '*' | readonly string[]>, TFields extends readonly BuilderField[]> {
|
|
260
|
+
/** Adds a field visible to an explicit set of levels */
|
|
261
|
+
visibleTo<TName extends string, TSchema extends ZodType, const TSet extends readonly TLevels[number][]>(name: TName, schema: TSchema, levels: TSet): CustomSchemaBuilder<TLevels, TGroups, readonly [...TFields, ResourceField<TName, TSchema, TSet[number]>]>;
|
|
262
|
+
/** Adds a has-one relation */
|
|
263
|
+
hasOne<TName extends string, TNestedFields extends readonly BuilderField[], TRef extends (keyof TGroups & string) | readonly TLevels[number][]>(name: TName, nestedSchema: ResourceSchema<TNestedFields>, visibility: TRef): CustomSchemaBuilder<TLevels, TGroups, readonly [
|
|
264
|
+
...TFields,
|
|
265
|
+
RelationField<TName, TNestedFields, ResolveRefToUnion<TLevels, TGroups, TRef>, 'one'>
|
|
266
|
+
]>;
|
|
267
|
+
/** Adds a has-many relation */
|
|
268
|
+
hasMany<TName extends string, TNestedFields extends readonly BuilderField[], TRef extends (keyof TGroups & string) | readonly TLevels[number][]>(name: TName, nestedSchema: ResourceSchema<TNestedFields>, visibility: TRef): CustomSchemaBuilder<TLevels, TGroups, readonly [
|
|
269
|
+
...TFields,
|
|
270
|
+
RelationField<TName, TNestedFields, ResolveRefToUnion<TLevels, TGroups, TRef>, 'many'>
|
|
271
|
+
]>;
|
|
272
|
+
/** Builds the final schema with one tagged view per level */
|
|
273
|
+
build(): CustomResourceSchemaWithViews<TFields, TLevels>;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Custom schema builder type with dynamic methods from groups and levels
|
|
277
|
+
*
|
|
278
|
+
* Group names become fluent methods that resolve to the group's level set.
|
|
279
|
+
* Level names (that don't collide with groups) become methods for single-level visibility.
|
|
280
|
+
*/
|
|
281
|
+
export type CustomSchemaBuilder<TLevels extends readonly string[], TGroups extends Record<string, '*' | readonly string[]>, TFields extends readonly BuilderField[] = readonly []> = {
|
|
282
|
+
[K in keyof TGroups & string]: <TName extends string, TSchema extends ZodType>(name: TName, schema: TSchema) => CustomSchemaBuilder<TLevels, TGroups, readonly [...TFields, ResourceField<TName, TSchema, ResolveGroupToUnion<TLevels, TGroups, K>>]>;
|
|
283
|
+
} & {
|
|
284
|
+
[K in TLevels[number] as K extends keyof TGroups ? never : K]: <TName extends string, TSchema extends ZodType>(name: TName, schema: TSchema) => CustomSchemaBuilder<TLevels, TGroups, readonly [...TFields, ResourceField<TName, TSchema, K>]>;
|
|
285
|
+
} & CustomSchemaBuilderMethods<TLevels, TGroups, TFields>;
|
|
305
286
|
/**
|
|
306
287
|
* Creates a new resource schema builder
|
|
307
288
|
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
289
|
+
* Without arguments, returns the default 3-level builder with
|
|
290
|
+
* `.public()`, `.authenticated()`, `.admin()` methods.
|
|
310
291
|
*
|
|
311
|
-
*
|
|
292
|
+
* With an `AccessLevelConfig` argument (from `defineAccessLevels()`),
|
|
293
|
+
* returns a custom builder whose methods correspond to the defined
|
|
294
|
+
* levels and groups.
|
|
312
295
|
*
|
|
313
|
-
* @example
|
|
296
|
+
* @example Default (3-level)
|
|
314
297
|
* ```typescript
|
|
315
|
-
* import { z } from 'zod';
|
|
316
|
-
* import { resourceSchema } from '@veloxts/router';
|
|
317
|
-
*
|
|
318
298
|
* const UserSchema = resourceSchema()
|
|
319
|
-
* .public('id', z.string()
|
|
320
|
-
* .
|
|
321
|
-
* .
|
|
322
|
-
* .authenticated('email', z.string().email())
|
|
323
|
-
* .authenticated('createdAt', z.date())
|
|
324
|
-
* .admin('internalNotes', z.string().nullable())
|
|
325
|
-
* .admin('lastLoginIp', z.string().nullable())
|
|
299
|
+
* .public('id', z.string())
|
|
300
|
+
* .authenticated('email', z.string())
|
|
301
|
+
* .admin('internalNotes', z.string())
|
|
326
302
|
* .build();
|
|
303
|
+
* ```
|
|
327
304
|
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
305
|
+
* @example Custom levels
|
|
306
|
+
* ```typescript
|
|
307
|
+
* const access = defineAccessLevels(
|
|
308
|
+
* ['public', 'reviewer', 'authenticated', 'moderator', 'admin'],
|
|
309
|
+
* { everyone: '*', staff: ['moderator', 'admin'] }
|
|
310
|
+
* );
|
|
311
|
+
*
|
|
312
|
+
* const ArticleSchema = resourceSchema(access)
|
|
313
|
+
* .everyone('id', z.string())
|
|
314
|
+
* .staff('moderationLog', z.string())
|
|
315
|
+
* .visibleTo('authorEmail', z.string(), ['authenticated', 'admin'])
|
|
316
|
+
* .build();
|
|
332
317
|
* ```
|
|
333
318
|
*/
|
|
334
319
|
export declare function resourceSchema(): ResourceSchemaBuilder<readonly []>;
|
|
320
|
+
export declare function resourceSchema<const TLevels extends readonly [string, ...string[]], const TGroups extends Record<string, '*' | readonly string[]>>(config: AccessLevelConfig<TLevels, TGroups>): CustomSchemaBuilder<TLevels, TGroups, readonly []>;
|
|
321
|
+
/**
|
|
322
|
+
* Type guard to check if a schema is a TaggedResourceSchema (has _level)
|
|
323
|
+
*
|
|
324
|
+
* Accepts any string _level to support custom access levels.
|
|
325
|
+
*/
|
|
326
|
+
export declare function isTaggedResourceSchema(value: unknown): value is TaggedResourceSchema;
|
|
335
327
|
/**
|
|
336
328
|
* Type guard to check if a value is a ResourceSchema
|
|
337
329
|
*/
|