@constructive-io/errors 0.9.0 → 0.10.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/README.md CHANGED
@@ -15,6 +15,9 @@ service or client without pulling in pgpm.
15
15
  registry, e.g. `throw errors.MODULE_NOT_FOUND({ name })`.
16
16
  - **`classify(code)`** — `public` or `internal`; unknown codes are `internal`
17
17
  (fail safe) so transports never leak unregistered errors.
18
+ - **`httpStatusFor(code)`** — the canonical code → HTTP status mapping, so no
19
+ transport keeps its own table. Unregistered codes answer `500` *and are
20
+ reported*, never silently.
18
21
 
19
22
  ```ts
20
23
  import { parse, format, errors, classify } from '@constructive-io/errors';
@@ -46,6 +49,32 @@ throw errors.ACCOUNT_EXISTS();
46
49
  pgpm CLI codes). These override the generated entries.
47
50
  - Unregistered codes still `parse()` and are classified `internal` (masked).
48
51
 
52
+ ## HTTP status
53
+
54
+ Every registry entry carries `http`, so an HTTP surface never needs its own
55
+ code → status table: `toError(err).http`, or `httpStatusFor(code)` when all you
56
+ have is a code.
57
+
58
+ ```ts
59
+ import { httpStatusFor, setUnmappedStatusReporter, toError } from '@constructive-io/errors';
60
+
61
+ setUnmappedStatusReporter(code => log.warn({ code }, 'error code has no HTTP status'));
62
+
63
+ const err = toError(caught);
64
+ res.status(err.http).json(err.toExtensions());
65
+
66
+ httpStatusFor('ACCOUNT_DISABLED'); // { status: 403, mapped: true }
67
+ httpStatusFor('BRAND_NEW_CODE'); // { status: 500, mapped: false } + one report
68
+ ```
69
+
70
+ `mapped: false` is the one case where a 500 does not mean "the server broke" —
71
+ it means the code never reached the registry. That is the failure mode this
72
+ exists to make loud: a refusal that is plainly a 403 or a 409 answering 500
73
+ looks like a crash, and the codes most likely to be missing are the newest ones.
74
+ When a constructive-db release adds codes, refresh the registry (below);
75
+ `__tests__/registry-sync.test.ts` fails if the snapshot and the generated
76
+ registry disagree.
77
+
49
78
  ## Regenerating the full registry
50
79
 
51
80
  The generated layer is produced from a committed audit snapshot:
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * GENERATED FILE — DO NOT EDIT BY HAND.
3
3
  *
4
- * Source of truth: the constructive-db error audit (531 distinct codes
4
+ * Source of truth: the constructive-db error audit (546 distinct codes
5
5
  * raised via EXCEPTION/THROW across deploy sources + generated output).
6
6
  * Regenerate with `python3 scripts/generate-registry.py` (see README.md).
7
7
  *
@@ -9,7 +9,7 @@
9
9
  * codes carry their raw message (with %-args rendered as {{argN}}). Curated
10
10
  * entries in `registry.ts` override anything here (typed context + refined copy).
11
11
  *
12
- * Counts: 531 total, 401 public, 130 internal.
12
+ * Counts: 546 total, 410 public, 136 internal.
13
13
  */
14
14
  import { type DefinedError } from '../define';
15
15
  import type { ErrorContext } from '../types';
@@ -24,4 +24,4 @@ export declare const generatedRegistry: Record<string, DefinedError<ErrorContext
24
24
  /** Audit metadata for each generated code. */
25
25
  export declare const GENERATED_CODE_META: Record<string, GeneratedCodeMeta>;
26
26
  /** Total number of codes collected from constructive-db. */
27
- export declare const GENERATED_CODE_COUNT = 531;
27
+ export declare const GENERATED_CODE_COUNT = 546;
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * GENERATED FILE — DO NOT EDIT BY HAND.
4
4
  *
5
- * Source of truth: the constructive-db error audit (531 distinct codes
5
+ * Source of truth: the constructive-db error audit (546 distinct codes
6
6
  * raised via EXCEPTION/THROW across deploy sources + generated output).
7
7
  * Regenerate with `python3 scripts/generate-registry.py` (see README.md).
8
8
  *
@@ -10,14 +10,14 @@
10
10
  * codes carry their raw message (with %-args rendered as {{argN}}). Curated
11
11
  * entries in `registry.ts` override anything here (typed context + refined copy).
12
12
  *
13
- * Counts: 531 total, 401 public, 130 internal.
13
+ * Counts: 546 total, 410 public, 136 internal.
14
14
  */
15
15
  import { defineError } from '../define';
16
16
  /** Every constructive-db error code, keyed by code. */
17
17
  export const generatedRegistry = {
18
- 'ACCOUNT_DISABLED': defineError({ code: 'ACCOUNT_DISABLED', class: 'public', http: 403, message: 'Your account has been disabled. Please contact support for assistance.' }),
19
- 'ACCOUNT_EXISTS': defineError({ code: 'ACCOUNT_EXISTS', class: 'public', http: 409, message: 'An account with this email already exists. Please sign in or use a different email.' }),
20
- 'ACCOUNT_LOCKED_EXCEED_ATTEMPTS': defineError({ code: 'ACCOUNT_LOCKED_EXCEED_ATTEMPTS', class: 'public', http: 423, message: 'Your account has been temporarily locked due to too many failed login attempts. Please try again later or reset your password.' }),
18
+ 'ACCOUNT_DISABLED': defineError({ code: 'ACCOUNT_DISABLED', class: 'public', http: 403, message: 'Account disabled.' }),
19
+ 'ACCOUNT_EXISTS': defineError({ code: 'ACCOUNT_EXISTS', class: 'public', http: 409, message: 'Account exists.' }),
20
+ 'ACCOUNT_LOCKED_EXCEED_ATTEMPTS': defineError({ code: 'ACCOUNT_LOCKED_EXCEED_ATTEMPTS', class: 'public', http: 423, message: 'Account locked exceed attempts.' }),
21
21
  'ACCOUNT_NOT_FOUND': defineError({ code: 'ACCOUNT_NOT_FOUND', class: 'public', http: 404, message: 'Account not found.' }),
22
22
  'ALREADY_SCHEDULED': defineError({ code: 'ALREADY_SCHEDULED', class: 'public', http: 409, message: 'Already scheduled.' }),
23
23
  'ALTER_TABLE_ADD_COLUMN': defineError({ code: 'ALTER_TABLE_ADD_COLUMN', class: 'internal', http: 500, message: 'ALTER_TABLE_ADD_COLUMN: a column cannot be both generated and an identity column' }),
@@ -27,6 +27,8 @@ export const generatedRegistry = {
27
27
  'APPLY_RLS': defineError({ code: 'APPLY_RLS', class: 'internal', http: 500, message: 'Apply rls.' }),
28
28
  'APPLY_RLS_BAD_ARGS': defineError({ code: 'APPLY_RLS_BAD_ARGS', class: 'internal', http: 500, message: 'Apply rls bad args.' }),
29
29
  'APPLY_TABLE_STEP_UP_TABLE_NOT_FOUND': defineError({ code: 'APPLY_TABLE_STEP_UP_TABLE_NOT_FOUND', class: 'public', http: 404, message: 'Apply table step up table not found.' }),
30
+ 'APP_INSTALLATION_MISSING': defineError({ code: 'APP_INSTALLATION_MISSING', class: 'public', http: 400, message: 'App installation missing.' }),
31
+ 'APP_INSTALL_NO_SERVICE': defineError({ code: 'APP_INSTALL_NO_SERVICE', class: 'public', http: 400, message: 'App install no service.' }),
30
32
  'ASSIGN_PROFILES_PERMISSION_REQUIRED': defineError({ code: 'ASSIGN_PROFILES_PERMISSION_REQUIRED', class: 'public', http: 403, message: 'Assign profiles permission required.' }),
31
33
  'AUTHZ_COLUMN_SECURITY_AUTHZ_NODE_REQUIRED': defineError({ code: 'AUTHZ_COLUMN_SECURITY_AUTHZ_NODE_REQUIRED', class: 'public', http: 400, message: 'Authz column security authz node required.' }),
32
34
  'AUTHZ_COLUMN_SECURITY_COLUMNS_REQUIRED': defineError({ code: 'AUTHZ_COLUMN_SECURITY_COLUMNS_REQUIRED', class: 'public', http: 400, message: 'Authz column security columns required.' }),
@@ -62,6 +64,7 @@ export const generatedRegistry = {
62
64
  'BAD_PRIVILEGE_FOR_POLICY': defineError({ code: 'BAD_PRIVILEGE_FOR_POLICY', class: 'public', http: 400, message: 'Bad privilege for policy.' }),
63
65
  'BAD_RLS_EXPRESSION': defineError({ code: 'BAD_RLS_EXPRESSION', class: 'public', http: 400, message: 'Bad rls expression.' }),
64
66
  'BAD_VIEW_EXPRESSION': defineError({ code: 'BAD_VIEW_EXPRESSION', class: 'public', http: 400, message: 'Bad view expression.' }),
67
+ 'BEHAVIOR_FRAGMENT_EMPTY': defineError({ code: 'BEHAVIOR_FRAGMENT_EMPTY', class: 'internal', http: 500, message: 'Behavior fragment empty.' }),
65
68
  'BM25': defineError({ code: 'BM25', class: 'public', http: 400, message: 'Bm25.' }),
66
69
  'BOOTSTRAP_DATABASE_MISSING': defineError({ code: 'BOOTSTRAP_DATABASE_MISSING', class: 'internal', http: 500, message: 'Bootstrap database missing.' }),
67
70
  'BOOTSTRAP_SOURCE_MISSING': defineError({ code: 'BOOTSTRAP_SOURCE_MISSING', class: 'internal', http: 500, message: 'Bootstrap source missing.' }),
@@ -98,6 +101,7 @@ export const generatedRegistry = {
98
101
  'CROSS_DATABASE_CHECK': defineError({ code: 'CROSS_DATABASE_CHECK', class: 'public', http: 400, message: 'Cross database check.' }),
99
102
  'CROSS_DATABASE_REF': defineError({ code: 'CROSS_DATABASE_REF', class: 'public', http: 400, message: 'Cross database ref.' }),
100
103
  'CSRF_TOKEN_REQUIRED': defineError({ code: 'CSRF_TOKEN_REQUIRED', class: 'public', http: 401, message: 'Csrf token required.' }),
104
+ 'DATABASE_CLAIM_REQUIRED': defineError({ code: 'DATABASE_CLAIM_REQUIRED', class: 'internal', http: 500, message: 'Database claim required.' }),
101
105
  'DATABASE_FIELD_RESERVED_WORD': defineError({ code: 'DATABASE_FIELD_RESERVED_WORD', class: 'public', http: 400, message: 'Database field reserved word.' }),
102
106
  'DATABASE_NOT_FOUND': defineError({ code: 'DATABASE_NOT_FOUND', class: 'public', http: 404, message: 'Database not found.' }),
103
107
  'DATABASE_SETTINGS_TABLES_SCOPE_REQUIRED': defineError({ code: 'DATABASE_SETTINGS_TABLES_SCOPE_REQUIRED', class: 'public', http: 400, message: 'Database settings tables scope required.' }),
@@ -129,6 +133,10 @@ export const generatedRegistry = {
129
133
  'DATA_GENERATED_TABLE_NOT_FOUND': defineError({ code: 'DATA_GENERATED_TABLE_NOT_FOUND', class: 'public', http: 404, message: 'Data generated table not found.' }),
130
134
  'DATA_GENERATED_TARGET_REQUIRED': defineError({ code: 'DATA_GENERATED_TARGET_REQUIRED', class: 'public', http: 400, message: 'Data generated target required.' }),
131
135
  'DATA_GENERATED_UNKNOWN_KIND': defineError({ code: 'DATA_GENERATED_UNKNOWN_KIND', class: 'public', http: 400, message: 'Data generated unknown kind.' }),
136
+ 'DATA_HISTORY_NO_COPY_FIELDS': defineError({ code: 'DATA_HISTORY_NO_COPY_FIELDS', class: 'public', http: 400, message: 'Data history no copy fields.' }),
137
+ 'DATA_HISTORY_ON_HISTORY_TABLE': defineError({ code: 'DATA_HISTORY_ON_HISTORY_TABLE', class: 'public', http: 400, message: 'Data history on history table.' }),
138
+ 'DATA_HISTORY_REQUIRES_PK': defineError({ code: 'DATA_HISTORY_REQUIRES_PK', class: 'public', http: 403, message: 'Data history requires pk.' }),
139
+ 'DATA_HISTORY_TABLE_NOT_FOUND': defineError({ code: 'DATA_HISTORY_TABLE_NOT_FOUND', class: 'public', http: 404, message: 'Data history table not found.' }),
132
140
  'DATA_I18N_FIELDS_REQUIRED': defineError({ code: 'DATA_I18N_FIELDS_REQUIRED', class: 'public', http: 400, message: 'Data i18n fields required.' }),
133
141
  'DATA_I18N_FIELD_NOT_FOUND': defineError({ code: 'DATA_I18N_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data i18n field not found.' }),
134
142
  'DATA_I18N_MODULE_NOT_PROVISIONED': defineError({ code: 'DATA_I18N_MODULE_NOT_PROVISIONED', class: 'public', http: 400, message: 'Data i18n module not provisioned.' }),
@@ -154,6 +162,7 @@ export const generatedRegistry = {
154
162
  'DATA_JOB_TRIGGER_CONDITIONS_WATCH_FIELDS_CONFLICT': defineError({ code: 'DATA_JOB_TRIGGER_CONDITIONS_WATCH_FIELDS_CONFLICT', class: 'public', http: 400, message: 'Data job trigger conditions watch fields conflict.' }),
155
163
  'DATA_JOB_TRIGGER_CONDITION_FIELD_NOT_FOUND': defineError({ code: 'DATA_JOB_TRIGGER_CONDITION_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data job trigger condition field not found.' }),
156
164
  'DATA_JOB_TRIGGER_CONDITION_FIELD_WATCH_FIELDS_CONFLICT': defineError({ code: 'DATA_JOB_TRIGGER_CONDITION_FIELD_WATCH_FIELDS_CONFLICT', class: 'public', http: 400, message: 'Data job trigger condition field watch fields conflict.' }),
165
+ 'DATA_JOB_TRIGGER_DATABASE_ID_FIELD_NOT_FOUND': defineError({ code: 'DATA_JOB_TRIGGER_DATABASE_ID_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data job trigger database id field not found.' }),
157
166
  'DATA_JOB_TRIGGER_ENTITY_FIELD_NOT_FOUND': defineError({ code: 'DATA_JOB_TRIGGER_ENTITY_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data job trigger entity field not found.' }),
158
167
  'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_NOT_FOUND': defineError({ code: 'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data job trigger entity lookup obj field not found.' }),
159
168
  'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_REQUIRED': defineError({ code: 'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_REQUIRED', class: 'public', http: 400, message: 'Data job trigger entity lookup obj field required.' }),
@@ -205,7 +214,7 @@ export const generatedRegistry = {
205
214
  'DOMAIN_ROOT_NOT_PUBLISHED': defineError({ code: 'DOMAIN_ROOT_NOT_PUBLISHED', class: 'internal', http: 500, message: 'Domain root not published.' }),
206
215
  'DOMAIN_VERIFY_BAD_METHOD': defineError({ code: 'DOMAIN_VERIFY_BAD_METHOD', class: 'internal', http: 500, message: 'DOMAIN_VERIFY_BAD_METHOD: unsupported verification method {{arg0}}', positional: ['arg0'] }),
207
216
  'DOMAIN_VERIFY_NO_CHALLENGE': defineError({ code: 'DOMAIN_VERIFY_NO_CHALLENGE', class: 'internal', http: 500, message: 'DOMAIN_VERIFY_NO_CHALLENGE: no outstanding {{arg0}} challenge for managed_domain {{arg1}}', positional: ['arg0', 'arg1'] }),
208
- 'EMAIL_NOT_VERIFIED': defineError({ code: 'EMAIL_NOT_VERIFIED', class: 'public', http: 400, message: 'Please verify your email before accepting this invitation.' }),
217
+ 'EMAIL_NOT_VERIFIED': defineError({ code: 'EMAIL_NOT_VERIFIED', class: 'public', http: 400, message: 'Email not verified.' }),
209
218
  'ENTITY_NOT_FOUND': defineError({ code: 'ENTITY_NOT_FOUND', class: 'internal', http: 500, message: 'Entity not found.' }),
210
219
  'ENTITY_TYPE_NOT_FOUND': defineError({ code: 'ENTITY_TYPE_NOT_FOUND', class: 'internal', http: 500, message: 'Entity type not found.' }),
211
220
  'ENTITY_TYPE_PROVISION': defineError({ code: 'ENTITY_TYPE_PROVISION', class: 'internal', http: 500, message: 'Entity type provision.' }),
@@ -238,10 +247,11 @@ export const generatedRegistry = {
238
247
  'EVENT_TRACKER_UNKNOWN_EVENT': defineError({ code: 'EVENT_TRACKER_UNKNOWN_EVENT', class: 'public', http: 400, message: 'Event tracker unknown event.' }),
239
248
  'EVENT_TRACKER_WATCH_FIELD_NOT_FOUND': defineError({ code: 'EVENT_TRACKER_WATCH_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Event tracker watch field not found.' }),
240
249
  'EXECUTION_NOT_FOUND': defineError({ code: 'EXECUTION_NOT_FOUND', class: 'internal', http: 500, message: 'Execution not found.' }),
241
- 'EXPIRED_TOKEN': defineError({ code: 'EXPIRED_TOKEN', class: 'public', http: 400, message: 'This reset link has expired. Please request a new one.' }),
250
+ 'EXPIRED_TOKEN': defineError({ code: 'EXPIRED_TOKEN', class: 'internal', http: 500, message: 'Expired token.' }),
242
251
  'EXTERNAL_MEMBERS_NOT_ALLOWED': defineError({ code: 'EXTERNAL_MEMBERS_NOT_ALLOWED', class: 'public', http: 403, message: 'External members not allowed.' }),
243
252
  'FEATURE_DISABLED': defineError({ code: 'FEATURE_DISABLED', class: 'public', http: 403, message: 'Feature disabled.', positional: ['arg0'] }),
244
253
  'FIELDS_MISMATCH': defineError({ code: 'FIELDS_MISMATCH', class: 'public', http: 400, message: 'Fields mismatch.' }),
254
+ 'FOREIGN_KEY_CONSTRAINT_NOT_FOUND': defineError({ code: 'FOREIGN_KEY_CONSTRAINT_NOT_FOUND', class: 'internal', http: 500, message: 'Foreign key constraint not found.' }),
245
255
  'FUNCTION_GRAPH_NOT_FOUND': defineError({ code: 'FUNCTION_GRAPH_NOT_FOUND', class: 'internal', http: 500, message: 'Function graph not found.' }),
246
256
  'FUNCTION_SCHEDULES': defineError({ code: 'FUNCTION_SCHEDULES', class: 'internal', http: 500, message: 'FUNCTION_SCHEDULES: function definition does not allow the cron access channel' }),
247
257
  'FUNCTION_SCHEDULES_INTERVAL': defineError({ code: 'FUNCTION_SCHEDULES_INTERVAL', class: 'internal', http: 500, message: 'FUNCTION_SCHEDULES_INTERVAL (min_schedule_interval)' }),
@@ -280,6 +290,7 @@ export const generatedRegistry = {
280
290
  'HIERARCHY_DEPTH_EXCEEDED': defineError({ code: 'HIERARCHY_DEPTH_EXCEEDED', class: 'internal', http: 500, message: 'Hierarchy depth exceeded.' }),
281
291
  'HIERARCHY_INACTIVE_MEMBER': defineError({ code: 'HIERARCHY_INACTIVE_MEMBER', class: 'internal', http: 500, message: 'HIERARCHY_INACTIVE_MEMBER: Cannot add user {{arg0}} to hierarchy - user must be an active member of the organization first', positional: ['arg0'] }),
282
292
  'HIERARCHY_MEMBER_IN_USE': defineError({ code: 'HIERARCHY_MEMBER_IN_USE', class: 'internal', http: 500, message: 'HIERARCHY_MEMBER_IN_USE: Cannot deactivate user {{arg0}} - user must be removed from the organization hierarchy first', positional: ['arg0'] }),
293
+ 'HISTORY': defineError({ code: 'HISTORY', class: 'internal', http: 500, message: 'HISTORY: history {{arg0}}.{{arg1}} registration has no copy_fields', positional: ['arg0', 'arg1'] }),
283
294
  'HOSTNAME_BINDING_SYNC': defineError({ code: 'HOSTNAME_BINDING_SYNC', class: 'internal', http: 500, message: 'HOSTNAME_BINDING_SYNC: unknown event {{arg0}}, expected UPSERT or DELETE', positional: ['arg0'] }),
284
295
  'HTTP_ROUTE_TARGET_NOT_FOUND': defineError({ code: 'HTTP_ROUTE_TARGET_NOT_FOUND', class: 'internal', http: 500, message: 'HTTP_ROUTE_TARGET_NOT_FOUND:' }),
285
296
  'HTTP_ROUTE_TARGET_OUTSIDE_SCOPE': defineError({ code: 'HTTP_ROUTE_TARGET_OUTSIDE_SCOPE', class: 'internal', http: 500, message: 'HTTP_ROUTE_TARGET_OUTSIDE_SCOPE:' }),
@@ -296,7 +307,7 @@ export const generatedRegistry = {
296
307
  'IMMUTABLE_PROPERTY': defineError({ code: 'IMMUTABLE_PROPERTY', class: 'internal', http: 500, message: 'Immutable property.' }),
297
308
  'IMMUTABLE_PROPS': defineError({ code: 'IMMUTABLE_PROPS', class: 'public', http: 403, message: 'Immutable props.' }),
298
309
  'IMMUTABLE_TIMESTAMPS': defineError({ code: 'IMMUTABLE_TIMESTAMPS', class: 'public', http: 403, message: 'Immutable timestamps.' }),
299
- 'INCORRECT_PASSWORD': defineError({ code: 'INCORRECT_PASSWORD', class: 'public', http: 401, message: 'The password you entered is incorrect. Please try again.' }),
310
+ 'INCORRECT_PASSWORD': defineError({ code: 'INCORRECT_PASSWORD', class: 'public', http: 401, message: 'Incorrect password.' }),
300
311
  'INFRA_API_NOT_PROVISIONED': defineError({ code: 'INFRA_API_NOT_PROVISIONED', class: 'public', http: 400, message: 'Infra api not provisioned.' }),
301
312
  'INSERT_GRAPH_EXECUTION_MODULE': defineError({ code: 'INSERT_GRAPH_EXECUTION_MODULE', class: 'internal', http: 500, message: 'Insert graph execution module.' }),
302
313
  'INTERNAL_ERROR': defineError({ code: 'INTERNAL_ERROR', class: 'internal', http: 500, message: 'Internal error.' }),
@@ -312,12 +323,12 @@ export const generatedRegistry = {
312
323
  'INVALID_MFA_CHALLENGE': defineError({ code: 'INVALID_MFA_CHALLENGE', class: 'public', http: 403, message: 'Invalid mfa challenge.' }),
313
324
  'INVALID_MFA_LEVEL': defineError({ code: 'INVALID_MFA_LEVEL', class: 'public', http: 403, message: 'Invalid mfa level.' }),
314
325
  'INVALID_ORGANIZATION': defineError({ code: 'INVALID_ORGANIZATION', class: 'public', http: 400, message: 'Invalid organization.' }),
315
- 'INVALID_TOKEN': defineError({ code: 'INVALID_TOKEN', class: 'public', http: 401, message: 'This reset link is invalid. Please request a new one.' }),
326
+ 'INVALID_TOKEN': defineError({ code: 'INVALID_TOKEN', class: 'public', http: 401, message: 'Invalid token.' }),
316
327
  'INVALID_USER': defineError({ code: 'INVALID_USER', class: 'public', http: 400, message: 'Invalid user.' }),
317
328
  'INVALID_WORKER_ID': defineError({ code: 'INVALID_WORKER_ID', class: 'internal', http: 500, message: 'Invalid worker id.' }),
318
- 'INVITE_EMAIL_NOT_FOUND': defineError({ code: 'INVITE_EMAIL_NOT_FOUND', class: 'public', http: 404, message: 'This invitation was sent to a different email address.' }),
319
- 'INVITE_LIMIT': defineError({ code: 'INVITE_LIMIT', class: 'public', http: 429, message: 'This invitation has reached its usage limit.' }),
320
- 'INVITE_NOT_FOUND': defineError({ code: 'INVITE_NOT_FOUND', class: 'public', http: 404, message: 'This invitation was not found.' }),
329
+ 'INVITE_EMAIL_NOT_FOUND': defineError({ code: 'INVITE_EMAIL_NOT_FOUND', class: 'public', http: 404, message: 'Invite email not found.' }),
330
+ 'INVITE_LIMIT': defineError({ code: 'INVITE_LIMIT', class: 'public', http: 429, message: 'Invite limit.' }),
331
+ 'INVITE_NOT_FOUND': defineError({ code: 'INVITE_NOT_FOUND', class: 'public', http: 404, message: 'Invite not found.' }),
321
332
  'INVOCATION_QUOTA_GATE': defineError({ code: 'INVOCATION_QUOTA_GATE', class: 'internal', http: 500, message: 'INVOCATION_QUOTA_GATE: quota_schema and quota_function are required — the generator must only build this trigger when billing is provisioned' }),
322
333
  'INVOCATION_USAGE': defineError({ code: 'INVOCATION_USAGE', class: 'internal', http: 500, message: 'INVOCATION_USAGE: billing_schema and record_usage_fn are required — the generator must only build this trigger when billing is provisioned' }),
323
334
  'LIMIT_ENFORCE_AGGREGATE_ENTITY_FIELD_NOT_FOUND': defineError({ code: 'LIMIT_ENFORCE_AGGREGATE_ENTITY_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Limit enforce aggregate entity field not found.' }),
@@ -401,10 +412,11 @@ export const generatedRegistry = {
401
412
  'ORG_API_KEY_NOT_FOUND': defineError({ code: 'ORG_API_KEY_NOT_FOUND', class: 'public', http: 404, message: 'Org api key not found.' }),
402
413
  'OWNER_FIELD_NOT_FOUND': defineError({ code: 'OWNER_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Owner field not found.' }),
403
414
  'OWNER_FIELD_NOT_IN_OWNER_TABLE': defineError({ code: 'OWNER_FIELD_NOT_IN_OWNER_TABLE', class: 'public', http: 400, message: 'Owner field not in owner table.' }),
415
+ 'PAGES_MODULE': defineError({ code: 'PAGES_MODULE', class: 'internal', http: 500, message: 'Pages module.' }),
404
416
  'PARENT_ENTITY_TYPE_NOT_FOUND': defineError({ code: 'PARENT_ENTITY_TYPE_NOT_FOUND', class: 'internal', http: 500, message: 'Parent entity type not found.' }),
405
417
  'PARENT_EXECUTION_NOT_FOUND': defineError({ code: 'PARENT_EXECUTION_NOT_FOUND', class: 'internal', http: 500, message: 'Parent execution not found.' }),
406
- 'PASSWORD_INSECURE': defineError({ code: 'PASSWORD_INSECURE', class: 'public', http: 400, message: 'This password is not secure enough. Please choose a stronger password.' }),
407
- 'PASSWORD_LEN': defineError({ code: 'PASSWORD_LEN', class: 'public', http: 400, message: 'Password must be between 8 and 63 characters long.' }),
418
+ 'PASSWORD_INSECURE': defineError({ code: 'PASSWORD_INSECURE', class: 'public', http: 400, message: 'Password insecure.' }),
419
+ 'PASSWORD_LEN': defineError({ code: 'PASSWORD_LEN', class: 'public', http: 400, message: 'Password len.' }),
408
420
  'PASSWORD_RESET_LOCKED_EXCEED_ATTEMPTS': defineError({ code: 'PASSWORD_RESET_LOCKED_EXCEED_ATTEMPTS', class: 'public', http: 423, message: 'Password reset locked exceed attempts.' }),
409
421
  'PASSWORD_SIGN_IN_DISABLED': defineError({ code: 'PASSWORD_SIGN_IN_DISABLED', class: 'public', http: 403, message: 'Password sign in disabled.' }),
410
422
  'PASSWORD_SIGN_UP_DISABLED': defineError({ code: 'PASSWORD_SIGN_UP_DISABLED', class: 'public', http: 403, message: 'Password sign up disabled.' }),
@@ -491,7 +503,7 @@ export const generatedRegistry = {
491
503
  'SEED_PLAN_PLAN_NAME_REQUIRED': defineError({ code: 'SEED_PLAN_PLAN_NAME_REQUIRED', class: 'internal', http: 500, message: 'Seed plan plan name required.' }),
492
504
  'SESSION_NOT_FOUND': defineError({ code: 'SESSION_NOT_FOUND', class: 'public', http: 404, message: 'Session not found.' }),
493
505
  'SETTINGS_SURFACE_NOT_PROVISIONED': defineError({ code: 'SETTINGS_SURFACE_NOT_PROVISIONED', class: 'public', http: 400, message: 'Settings surface not provisioned.' }),
494
- 'SIGN_UP_DISABLED': defineError({ code: 'SIGN_UP_DISABLED', class: 'public', http: 403, message: 'New registrations are currently disabled.' }),
506
+ 'SIGN_UP_DISABLED': defineError({ code: 'SIGN_UP_DISABLED', class: 'public', http: 403, message: 'Sign up disabled.' }),
495
507
  'SITE_NOT_PROVISIONED': defineError({ code: 'SITE_NOT_PROVISIONED', class: 'public', http: 400, message: 'Site not provisioned.' }),
496
508
  'SITE_SURFACE_NOT_PROVISIONED': defineError({ code: 'SITE_SURFACE_NOT_PROVISIONED', class: 'public', http: 400, message: 'Site surface not provisioned.' }),
497
509
  'SMS_SIGN_IN_DISABLED': defineError({ code: 'SMS_SIGN_IN_DISABLED', class: 'public', http: 403, message: 'Sms sign in disabled.' }),
@@ -500,12 +512,15 @@ export const generatedRegistry = {
500
512
  'SOURCE_EMAILS_NOT_FOUND': defineError({ code: 'SOURCE_EMAILS_NOT_FOUND', class: 'internal', http: 500, message: 'Source emails not found.' }),
501
513
  'SOURCE_SECRETS_NOT_FOUND': defineError({ code: 'SOURCE_SECRETS_NOT_FOUND', class: 'internal', http: 500, message: 'Source secrets not found.' }),
502
514
  'SOURCE_USERS_NOT_FOUND': defineError({ code: 'SOURCE_USERS_NOT_FOUND', class: 'internal', http: 500, message: 'Source users not found.' }),
515
+ 'STATIC_SITES_LIMIT': defineError({ code: 'STATIC_SITES_LIMIT', class: 'public', http: 429, message: 'Static sites limit.' }),
503
516
  'STEP_UP_INVALID_TYPE': defineError({ code: 'STEP_UP_INVALID_TYPE', class: 'public', http: 403, message: 'This action requires verification that is not configured correctly. Please contact support.' }),
504
- 'STEP_UP_REQUIRED': defineError({ code: 'STEP_UP_REQUIRED', class: 'public', http: 403, message: 'Please verify your identity to continue.' }),
517
+ 'STEP_UP_REQUIRED': defineError({ code: 'STEP_UP_REQUIRED', class: 'public', http: 403, message: 'Step up required.' }),
505
518
  'STEP_UP_REQUIRED_FRESH_AUTH': defineError({ code: 'STEP_UP_REQUIRED_FRESH_AUTH', class: 'public', http: 403, message: 'Please verify your identity to continue.' }),
506
519
  'STEP_UP_REQUIRED_MFA': defineError({ code: 'STEP_UP_REQUIRED_MFA', class: 'public', http: 403, message: 'Please enter a code from your authenticator app to continue.' }),
507
520
  'STEP_UP_REQUIRED_PASSWORD': defineError({ code: 'STEP_UP_REQUIRED_PASSWORD', class: 'public', http: 403, message: 'Please re-enter your password to continue.' }),
508
- 'STEP_UP_REQUIRED_PASSWORD_OR_MFA': defineError({ code: 'STEP_UP_REQUIRED_PASSWORD_OR_MFA', class: 'public', http: 403, message: 'Please verify your identity to continue.' }),
521
+ 'SUBDOMAIN_APEX_NOT_PUBLISHED': defineError({ code: 'SUBDOMAIN_APEX_NOT_PUBLISHED', class: 'public', http: 400, message: 'Subdomain apex not published.' }),
522
+ 'SUBDOMAIN_LABEL_EXHAUSTED': defineError({ code: 'SUBDOMAIN_LABEL_EXHAUSTED', class: 'public', http: 400, message: 'Subdomain label exhausted.' }),
523
+ 'SUBDOMAIN_LABEL_INVALID': defineError({ code: 'SUBDOMAIN_LABEL_INVALID', class: 'public', http: 400, message: 'Subdomain label invalid.' }),
509
524
  'SUPER_CONSTRUCTIVE_REQUIRED': defineError({ code: 'SUPER_CONSTRUCTIVE_REQUIRED', class: 'public', http: 400, message: 'Super constructive required.' }),
510
525
  'TABLE_MODULE': defineError({ code: 'TABLE_MODULE', class: 'internal', http: 500, message: 'Table module.' }),
511
526
  'TABLE_MODULE_TABLE_NOT_FOUND': defineError({ code: 'TABLE_MODULE_TABLE_NOT_FOUND', class: 'internal', http: 500, message: 'Table module table not found.' }),
@@ -561,6 +576,8 @@ export const GENERATED_CODE_META = {
561
576
  'APPLY_RLS': { class: 'internal', dynamic: false, generatedOnly: false },
562
577
  'APPLY_RLS_BAD_ARGS': { class: 'internal', dynamic: false, generatedOnly: false },
563
578
  'APPLY_TABLE_STEP_UP_TABLE_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
579
+ 'APP_INSTALLATION_MISSING': { class: 'public', dynamic: false, generatedOnly: false },
580
+ 'APP_INSTALL_NO_SERVICE': { class: 'public', dynamic: false, generatedOnly: false },
564
581
  'ASSIGN_PROFILES_PERMISSION_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
565
582
  'AUTHZ_COLUMN_SECURITY_AUTHZ_NODE_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
566
583
  'AUTHZ_COLUMN_SECURITY_COLUMNS_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
@@ -596,6 +613,7 @@ export const GENERATED_CODE_META = {
596
613
  'BAD_PRIVILEGE_FOR_POLICY': { class: 'public', dynamic: false, generatedOnly: false },
597
614
  'BAD_RLS_EXPRESSION': { class: 'public', dynamic: false, generatedOnly: false },
598
615
  'BAD_VIEW_EXPRESSION': { class: 'public', dynamic: false, generatedOnly: false },
616
+ 'BEHAVIOR_FRAGMENT_EMPTY': { class: 'internal', dynamic: false, generatedOnly: false },
599
617
  'BM25': { class: 'public', dynamic: false, generatedOnly: false },
600
618
  'BOOTSTRAP_DATABASE_MISSING': { class: 'internal', dynamic: false, generatedOnly: false },
601
619
  'BOOTSTRAP_SOURCE_MISSING': { class: 'internal', dynamic: false, generatedOnly: false },
@@ -632,6 +650,7 @@ export const GENERATED_CODE_META = {
632
650
  'CROSS_DATABASE_CHECK': { class: 'public', dynamic: false, generatedOnly: false },
633
651
  'CROSS_DATABASE_REF': { class: 'public', dynamic: false, generatedOnly: false },
634
652
  'CSRF_TOKEN_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
653
+ 'DATABASE_CLAIM_REQUIRED': { class: 'internal', dynamic: false, generatedOnly: true },
635
654
  'DATABASE_FIELD_RESERVED_WORD': { class: 'public', dynamic: false, generatedOnly: false },
636
655
  'DATABASE_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
637
656
  'DATABASE_SETTINGS_TABLES_SCOPE_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
@@ -663,6 +682,10 @@ export const GENERATED_CODE_META = {
663
682
  'DATA_GENERATED_TABLE_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
664
683
  'DATA_GENERATED_TARGET_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
665
684
  'DATA_GENERATED_UNKNOWN_KIND': { class: 'public', dynamic: false, generatedOnly: false },
685
+ 'DATA_HISTORY_NO_COPY_FIELDS': { class: 'public', dynamic: false, generatedOnly: false },
686
+ 'DATA_HISTORY_ON_HISTORY_TABLE': { class: 'public', dynamic: false, generatedOnly: false },
687
+ 'DATA_HISTORY_REQUIRES_PK': { class: 'public', dynamic: false, generatedOnly: false },
688
+ 'DATA_HISTORY_TABLE_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
666
689
  'DATA_I18N_FIELDS_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
667
690
  'DATA_I18N_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
668
691
  'DATA_I18N_MODULE_NOT_PROVISIONED': { class: 'public', dynamic: false, generatedOnly: false },
@@ -688,6 +711,7 @@ export const GENERATED_CODE_META = {
688
711
  'DATA_JOB_TRIGGER_CONDITIONS_WATCH_FIELDS_CONFLICT': { class: 'public', dynamic: false, generatedOnly: false },
689
712
  'DATA_JOB_TRIGGER_CONDITION_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
690
713
  'DATA_JOB_TRIGGER_CONDITION_FIELD_WATCH_FIELDS_CONFLICT': { class: 'public', dynamic: false, generatedOnly: false },
714
+ 'DATA_JOB_TRIGGER_DATABASE_ID_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
691
715
  'DATA_JOB_TRIGGER_ENTITY_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
692
716
  'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
693
717
  'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
@@ -772,10 +796,11 @@ export const GENERATED_CODE_META = {
772
796
  'EVENT_TRACKER_UNKNOWN_EVENT': { class: 'public', dynamic: false, generatedOnly: false },
773
797
  'EVENT_TRACKER_WATCH_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
774
798
  'EXECUTION_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
775
- 'EXPIRED_TOKEN': { class: 'public', dynamic: false, generatedOnly: false },
799
+ 'EXPIRED_TOKEN': { class: 'internal', dynamic: false, generatedOnly: false },
776
800
  'EXTERNAL_MEMBERS_NOT_ALLOWED': { class: 'public', dynamic: false, generatedOnly: false },
777
801
  'FEATURE_DISABLED': { class: 'public', dynamic: true, generatedOnly: false },
778
802
  'FIELDS_MISMATCH': { class: 'public', dynamic: false, generatedOnly: false },
803
+ 'FOREIGN_KEY_CONSTRAINT_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
779
804
  'FUNCTION_GRAPH_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
780
805
  'FUNCTION_SCHEDULES': { class: 'internal', dynamic: false, generatedOnly: false },
781
806
  'FUNCTION_SCHEDULES_INTERVAL': { class: 'internal', dynamic: false, generatedOnly: false },
@@ -814,6 +839,7 @@ export const GENERATED_CODE_META = {
814
839
  'HIERARCHY_DEPTH_EXCEEDED': { class: 'internal', dynamic: false, generatedOnly: false },
815
840
  'HIERARCHY_INACTIVE_MEMBER': { class: 'internal', dynamic: true, generatedOnly: false },
816
841
  'HIERARCHY_MEMBER_IN_USE': { class: 'internal', dynamic: true, generatedOnly: false },
842
+ 'HISTORY': { class: 'internal', dynamic: true, generatedOnly: false },
817
843
  'HOSTNAME_BINDING_SYNC': { class: 'internal', dynamic: true, generatedOnly: false },
818
844
  'HTTP_ROUTE_TARGET_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
819
845
  'HTTP_ROUTE_TARGET_OUTSIDE_SCOPE': { class: 'internal', dynamic: false, generatedOnly: false },
@@ -935,6 +961,7 @@ export const GENERATED_CODE_META = {
935
961
  'ORG_API_KEY_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
936
962
  'OWNER_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
937
963
  'OWNER_FIELD_NOT_IN_OWNER_TABLE': { class: 'public', dynamic: false, generatedOnly: false },
964
+ 'PAGES_MODULE': { class: 'internal', dynamic: false, generatedOnly: false },
938
965
  'PARENT_ENTITY_TYPE_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
939
966
  'PARENT_EXECUTION_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
940
967
  'PASSWORD_INSECURE': { class: 'public', dynamic: false, generatedOnly: false },
@@ -1034,12 +1061,15 @@ export const GENERATED_CODE_META = {
1034
1061
  'SOURCE_EMAILS_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
1035
1062
  'SOURCE_SECRETS_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
1036
1063
  'SOURCE_USERS_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
1064
+ 'STATIC_SITES_LIMIT': { class: 'public', dynamic: false, generatedOnly: false },
1037
1065
  'STEP_UP_INVALID_TYPE': { class: 'public', dynamic: false, generatedOnly: false },
1038
1066
  'STEP_UP_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
1039
1067
  'STEP_UP_REQUIRED_FRESH_AUTH': { class: 'public', dynamic: false, generatedOnly: false },
1040
1068
  'STEP_UP_REQUIRED_MFA': { class: 'public', dynamic: false, generatedOnly: false },
1041
1069
  'STEP_UP_REQUIRED_PASSWORD': { class: 'public', dynamic: false, generatedOnly: false },
1042
- 'STEP_UP_REQUIRED_PASSWORD_OR_MFA': { class: 'public', dynamic: false, generatedOnly: true },
1070
+ 'SUBDOMAIN_APEX_NOT_PUBLISHED': { class: 'public', dynamic: false, generatedOnly: false },
1071
+ 'SUBDOMAIN_LABEL_EXHAUSTED': { class: 'public', dynamic: false, generatedOnly: false },
1072
+ 'SUBDOMAIN_LABEL_INVALID': { class: 'public', dynamic: false, generatedOnly: false },
1043
1073
  'SUPER_CONSTRUCTIVE_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
1044
1074
  'TABLE_MODULE': { class: 'internal', dynamic: false, generatedOnly: false },
1045
1075
  'TABLE_MODULE_TABLE_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
@@ -1082,4 +1112,4 @@ export const GENERATED_CODE_META = {
1082
1112
  'WITH_CHECK_NOT_ALLOWED': { class: 'public', dynamic: false, generatedOnly: false },
1083
1113
  };
1084
1114
  /** Total number of codes collected from constructive-db. */
1085
- export const GENERATED_CODE_COUNT = 531;
1115
+ export const GENERATED_CODE_COUNT = 546;
package/esm/http.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /** Status used when a code carries no mapping. */
2
+ export declare const UNMAPPED_HTTP_STATUS = 500;
3
+ export interface HttpStatusResolution {
4
+ /** The status a transport should send. */
5
+ status: number;
6
+ /**
7
+ * Whether `status` came from the registry. `false` means the code is not
8
+ * registered and `status` is the {@link UNMAPPED_HTTP_STATUS} fallback — the
9
+ * one case where a 500 does not mean "the server broke".
10
+ */
11
+ mapped: boolean;
12
+ }
13
+ /** Notified once per unmapped code. */
14
+ export type UnmappedStatusReporter = (code: string) => void;
15
+ /**
16
+ * Replace the unmapped-code reporter (pass `null` to restore the default, or a
17
+ * no-op to silence it). Transports with a real logger should route it there.
18
+ */
19
+ export declare function setUnmappedStatusReporter(next: UnmappedStatusReporter | null): void;
20
+ /** Test seam: forget which codes have already been reported. */
21
+ export declare function resetUnmappedStatusReports(): void;
22
+ /**
23
+ * Resolve the HTTP status for an error code.
24
+ *
25
+ * An unregistered code is reported (once per code) rather than silently
26
+ * degrading to 500: a refusal that is plainly a 403 or 409 turning into a 500
27
+ * looks like a crash, and the only way anyone notices is by reading the
28
+ * transport's source.
29
+ */
30
+ export declare function httpStatusFor(code: string | null | undefined): HttpStatusResolution;
package/esm/http.js ADDED
@@ -0,0 +1,39 @@
1
+ import { getDefinition } from './registry';
2
+ /** Status used when a code carries no mapping. */
3
+ export const UNMAPPED_HTTP_STATUS = 500;
4
+ const reported = new Set();
5
+ const defaultReporter = code => {
6
+ // eslint-disable-next-line no-console
7
+ console.warn(`[constructive-errors] no HTTP status mapping for ${code}; responding ${UNMAPPED_HTTP_STATUS}. ` +
8
+ 'Register the code so intentional refusals stop surfacing as server errors.');
9
+ };
10
+ let reporter = defaultReporter;
11
+ /**
12
+ * Replace the unmapped-code reporter (pass `null` to restore the default, or a
13
+ * no-op to silence it). Transports with a real logger should route it there.
14
+ */
15
+ export function setUnmappedStatusReporter(next) {
16
+ reporter = next ?? defaultReporter;
17
+ }
18
+ /** Test seam: forget which codes have already been reported. */
19
+ export function resetUnmappedStatusReports() {
20
+ reported.clear();
21
+ }
22
+ /**
23
+ * Resolve the HTTP status for an error code.
24
+ *
25
+ * An unregistered code is reported (once per code) rather than silently
26
+ * degrading to 500: a refusal that is plainly a 403 or 409 turning into a 500
27
+ * looks like a crash, and the only way anyone notices is by reading the
28
+ * transport's source.
29
+ */
30
+ export function httpStatusFor(code) {
31
+ const def = code ? getDefinition(code) : undefined;
32
+ if (def)
33
+ return { status: def.http, mapped: true };
34
+ if (code && !reported.has(code)) {
35
+ reported.add(code);
36
+ reporter(code);
37
+ }
38
+ return { status: UNMAPPED_HTTP_STATUS, mapped: false };
39
+ }
package/esm/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './classify';
2
2
  export * from './error';
3
3
  export * from './factory';
4
4
  export * from './format';
5
+ export * from './http';
5
6
  export * from './interpolate';
6
7
  export * from './parse';
7
8
  export * from './pg';
package/esm/index.js CHANGED
@@ -2,6 +2,7 @@ export * from './classify';
2
2
  export * from './error';
3
3
  export * from './factory';
4
4
  export * from './format';
5
+ export * from './http';
5
6
  export * from './interpolate';
6
7
  export * from './parse';
7
8
  export * from './pg';
package/esm/parse.d.ts CHANGED
@@ -25,6 +25,8 @@ export declare function parse(error: unknown): ParsedError;
25
25
  * Delegates to {@link parse} for code/context/class recovery, then resolves a
26
26
  * localized message from the registry catalogs (falling back to the source
27
27
  * error's raw message when the code is unknown) and the registry's HTTP hint.
28
- * Codes that could not be resolved become `UNKNOWN_ERROR` (internal).
28
+ * Codes that could not be resolved become `UNKNOWN_ERROR` (internal); a code
29
+ * with no registered status is reported by {@link httpStatusFor} rather than
30
+ * quietly becoming a 500.
29
31
  */
30
32
  export declare function toError(error: unknown, locale?: string): ConstructiveError;
package/esm/parse.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { classify } from './classify';
2
2
  import { ConstructiveError } from './error';
3
3
  import { format } from './format';
4
+ import { httpStatusFor } from './http';
4
5
  import { extractPgErrorFields, RAISE_EXCEPTION_SQLSTATE, SQLSTATE_TO_CODE } from './pg';
5
6
  import { getDefinition } from './registry';
6
7
  /** Leading ALL_CAPS token, optionally followed by `(arg, arg)` positional args. */
@@ -178,7 +179,9 @@ export function parse(error) {
178
179
  * Delegates to {@link parse} for code/context/class recovery, then resolves a
179
180
  * localized message from the registry catalogs (falling back to the source
180
181
  * error's raw message when the code is unknown) and the registry's HTTP hint.
181
- * Codes that could not be resolved become `UNKNOWN_ERROR` (internal).
182
+ * Codes that could not be resolved become `UNKNOWN_ERROR` (internal); a code
183
+ * with no registered status is reported by {@link httpStatusFor} rather than
184
+ * quietly becoming a 500.
182
185
  */
183
186
  export function toError(error, locale) {
184
187
  if (error instanceof ConstructiveError)
@@ -193,7 +196,7 @@ export function toError(error, locale) {
193
196
  code,
194
197
  message,
195
198
  errorClass: parsed.class,
196
- http: def?.http ?? 500,
199
+ http: def ? def.http : httpStatusFor(code).status,
197
200
  context: parsed.context
198
201
  });
199
202
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * GENERATED FILE — DO NOT EDIT BY HAND.
3
3
  *
4
- * Source of truth: the constructive-db error audit (531 distinct codes
4
+ * Source of truth: the constructive-db error audit (546 distinct codes
5
5
  * raised via EXCEPTION/THROW across deploy sources + generated output).
6
6
  * Regenerate with `python3 scripts/generate-registry.py` (see README.md).
7
7
  *
@@ -9,7 +9,7 @@
9
9
  * codes carry their raw message (with %-args rendered as {{argN}}). Curated
10
10
  * entries in `registry.ts` override anything here (typed context + refined copy).
11
11
  *
12
- * Counts: 531 total, 401 public, 130 internal.
12
+ * Counts: 546 total, 410 public, 136 internal.
13
13
  */
14
14
  import { type DefinedError } from '../define';
15
15
  import type { ErrorContext } from '../types';
@@ -24,4 +24,4 @@ export declare const generatedRegistry: Record<string, DefinedError<ErrorContext
24
24
  /** Audit metadata for each generated code. */
25
25
  export declare const GENERATED_CODE_META: Record<string, GeneratedCodeMeta>;
26
26
  /** Total number of codes collected from constructive-db. */
27
- export declare const GENERATED_CODE_COUNT = 531;
27
+ export declare const GENERATED_CODE_COUNT = 546;
@@ -5,7 +5,7 @@ exports.GENERATED_CODE_COUNT = exports.GENERATED_CODE_META = exports.generatedRe
5
5
  /**
6
6
  * GENERATED FILE — DO NOT EDIT BY HAND.
7
7
  *
8
- * Source of truth: the constructive-db error audit (531 distinct codes
8
+ * Source of truth: the constructive-db error audit (546 distinct codes
9
9
  * raised via EXCEPTION/THROW across deploy sources + generated output).
10
10
  * Regenerate with `python3 scripts/generate-registry.py` (see README.md).
11
11
  *
@@ -13,14 +13,14 @@ exports.GENERATED_CODE_COUNT = exports.GENERATED_CODE_META = exports.generatedRe
13
13
  * codes carry their raw message (with %-args rendered as {{argN}}). Curated
14
14
  * entries in `registry.ts` override anything here (typed context + refined copy).
15
15
  *
16
- * Counts: 531 total, 401 public, 130 internal.
16
+ * Counts: 546 total, 410 public, 136 internal.
17
17
  */
18
18
  const define_1 = require("../define");
19
19
  /** Every constructive-db error code, keyed by code. */
20
20
  exports.generatedRegistry = {
21
- 'ACCOUNT_DISABLED': (0, define_1.defineError)({ code: 'ACCOUNT_DISABLED', class: 'public', http: 403, message: 'Your account has been disabled. Please contact support for assistance.' }),
22
- 'ACCOUNT_EXISTS': (0, define_1.defineError)({ code: 'ACCOUNT_EXISTS', class: 'public', http: 409, message: 'An account with this email already exists. Please sign in or use a different email.' }),
23
- 'ACCOUNT_LOCKED_EXCEED_ATTEMPTS': (0, define_1.defineError)({ code: 'ACCOUNT_LOCKED_EXCEED_ATTEMPTS', class: 'public', http: 423, message: 'Your account has been temporarily locked due to too many failed login attempts. Please try again later or reset your password.' }),
21
+ 'ACCOUNT_DISABLED': (0, define_1.defineError)({ code: 'ACCOUNT_DISABLED', class: 'public', http: 403, message: 'Account disabled.' }),
22
+ 'ACCOUNT_EXISTS': (0, define_1.defineError)({ code: 'ACCOUNT_EXISTS', class: 'public', http: 409, message: 'Account exists.' }),
23
+ 'ACCOUNT_LOCKED_EXCEED_ATTEMPTS': (0, define_1.defineError)({ code: 'ACCOUNT_LOCKED_EXCEED_ATTEMPTS', class: 'public', http: 423, message: 'Account locked exceed attempts.' }),
24
24
  'ACCOUNT_NOT_FOUND': (0, define_1.defineError)({ code: 'ACCOUNT_NOT_FOUND', class: 'public', http: 404, message: 'Account not found.' }),
25
25
  'ALREADY_SCHEDULED': (0, define_1.defineError)({ code: 'ALREADY_SCHEDULED', class: 'public', http: 409, message: 'Already scheduled.' }),
26
26
  'ALTER_TABLE_ADD_COLUMN': (0, define_1.defineError)({ code: 'ALTER_TABLE_ADD_COLUMN', class: 'internal', http: 500, message: 'ALTER_TABLE_ADD_COLUMN: a column cannot be both generated and an identity column' }),
@@ -30,6 +30,8 @@ exports.generatedRegistry = {
30
30
  'APPLY_RLS': (0, define_1.defineError)({ code: 'APPLY_RLS', class: 'internal', http: 500, message: 'Apply rls.' }),
31
31
  'APPLY_RLS_BAD_ARGS': (0, define_1.defineError)({ code: 'APPLY_RLS_BAD_ARGS', class: 'internal', http: 500, message: 'Apply rls bad args.' }),
32
32
  'APPLY_TABLE_STEP_UP_TABLE_NOT_FOUND': (0, define_1.defineError)({ code: 'APPLY_TABLE_STEP_UP_TABLE_NOT_FOUND', class: 'public', http: 404, message: 'Apply table step up table not found.' }),
33
+ 'APP_INSTALLATION_MISSING': (0, define_1.defineError)({ code: 'APP_INSTALLATION_MISSING', class: 'public', http: 400, message: 'App installation missing.' }),
34
+ 'APP_INSTALL_NO_SERVICE': (0, define_1.defineError)({ code: 'APP_INSTALL_NO_SERVICE', class: 'public', http: 400, message: 'App install no service.' }),
33
35
  'ASSIGN_PROFILES_PERMISSION_REQUIRED': (0, define_1.defineError)({ code: 'ASSIGN_PROFILES_PERMISSION_REQUIRED', class: 'public', http: 403, message: 'Assign profiles permission required.' }),
34
36
  'AUTHZ_COLUMN_SECURITY_AUTHZ_NODE_REQUIRED': (0, define_1.defineError)({ code: 'AUTHZ_COLUMN_SECURITY_AUTHZ_NODE_REQUIRED', class: 'public', http: 400, message: 'Authz column security authz node required.' }),
35
37
  'AUTHZ_COLUMN_SECURITY_COLUMNS_REQUIRED': (0, define_1.defineError)({ code: 'AUTHZ_COLUMN_SECURITY_COLUMNS_REQUIRED', class: 'public', http: 400, message: 'Authz column security columns required.' }),
@@ -65,6 +67,7 @@ exports.generatedRegistry = {
65
67
  'BAD_PRIVILEGE_FOR_POLICY': (0, define_1.defineError)({ code: 'BAD_PRIVILEGE_FOR_POLICY', class: 'public', http: 400, message: 'Bad privilege for policy.' }),
66
68
  'BAD_RLS_EXPRESSION': (0, define_1.defineError)({ code: 'BAD_RLS_EXPRESSION', class: 'public', http: 400, message: 'Bad rls expression.' }),
67
69
  'BAD_VIEW_EXPRESSION': (0, define_1.defineError)({ code: 'BAD_VIEW_EXPRESSION', class: 'public', http: 400, message: 'Bad view expression.' }),
70
+ 'BEHAVIOR_FRAGMENT_EMPTY': (0, define_1.defineError)({ code: 'BEHAVIOR_FRAGMENT_EMPTY', class: 'internal', http: 500, message: 'Behavior fragment empty.' }),
68
71
  'BM25': (0, define_1.defineError)({ code: 'BM25', class: 'public', http: 400, message: 'Bm25.' }),
69
72
  'BOOTSTRAP_DATABASE_MISSING': (0, define_1.defineError)({ code: 'BOOTSTRAP_DATABASE_MISSING', class: 'internal', http: 500, message: 'Bootstrap database missing.' }),
70
73
  'BOOTSTRAP_SOURCE_MISSING': (0, define_1.defineError)({ code: 'BOOTSTRAP_SOURCE_MISSING', class: 'internal', http: 500, message: 'Bootstrap source missing.' }),
@@ -101,6 +104,7 @@ exports.generatedRegistry = {
101
104
  'CROSS_DATABASE_CHECK': (0, define_1.defineError)({ code: 'CROSS_DATABASE_CHECK', class: 'public', http: 400, message: 'Cross database check.' }),
102
105
  'CROSS_DATABASE_REF': (0, define_1.defineError)({ code: 'CROSS_DATABASE_REF', class: 'public', http: 400, message: 'Cross database ref.' }),
103
106
  'CSRF_TOKEN_REQUIRED': (0, define_1.defineError)({ code: 'CSRF_TOKEN_REQUIRED', class: 'public', http: 401, message: 'Csrf token required.' }),
107
+ 'DATABASE_CLAIM_REQUIRED': (0, define_1.defineError)({ code: 'DATABASE_CLAIM_REQUIRED', class: 'internal', http: 500, message: 'Database claim required.' }),
104
108
  'DATABASE_FIELD_RESERVED_WORD': (0, define_1.defineError)({ code: 'DATABASE_FIELD_RESERVED_WORD', class: 'public', http: 400, message: 'Database field reserved word.' }),
105
109
  'DATABASE_NOT_FOUND': (0, define_1.defineError)({ code: 'DATABASE_NOT_FOUND', class: 'public', http: 404, message: 'Database not found.' }),
106
110
  'DATABASE_SETTINGS_TABLES_SCOPE_REQUIRED': (0, define_1.defineError)({ code: 'DATABASE_SETTINGS_TABLES_SCOPE_REQUIRED', class: 'public', http: 400, message: 'Database settings tables scope required.' }),
@@ -132,6 +136,10 @@ exports.generatedRegistry = {
132
136
  'DATA_GENERATED_TABLE_NOT_FOUND': (0, define_1.defineError)({ code: 'DATA_GENERATED_TABLE_NOT_FOUND', class: 'public', http: 404, message: 'Data generated table not found.' }),
133
137
  'DATA_GENERATED_TARGET_REQUIRED': (0, define_1.defineError)({ code: 'DATA_GENERATED_TARGET_REQUIRED', class: 'public', http: 400, message: 'Data generated target required.' }),
134
138
  'DATA_GENERATED_UNKNOWN_KIND': (0, define_1.defineError)({ code: 'DATA_GENERATED_UNKNOWN_KIND', class: 'public', http: 400, message: 'Data generated unknown kind.' }),
139
+ 'DATA_HISTORY_NO_COPY_FIELDS': (0, define_1.defineError)({ code: 'DATA_HISTORY_NO_COPY_FIELDS', class: 'public', http: 400, message: 'Data history no copy fields.' }),
140
+ 'DATA_HISTORY_ON_HISTORY_TABLE': (0, define_1.defineError)({ code: 'DATA_HISTORY_ON_HISTORY_TABLE', class: 'public', http: 400, message: 'Data history on history table.' }),
141
+ 'DATA_HISTORY_REQUIRES_PK': (0, define_1.defineError)({ code: 'DATA_HISTORY_REQUIRES_PK', class: 'public', http: 403, message: 'Data history requires pk.' }),
142
+ 'DATA_HISTORY_TABLE_NOT_FOUND': (0, define_1.defineError)({ code: 'DATA_HISTORY_TABLE_NOT_FOUND', class: 'public', http: 404, message: 'Data history table not found.' }),
135
143
  'DATA_I18N_FIELDS_REQUIRED': (0, define_1.defineError)({ code: 'DATA_I18N_FIELDS_REQUIRED', class: 'public', http: 400, message: 'Data i18n fields required.' }),
136
144
  'DATA_I18N_FIELD_NOT_FOUND': (0, define_1.defineError)({ code: 'DATA_I18N_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data i18n field not found.' }),
137
145
  'DATA_I18N_MODULE_NOT_PROVISIONED': (0, define_1.defineError)({ code: 'DATA_I18N_MODULE_NOT_PROVISIONED', class: 'public', http: 400, message: 'Data i18n module not provisioned.' }),
@@ -157,6 +165,7 @@ exports.generatedRegistry = {
157
165
  'DATA_JOB_TRIGGER_CONDITIONS_WATCH_FIELDS_CONFLICT': (0, define_1.defineError)({ code: 'DATA_JOB_TRIGGER_CONDITIONS_WATCH_FIELDS_CONFLICT', class: 'public', http: 400, message: 'Data job trigger conditions watch fields conflict.' }),
158
166
  'DATA_JOB_TRIGGER_CONDITION_FIELD_NOT_FOUND': (0, define_1.defineError)({ code: 'DATA_JOB_TRIGGER_CONDITION_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data job trigger condition field not found.' }),
159
167
  'DATA_JOB_TRIGGER_CONDITION_FIELD_WATCH_FIELDS_CONFLICT': (0, define_1.defineError)({ code: 'DATA_JOB_TRIGGER_CONDITION_FIELD_WATCH_FIELDS_CONFLICT', class: 'public', http: 400, message: 'Data job trigger condition field watch fields conflict.' }),
168
+ 'DATA_JOB_TRIGGER_DATABASE_ID_FIELD_NOT_FOUND': (0, define_1.defineError)({ code: 'DATA_JOB_TRIGGER_DATABASE_ID_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data job trigger database id field not found.' }),
160
169
  'DATA_JOB_TRIGGER_ENTITY_FIELD_NOT_FOUND': (0, define_1.defineError)({ code: 'DATA_JOB_TRIGGER_ENTITY_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data job trigger entity field not found.' }),
161
170
  'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_NOT_FOUND': (0, define_1.defineError)({ code: 'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Data job trigger entity lookup obj field not found.' }),
162
171
  'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_REQUIRED': (0, define_1.defineError)({ code: 'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_REQUIRED', class: 'public', http: 400, message: 'Data job trigger entity lookup obj field required.' }),
@@ -208,7 +217,7 @@ exports.generatedRegistry = {
208
217
  'DOMAIN_ROOT_NOT_PUBLISHED': (0, define_1.defineError)({ code: 'DOMAIN_ROOT_NOT_PUBLISHED', class: 'internal', http: 500, message: 'Domain root not published.' }),
209
218
  'DOMAIN_VERIFY_BAD_METHOD': (0, define_1.defineError)({ code: 'DOMAIN_VERIFY_BAD_METHOD', class: 'internal', http: 500, message: 'DOMAIN_VERIFY_BAD_METHOD: unsupported verification method {{arg0}}', positional: ['arg0'] }),
210
219
  'DOMAIN_VERIFY_NO_CHALLENGE': (0, define_1.defineError)({ code: 'DOMAIN_VERIFY_NO_CHALLENGE', class: 'internal', http: 500, message: 'DOMAIN_VERIFY_NO_CHALLENGE: no outstanding {{arg0}} challenge for managed_domain {{arg1}}', positional: ['arg0', 'arg1'] }),
211
- 'EMAIL_NOT_VERIFIED': (0, define_1.defineError)({ code: 'EMAIL_NOT_VERIFIED', class: 'public', http: 400, message: 'Please verify your email before accepting this invitation.' }),
220
+ 'EMAIL_NOT_VERIFIED': (0, define_1.defineError)({ code: 'EMAIL_NOT_VERIFIED', class: 'public', http: 400, message: 'Email not verified.' }),
212
221
  'ENTITY_NOT_FOUND': (0, define_1.defineError)({ code: 'ENTITY_NOT_FOUND', class: 'internal', http: 500, message: 'Entity not found.' }),
213
222
  'ENTITY_TYPE_NOT_FOUND': (0, define_1.defineError)({ code: 'ENTITY_TYPE_NOT_FOUND', class: 'internal', http: 500, message: 'Entity type not found.' }),
214
223
  'ENTITY_TYPE_PROVISION': (0, define_1.defineError)({ code: 'ENTITY_TYPE_PROVISION', class: 'internal', http: 500, message: 'Entity type provision.' }),
@@ -241,10 +250,11 @@ exports.generatedRegistry = {
241
250
  'EVENT_TRACKER_UNKNOWN_EVENT': (0, define_1.defineError)({ code: 'EVENT_TRACKER_UNKNOWN_EVENT', class: 'public', http: 400, message: 'Event tracker unknown event.' }),
242
251
  'EVENT_TRACKER_WATCH_FIELD_NOT_FOUND': (0, define_1.defineError)({ code: 'EVENT_TRACKER_WATCH_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Event tracker watch field not found.' }),
243
252
  'EXECUTION_NOT_FOUND': (0, define_1.defineError)({ code: 'EXECUTION_NOT_FOUND', class: 'internal', http: 500, message: 'Execution not found.' }),
244
- 'EXPIRED_TOKEN': (0, define_1.defineError)({ code: 'EXPIRED_TOKEN', class: 'public', http: 400, message: 'This reset link has expired. Please request a new one.' }),
253
+ 'EXPIRED_TOKEN': (0, define_1.defineError)({ code: 'EXPIRED_TOKEN', class: 'internal', http: 500, message: 'Expired token.' }),
245
254
  'EXTERNAL_MEMBERS_NOT_ALLOWED': (0, define_1.defineError)({ code: 'EXTERNAL_MEMBERS_NOT_ALLOWED', class: 'public', http: 403, message: 'External members not allowed.' }),
246
255
  'FEATURE_DISABLED': (0, define_1.defineError)({ code: 'FEATURE_DISABLED', class: 'public', http: 403, message: 'Feature disabled.', positional: ['arg0'] }),
247
256
  'FIELDS_MISMATCH': (0, define_1.defineError)({ code: 'FIELDS_MISMATCH', class: 'public', http: 400, message: 'Fields mismatch.' }),
257
+ 'FOREIGN_KEY_CONSTRAINT_NOT_FOUND': (0, define_1.defineError)({ code: 'FOREIGN_KEY_CONSTRAINT_NOT_FOUND', class: 'internal', http: 500, message: 'Foreign key constraint not found.' }),
248
258
  'FUNCTION_GRAPH_NOT_FOUND': (0, define_1.defineError)({ code: 'FUNCTION_GRAPH_NOT_FOUND', class: 'internal', http: 500, message: 'Function graph not found.' }),
249
259
  'FUNCTION_SCHEDULES': (0, define_1.defineError)({ code: 'FUNCTION_SCHEDULES', class: 'internal', http: 500, message: 'FUNCTION_SCHEDULES: function definition does not allow the cron access channel' }),
250
260
  'FUNCTION_SCHEDULES_INTERVAL': (0, define_1.defineError)({ code: 'FUNCTION_SCHEDULES_INTERVAL', class: 'internal', http: 500, message: 'FUNCTION_SCHEDULES_INTERVAL (min_schedule_interval)' }),
@@ -283,6 +293,7 @@ exports.generatedRegistry = {
283
293
  'HIERARCHY_DEPTH_EXCEEDED': (0, define_1.defineError)({ code: 'HIERARCHY_DEPTH_EXCEEDED', class: 'internal', http: 500, message: 'Hierarchy depth exceeded.' }),
284
294
  'HIERARCHY_INACTIVE_MEMBER': (0, define_1.defineError)({ code: 'HIERARCHY_INACTIVE_MEMBER', class: 'internal', http: 500, message: 'HIERARCHY_INACTIVE_MEMBER: Cannot add user {{arg0}} to hierarchy - user must be an active member of the organization first', positional: ['arg0'] }),
285
295
  'HIERARCHY_MEMBER_IN_USE': (0, define_1.defineError)({ code: 'HIERARCHY_MEMBER_IN_USE', class: 'internal', http: 500, message: 'HIERARCHY_MEMBER_IN_USE: Cannot deactivate user {{arg0}} - user must be removed from the organization hierarchy first', positional: ['arg0'] }),
296
+ 'HISTORY': (0, define_1.defineError)({ code: 'HISTORY', class: 'internal', http: 500, message: 'HISTORY: history {{arg0}}.{{arg1}} registration has no copy_fields', positional: ['arg0', 'arg1'] }),
286
297
  'HOSTNAME_BINDING_SYNC': (0, define_1.defineError)({ code: 'HOSTNAME_BINDING_SYNC', class: 'internal', http: 500, message: 'HOSTNAME_BINDING_SYNC: unknown event {{arg0}}, expected UPSERT or DELETE', positional: ['arg0'] }),
287
298
  'HTTP_ROUTE_TARGET_NOT_FOUND': (0, define_1.defineError)({ code: 'HTTP_ROUTE_TARGET_NOT_FOUND', class: 'internal', http: 500, message: 'HTTP_ROUTE_TARGET_NOT_FOUND:' }),
288
299
  'HTTP_ROUTE_TARGET_OUTSIDE_SCOPE': (0, define_1.defineError)({ code: 'HTTP_ROUTE_TARGET_OUTSIDE_SCOPE', class: 'internal', http: 500, message: 'HTTP_ROUTE_TARGET_OUTSIDE_SCOPE:' }),
@@ -299,7 +310,7 @@ exports.generatedRegistry = {
299
310
  'IMMUTABLE_PROPERTY': (0, define_1.defineError)({ code: 'IMMUTABLE_PROPERTY', class: 'internal', http: 500, message: 'Immutable property.' }),
300
311
  'IMMUTABLE_PROPS': (0, define_1.defineError)({ code: 'IMMUTABLE_PROPS', class: 'public', http: 403, message: 'Immutable props.' }),
301
312
  'IMMUTABLE_TIMESTAMPS': (0, define_1.defineError)({ code: 'IMMUTABLE_TIMESTAMPS', class: 'public', http: 403, message: 'Immutable timestamps.' }),
302
- 'INCORRECT_PASSWORD': (0, define_1.defineError)({ code: 'INCORRECT_PASSWORD', class: 'public', http: 401, message: 'The password you entered is incorrect. Please try again.' }),
313
+ 'INCORRECT_PASSWORD': (0, define_1.defineError)({ code: 'INCORRECT_PASSWORD', class: 'public', http: 401, message: 'Incorrect password.' }),
303
314
  'INFRA_API_NOT_PROVISIONED': (0, define_1.defineError)({ code: 'INFRA_API_NOT_PROVISIONED', class: 'public', http: 400, message: 'Infra api not provisioned.' }),
304
315
  'INSERT_GRAPH_EXECUTION_MODULE': (0, define_1.defineError)({ code: 'INSERT_GRAPH_EXECUTION_MODULE', class: 'internal', http: 500, message: 'Insert graph execution module.' }),
305
316
  'INTERNAL_ERROR': (0, define_1.defineError)({ code: 'INTERNAL_ERROR', class: 'internal', http: 500, message: 'Internal error.' }),
@@ -315,12 +326,12 @@ exports.generatedRegistry = {
315
326
  'INVALID_MFA_CHALLENGE': (0, define_1.defineError)({ code: 'INVALID_MFA_CHALLENGE', class: 'public', http: 403, message: 'Invalid mfa challenge.' }),
316
327
  'INVALID_MFA_LEVEL': (0, define_1.defineError)({ code: 'INVALID_MFA_LEVEL', class: 'public', http: 403, message: 'Invalid mfa level.' }),
317
328
  'INVALID_ORGANIZATION': (0, define_1.defineError)({ code: 'INVALID_ORGANIZATION', class: 'public', http: 400, message: 'Invalid organization.' }),
318
- 'INVALID_TOKEN': (0, define_1.defineError)({ code: 'INVALID_TOKEN', class: 'public', http: 401, message: 'This reset link is invalid. Please request a new one.' }),
329
+ 'INVALID_TOKEN': (0, define_1.defineError)({ code: 'INVALID_TOKEN', class: 'public', http: 401, message: 'Invalid token.' }),
319
330
  'INVALID_USER': (0, define_1.defineError)({ code: 'INVALID_USER', class: 'public', http: 400, message: 'Invalid user.' }),
320
331
  'INVALID_WORKER_ID': (0, define_1.defineError)({ code: 'INVALID_WORKER_ID', class: 'internal', http: 500, message: 'Invalid worker id.' }),
321
- 'INVITE_EMAIL_NOT_FOUND': (0, define_1.defineError)({ code: 'INVITE_EMAIL_NOT_FOUND', class: 'public', http: 404, message: 'This invitation was sent to a different email address.' }),
322
- 'INVITE_LIMIT': (0, define_1.defineError)({ code: 'INVITE_LIMIT', class: 'public', http: 429, message: 'This invitation has reached its usage limit.' }),
323
- 'INVITE_NOT_FOUND': (0, define_1.defineError)({ code: 'INVITE_NOT_FOUND', class: 'public', http: 404, message: 'This invitation was not found.' }),
332
+ 'INVITE_EMAIL_NOT_FOUND': (0, define_1.defineError)({ code: 'INVITE_EMAIL_NOT_FOUND', class: 'public', http: 404, message: 'Invite email not found.' }),
333
+ 'INVITE_LIMIT': (0, define_1.defineError)({ code: 'INVITE_LIMIT', class: 'public', http: 429, message: 'Invite limit.' }),
334
+ 'INVITE_NOT_FOUND': (0, define_1.defineError)({ code: 'INVITE_NOT_FOUND', class: 'public', http: 404, message: 'Invite not found.' }),
324
335
  'INVOCATION_QUOTA_GATE': (0, define_1.defineError)({ code: 'INVOCATION_QUOTA_GATE', class: 'internal', http: 500, message: 'INVOCATION_QUOTA_GATE: quota_schema and quota_function are required — the generator must only build this trigger when billing is provisioned' }),
325
336
  'INVOCATION_USAGE': (0, define_1.defineError)({ code: 'INVOCATION_USAGE', class: 'internal', http: 500, message: 'INVOCATION_USAGE: billing_schema and record_usage_fn are required — the generator must only build this trigger when billing is provisioned' }),
326
337
  'LIMIT_ENFORCE_AGGREGATE_ENTITY_FIELD_NOT_FOUND': (0, define_1.defineError)({ code: 'LIMIT_ENFORCE_AGGREGATE_ENTITY_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Limit enforce aggregate entity field not found.' }),
@@ -404,10 +415,11 @@ exports.generatedRegistry = {
404
415
  'ORG_API_KEY_NOT_FOUND': (0, define_1.defineError)({ code: 'ORG_API_KEY_NOT_FOUND', class: 'public', http: 404, message: 'Org api key not found.' }),
405
416
  'OWNER_FIELD_NOT_FOUND': (0, define_1.defineError)({ code: 'OWNER_FIELD_NOT_FOUND', class: 'public', http: 404, message: 'Owner field not found.' }),
406
417
  'OWNER_FIELD_NOT_IN_OWNER_TABLE': (0, define_1.defineError)({ code: 'OWNER_FIELD_NOT_IN_OWNER_TABLE', class: 'public', http: 400, message: 'Owner field not in owner table.' }),
418
+ 'PAGES_MODULE': (0, define_1.defineError)({ code: 'PAGES_MODULE', class: 'internal', http: 500, message: 'Pages module.' }),
407
419
  'PARENT_ENTITY_TYPE_NOT_FOUND': (0, define_1.defineError)({ code: 'PARENT_ENTITY_TYPE_NOT_FOUND', class: 'internal', http: 500, message: 'Parent entity type not found.' }),
408
420
  'PARENT_EXECUTION_NOT_FOUND': (0, define_1.defineError)({ code: 'PARENT_EXECUTION_NOT_FOUND', class: 'internal', http: 500, message: 'Parent execution not found.' }),
409
- 'PASSWORD_INSECURE': (0, define_1.defineError)({ code: 'PASSWORD_INSECURE', class: 'public', http: 400, message: 'This password is not secure enough. Please choose a stronger password.' }),
410
- 'PASSWORD_LEN': (0, define_1.defineError)({ code: 'PASSWORD_LEN', class: 'public', http: 400, message: 'Password must be between 8 and 63 characters long.' }),
421
+ 'PASSWORD_INSECURE': (0, define_1.defineError)({ code: 'PASSWORD_INSECURE', class: 'public', http: 400, message: 'Password insecure.' }),
422
+ 'PASSWORD_LEN': (0, define_1.defineError)({ code: 'PASSWORD_LEN', class: 'public', http: 400, message: 'Password len.' }),
411
423
  'PASSWORD_RESET_LOCKED_EXCEED_ATTEMPTS': (0, define_1.defineError)({ code: 'PASSWORD_RESET_LOCKED_EXCEED_ATTEMPTS', class: 'public', http: 423, message: 'Password reset locked exceed attempts.' }),
412
424
  'PASSWORD_SIGN_IN_DISABLED': (0, define_1.defineError)({ code: 'PASSWORD_SIGN_IN_DISABLED', class: 'public', http: 403, message: 'Password sign in disabled.' }),
413
425
  'PASSWORD_SIGN_UP_DISABLED': (0, define_1.defineError)({ code: 'PASSWORD_SIGN_UP_DISABLED', class: 'public', http: 403, message: 'Password sign up disabled.' }),
@@ -494,7 +506,7 @@ exports.generatedRegistry = {
494
506
  'SEED_PLAN_PLAN_NAME_REQUIRED': (0, define_1.defineError)({ code: 'SEED_PLAN_PLAN_NAME_REQUIRED', class: 'internal', http: 500, message: 'Seed plan plan name required.' }),
495
507
  'SESSION_NOT_FOUND': (0, define_1.defineError)({ code: 'SESSION_NOT_FOUND', class: 'public', http: 404, message: 'Session not found.' }),
496
508
  'SETTINGS_SURFACE_NOT_PROVISIONED': (0, define_1.defineError)({ code: 'SETTINGS_SURFACE_NOT_PROVISIONED', class: 'public', http: 400, message: 'Settings surface not provisioned.' }),
497
- 'SIGN_UP_DISABLED': (0, define_1.defineError)({ code: 'SIGN_UP_DISABLED', class: 'public', http: 403, message: 'New registrations are currently disabled.' }),
509
+ 'SIGN_UP_DISABLED': (0, define_1.defineError)({ code: 'SIGN_UP_DISABLED', class: 'public', http: 403, message: 'Sign up disabled.' }),
498
510
  'SITE_NOT_PROVISIONED': (0, define_1.defineError)({ code: 'SITE_NOT_PROVISIONED', class: 'public', http: 400, message: 'Site not provisioned.' }),
499
511
  'SITE_SURFACE_NOT_PROVISIONED': (0, define_1.defineError)({ code: 'SITE_SURFACE_NOT_PROVISIONED', class: 'public', http: 400, message: 'Site surface not provisioned.' }),
500
512
  'SMS_SIGN_IN_DISABLED': (0, define_1.defineError)({ code: 'SMS_SIGN_IN_DISABLED', class: 'public', http: 403, message: 'Sms sign in disabled.' }),
@@ -503,12 +515,15 @@ exports.generatedRegistry = {
503
515
  'SOURCE_EMAILS_NOT_FOUND': (0, define_1.defineError)({ code: 'SOURCE_EMAILS_NOT_FOUND', class: 'internal', http: 500, message: 'Source emails not found.' }),
504
516
  'SOURCE_SECRETS_NOT_FOUND': (0, define_1.defineError)({ code: 'SOURCE_SECRETS_NOT_FOUND', class: 'internal', http: 500, message: 'Source secrets not found.' }),
505
517
  'SOURCE_USERS_NOT_FOUND': (0, define_1.defineError)({ code: 'SOURCE_USERS_NOT_FOUND', class: 'internal', http: 500, message: 'Source users not found.' }),
518
+ 'STATIC_SITES_LIMIT': (0, define_1.defineError)({ code: 'STATIC_SITES_LIMIT', class: 'public', http: 429, message: 'Static sites limit.' }),
506
519
  'STEP_UP_INVALID_TYPE': (0, define_1.defineError)({ code: 'STEP_UP_INVALID_TYPE', class: 'public', http: 403, message: 'This action requires verification that is not configured correctly. Please contact support.' }),
507
- 'STEP_UP_REQUIRED': (0, define_1.defineError)({ code: 'STEP_UP_REQUIRED', class: 'public', http: 403, message: 'Please verify your identity to continue.' }),
520
+ 'STEP_UP_REQUIRED': (0, define_1.defineError)({ code: 'STEP_UP_REQUIRED', class: 'public', http: 403, message: 'Step up required.' }),
508
521
  'STEP_UP_REQUIRED_FRESH_AUTH': (0, define_1.defineError)({ code: 'STEP_UP_REQUIRED_FRESH_AUTH', class: 'public', http: 403, message: 'Please verify your identity to continue.' }),
509
522
  'STEP_UP_REQUIRED_MFA': (0, define_1.defineError)({ code: 'STEP_UP_REQUIRED_MFA', class: 'public', http: 403, message: 'Please enter a code from your authenticator app to continue.' }),
510
523
  'STEP_UP_REQUIRED_PASSWORD': (0, define_1.defineError)({ code: 'STEP_UP_REQUIRED_PASSWORD', class: 'public', http: 403, message: 'Please re-enter your password to continue.' }),
511
- 'STEP_UP_REQUIRED_PASSWORD_OR_MFA': (0, define_1.defineError)({ code: 'STEP_UP_REQUIRED_PASSWORD_OR_MFA', class: 'public', http: 403, message: 'Please verify your identity to continue.' }),
524
+ 'SUBDOMAIN_APEX_NOT_PUBLISHED': (0, define_1.defineError)({ code: 'SUBDOMAIN_APEX_NOT_PUBLISHED', class: 'public', http: 400, message: 'Subdomain apex not published.' }),
525
+ 'SUBDOMAIN_LABEL_EXHAUSTED': (0, define_1.defineError)({ code: 'SUBDOMAIN_LABEL_EXHAUSTED', class: 'public', http: 400, message: 'Subdomain label exhausted.' }),
526
+ 'SUBDOMAIN_LABEL_INVALID': (0, define_1.defineError)({ code: 'SUBDOMAIN_LABEL_INVALID', class: 'public', http: 400, message: 'Subdomain label invalid.' }),
512
527
  'SUPER_CONSTRUCTIVE_REQUIRED': (0, define_1.defineError)({ code: 'SUPER_CONSTRUCTIVE_REQUIRED', class: 'public', http: 400, message: 'Super constructive required.' }),
513
528
  'TABLE_MODULE': (0, define_1.defineError)({ code: 'TABLE_MODULE', class: 'internal', http: 500, message: 'Table module.' }),
514
529
  'TABLE_MODULE_TABLE_NOT_FOUND': (0, define_1.defineError)({ code: 'TABLE_MODULE_TABLE_NOT_FOUND', class: 'internal', http: 500, message: 'Table module table not found.' }),
@@ -564,6 +579,8 @@ exports.GENERATED_CODE_META = {
564
579
  'APPLY_RLS': { class: 'internal', dynamic: false, generatedOnly: false },
565
580
  'APPLY_RLS_BAD_ARGS': { class: 'internal', dynamic: false, generatedOnly: false },
566
581
  'APPLY_TABLE_STEP_UP_TABLE_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
582
+ 'APP_INSTALLATION_MISSING': { class: 'public', dynamic: false, generatedOnly: false },
583
+ 'APP_INSTALL_NO_SERVICE': { class: 'public', dynamic: false, generatedOnly: false },
567
584
  'ASSIGN_PROFILES_PERMISSION_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
568
585
  'AUTHZ_COLUMN_SECURITY_AUTHZ_NODE_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
569
586
  'AUTHZ_COLUMN_SECURITY_COLUMNS_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
@@ -599,6 +616,7 @@ exports.GENERATED_CODE_META = {
599
616
  'BAD_PRIVILEGE_FOR_POLICY': { class: 'public', dynamic: false, generatedOnly: false },
600
617
  'BAD_RLS_EXPRESSION': { class: 'public', dynamic: false, generatedOnly: false },
601
618
  'BAD_VIEW_EXPRESSION': { class: 'public', dynamic: false, generatedOnly: false },
619
+ 'BEHAVIOR_FRAGMENT_EMPTY': { class: 'internal', dynamic: false, generatedOnly: false },
602
620
  'BM25': { class: 'public', dynamic: false, generatedOnly: false },
603
621
  'BOOTSTRAP_DATABASE_MISSING': { class: 'internal', dynamic: false, generatedOnly: false },
604
622
  'BOOTSTRAP_SOURCE_MISSING': { class: 'internal', dynamic: false, generatedOnly: false },
@@ -635,6 +653,7 @@ exports.GENERATED_CODE_META = {
635
653
  'CROSS_DATABASE_CHECK': { class: 'public', dynamic: false, generatedOnly: false },
636
654
  'CROSS_DATABASE_REF': { class: 'public', dynamic: false, generatedOnly: false },
637
655
  'CSRF_TOKEN_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
656
+ 'DATABASE_CLAIM_REQUIRED': { class: 'internal', dynamic: false, generatedOnly: true },
638
657
  'DATABASE_FIELD_RESERVED_WORD': { class: 'public', dynamic: false, generatedOnly: false },
639
658
  'DATABASE_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
640
659
  'DATABASE_SETTINGS_TABLES_SCOPE_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
@@ -666,6 +685,10 @@ exports.GENERATED_CODE_META = {
666
685
  'DATA_GENERATED_TABLE_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
667
686
  'DATA_GENERATED_TARGET_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
668
687
  'DATA_GENERATED_UNKNOWN_KIND': { class: 'public', dynamic: false, generatedOnly: false },
688
+ 'DATA_HISTORY_NO_COPY_FIELDS': { class: 'public', dynamic: false, generatedOnly: false },
689
+ 'DATA_HISTORY_ON_HISTORY_TABLE': { class: 'public', dynamic: false, generatedOnly: false },
690
+ 'DATA_HISTORY_REQUIRES_PK': { class: 'public', dynamic: false, generatedOnly: false },
691
+ 'DATA_HISTORY_TABLE_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
669
692
  'DATA_I18N_FIELDS_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
670
693
  'DATA_I18N_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
671
694
  'DATA_I18N_MODULE_NOT_PROVISIONED': { class: 'public', dynamic: false, generatedOnly: false },
@@ -691,6 +714,7 @@ exports.GENERATED_CODE_META = {
691
714
  'DATA_JOB_TRIGGER_CONDITIONS_WATCH_FIELDS_CONFLICT': { class: 'public', dynamic: false, generatedOnly: false },
692
715
  'DATA_JOB_TRIGGER_CONDITION_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
693
716
  'DATA_JOB_TRIGGER_CONDITION_FIELD_WATCH_FIELDS_CONFLICT': { class: 'public', dynamic: false, generatedOnly: false },
717
+ 'DATA_JOB_TRIGGER_DATABASE_ID_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
694
718
  'DATA_JOB_TRIGGER_ENTITY_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
695
719
  'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
696
720
  'DATA_JOB_TRIGGER_ENTITY_LOOKUP_OBJ_FIELD_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
@@ -775,10 +799,11 @@ exports.GENERATED_CODE_META = {
775
799
  'EVENT_TRACKER_UNKNOWN_EVENT': { class: 'public', dynamic: false, generatedOnly: false },
776
800
  'EVENT_TRACKER_WATCH_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
777
801
  'EXECUTION_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
778
- 'EXPIRED_TOKEN': { class: 'public', dynamic: false, generatedOnly: false },
802
+ 'EXPIRED_TOKEN': { class: 'internal', dynamic: false, generatedOnly: false },
779
803
  'EXTERNAL_MEMBERS_NOT_ALLOWED': { class: 'public', dynamic: false, generatedOnly: false },
780
804
  'FEATURE_DISABLED': { class: 'public', dynamic: true, generatedOnly: false },
781
805
  'FIELDS_MISMATCH': { class: 'public', dynamic: false, generatedOnly: false },
806
+ 'FOREIGN_KEY_CONSTRAINT_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
782
807
  'FUNCTION_GRAPH_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
783
808
  'FUNCTION_SCHEDULES': { class: 'internal', dynamic: false, generatedOnly: false },
784
809
  'FUNCTION_SCHEDULES_INTERVAL': { class: 'internal', dynamic: false, generatedOnly: false },
@@ -817,6 +842,7 @@ exports.GENERATED_CODE_META = {
817
842
  'HIERARCHY_DEPTH_EXCEEDED': { class: 'internal', dynamic: false, generatedOnly: false },
818
843
  'HIERARCHY_INACTIVE_MEMBER': { class: 'internal', dynamic: true, generatedOnly: false },
819
844
  'HIERARCHY_MEMBER_IN_USE': { class: 'internal', dynamic: true, generatedOnly: false },
845
+ 'HISTORY': { class: 'internal', dynamic: true, generatedOnly: false },
820
846
  'HOSTNAME_BINDING_SYNC': { class: 'internal', dynamic: true, generatedOnly: false },
821
847
  'HTTP_ROUTE_TARGET_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
822
848
  'HTTP_ROUTE_TARGET_OUTSIDE_SCOPE': { class: 'internal', dynamic: false, generatedOnly: false },
@@ -938,6 +964,7 @@ exports.GENERATED_CODE_META = {
938
964
  'ORG_API_KEY_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
939
965
  'OWNER_FIELD_NOT_FOUND': { class: 'public', dynamic: false, generatedOnly: false },
940
966
  'OWNER_FIELD_NOT_IN_OWNER_TABLE': { class: 'public', dynamic: false, generatedOnly: false },
967
+ 'PAGES_MODULE': { class: 'internal', dynamic: false, generatedOnly: false },
941
968
  'PARENT_ENTITY_TYPE_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
942
969
  'PARENT_EXECUTION_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
943
970
  'PASSWORD_INSECURE': { class: 'public', dynamic: false, generatedOnly: false },
@@ -1037,12 +1064,15 @@ exports.GENERATED_CODE_META = {
1037
1064
  'SOURCE_EMAILS_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
1038
1065
  'SOURCE_SECRETS_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
1039
1066
  'SOURCE_USERS_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
1067
+ 'STATIC_SITES_LIMIT': { class: 'public', dynamic: false, generatedOnly: false },
1040
1068
  'STEP_UP_INVALID_TYPE': { class: 'public', dynamic: false, generatedOnly: false },
1041
1069
  'STEP_UP_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
1042
1070
  'STEP_UP_REQUIRED_FRESH_AUTH': { class: 'public', dynamic: false, generatedOnly: false },
1043
1071
  'STEP_UP_REQUIRED_MFA': { class: 'public', dynamic: false, generatedOnly: false },
1044
1072
  'STEP_UP_REQUIRED_PASSWORD': { class: 'public', dynamic: false, generatedOnly: false },
1045
- 'STEP_UP_REQUIRED_PASSWORD_OR_MFA': { class: 'public', dynamic: false, generatedOnly: true },
1073
+ 'SUBDOMAIN_APEX_NOT_PUBLISHED': { class: 'public', dynamic: false, generatedOnly: false },
1074
+ 'SUBDOMAIN_LABEL_EXHAUSTED': { class: 'public', dynamic: false, generatedOnly: false },
1075
+ 'SUBDOMAIN_LABEL_INVALID': { class: 'public', dynamic: false, generatedOnly: false },
1046
1076
  'SUPER_CONSTRUCTIVE_REQUIRED': { class: 'public', dynamic: false, generatedOnly: false },
1047
1077
  'TABLE_MODULE': { class: 'internal', dynamic: false, generatedOnly: false },
1048
1078
  'TABLE_MODULE_TABLE_NOT_FOUND': { class: 'internal', dynamic: false, generatedOnly: false },
@@ -1085,4 +1115,4 @@ exports.GENERATED_CODE_META = {
1085
1115
  'WITH_CHECK_NOT_ALLOWED': { class: 'public', dynamic: false, generatedOnly: false },
1086
1116
  };
1087
1117
  /** Total number of codes collected from constructive-db. */
1088
- exports.GENERATED_CODE_COUNT = 531;
1118
+ exports.GENERATED_CODE_COUNT = 546;
package/http.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /** Status used when a code carries no mapping. */
2
+ export declare const UNMAPPED_HTTP_STATUS = 500;
3
+ export interface HttpStatusResolution {
4
+ /** The status a transport should send. */
5
+ status: number;
6
+ /**
7
+ * Whether `status` came from the registry. `false` means the code is not
8
+ * registered and `status` is the {@link UNMAPPED_HTTP_STATUS} fallback — the
9
+ * one case where a 500 does not mean "the server broke".
10
+ */
11
+ mapped: boolean;
12
+ }
13
+ /** Notified once per unmapped code. */
14
+ export type UnmappedStatusReporter = (code: string) => void;
15
+ /**
16
+ * Replace the unmapped-code reporter (pass `null` to restore the default, or a
17
+ * no-op to silence it). Transports with a real logger should route it there.
18
+ */
19
+ export declare function setUnmappedStatusReporter(next: UnmappedStatusReporter | null): void;
20
+ /** Test seam: forget which codes have already been reported. */
21
+ export declare function resetUnmappedStatusReports(): void;
22
+ /**
23
+ * Resolve the HTTP status for an error code.
24
+ *
25
+ * An unregistered code is reported (once per code) rather than silently
26
+ * degrading to 500: a refusal that is plainly a 403 or 409 turning into a 500
27
+ * looks like a crash, and the only way anyone notices is by reading the
28
+ * transport's source.
29
+ */
30
+ export declare function httpStatusFor(code: string | null | undefined): HttpStatusResolution;
package/http.js ADDED
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UNMAPPED_HTTP_STATUS = void 0;
4
+ exports.setUnmappedStatusReporter = setUnmappedStatusReporter;
5
+ exports.resetUnmappedStatusReports = resetUnmappedStatusReports;
6
+ exports.httpStatusFor = httpStatusFor;
7
+ const registry_1 = require("./registry");
8
+ /** Status used when a code carries no mapping. */
9
+ exports.UNMAPPED_HTTP_STATUS = 500;
10
+ const reported = new Set();
11
+ const defaultReporter = code => {
12
+ // eslint-disable-next-line no-console
13
+ console.warn(`[constructive-errors] no HTTP status mapping for ${code}; responding ${exports.UNMAPPED_HTTP_STATUS}. ` +
14
+ 'Register the code so intentional refusals stop surfacing as server errors.');
15
+ };
16
+ let reporter = defaultReporter;
17
+ /**
18
+ * Replace the unmapped-code reporter (pass `null` to restore the default, or a
19
+ * no-op to silence it). Transports with a real logger should route it there.
20
+ */
21
+ function setUnmappedStatusReporter(next) {
22
+ reporter = next ?? defaultReporter;
23
+ }
24
+ /** Test seam: forget which codes have already been reported. */
25
+ function resetUnmappedStatusReports() {
26
+ reported.clear();
27
+ }
28
+ /**
29
+ * Resolve the HTTP status for an error code.
30
+ *
31
+ * An unregistered code is reported (once per code) rather than silently
32
+ * degrading to 500: a refusal that is plainly a 403 or 409 turning into a 500
33
+ * looks like a crash, and the only way anyone notices is by reading the
34
+ * transport's source.
35
+ */
36
+ function httpStatusFor(code) {
37
+ const def = code ? (0, registry_1.getDefinition)(code) : undefined;
38
+ if (def)
39
+ return { status: def.http, mapped: true };
40
+ if (code && !reported.has(code)) {
41
+ reported.add(code);
42
+ reporter(code);
43
+ }
44
+ return { status: exports.UNMAPPED_HTTP_STATUS, mapped: false };
45
+ }
package/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './classify';
2
2
  export * from './error';
3
3
  export * from './factory';
4
4
  export * from './format';
5
+ export * from './http';
5
6
  export * from './interpolate';
6
7
  export * from './parse';
7
8
  export * from './pg';
package/index.js CHANGED
@@ -18,6 +18,7 @@ __exportStar(require("./classify"), exports);
18
18
  __exportStar(require("./error"), exports);
19
19
  __exportStar(require("./factory"), exports);
20
20
  __exportStar(require("./format"), exports);
21
+ __exportStar(require("./http"), exports);
21
22
  __exportStar(require("./interpolate"), exports);
22
23
  __exportStar(require("./parse"), exports);
23
24
  __exportStar(require("./pg"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/errors",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "Canonical Constructive error system: registry, codes, i18n messages, and cross-source (PostgreSQL/GraphQL/client) parsing. Zero runtime dependencies.",
6
6
  "main": "index.js",
@@ -41,5 +41,5 @@
41
41
  "makage": "^0.3.0",
42
42
  "ts-node": "^10.9.2"
43
43
  },
44
- "gitHead": "d508453a32a2f1d70ff6916871046615226e6f33"
44
+ "gitHead": "43121004a8bedd2c5fcf7bf33208079d55bad01f"
45
45
  }
package/parse.d.ts CHANGED
@@ -25,6 +25,8 @@ export declare function parse(error: unknown): ParsedError;
25
25
  * Delegates to {@link parse} for code/context/class recovery, then resolves a
26
26
  * localized message from the registry catalogs (falling back to the source
27
27
  * error's raw message when the code is unknown) and the registry's HTTP hint.
28
- * Codes that could not be resolved become `UNKNOWN_ERROR` (internal).
28
+ * Codes that could not be resolved become `UNKNOWN_ERROR` (internal); a code
29
+ * with no registered status is reported by {@link httpStatusFor} rather than
30
+ * quietly becoming a 500.
29
31
  */
30
32
  export declare function toError(error: unknown, locale?: string): ConstructiveError;
package/parse.js CHANGED
@@ -5,6 +5,7 @@ exports.toError = toError;
5
5
  const classify_1 = require("./classify");
6
6
  const error_1 = require("./error");
7
7
  const format_1 = require("./format");
8
+ const http_1 = require("./http");
8
9
  const pg_1 = require("./pg");
9
10
  const registry_1 = require("./registry");
10
11
  /** Leading ALL_CAPS token, optionally followed by `(arg, arg)` positional args. */
@@ -182,7 +183,9 @@ function parse(error) {
182
183
  * Delegates to {@link parse} for code/context/class recovery, then resolves a
183
184
  * localized message from the registry catalogs (falling back to the source
184
185
  * error's raw message when the code is unknown) and the registry's HTTP hint.
185
- * Codes that could not be resolved become `UNKNOWN_ERROR` (internal).
186
+ * Codes that could not be resolved become `UNKNOWN_ERROR` (internal); a code
187
+ * with no registered status is reported by {@link httpStatusFor} rather than
188
+ * quietly becoming a 500.
186
189
  */
187
190
  function toError(error, locale) {
188
191
  if (error instanceof error_1.ConstructiveError)
@@ -197,7 +200,7 @@ function toError(error, locale) {
197
200
  code,
198
201
  message,
199
202
  errorClass: parsed.class,
200
- http: def?.http ?? 500,
203
+ http: def ? def.http : (0, http_1.httpStatusFor)(code).status,
201
204
  context: parsed.context
202
205
  });
203
206
  }