@vertz/server 0.2.0 → 0.2.3

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.
Files changed (4) hide show
  1. package/README.md +371 -0
  2. package/dist/index.d.ts +278 -235
  3. package/dist/index.js +1193 -100
  4. package/package.json +10 -9
package/dist/index.d.ts CHANGED
@@ -1,230 +1,167 @@
1
- import { AccumulateProvides, AppBuilder, AppConfig, BootInstruction, BootSequence, CorsConfig, Ctx, DeepReadonly, Deps, EnvConfig, ExtractMethods, HandlerCtx, HttpMethod, HttpStatusCode, Infer as Infer2, InferSchema, ListenOptions, MiddlewareDef, Module, ModuleBootInstruction, ModuleDef, NamedMiddlewareDef, NamedModule, NamedModuleDef, NamedRouterDef, NamedServiceDef, RawRequest, ResolveInjectMap, RouterDef, ServerAdapter, ServerHandle, ServiceBootInstruction, ServiceDef, ServiceFactory } from "@vertz/core";
2
- import { BadRequestException, ConflictException, createEnv, createImmutableProxy, createMiddleware, createModule, createModuleDef, createServer, deepFreeze, ForbiddenException, InternalServerErrorException, makeImmutable, NotFoundException, ServiceUnavailableException, UnauthorizedException, ValidationException, VertzException, vertz } from "@vertz/core";
3
- interface JsonbValidator<T> {
4
- parse(value: unknown): T;
5
- }
6
- interface ColumnMetadata {
7
- readonly sqlType: string;
8
- readonly primary: boolean;
9
- readonly unique: boolean;
10
- readonly nullable: boolean;
11
- readonly hasDefault: boolean;
12
- readonly sensitive: boolean;
13
- readonly hidden: boolean;
14
- readonly isTenant: boolean;
15
- readonly references: {
16
- readonly table: string;
17
- readonly column: string;
18
- } | null;
19
- readonly check: string | null;
20
- readonly defaultValue?: unknown;
21
- readonly format?: string;
22
- readonly length?: number;
23
- readonly precision?: number;
24
- readonly scale?: number;
25
- readonly enumName?: string;
26
- readonly enumValues?: readonly string[];
27
- readonly validator?: JsonbValidator<unknown>;
28
- }
29
- /** Phantom symbol to carry the TypeScript type without a runtime value. */
30
- declare const PhantomType: unique symbol;
31
- interface ColumnBuilder<
32
- TType,
33
- TMeta extends ColumnMetadata = ColumnMetadata
34
- > {
35
- /** Phantom field -- only exists at the type level for inference. Do not access at runtime. */
36
- readonly [PhantomType]: TType;
37
- readonly _meta: TMeta;
38
- primary(): ColumnBuilder<TType, Omit<TMeta, "primary" | "hasDefault"> & {
39
- readonly primary: true;
40
- readonly hasDefault: true;
41
- }>;
42
- unique(): ColumnBuilder<TType, Omit<TMeta, "unique"> & {
43
- readonly unique: true;
44
- }>;
45
- nullable(): ColumnBuilder<TType | null, Omit<TMeta, "nullable"> & {
46
- readonly nullable: true;
47
- }>;
48
- default(value: TType | "now"): ColumnBuilder<TType, Omit<TMeta, "hasDefault"> & {
49
- readonly hasDefault: true;
50
- readonly defaultValue: TType | "now";
51
- }>;
52
- sensitive(): ColumnBuilder<TType, Omit<TMeta, "sensitive"> & {
53
- readonly sensitive: true;
54
- }>;
55
- hidden(): ColumnBuilder<TType, Omit<TMeta, "hidden"> & {
56
- readonly hidden: true;
57
- }>;
58
- check(sql: string): ColumnBuilder<TType, Omit<TMeta, "check"> & {
59
- readonly check: string;
60
- }>;
61
- references(table: string, column?: string): ColumnBuilder<TType, Omit<TMeta, "references"> & {
62
- readonly references: {
63
- readonly table: string;
64
- readonly column: string;
65
- };
66
- }>;
1
+ import { AccumulateProvides, AppBuilder as AppBuilder2, AppConfig as AppConfig2, CorsConfig, Ctx, DeepReadonly, Deps, EnvConfig, HandlerCtx, HttpMethod, HttpStatusCode, Infer, InferSchema, ListenOptions, MiddlewareDef, NamedMiddlewareDef, RawRequest, ServerAdapter, ServerHandle } from "@vertz/core";
2
+ import { BadRequestException, ConflictException, createEnv, createImmutableProxy, createMiddleware, deepFreeze, ForbiddenException, InternalServerErrorException, makeImmutable, NotFoundException, ServiceUnavailableException, UnauthorizedException, ValidationException, VertzException, vertz } from "@vertz/core";
3
+ import { ModelDef as ModelDef2, RelationDef, SchemaLike, TableDef } from "@vertz/db";
4
+ import { ModelDef } from "@vertz/db";
5
+ import { EntityDbAdapter, ListOptions } from "@vertz/db";
6
+ import { EntityError, Result } from "@vertz/errors";
7
+ import { EntityDbAdapter as EntityDbAdapter2, ListOptions as ListOptions2 } from "@vertz/db";
8
+ interface ListResult<T = Record<string, unknown>> {
9
+ items: T[];
10
+ total: number;
11
+ limit: number;
12
+ nextCursor: string | null;
13
+ hasNextPage: boolean;
14
+ }
15
+ interface CrudResult<T = unknown> {
16
+ status: number;
17
+ body: T;
67
18
  }
68
- type InferColumnType<C> = C extends ColumnBuilder<infer T, ColumnMetadata> ? T : never;
69
- interface ThroughDef<TJoin extends TableDef<ColumnRecord> = TableDef<ColumnRecord>> {
70
- readonly table: () => TJoin;
71
- readonly thisKey: string;
72
- readonly thatKey: string;
19
+ interface CrudHandlers {
20
+ list(ctx: EntityContext, options?: ListOptions): Promise<Result<CrudResult<ListResult>, EntityError>>;
21
+ get(ctx: EntityContext, id: string): Promise<Result<CrudResult<Record<string, unknown>>, EntityError>>;
22
+ create(ctx: EntityContext, data: Record<string, unknown>): Promise<Result<CrudResult<Record<string, unknown>>, EntityError>>;
23
+ update(ctx: EntityContext, id: string, data: Record<string, unknown>): Promise<Result<CrudResult<Record<string, unknown>>, EntityError>>;
24
+ delete(ctx: EntityContext, id: string): Promise<Result<CrudResult<null>, EntityError>>;
73
25
  }
74
- interface RelationDef<
75
- TTarget extends TableDef<ColumnRecord> = TableDef<ColumnRecord>,
76
- TType extends "one" | "many" = "one" | "many"
77
- > {
78
- readonly _type: TType;
79
- readonly _target: () => TTarget;
80
- readonly _foreignKey: string | null;
81
- readonly _through: ThroughDef | null;
82
- }
83
- interface IndexDef {
84
- readonly columns: readonly string[];
85
- }
86
- /** A record of column builders -- the shape passed to d.table(). */
87
- type ColumnRecord = Record<string, ColumnBuilder<unknown, ColumnMetadata>>;
88
- /** Extract the TypeScript type from every column in a record. */
89
- type InferColumns<T extends ColumnRecord> = { [K in keyof T] : InferColumnType<T[K]> };
90
- /** Keys of columns where a given metadata flag is `true`. */
91
- type ColumnKeysWhere<
92
- T extends ColumnRecord,
93
- Flag extends keyof ColumnMetadata
94
- > = { [K in keyof T] : T[K] extends ColumnBuilder<unknown, infer M> ? M extends Record<Flag, true> ? K : never : never }[keyof T];
95
- /** Keys of columns where a given metadata flag is NOT `true` (i.e., false). */
96
- type ColumnKeysWhereNot<
97
- T extends ColumnRecord,
98
- Flag extends keyof ColumnMetadata
99
- > = { [K in keyof T] : T[K] extends ColumnBuilder<unknown, infer M> ? M extends Record<Flag, true> ? never : K : never }[keyof T];
100
- /**
101
- * $infer -- default SELECT type.
102
- * Excludes hidden columns. Includes everything else (including sensitive).
103
- */
104
- type Infer<T extends ColumnRecord> = { [K in ColumnKeysWhereNot<T, "hidden">] : InferColumnType<T[K]> };
105
- /**
106
- * $infer_all -- all columns including hidden.
107
- */
108
- type InferAll<T extends ColumnRecord> = InferColumns<T>;
109
- /**
110
- * $insert -- write type. ALL columns included (visibility is read-side only).
111
- * Columns with hasDefault: true become optional.
112
- */
113
- type Insert<T extends ColumnRecord> = { [K in ColumnKeysWhereNot<T, "hasDefault">] : InferColumnType<T[K]> } & { [K in ColumnKeysWhere<T, "hasDefault">]? : InferColumnType<T[K]> };
114
- /**
115
- * $update -- write type. ALL non-primary-key columns, all optional.
116
- * Primary key excluded (you don't update a PK).
117
- */
118
- type Update<T extends ColumnRecord> = { [K in ColumnKeysWhereNot<T, "primary">]? : InferColumnType<T[K]> };
26
+ declare function createCrudHandlers(def: EntityDefinition, db: EntityDbAdapter): CrudHandlers;
119
27
  /**
120
- * $not_sensitive -- excludes columns marked .sensitive() OR .hidden().
121
- * (hidden implies sensitive for read purposes)
28
+ * EntityOperations typed CRUD facade for a single entity.
29
+ *
30
+ * When used as `ctx.entity`, TModel fills in actual column types.
31
+ * When used as `ctx.entities.*`, TModel defaults to `ModelDef` (loose typing).
122
32
  */
123
- type NotSensitive<T extends ColumnRecord> = { [K in ColumnKeysWhereNot<T, "sensitive"> & ColumnKeysWhereNot<T, "hidden"> & keyof T] : InferColumnType<T[K]> };
33
+ interface EntityOperations<TModel extends ModelDef = ModelDef> {
34
+ get(id: string): Promise<TModel["table"]["$response"]>;
35
+ list(options?: ListOptions2): Promise<ListResult<TModel["table"]["$response"]>>;
36
+ create(data: TModel["table"]["$create_input"]): Promise<TModel["table"]["$response"]>;
37
+ update(id: string, data: TModel["table"]["$update_input"]): Promise<TModel["table"]["$response"]>;
38
+ delete(id: string): Promise<void>;
39
+ }
40
+ /** Extracts the model type from an EntityDefinition */
41
+ type ExtractModel<T> = T extends EntityDefinition<infer M> ? M : ModelDef2;
124
42
  /**
125
- * $not_hidden -- excludes columns marked .hidden().
126
- * Same as $infer (excludes hidden, keeps sensitive).
43
+ * Maps an inject config `{ key: EntityDefinition<TModel> }` to
44
+ * `{ key: EntityOperations<TModel> }` for typed ctx.entities access.
127
45
  */
128
- type NotHidden<T extends ColumnRecord> = { [K in ColumnKeysWhereNot<T, "hidden">] : InferColumnType<T[K]> };
129
- interface TableDef<TColumns extends ColumnRecord = ColumnRecord> {
130
- readonly _name: string;
131
- readonly _columns: TColumns;
132
- readonly _indexes: readonly IndexDef[];
133
- readonly _shared: boolean;
134
- /** Default SELECT type -- excludes hidden columns. */
135
- readonly $infer: Infer<TColumns>;
136
- /** All columns including hidden. */
137
- readonly $infer_all: InferAll<TColumns>;
138
- /** Insert type -- defaulted columns optional. ALL columns included. */
139
- readonly $insert: Insert<TColumns>;
140
- /** Update type -- all non-PK columns optional. ALL columns included. */
141
- readonly $update: Update<TColumns>;
142
- /** Excludes sensitive and hidden columns. */
143
- readonly $not_sensitive: NotSensitive<TColumns>;
144
- /** Excludes hidden columns. */
145
- readonly $not_hidden: NotHidden<TColumns>;
146
- /** Mark this table as shared / cross-tenant. */
147
- shared(): TableDef<TColumns>;
148
- }
149
- /** Relations record — maps relation names to RelationDef. */
150
- type RelationsRecord = Record<string, RelationDef>;
151
- /** A table entry in the database registry, pairing a table with its relations. */
152
- interface TableEntry<
153
- TTable extends TableDef<ColumnRecord> = TableDef<ColumnRecord>,
154
- TRelations extends RelationsRecord = RelationsRecord
46
+ type InjectToOperations<TInject extends Record<string, EntityDefinition> = {}> = { readonly [K in keyof TInject] : EntityOperations<ExtractModel<TInject[K]>> };
47
+ interface BaseContext {
48
+ readonly userId: string | null;
49
+ authenticated(): boolean;
50
+ tenant(): boolean;
51
+ role(...roles: string[]): boolean;
52
+ }
53
+ interface EntityContext<
54
+ TModel extends ModelDef2 = ModelDef2,
55
+ TInject extends Record<string, EntityDefinition> = {}
56
+ > extends BaseContext {
57
+ /** Typed CRUD on the current entity */
58
+ readonly entity: EntityOperations<TModel>;
59
+ /** Typed access to injected entities only */
60
+ readonly entities: InjectToOperations<TInject>;
61
+ }
62
+ type AccessRule = false | ((ctx: BaseContext, row: Record<string, unknown>) => boolean | Promise<boolean>);
63
+ interface EntityBeforeHooks<
64
+ TCreateInput = unknown,
65
+ TUpdateInput = unknown
155
66
  > {
156
- readonly table: TTable;
157
- readonly relations: TRelations;
158
- }
159
- type DomainType = "persisted" | "process" | "view" | "session";
160
- interface DomainContext<TRow = any> {
161
- user: {
162
- id: string;
163
- role: string;
164
- [key: string]: unknown;
165
- } | null;
166
- tenant: {
167
- id: string;
168
- [key: string]: unknown;
169
- } | null;
170
- request: {
171
- method: string;
172
- path: string;
173
- headers: Record<string, string>;
174
- ip: string;
67
+ readonly create?: (data: TCreateInput, ctx: EntityContext) => TCreateInput | Promise<TCreateInput>;
68
+ readonly update?: (data: TUpdateInput, ctx: EntityContext) => TUpdateInput | Promise<TUpdateInput>;
69
+ }
70
+ interface EntityAfterHooks<TResponse = unknown> {
71
+ readonly create?: (result: TResponse, ctx: EntityContext) => void | Promise<void>;
72
+ readonly update?: (prev: TResponse, next: TResponse, ctx: EntityContext) => void | Promise<void>;
73
+ readonly delete?: (row: TResponse, ctx: EntityContext) => void | Promise<void>;
74
+ }
75
+ interface EntityActionDef<
76
+ TInput = unknown,
77
+ TOutput = unknown,
78
+ TResponse = unknown,
79
+ TCtx extends EntityContext = EntityContext
80
+ > {
81
+ readonly method?: string;
82
+ readonly path?: string;
83
+ readonly body: SchemaLike<TInput>;
84
+ readonly response: SchemaLike<TOutput>;
85
+ readonly handler: (input: TInput, ctx: TCtx, row: TResponse | null) => Promise<TOutput>;
86
+ }
87
+ /** Extract column keys from a RelationDef's target table. */
88
+ type RelationColumnKeys<R> = R extends RelationDef<infer TTarget> ? TTarget extends TableDef<infer TCols> ? Extract<keyof TCols, string> : string : string;
89
+ type EntityRelationsConfig<TRelations extends Record<string, RelationDef> = Record<string, RelationDef>> = { [K in keyof TRelations]? : true | false | { [F in RelationColumnKeys<TRelations[K]>]? : true } };
90
+ interface EntityConfig<
91
+ TModel extends ModelDef2 = ModelDef2,
92
+ TActions extends Record<string, EntityActionDef<any, any, any, any>> = {},
93
+ TInject extends Record<string, EntityDefinition> = {}
94
+ > {
95
+ readonly model: TModel;
96
+ readonly inject?: TInject;
97
+ readonly access?: Partial<Record<"list" | "get" | "create" | "update" | "delete" | Extract<keyof NoInfer<TActions>, string>, AccessRule>>;
98
+ readonly before?: {
99
+ readonly create?: (data: TModel["table"]["$create_input"], ctx: EntityContext<TModel, TInject>) => TModel["table"]["$create_input"] | Promise<TModel["table"]["$create_input"]>;
100
+ readonly update?: (data: TModel["table"]["$update_input"], ctx: EntityContext<TModel, TInject>) => TModel["table"]["$update_input"] | Promise<TModel["table"]["$update_input"]>;
175
101
  };
176
- db: Record<string, any>;
177
- services: Record<string, unknown>;
178
- defaultHandler: (data: any) => Promise<TRow>;
179
- }
180
- type AccessRule<TRow> = (row: TRow, ctx: DomainContext<TRow>) => boolean;
181
- interface AccessRules<TRow> {
182
- read?: AccessRule<TRow>;
183
- create?: AccessRule<Partial<TRow>>;
184
- update?: AccessRule<TRow>;
185
- delete?: AccessRule<TRow>;
186
- }
187
- type Result<
188
- T,
189
- E = any
190
- > = {
191
- ok: true;
192
- data: T;
193
- } | {
194
- ok: false;
195
- error: E;
196
- };
197
- interface DomainError {
198
- type: string;
199
- code: string;
200
- message: string;
201
- entity?: string;
202
- field?: string;
203
- }
204
- interface DomainOptions<TEntry extends TableEntry<any, any>> {
205
- type: DomainType;
206
- table: TEntry;
207
- fields?: any;
208
- expose?: any;
209
- access?: AccessRules<any>;
210
- handlers?: any;
211
- actions?: Record<string, any>;
212
- }
213
- interface DomainDefinition2<TEntry extends TableEntry<any, any> = TableEntry<any, any>> {
214
- readonly name: string;
215
- readonly type: DomainType;
216
- readonly table: TEntry;
217
- readonly exposedRelations: Record<string, any>;
218
- readonly access: AccessRules<any>;
219
- readonly handlers: any;
220
- readonly actions: Record<string, any>;
102
+ readonly after?: {
103
+ readonly create?: (result: TModel["table"]["$response"], ctx: EntityContext<TModel, TInject>) => void | Promise<void>;
104
+ readonly update?: (prev: TModel["table"]["$response"], next: TModel["table"]["$response"], ctx: EntityContext<TModel, TInject>) => void | Promise<void>;
105
+ readonly delete?: (row: TModel["table"]["$response"], ctx: EntityContext<TModel, TInject>) => void | Promise<void>;
106
+ };
107
+ readonly actions?: { readonly [K in keyof TActions] : TActions[K] };
108
+ readonly relations?: EntityRelationsConfig<TModel["relations"]>;
221
109
  }
110
+ interface EntityDefinition<TModel extends ModelDef2 = ModelDef2> {
111
+ readonly kind: "entity";
112
+ readonly name: string;
113
+ readonly model: TModel;
114
+ readonly inject: Record<string, EntityDefinition>;
115
+ readonly access: Partial<Record<string, AccessRule>>;
116
+ readonly before: EntityBeforeHooks;
117
+ readonly after: EntityAfterHooks;
118
+ readonly actions: Record<string, EntityActionDef>;
119
+ readonly relations: EntityRelationsConfig<TModel["relations"]>;
120
+ }
121
+ import { SchemaLike as SchemaLike2 } from "@vertz/db";
122
+ /** Extracts the model type from an EntityDefinition */
123
+ type ExtractModel2<T> = T extends EntityDefinition<infer M> ? M : never;
222
124
  /**
223
- * STUB: domain() function for TDD red phase
224
- * This returns a properly shaped, frozen object that passes all structure tests.
225
- * Business logic (CRUD generation, access enforcement, etc.) will be implemented next.
125
+ * Maps an inject config `{ key: EntityDefinition<TModel> }` to
126
+ * `{ key: EntityOperations<TModel> }` for typed ctx.entities access.
226
127
  */
227
- declare function domain<TEntry extends TableEntry<any, any>>(name?: string, options?: DomainOptions<TEntry>): DomainDefinition2<TEntry>;
128
+ type InjectToOperations2<TInject extends Record<string, EntityDefinition> = {}> = { readonly [K in keyof TInject] : EntityOperations<ExtractModel2<TInject[K]>> };
129
+ interface ActionContext<TInject extends Record<string, EntityDefinition> = {}> extends BaseContext {
130
+ /** Typed access to injected entities only */
131
+ readonly entities: InjectToOperations2<TInject>;
132
+ }
133
+ interface ActionActionDef<
134
+ TInput = unknown,
135
+ TOutput = unknown,
136
+ TCtx extends ActionContext = ActionContext
137
+ > {
138
+ readonly method?: string;
139
+ readonly path?: string;
140
+ readonly body: SchemaLike2<TInput>;
141
+ readonly response: SchemaLike2<TOutput>;
142
+ readonly handler: (input: TInput, ctx: TCtx) => Promise<TOutput>;
143
+ }
144
+ interface ActionConfig<
145
+ TActions extends Record<string, ActionActionDef<any, any, any>> = Record<string, ActionActionDef<any, any, any>>,
146
+ TInject extends Record<string, EntityDefinition> = {}
147
+ > {
148
+ readonly inject?: TInject;
149
+ readonly access?: Partial<Record<Extract<keyof NoInfer<TActions>, string>, AccessRule>>;
150
+ readonly actions: { readonly [K in keyof TActions] : TActions[K] };
151
+ }
152
+ interface ActionDefinition {
153
+ readonly kind: "action";
154
+ readonly name: string;
155
+ readonly inject: Record<string, EntityDefinition>;
156
+ readonly access: Partial<Record<string, AccessRule>>;
157
+ readonly actions: Record<string, ActionActionDef>;
158
+ }
159
+ declare function action<
160
+ TInject extends Record<string, EntityDefinition> = {},
161
+ TActions extends Record<string, ActionActionDef<any, any, any>> = Record<string, ActionActionDef<any, any, any>>
162
+ >(name: string, config: ActionConfig<TActions, TInject>): ActionDefinition;
163
+ import { AuthValidationError } from "@vertz/errors";
164
+ import { AuthError, Result as Result2 } from "@vertz/errors";
228
165
  type SessionStrategy = "jwt" | "database" | "hybrid";
229
166
  interface CookieConfig {
230
167
  name?: string;
@@ -262,6 +199,18 @@ interface AuthConfig {
262
199
  jwtAlgorithm?: "HS256" | "HS384" | "HS512" | "RS256";
263
200
  /** Custom claims function for JWT payload */
264
201
  claims?: (user: AuthUser) => Record<string, unknown>;
202
+ /**
203
+ * Whether the app runs in production mode.
204
+ * Controls security enforcement (JWT secret requirement, CSRF validation).
205
+ * Defaults to true when process.env is unavailable (secure-by-default for edge runtimes).
206
+ */
207
+ isProduction?: boolean;
208
+ /**
209
+ * Directory to persist auto-generated dev JWT secret.
210
+ * Defaults to `.vertz` in the current working directory.
211
+ * Only used in non-production mode when jwtSecret is not provided.
212
+ */
213
+ devSecretPath?: string;
265
214
  }
266
215
  interface AuthUser {
267
216
  id: string;
@@ -296,11 +245,11 @@ interface SignInInput {
296
245
  password: string;
297
246
  }
298
247
  interface AuthApi {
299
- signUp: (data: SignUpInput) => Promise<AuthResult<Session>>;
300
- signIn: (data: SignInInput) => Promise<AuthResult<Session>>;
301
- signOut: (ctx: AuthContext) => Promise<AuthResult<void>>;
302
- getSession: (headers: Headers) => Promise<AuthResult<Session | null>>;
303
- refreshSession: (ctx: AuthContext) => Promise<AuthResult<Session>>;
248
+ signUp: (data: SignUpInput) => Promise<Result2<Session, AuthError>>;
249
+ signIn: (data: SignInInput) => Promise<Result2<Session, AuthError>>;
250
+ signOut: (ctx: AuthContext) => Promise<Result2<void, AuthError>>;
251
+ getSession: (headers: Headers) => Promise<Result2<Session | null, AuthError>>;
252
+ refreshSession: (ctx: AuthContext) => Promise<Result2<Session, AuthError>>;
304
253
  }
305
254
  interface AuthInstance {
306
255
  /** HTTP handler for auth routes */
@@ -317,18 +266,6 @@ interface AuthContext {
317
266
  request: Request;
318
267
  ip?: string;
319
268
  }
320
- type AuthResult<T> = {
321
- ok: true;
322
- data: T;
323
- } | {
324
- ok: false;
325
- error: AuthError;
326
- };
327
- interface AuthError {
328
- code: string;
329
- message: string;
330
- status: number;
331
- }
332
269
  interface RateLimitResult {
333
270
  allowed: boolean;
334
271
  remaining: number;
@@ -381,6 +318,112 @@ declare function createAccess(config: AccessConfig): AccessInstance;
381
318
  declare const defaultAccess: AccessInstance;
382
319
  declare function hashPassword(password: string): Promise<string>;
383
320
  declare function verifyPassword(password: string, hash: string): Promise<boolean>;
384
- declare function validatePassword(password: string, requirements?: PasswordRequirements): AuthError | null;
321
+ declare function validatePassword(password: string, requirements?: PasswordRequirements): AuthValidationError | null;
385
322
  declare function createAuth(config: AuthConfig): AuthInstance;
386
- export { vertz, verifyPassword, validatePassword, makeImmutable, hashPassword, domain, defaultAccess, deepFreeze, createServer, createModuleDef, createModule, createMiddleware, createImmutableProxy, createEnv, createAuth, createAccess, VertzException, ValidationException, UnauthorizedException, SignUpInput, SignInInput, SessionStrategy, SessionPayload, SessionConfig, Session, ServiceUnavailableException, ServiceFactory, ServiceDef, ServiceBootInstruction, ServerHandle, ServerAdapter, RouterDef, Result, Resource, ResolveInjectMap, RawRequest, RateLimitResult, RateLimitConfig, PasswordRequirements, NotFoundException, NamedServiceDef, NamedRouterDef, NamedModuleDef, NamedModule, NamedMiddlewareDef, ModuleDef, ModuleBootInstruction, Module, MiddlewareDef, ListenOptions, InternalServerErrorException, InferSchema, Infer2 as Infer, HttpStatusCode, HttpMethod, HandlerCtx, ForbiddenException, ExtractMethods, EnvConfig, EntitlementDefinition, Entitlement, EmailPasswordConfig, DomainType, DomainOptions, DomainError, DomainDefinition2 as DomainDefinition, DomainContext, Deps, DeepReadonly, Ctx, CorsConfig, CookieConfig, ConflictException, BootSequence, BootInstruction, BadRequestException, AuthorizationError, AuthUser, AuthResult, AuthInstance, AuthError, AuthContext, AuthConfig, AuthApi, AppConfig, AppBuilder, AccumulateProvides, AccessRules, AccessRule, AccessInstance, AccessConfig };
323
+ import { AppBuilder, AppConfig } from "@vertz/core";
324
+ import { DatabaseClient, EntityDbAdapter as EntityDbAdapter3, ModelEntry as ModelEntry2 } from "@vertz/db";
325
+ interface ServerConfig extends Omit<AppConfig, "_entityDbFactory" | "entities"> {
326
+ /** Entity definitions created via entity() from @vertz/server */
327
+ entities?: EntityDefinition[];
328
+ /** Standalone action definitions created via action() from @vertz/server */
329
+ actions?: ActionDefinition[];
330
+ /**
331
+ * Database for entity CRUD operations.
332
+ * Accepts either:
333
+ * - A DatabaseClient from createDb() (recommended — auto-bridged per entity)
334
+ * - An EntityDbAdapter (deprecated — simple adapter with get/list/create/update/delete)
335
+ */
336
+ db?: DatabaseClient<Record<string, ModelEntry2>> | EntityDbAdapter3;
337
+ /** @internal Factory to create a DB adapter for each entity. Prefer `db` instead. */
338
+ _entityDbFactory?: (entityDef: EntityDefinition) => EntityDbAdapter3;
339
+ }
340
+ /**
341
+ * Creates an HTTP server with entity route generation.
342
+ * Wraps @vertz/core's createServer to inject entity CRUD handlers.
343
+ */
344
+ declare function createServer(config: ServerConfig): AppBuilder;
345
+ import { EntityForbiddenError, Result as Result3 } from "@vertz/errors";
346
+ /**
347
+ * Evaluates an access rule for the given operation.
348
+ * Returns err(EntityForbiddenError) if access is denied.
349
+ *
350
+ * Accepts BaseContext so both EntityContext and ActionContext can use it.
351
+ *
352
+ * - No rule defined → deny (deny by default)
353
+ * - Rule is false → operation is disabled
354
+ * - Rule is a function → evaluate and deny if returns false
355
+ */
356
+ declare function enforceAccess(operation: string, accessRules: Partial<Record<string, AccessRule>>, ctx: BaseContext, row?: Record<string, unknown>): Promise<Result3<void, EntityForbiddenError>>;
357
+ import { ModelDef as ModelDef3 } from "@vertz/db";
358
+ /**
359
+ * Request info extracted from HTTP context / auth middleware.
360
+ */
361
+ interface RequestInfo {
362
+ readonly userId?: string | null;
363
+ readonly tenantId?: string | null;
364
+ readonly roles?: readonly string[];
365
+ }
366
+ /**
367
+ * Creates an EntityContext from request info, entity operations, and registry proxy.
368
+ */
369
+ declare function createEntityContext<TModel extends ModelDef3 = ModelDef3>(request: RequestInfo, entityOps: EntityOperations<TModel>, registryProxy: Record<string, EntityOperations>): EntityContext<TModel>;
370
+ import { ModelDef as ModelDef4 } from "@vertz/db";
371
+ declare function entity<
372
+ TModel extends ModelDef4,
373
+ TInject extends Record<string, EntityDefinition> = {},
374
+ TActions extends Record<string, EntityActionDef<any, any, TModel["table"]["$response"], EntityContext<TModel, TInject>>> = {}
375
+ >(name: string, config: EntityConfig<TModel, TActions, TInject>): EntityDefinition<TModel>;
376
+ declare class EntityRegistry {
377
+ private readonly entries;
378
+ register(name: string, ops: EntityOperations): void;
379
+ get(name: string): EntityOperations;
380
+ has(name: string): boolean;
381
+ /** Create a Proxy for dot-access: proxy.users.get(id) */
382
+ createProxy(): Record<string, EntityOperations>;
383
+ /**
384
+ * Create a scoped Proxy limited to injected entities only.
385
+ * Accessing a non-injected entity throws at runtime.
386
+ */
387
+ createScopedProxy(inject: Record<string, EntityDefinition>): Record<string, EntityOperations>;
388
+ }
389
+ interface EntityErrorResult {
390
+ status: number;
391
+ body: {
392
+ error: {
393
+ code: string;
394
+ message: string;
395
+ details?: unknown;
396
+ };
397
+ };
398
+ }
399
+ /**
400
+ * Maps an error to a consistent EDA error response.
401
+ * VertzExceptions are mapped to their status code + a readable error code.
402
+ * EntityErrors from @vertz/errors are mapped by their error code.
403
+ * Unknown errors produce a generic 500 response that never leaks internals.
404
+ */
405
+ declare function entityErrorHandler(error: unknown): EntityErrorResult;
406
+ import { TableDef as TableDef2 } from "@vertz/db";
407
+ /**
408
+ * Strips hidden columns from response data.
409
+ * Used after DB reads to remove sensitive fields from API responses.
410
+ */
411
+ declare function stripHiddenFields(table: TableDef2, data: Record<string, unknown>): Record<string, unknown>;
412
+ /**
413
+ * Strips readOnly and primary key columns from input data.
414
+ * Used before DB writes to prevent setting immutable fields.
415
+ */
416
+ declare function stripReadOnlyFields(table: TableDef2, data: Record<string, unknown>): Record<string, unknown>;
417
+ import { EntityRouteEntry } from "@vertz/core";
418
+ interface EntityRouteOptions {
419
+ apiPrefix?: string;
420
+ }
421
+ /**
422
+ * Generates HTTP route entries for a single entity definition.
423
+ *
424
+ * Each entity produces up to 5 CRUD routes + N custom action routes.
425
+ * Operations with no access rule are skipped (deny by default = no route).
426
+ * Operations explicitly disabled (access: false) get a 405 handler.
427
+ */
428
+ declare function generateEntityRoutes(def: EntityDefinition, registry: EntityRegistry, db: EntityDbAdapter2, options?: EntityRouteOptions): EntityRouteEntry[];
429
+ export { vertz, verifyPassword, validatePassword, stripReadOnlyFields, stripHiddenFields, makeImmutable, hashPassword, generateEntityRoutes, entityErrorHandler, entity, enforceAccess, defaultAccess, deepFreeze, createServer, createMiddleware, createImmutableProxy, createEnv, createEntityContext, createCrudHandlers, createAuth, createAccess, action, VertzException, ValidationException, UnauthorizedException, SignUpInput, SignInInput, SessionStrategy, SessionPayload, SessionConfig, Session, ServiceUnavailableException, ServerHandle, ServerConfig, ServerAdapter, Resource, RequestInfo, RawRequest, RateLimitResult, RateLimitConfig, PasswordRequirements, NotFoundException, NamedMiddlewareDef, MiddlewareDef, ListenOptions, ListResult, ListOptions2 as ListOptions, InternalServerErrorException, InferSchema, Infer, HttpStatusCode, HttpMethod, HandlerCtx, ForbiddenException, EnvConfig, EntityRouteOptions, EntityRelationsConfig, EntityRegistry, EntityOperations, EntityErrorResult, EntityDefinition, EntityDbAdapter2 as EntityDbAdapter, EntityContext, EntityConfig, EntityActionDef, EntitlementDefinition, Entitlement, EmailPasswordConfig, Deps, DeepReadonly, Ctx, CrudResult, CrudHandlers, CorsConfig, CookieConfig, ConflictException, BaseContext, BadRequestException, AuthorizationError, AuthUser, AuthInstance, AuthContext, AuthConfig, AuthApi, AppConfig2 as AppConfig, AppBuilder2 as AppBuilder, ActionDefinition, ActionContext, ActionConfig, ActionActionDef, AccumulateProvides, AccessRule, AccessInstance, AccessConfig };