@fuzdev/fuz_app 0.89.0 → 0.91.0
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/auth/CLAUDE.md +7 -0
- package/dist/auth/account_action_specs.d.ts +1 -1
- package/dist/auth/account_route_schema.d.ts +2 -2
- package/dist/auth/account_schema.d.ts +3 -3
- package/dist/auth/admin_action_specs.d.ts +8 -8
- package/dist/auth/all_cell_actions.d.ts +35 -0
- package/dist/auth/all_cell_actions.d.ts.map +1 -0
- package/dist/auth/all_cell_actions.js +32 -0
- package/dist/auth/audit_log_schema.d.ts +1 -1
- package/dist/auth/invite_schema.d.ts +2 -2
- package/dist/auth/signup_route_schema.d.ts +2 -2
- package/dist/auth/signup_route_schema.d.ts.map +1 -1
- package/dist/auth/signup_route_schema.js +2 -2
- package/dist/primitive_schemas.d.ts +27 -1
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +55 -2
- package/dist/server/env.d.ts +1 -1
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +15 -3
- package/dist/testing/CLAUDE.md +135 -10
- package/dist/testing/app_server.d.ts +16 -5
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +3 -1
- package/dist/testing/cross_backend/action_manifest.d.ts +113 -0
- package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -0
- package/dist/testing/cross_backend/action_manifest.js +89 -0
- package/dist/testing/cross_backend/action_manifest_parity.d.ts +67 -0
- package/dist/testing/cross_backend/action_manifest_parity.d.ts.map +1 -0
- package/dist/testing/cross_backend/action_manifest_parity.js +88 -0
- package/dist/testing/cross_backend/capabilities.d.ts +47 -30
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +19 -4
- package/dist/testing/cross_backend/default_backend_configs.d.ts +31 -15
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +30 -14
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +6 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts +81 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -0
- package/dist/testing/cross_backend/full_spine_mount.js +49 -0
- package/dist/testing/cross_backend/identity_parity.d.ts +13 -0
- package/dist/testing/cross_backend/identity_parity.d.ts.map +1 -0
- package/dist/testing/cross_backend/identity_parity.js +465 -0
- package/dist/testing/cross_backend/method_coverage.d.ts +71 -0
- package/dist/testing/cross_backend/method_coverage.d.ts.map +1 -0
- package/dist/testing/cross_backend/method_coverage.js +89 -0
- package/dist/testing/cross_backend/setup.d.ts +21 -0
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +33 -1
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +9 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +76 -2
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +63 -2
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +7 -0
- package/package.json +2 -2
package/dist/auth/CLAUDE.md
CHANGED
|
@@ -211,6 +211,13 @@ auth, ids)`: batched strict relation-read filter. Every relation read —
|
|
|
211
211
|
`cell_item_actions.ts` / `cell_audit_actions.ts` — the relation + ACL +
|
|
212
212
|
audit handlers, exporting `to_grant_json` / `to_field_json` /
|
|
213
213
|
`to_item_json` reused by the `cell_get` bundle.
|
|
214
|
+
- `auth/all_cell_actions.ts` — `create_all_cell_actions(deps, {roles})`, the
|
|
215
|
+
handler-side twin of `all_cell_action_specs` and sibling of
|
|
216
|
+
`create_standard_rpc_actions`: bundles all five cell factories (CRUD + grant
|
|
217
|
+
- field + item + audit) into one list. Reach for this whenever a backend
|
|
218
|
+
mounts the full cell layer — a single source so an HTTP-RPC mount and a WS
|
|
219
|
+
mount (or two backends) can't diverge on which cell verbs they expose.
|
|
220
|
+
Distinct from `create_cell_actions` (the CRUD-only factory it bundles).
|
|
214
221
|
- `auth/cell_audit_metadata.ts` + `cell_grant_audit_metadata.ts` +
|
|
215
222
|
`cell_field_audit_metadata.ts` + `cell_item_audit_metadata.ts` — audit
|
|
216
223
|
metadata envelopes (IDs only), registered via `extra_events:` on the
|
|
@@ -99,7 +99,7 @@ export declare const account_verify_action_spec: {
|
|
|
99
99
|
output: z.ZodObject<{
|
|
100
100
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
101
101
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
102
|
-
email: z.ZodNullable<z.
|
|
102
|
+
email: z.ZodNullable<z.ZodString>;
|
|
103
103
|
email_verified: z.ZodBoolean;
|
|
104
104
|
created_at: z.ZodString;
|
|
105
105
|
}, z.core.$strict>;
|
|
@@ -21,7 +21,7 @@ export declare const AccountStatusOutput: z.ZodObject<{
|
|
|
21
21
|
account: z.ZodObject<{
|
|
22
22
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
23
23
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
24
|
-
email: z.ZodNullable<z.
|
|
24
|
+
email: z.ZodNullable<z.ZodString>;
|
|
25
25
|
email_verified: z.ZodBoolean;
|
|
26
26
|
created_at: z.ZodString;
|
|
27
27
|
}, z.core.$strict>;
|
|
@@ -108,7 +108,7 @@ export declare const account_status_route_shape: {
|
|
|
108
108
|
account: z.ZodObject<{
|
|
109
109
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
110
110
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
111
|
-
email: z.ZodNullable<z.
|
|
111
|
+
email: z.ZodNullable<z.ZodString>;
|
|
112
112
|
email_verified: z.ZodBoolean;
|
|
113
113
|
created_at: z.ZodString;
|
|
114
114
|
}, z.core.$strict>;
|
|
@@ -124,7 +124,7 @@ export interface ApiToken {
|
|
|
124
124
|
export declare const SessionAccountJson: z.ZodObject<{
|
|
125
125
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
126
126
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
127
|
-
email: z.ZodNullable<z.
|
|
127
|
+
email: z.ZodNullable<z.ZodString>;
|
|
128
128
|
email_verified: z.ZodBoolean;
|
|
129
129
|
created_at: z.ZodString;
|
|
130
130
|
}, z.core.$strict>;
|
|
@@ -170,7 +170,7 @@ export type ActorSummaryJson = z.infer<typeof ActorSummaryJson>;
|
|
|
170
170
|
export declare const AdminAccountJson: z.ZodObject<{
|
|
171
171
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
172
172
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
173
|
-
email: z.ZodNullable<z.
|
|
173
|
+
email: z.ZodNullable<z.ZodString>;
|
|
174
174
|
email_verified: z.ZodBoolean;
|
|
175
175
|
created_at: z.ZodString;
|
|
176
176
|
updated_at: z.ZodString;
|
|
@@ -207,7 +207,7 @@ export declare const AdminAccountEntryJson: z.ZodObject<{
|
|
|
207
207
|
account: z.ZodObject<{
|
|
208
208
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
209
209
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
210
|
-
email: z.ZodNullable<z.
|
|
210
|
+
email: z.ZodNullable<z.ZodString>;
|
|
211
211
|
email_verified: z.ZodBoolean;
|
|
212
212
|
created_at: z.ZodString;
|
|
213
213
|
updated_at: z.ZodString;
|
|
@@ -37,7 +37,7 @@ export declare const AdminAccountListOutput: z.ZodObject<{
|
|
|
37
37
|
account: z.ZodObject<{
|
|
38
38
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
39
39
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
40
|
-
email: z.ZodNullable<z.
|
|
40
|
+
email: z.ZodNullable<z.ZodString>;
|
|
41
41
|
email_verified: z.ZodBoolean;
|
|
42
42
|
created_at: z.ZodString;
|
|
43
43
|
updated_at: z.ZodString;
|
|
@@ -184,7 +184,7 @@ export declare const AuditLogRoleGrantHistoryOutput: z.ZodObject<{
|
|
|
184
184
|
export type AuditLogRoleGrantHistoryOutput = z.infer<typeof AuditLogRoleGrantHistoryOutput>;
|
|
185
185
|
/** Input for `invite_create`. At least one of `email` / `username` must be provided. */
|
|
186
186
|
export declare const InviteCreateInput: z.ZodObject<{
|
|
187
|
-
email: z.ZodOptional<z.ZodNullable<z.
|
|
187
|
+
email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
188
188
|
username: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
189
189
|
acting: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
190
190
|
}, z.core.$strict>;
|
|
@@ -194,7 +194,7 @@ export declare const InviteCreateOutput: z.ZodObject<{
|
|
|
194
194
|
ok: z.ZodLiteral<true>;
|
|
195
195
|
invite: z.ZodObject<{
|
|
196
196
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
197
|
-
email: z.ZodNullable<z.
|
|
197
|
+
email: z.ZodNullable<z.ZodString>;
|
|
198
198
|
username: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
199
199
|
claimed_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
200
200
|
claimed_at: z.ZodNullable<z.ZodString>;
|
|
@@ -212,7 +212,7 @@ export type InviteListInput = z.infer<typeof InviteListInput>;
|
|
|
212
212
|
export declare const InviteListOutput: z.ZodObject<{
|
|
213
213
|
invites: z.ZodArray<z.ZodObject<{
|
|
214
214
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
215
|
-
email: z.ZodNullable<z.
|
|
215
|
+
email: z.ZodNullable<z.ZodString>;
|
|
216
216
|
username: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
217
217
|
claimed_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
218
218
|
claimed_at: z.ZodNullable<z.ZodString>;
|
|
@@ -368,7 +368,7 @@ export declare const admin_account_list_action_spec: {
|
|
|
368
368
|
account: z.ZodObject<{
|
|
369
369
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
370
370
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
371
|
-
email: z.ZodNullable<z.
|
|
371
|
+
email: z.ZodNullable<z.ZodString>;
|
|
372
372
|
email_verified: z.ZodBoolean;
|
|
373
373
|
created_at: z.ZodString;
|
|
374
374
|
updated_at: z.ZodString;
|
|
@@ -590,7 +590,7 @@ export declare const invite_create_action_spec: {
|
|
|
590
590
|
};
|
|
591
591
|
side_effects: true;
|
|
592
592
|
input: z.ZodObject<{
|
|
593
|
-
email: z.ZodOptional<z.ZodNullable<z.
|
|
593
|
+
email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
594
594
|
username: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
595
595
|
acting: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
596
596
|
}, z.core.$strict>;
|
|
@@ -598,7 +598,7 @@ export declare const invite_create_action_spec: {
|
|
|
598
598
|
ok: z.ZodLiteral<true>;
|
|
599
599
|
invite: z.ZodObject<{
|
|
600
600
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
601
|
-
email: z.ZodNullable<z.
|
|
601
|
+
email: z.ZodNullable<z.ZodString>;
|
|
602
602
|
username: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
603
603
|
claimed_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
604
604
|
claimed_at: z.ZodNullable<z.ZodString>;
|
|
@@ -632,7 +632,7 @@ export declare const invite_list_action_spec: {
|
|
|
632
632
|
output: z.ZodObject<{
|
|
633
633
|
invites: z.ZodArray<z.ZodObject<{
|
|
634
634
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
635
|
-
email: z.ZodNullable<z.
|
|
635
|
+
email: z.ZodNullable<z.ZodString>;
|
|
636
636
|
username: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
637
637
|
claimed_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
638
638
|
claimed_at: z.ZodNullable<z.ZodString>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RpcAction } from '../actions/action_rpc.js';
|
|
2
|
+
import { type CellActionDeps } from './cell_actions.js';
|
|
3
|
+
import type { RoleSchemaResult } from './role_schema.js';
|
|
4
|
+
/**
|
|
5
|
+
* Options for `create_all_cell_actions`.
|
|
6
|
+
*
|
|
7
|
+
* `roles` flows to the `cell_grant_*` sub-factory — actor-shaped grants are
|
|
8
|
+
* role-validated against this registry, so callers thread their app's role
|
|
9
|
+
* schema (the same one `create_standard_rpc_actions` takes).
|
|
10
|
+
*/
|
|
11
|
+
export interface AllCellActionsOptions {
|
|
12
|
+
readonly roles: RoleSchemaResult;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the full cell RPC action set — CRUD (`create_cell_actions`,
|
|
16
|
+
* which also carries `cell_clone`) + grant ACL + field + item relations +
|
|
17
|
+
* per-cell audit — as a single handler-bound bundle.
|
|
18
|
+
*
|
|
19
|
+
* The handler-side twin of the `all_cell_action_specs` spec bundle and the
|
|
20
|
+
* sibling of `create_standard_rpc_actions`. Assembling the five cell factories
|
|
21
|
+
* here means an HTTP-RPC mount and a WS mount (or two different backends)
|
|
22
|
+
* can't silently diverge on which cell verbs they expose — the
|
|
23
|
+
* `spine_method_coverage` reconciliation gate enforces that the spine's live
|
|
24
|
+
* mount matches its coverage manifest, and this aggregator is the single list
|
|
25
|
+
* every mount draws from.
|
|
26
|
+
*
|
|
27
|
+
* Distinct from `create_cell_actions` (the CRUD-only factory this bundles) —
|
|
28
|
+
* reach for this whenever a backend mounts the complete cell layer.
|
|
29
|
+
*
|
|
30
|
+
* @param deps - `CellActionDeps` (`log`, `audit`, optional `validate_data`)
|
|
31
|
+
* @param options - the role schema for grant validation
|
|
32
|
+
* @returns every cell `RpcAction`, in mount order
|
|
33
|
+
*/
|
|
34
|
+
export declare const create_all_cell_actions: (deps: CellActionDeps, options: AllCellActionsOptions) => Array<RpcAction>;
|
|
35
|
+
//# sourceMappingURL=all_cell_actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all_cell_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/all_cell_actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAsB,KAAK,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAK3E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,cAAc,EACpB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAMjB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { create_cell_actions } from './cell_actions.js';
|
|
2
|
+
import { create_cell_grant_actions } from './cell_grant_actions.js';
|
|
3
|
+
import { create_cell_field_actions } from './cell_field_actions.js';
|
|
4
|
+
import { create_cell_item_actions } from './cell_item_actions.js';
|
|
5
|
+
import { create_cell_audit_actions } from './cell_audit_actions.js';
|
|
6
|
+
/**
|
|
7
|
+
* Build the full cell RPC action set — CRUD (`create_cell_actions`,
|
|
8
|
+
* which also carries `cell_clone`) + grant ACL + field + item relations +
|
|
9
|
+
* per-cell audit — as a single handler-bound bundle.
|
|
10
|
+
*
|
|
11
|
+
* The handler-side twin of the `all_cell_action_specs` spec bundle and the
|
|
12
|
+
* sibling of `create_standard_rpc_actions`. Assembling the five cell factories
|
|
13
|
+
* here means an HTTP-RPC mount and a WS mount (or two different backends)
|
|
14
|
+
* can't silently diverge on which cell verbs they expose — the
|
|
15
|
+
* `spine_method_coverage` reconciliation gate enforces that the spine's live
|
|
16
|
+
* mount matches its coverage manifest, and this aggregator is the single list
|
|
17
|
+
* every mount draws from.
|
|
18
|
+
*
|
|
19
|
+
* Distinct from `create_cell_actions` (the CRUD-only factory this bundles) —
|
|
20
|
+
* reach for this whenever a backend mounts the complete cell layer.
|
|
21
|
+
*
|
|
22
|
+
* @param deps - `CellActionDeps` (`log`, `audit`, optional `validate_data`)
|
|
23
|
+
* @param options - the role schema for grant validation
|
|
24
|
+
* @returns every cell `RpcAction`, in mount order
|
|
25
|
+
*/
|
|
26
|
+
export const create_all_cell_actions = (deps, options) => [
|
|
27
|
+
...create_cell_actions(deps),
|
|
28
|
+
...create_cell_grant_actions({ ...deps, roles: options.roles }),
|
|
29
|
+
...create_cell_field_actions(deps),
|
|
30
|
+
...create_cell_item_actions(deps),
|
|
31
|
+
...create_cell_audit_actions(),
|
|
32
|
+
];
|
|
@@ -83,7 +83,7 @@ export declare const audit_metadata_schemas: Readonly<{
|
|
|
83
83
|
invite_id: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
84
84
|
open_signup: z.ZodOptional<z.ZodBoolean>;
|
|
85
85
|
reason: z.ZodOptional<z.ZodString>;
|
|
86
|
-
email: z.ZodOptional<z.
|
|
86
|
+
email: z.ZodOptional<z.ZodString>;
|
|
87
87
|
}, z.core.$loose>;
|
|
88
88
|
password_change: z.ZodNullable<z.ZodObject<{
|
|
89
89
|
sessions_revoked: z.ZodOptional<z.ZodNumber>;
|
|
@@ -22,7 +22,7 @@ export interface Invite {
|
|
|
22
22
|
/** Zod schema for client-safe invite data. */
|
|
23
23
|
export declare const InviteJson: z.ZodObject<{
|
|
24
24
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
25
|
-
email: z.ZodNullable<z.
|
|
25
|
+
email: z.ZodNullable<z.ZodString>;
|
|
26
26
|
username: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
27
27
|
claimed_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
28
28
|
claimed_at: z.ZodNullable<z.ZodString>;
|
|
@@ -33,7 +33,7 @@ export type InviteJson = z.infer<typeof InviteJson>;
|
|
|
33
33
|
/** Zod schema for invite data with resolved creator/claimer usernames. */
|
|
34
34
|
export declare const InviteWithUsernamesJson: z.ZodObject<{
|
|
35
35
|
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
36
|
-
email: z.ZodNullable<z.
|
|
36
|
+
email: z.ZodNullable<z.ZodString>;
|
|
37
37
|
username: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
38
38
|
claimed_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
39
39
|
claimed_at: z.ZodNullable<z.ZodString>;
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { z } from 'zod';
|
|
15
15
|
import type { RouteSpec } from '../http/route_spec.js';
|
|
16
|
-
/** Input for `POST /signup`. `email` is optional and must match any referenced invite. */
|
|
16
|
+
/** Input for `POST /signup`. `email` is optional (absent or `null` = no email) and must match any referenced invite. */
|
|
17
17
|
export declare const SignupInput: z.ZodObject<{
|
|
18
18
|
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
19
19
|
password: z.ZodString;
|
|
20
|
-
email: z.ZodOptional<z.
|
|
20
|
+
email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
21
21
|
}, z.core.$strict>;
|
|
22
22
|
export type SignupInput = z.infer<typeof SignupInput>;
|
|
23
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAKtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAQrD,
|
|
1
|
+
{"version":3,"file":"signup_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAKtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAQrD,wHAAwH;AACxH,eAAO,MAAM,WAAW;;;;kBAItB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;;;;;;;;;kBAIvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,4EAA4E;AAC5E,MAAM,WAAW,uBAAuB;IACvC,iFAAiF;IACjF,2BAA2B,EAAE,OAAO,CAAC;CACrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,uBAAuB,KAC9B,IAAI,CAAC,SAAS,EAAE,SAAS,CAgB1B,CAAC"}
|
|
@@ -16,11 +16,11 @@ import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
|
16
16
|
import { Username, Email } from '../primitive_schemas.js';
|
|
17
17
|
import { Password } from './password.js';
|
|
18
18
|
import { ERROR_NO_MATCHING_INVITE, ERROR_SIGNUP_CONFLICT, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
|
|
19
|
-
/** Input for `POST /signup`. `email` is optional and must match any referenced invite. */
|
|
19
|
+
/** Input for `POST /signup`. `email` is optional (absent or `null` = no email) and must match any referenced invite. */
|
|
20
20
|
export const SignupInput = z.strictObject({
|
|
21
21
|
username: Username,
|
|
22
22
|
password: Password,
|
|
23
|
-
email: Email.
|
|
23
|
+
email: Email.nullish(),
|
|
24
24
|
});
|
|
25
25
|
/**
|
|
26
26
|
* Output for `POST /signup`.
|
|
@@ -40,16 +40,42 @@ export type Username = z.infer<typeof Username>;
|
|
|
40
40
|
* per-account rate-limit key and DB lookup see a uniform value
|
|
41
41
|
* regardless of casing or surrounding whitespace. Mirrors the storage
|
|
42
42
|
* canonicalization on `Username` so submission and storage agree.
|
|
43
|
+
*
|
|
44
|
+
* The trailing `.refine` rejects a whitespace-only identifier: `.min(1)`
|
|
45
|
+
* runs on the raw string (so `" "` passes it), and without the post-trim
|
|
46
|
+
* check the value would canonicalize to `""` and fall through to a
|
|
47
|
+
* lookup-miss 401 instead of a 400 — an empty identifier is malformed
|
|
48
|
+
* input, not a wrong credential. Keeps the Rust spine's
|
|
49
|
+
* `account_login` (which rejects empty-after-trim) in parity.
|
|
43
50
|
*/
|
|
44
51
|
export declare const UsernameProvided: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
45
52
|
export type UsernameProvided = z.infer<typeof UsernameProvided>;
|
|
53
|
+
/**
|
|
54
|
+
* Maximum email length in **bytes** — RFC 5321 §4.5.3.1.3 path-length limit
|
|
55
|
+
* (the limit is octets). `Email` bounds the UTF-8 byte length, matching the
|
|
56
|
+
* Rust spine's `s.len()` check; for an all-ASCII address bytes == characters.
|
|
57
|
+
*/
|
|
58
|
+
export declare const EMAIL_LENGTH_MAX = 254;
|
|
46
59
|
/**
|
|
47
60
|
* Email validation. Lives here rather than `@fuzdev/fuz_util` because every
|
|
48
61
|
* current consumer pairs it with `Username` (signup, invites, audit log) —
|
|
49
62
|
* keeping the two together avoids a cross-package import for the
|
|
50
63
|
* identity-primitive bundle. Promote to fuz_util if a non-identity consumer
|
|
51
64
|
* surfaces.
|
|
65
|
+
*
|
|
66
|
+
* Deliberately permissive — a structural shape check (`EMAIL_REGEX` plus the
|
|
67
|
+
* `EMAIL_LENGTH_MAX` byte bound), not RFC 5322 conformance or deliverability
|
|
68
|
+
* (real delivery is proven by a confirmation email). Replaces Zod's stricter
|
|
69
|
+
* `z.email()` so the rule is one explicit regex the Rust spine's
|
|
70
|
+
* `is_valid_email` mirrors; `z.email()`'s internal regex (2+ char TLD, no
|
|
71
|
+
* consecutive dots) was brittle to keep in cross-impl parity and rejected
|
|
72
|
+
* addresses like `a@b.c`. The length bound is the UTF-8 **byte** count (RFC
|
|
73
|
+
* 5321 measures octets), so a multibyte address is bounded identically to the
|
|
74
|
+
* Rust spine's `s.len()` rather than diverging on JS's UTF-16 `.length`. No
|
|
75
|
+
* transform: case is preserved and surrounding whitespace is rejected (not
|
|
76
|
+
* trimmed), so storage is verbatim and the case-insensitive lookup rides the
|
|
77
|
+
* DB-side `LOWER(email)`.
|
|
52
78
|
*/
|
|
53
|
-
export declare const Email: z.
|
|
79
|
+
export declare const Email: z.ZodString;
|
|
54
80
|
export type Email = z.infer<typeof Email>;
|
|
55
81
|
//# sourceMappingURL=primitive_schemas.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primitive_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/primitive_schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAItB,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,gKAAgK;AAChK,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,wDAKc,CAAC;AACpC,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEhD
|
|
1
|
+
{"version":3,"file":"primitive_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/primitive_schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAItB,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,gKAAgK;AAChK,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,wDAKc,CAAC;AACpC,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,wDAKmE,CAAC;AACjG,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAqBpC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,KAAK,aAYhB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC"}
|
|
@@ -45,17 +45,70 @@ export const Username = z
|
|
|
45
45
|
* per-account rate-limit key and DB lookup see a uniform value
|
|
46
46
|
* regardless of casing or surrounding whitespace. Mirrors the storage
|
|
47
47
|
* canonicalization on `Username` so submission and storage agree.
|
|
48
|
+
*
|
|
49
|
+
* The trailing `.refine` rejects a whitespace-only identifier: `.min(1)`
|
|
50
|
+
* runs on the raw string (so `" "` passes it), and without the post-trim
|
|
51
|
+
* check the value would canonicalize to `""` and fall through to a
|
|
52
|
+
* lookup-miss 401 instead of a 400 — an empty identifier is malformed
|
|
53
|
+
* input, not a wrong credential. Keeps the Rust spine's
|
|
54
|
+
* `account_login` (which rejects empty-after-trim) in parity.
|
|
48
55
|
*/
|
|
49
56
|
export const UsernameProvided = z
|
|
50
57
|
.string()
|
|
51
58
|
.min(1)
|
|
52
59
|
.max(USERNAME_PROVIDED_LENGTH_MAX)
|
|
53
|
-
.transform((s) => s.trim().toLowerCase())
|
|
60
|
+
.transform((s) => s.trim().toLowerCase())
|
|
61
|
+
.refine((s) => s.length > 0, { message: 'Username must not be empty after trimming whitespace' });
|
|
62
|
+
/**
|
|
63
|
+
* Maximum email length in **bytes** — RFC 5321 §4.5.3.1.3 path-length limit
|
|
64
|
+
* (the limit is octets). `Email` bounds the UTF-8 byte length, matching the
|
|
65
|
+
* Rust spine's `s.len()` check; for an all-ASCII address bytes == characters.
|
|
66
|
+
*/
|
|
67
|
+
export const EMAIL_LENGTH_MAX = 254;
|
|
68
|
+
/**
|
|
69
|
+
* Loose email shape `local@domain.tld`: a non-empty local part, exactly one
|
|
70
|
+
* `@`, a domain with an interior dot (a non-empty label on each side), and
|
|
71
|
+
* no whitespace. Accepts `a@b.c` (single-char TLDs are fine) and
|
|
72
|
+
* `user+tag@a.co`; rejects `notanemail`, `@x.com`, `user@`, `user@host`, and
|
|
73
|
+
* any whitespace.
|
|
74
|
+
*
|
|
75
|
+
* The negated class excludes `\s` **plus** U+0085 (NEL): JS's `\s` omits
|
|
76
|
+
* U+0085 but Unicode `White_Space` (Rust's `char::is_whitespace`) includes
|
|
77
|
+
* it, so excluding it keeps the whitespace set identical across the twins.
|
|
78
|
+
* (The Rust side correspondingly also rejects U+FEFF, which JS `\s` treats as
|
|
79
|
+
* whitespace but Unicode `White_Space` does not — so both spines reject
|
|
80
|
+
* exactly `White_Space ∪ {U+FEFF}`.)
|
|
81
|
+
*/
|
|
82
|
+
const EMAIL_REGEX = /^[^\s\u0085@]+@[^\s\u0085@]+\.[^\s\u0085@]+$/;
|
|
83
|
+
/** UTF-8 encoder for the `EMAIL_LENGTH_MAX` byte (octet) bound. */
|
|
84
|
+
const utf8_encoder = new TextEncoder();
|
|
54
85
|
/**
|
|
55
86
|
* Email validation. Lives here rather than `@fuzdev/fuz_util` because every
|
|
56
87
|
* current consumer pairs it with `Username` (signup, invites, audit log) —
|
|
57
88
|
* keeping the two together avoids a cross-package import for the
|
|
58
89
|
* identity-primitive bundle. Promote to fuz_util if a non-identity consumer
|
|
59
90
|
* surfaces.
|
|
91
|
+
*
|
|
92
|
+
* Deliberately permissive — a structural shape check (`EMAIL_REGEX` plus the
|
|
93
|
+
* `EMAIL_LENGTH_MAX` byte bound), not RFC 5322 conformance or deliverability
|
|
94
|
+
* (real delivery is proven by a confirmation email). Replaces Zod's stricter
|
|
95
|
+
* `z.email()` so the rule is one explicit regex the Rust spine's
|
|
96
|
+
* `is_valid_email` mirrors; `z.email()`'s internal regex (2+ char TLD, no
|
|
97
|
+
* consecutive dots) was brittle to keep in cross-impl parity and rejected
|
|
98
|
+
* addresses like `a@b.c`. The length bound is the UTF-8 **byte** count (RFC
|
|
99
|
+
* 5321 measures octets), so a multibyte address is bounded identically to the
|
|
100
|
+
* Rust spine's `s.len()` rather than diverging on JS's UTF-16 `.length`. No
|
|
101
|
+
* transform: case is preserved and surrounding whitespace is rejected (not
|
|
102
|
+
* trimmed), so storage is verbatim and the case-insensitive lookup rides the
|
|
103
|
+
* DB-side `LOWER(email)`.
|
|
60
104
|
*/
|
|
61
|
-
export const Email = z
|
|
105
|
+
export const Email = z
|
|
106
|
+
.string()
|
|
107
|
+
.regex(EMAIL_REGEX)
|
|
108
|
+
// `s.length` (UTF-16 units) is a cheap upper bound that short-circuits the
|
|
109
|
+
// `encode` on absurd inputs: a valid (<= 254-byte) address is necessarily
|
|
110
|
+
// <= 254 code units, so this never rejects one — the byte count is the real
|
|
111
|
+
// RFC-5321 octet bound, matching the Rust spine's `s.len()`.
|
|
112
|
+
.refine((s) => s.length <= EMAIL_LENGTH_MAX && utf8_encoder.encode(s).length <= EMAIL_LENGTH_MAX, {
|
|
113
|
+
message: `Email must be at most ${EMAIL_LENGTH_MAX} bytes`,
|
|
114
|
+
});
|
package/dist/server/env.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export declare const BaseServerEnv: z.ZodObject<{
|
|
|
33
33
|
PUBLIC_FUZ_CONTACT_EMAIL: z.ZodOptional<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>>;
|
|
34
34
|
FUZ_BOOTSTRAP_TOKEN_PATH: z.ZodOptional<z.ZodString>;
|
|
35
35
|
SMTP_HOST: z.ZodOptional<z.ZodString>;
|
|
36
|
-
SMTP_USER: z.ZodOptional<z.
|
|
36
|
+
SMTP_USER: z.ZodOptional<z.ZodString>;
|
|
37
37
|
SMTP_PASSWORD: z.ZodOptional<z.ZodString>;
|
|
38
38
|
FUZ_FACTS_DIR: z.ZodDefault<z.ZodString>;
|
|
39
39
|
FUZ_FACTS_X_ACCEL_REDIRECT_PREFIX: z.ZodOptional<z.ZodString>;
|
package/dist/server/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAG1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAG1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;kBAoDxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,wBAAwB,GAAG,qBAAqB,CAAC;IACxD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;AAE9E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,sBA4BxD,CAAC"}
|
package/dist/server/env.js
CHANGED
|
@@ -21,7 +21,13 @@ import { parse_allowed_origins } from '../http/origin.js';
|
|
|
21
21
|
*/
|
|
22
22
|
export const BaseServerEnv = z.strictObject({
|
|
23
23
|
NODE_ENV: z.enum(['development', 'production']).meta({ description: 'Runtime environment mode' }),
|
|
24
|
-
PORT: z.coerce
|
|
24
|
+
PORT: z.coerce
|
|
25
|
+
.number()
|
|
26
|
+
.int()
|
|
27
|
+
.min(1)
|
|
28
|
+
.max(65535)
|
|
29
|
+
.default(4040)
|
|
30
|
+
.meta({ description: 'HTTP server port' }),
|
|
25
31
|
HOST: z.string().default('localhost').meta({ description: 'HTTP server bind address' }),
|
|
26
32
|
DATABASE_URL: z.string().min(1).meta({
|
|
27
33
|
description: 'Database URL (postgres://, file://, or memory://)',
|
|
@@ -45,10 +51,16 @@ export const BaseServerEnv = z.strictObject({
|
|
|
45
51
|
.optional()
|
|
46
52
|
.meta({ description: 'Path to one-shot admin bootstrap token', sensitivity: 'secret' }),
|
|
47
53
|
SMTP_HOST: z.string().optional().meta({ description: 'SMTP server hostname' }),
|
|
54
|
+
// SMTP usernames are frequently not emails (SendGrid uses "apikey",
|
|
55
|
+
// AWS SES / Postmark use token-style credentials), so validate as a plain
|
|
56
|
+
// string — `z.email()` here would reject valid provider configs. Marked
|
|
57
|
+
// `secret`: those token-style usernames are credentials (Postmark reuses
|
|
58
|
+
// the server token as both user and password), so the value is masked in
|
|
59
|
+
// the startup summary / logs like `SMTP_PASSWORD`.
|
|
48
60
|
SMTP_USER: z
|
|
49
|
-
.
|
|
61
|
+
.string()
|
|
50
62
|
.optional()
|
|
51
|
-
.meta({ description: 'SMTP authentication username' }),
|
|
63
|
+
.meta({ description: 'SMTP authentication username', sensitivity: 'secret' }),
|
|
52
64
|
SMTP_PASSWORD: z
|
|
53
65
|
.string()
|
|
54
66
|
.optional()
|