@vxil/feature-configs 0.2.0 → 0.3.1

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 ADDED
@@ -0,0 +1,58 @@
1
+ # @vxil/feature-configs
2
+
3
+ The typed config schemas for every Vxil feature — the shapes a `vxil.config.ts`
4
+ is built on, published so your editor and your CI can read the same contract the
5
+ platform validates against.
6
+
7
+ ```bash
8
+ npm i -D @vxil/feature-configs
9
+ ```
10
+
11
+ Most people never import this directly: writing
12
+ [`defineConfig`](https://www.npmjs.com/package/@vxil/config) gives you these
13
+ types automatically. Reach for it when you want to validate a manifest yourself,
14
+ generate config, or type a helper.
15
+
16
+ ## What's in it
17
+
18
+ - **One schema per feature**, all 19 of them — `NotificationsConfigSchema`,
19
+ `AuthConfigSchema`, `CmsConfigSchema`, `PaymentsConfigSchema`,
20
+ `FunctionsConfigSchema`, … — plus the matching `Static<>` types
21
+ (`NotificationsConfig`, `AuthConfig`, …).
22
+ - **`FEATURE_KEYS`** — the canonical feature list, and **`FEATURE_SCHEMAS`** —
23
+ the key → schema map.
24
+ - **`validateFeatureConfig(feature, raw)`** — the exact validation the control
25
+ plane runs, returning `{ ok, errors }` with JSON-pointer-style messages. Use it
26
+ in a pre-commit hook or a test to fail on a bad config before you push it.
27
+ - **`hooks`** (`@vxil/feature-configs/hooks`) — the CMS lifecycle-hook expression
28
+ grammar: the same closed, sandboxed parser/validator the server uses, so a
29
+ malformed `validate` / `derive` expression is caught in your editor.
30
+
31
+ ```ts
32
+ import { validateFeatureConfig, FEATURE_KEYS } from '@vxil/feature-configs';
33
+
34
+ const result = validateFeatureConfig('notifications', {
35
+ enabled: true,
36
+ fromEmail: 'noreply@acme.com',
37
+ });
38
+ if (!result.ok) throw new Error(result.errors.join('\n'));
39
+ ```
40
+
41
+ ## Why the schemas are small on purpose
42
+
43
+ Every feature's config is capped at a fixed number of top-level leaves. A knob
44
+ earns its slot by having a reader; one that nothing reads gets deleted rather
45
+ than left to rot as a promise the product does not keep. That is why the schemas
46
+ here are the whole truth about what a feature can be configured to do — and why
47
+ a version bump may *remove* a knob that was never wired. Removals are listed in
48
+ the feature reference at [vxil.com](https://vxil.com).
49
+
50
+ ## Related
51
+
52
+ - [`@vxil/config`](https://www.npmjs.com/package/@vxil/config) — `defineConfig()`.
53
+ - [`@vxil/cli`](https://www.npmjs.com/package/@vxil/cli) — the `vxil` command (`plan` / `push` / `gen`).
54
+ - [`@vxil/sdk`](https://www.npmjs.com/package/@vxil/sdk) — the typed runtime client.
55
+
56
+ Docs: [vxil.com](https://vxil.com) · Dashboard: [vxil.com/dashboard](https://vxil.com/dashboard)
57
+
58
+ MIT © techmaker.io
package/dist/hooks.d.ts CHANGED
@@ -10,7 +10,7 @@ export declare const HOOK_LIMITS: {
10
10
  readonly maxReadHooksPerCollection: 10;
11
11
  };
12
12
  /** The ONLY root variables an expression may reference. `caller` is the VERIFIED
13
- * end-user principal (docs/end-user-principals-design.md §5.6) — read-only and
13
+ * end-user principal (https://vxil.com/docs/guide/09-security-and-multitenancy) — read-only and
14
14
  * populated ONLY on the READ path (runReadHooks); on the write path and in
15
15
  * server-caller mode it is null, exactly like `before` on a create. It carries
16
16
  * `caller.endUserId` (the verified session sub, or null) and `caller.principal`
package/dist/hooks.js CHANGED
@@ -1,4 +1,4 @@
1
- // CMS lifecycle hooks — the SAFE expression engine (docs/cms-lifecycle-hooks-rung2-design.md, Lane A).
1
+ // CMS lifecycle hooks — the SAFE expression engine (https://vxil.com/docs/guide/07-validation-and-hooks).
2
2
  //
3
3
  // A "code-based" lifecycle hook is a small EXPRESSION the tenant authors. Three kinds:
4
4
  // - validate: the expression must evaluate truthy or the write is REJECTED (422)
@@ -17,8 +17,8 @@
17
17
  // CROSS-ROW AGGREGATION STAYS OUT — BY DESIGN. Read hooks are a pure function
18
18
  // of ONE row (+ `now`): there are no new root variables, no array/aggregate
19
19
  // functions, no access to sibling rows or other collections. Counts/sums/
20
- // group-bys across rows are the §5 identity-fork anti-item (docs/features/
21
- // cms.md §6.7) and must never enter this engine.
20
+ // group-bys across rows are the identity-fork anti-item (function territory:
21
+ // vxil.com/docs/guide/07-validation-and-hooks) and must never enter this engine.
22
22
  //
23
23
  // SAFETY (the "not malicious" guarantee) is structural, not heuristic:
24
24
  // * No JS is executed. `eval`/`Function`/the host runtime are never touched. The
@@ -61,7 +61,7 @@ export const HOOK_LIMITS = {
61
61
  };
62
62
  // ── allow-lists ─────────────────────────────────────────────────────────────
63
63
  /** The ONLY root variables an expression may reference. `caller` is the VERIFIED
64
- * end-user principal (docs/end-user-principals-design.md §5.6) — read-only and
64
+ * end-user principal (https://vxil.com/docs/guide/09-security-and-multitenancy) — read-only and
65
65
  * populated ONLY on the READ path (runReadHooks); on the write path and in
66
66
  * server-caller mode it is null, exactly like `before` on a create. It carries
67
67
  * `caller.endUserId` (the verified session sub, or null) and `caller.principal`
package/dist/index.d.ts CHANGED
@@ -5,6 +5,32 @@ export declare const RESERVED_CREDIT_TYPES: ReadonlySet<string>;
5
5
  /** True when a credit_type is a vxil-COGS reserved type (grant/consume/config
6
6
  * of which is restricted to internal platform machinery). */
7
7
  export declare function isReservedCreditType(creditType: string): boolean;
8
+ export declare const NOTIFICATION_TEMPLATE_PLACEHOLDERS: Readonly<Record<string, readonly string[]>>;
9
+ /** Per-override caps (D4). Bodies are emails, not documents. */
10
+ export declare const NOTIF_OVERRIDE_SUBJECT_MAX = 500;
11
+ export declare const NOTIF_OVERRIDE_BODY_MAX = 20000;
12
+ /** Whole-map caps: keeps one manifest (and the KV row every send reads) small. */
13
+ export declare const NOTIF_OVERRIDE_MAX_LOCALES_PER_TEMPLATE = 20;
14
+ export declare const NOTIF_OVERRIDE_MAX_BYTES: number;
15
+ /**
16
+ * D4 — validate `templates.overrides` at CONFIG-WRITE time. Returns [] when the
17
+ * bag is absent or clean. Every rule fails LOUD rather than shipping something
18
+ * that silently renders wrong in a customer's inbox:
19
+ * • overrides present with `allowOverride:false` → rejected (never inert);
20
+ * • an unknown template id / locale tag → rejected;
21
+ * • an unknown `{{placeholder}}` in subject/html/text → rejected (the class of
22
+ * typo that renders as an empty string in a live email);
23
+ * • size caps (per-locale count, whole-map bytes) → rejected.
24
+ * Pure, so it is unit-tested without a control plane.
25
+ */
26
+ export declare function validateNotificationOverrides(templates: {
27
+ allowOverride?: boolean;
28
+ overrides?: Record<string, Record<string, {
29
+ subject?: string;
30
+ html?: string;
31
+ text?: string;
32
+ }>>;
33
+ } | undefined): string[];
8
34
  export declare const NotificationsConfigSchema: import("@sinclair/typebox").TObject<{
9
35
  enabled: import("@sinclair/typebox").TBoolean;
10
36
  fromEmail: import("@sinclair/typebox").TString;
@@ -25,9 +51,14 @@ export declare const NotificationsConfigSchema: import("@sinclair/typebox").TObj
25
51
  perDay: import("@sinclair/typebox").TInteger;
26
52
  perTenantSec: import("@sinclair/typebox").TInteger;
27
53
  }>;
28
- templates: import("@sinclair/typebox").TObject<{
54
+ templates: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
29
55
  allowOverride: import("@sinclair/typebox").TBoolean;
30
- }>;
56
+ overrides: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
57
+ subject: import("@sinclair/typebox").TString;
58
+ html: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
59
+ text: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
60
+ }>>>>;
61
+ }>>;
31
62
  /** in-app inbox channel (send with channel: 'inbox' | 'both') */
32
63
  inboxEnabled: import("@sinclair/typebox").TBoolean;
33
64
  /** §11b.5 broadcast campaigns channel. Optional bag (= 1 leaf): absent means
@@ -100,16 +131,29 @@ export declare const JobsConfigSchema: import("@sinclair/typebox").TObject<{
100
131
  }>;
101
132
  }>;
102
133
  export type JobsConfig = Static<typeof JobsConfigSchema>;
134
+ declare const OidcProviderSchema: import("@sinclair/typebox").TObject<{
135
+ issuer: import("@sinclair/typebox").TString;
136
+ clientId: import("@sinclair/typebox").TString;
137
+ clientSecretRef: import("@sinclair/typebox").TString;
138
+ scopes: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
139
+ claims: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
140
+ email: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
141
+ name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
142
+ roles: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
143
+ }>>;
144
+ allowedDomains: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
145
+ autoLink: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
146
+ }>;
103
147
  export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
104
148
  enabled: import("@sinclair/typebox").TBoolean;
105
- methods: import("@sinclair/typebox").TObject<{
149
+ methods: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
106
150
  emailPassword: import("@sinclair/typebox").TBoolean;
107
151
  magicLink: import("@sinclair/typebox").TBoolean;
108
152
  google: import("@sinclair/typebox").TBoolean;
109
153
  github: import("@sinclair/typebox").TBoolean;
110
154
  apple: import("@sinclair/typebox").TBoolean;
111
155
  facebook: import("@sinclair/typebox").TBoolean;
112
- }>;
156
+ }>>;
113
157
  providers: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
114
158
  google: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
115
159
  clientIdRef: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -133,10 +177,24 @@ export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
133
177
  clientSecretRef: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
134
178
  audRefs: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
135
179
  }>>;
180
+ oidc: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
181
+ issuer: import("@sinclair/typebox").TString;
182
+ clientId: import("@sinclair/typebox").TString;
183
+ clientSecretRef: import("@sinclair/typebox").TString;
184
+ scopes: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
185
+ claims: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
186
+ email: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
187
+ name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
188
+ roles: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
189
+ }>>;
190
+ allowedDomains: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
191
+ autoLink: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
192
+ }>>;
136
193
  }>>;
137
194
  session: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
138
195
  ttlMinutes: import("@sinclair/typebox").TInteger;
139
196
  refreshTtlDays: import("@sinclair/typebox").TInteger;
197
+ maxConcurrent: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
140
198
  }>>;
141
199
  password: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
142
200
  minLength: import("@sinclair/typebox").TInteger;
@@ -153,6 +211,7 @@ export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
153
211
  codeTtlMinutes: import("@sinclair/typebox").TInteger;
154
212
  maxAttempts: import("@sinclair/typebox").TInteger;
155
213
  resendCooldownSec: import("@sinclair/typebox").TInteger;
214
+ testRecipients: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
156
215
  }>>;
157
216
  anonymous: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
158
217
  enabled: import("@sinclair/typebox").TBoolean;
@@ -160,7 +219,21 @@ export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
160
219
  orgClaims: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
161
220
  enabled: import("@sinclair/typebox").TBoolean;
162
221
  }>>;
222
+ security: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
223
+ lockout: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
224
+ maxFailures: import("@sinclair/typebox").TInteger;
225
+ windowMinutes: import("@sinclair/typebox").TInteger;
226
+ lockMinutes: import("@sinclair/typebox").TInteger;
227
+ }>>;
228
+ breachedPasswords: import("@sinclair/typebox").TBoolean;
229
+ captchaSecretRef: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
230
+ allowedRedirectOrigins: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
231
+ }>>;
163
232
  }>;
233
+ /** The security bag as persisted (present ⇒ leaf defaults applied). */
234
+ export type AuthSecurityConfig = NonNullable<Static<typeof AuthConfigSchema>['security']>;
235
+ /** The RB-2 generic OIDC / SSO issuer block (`providers.oidc`). */
236
+ export type AuthOidcConfig = Static<typeof OidcProviderSchema>;
164
237
  export type AuthConfig = Static<typeof AuthConfigSchema>;
165
238
  export declare const RateLimitsConfigSchema: import("@sinclair/typebox").TObject<{
166
239
  enabled: import("@sinclair/typebox").TBoolean;
@@ -172,7 +245,6 @@ export declare const RateLimitsConfigSchema: import("@sinclair/typebox").TObject
172
245
  export type RateLimitsConfig = Static<typeof RateLimitsConfigSchema>;
173
246
  export declare const FilesConfigSchema: import("@sinclair/typebox").TObject<{
174
247
  enabled: import("@sinclair/typebox").TBoolean;
175
- bucketRef: import("@sinclair/typebox").TString;
176
248
  uploadUrlTtl: import("@sinclair/typebox").TInteger;
177
249
  downloadUrlTtl: import("@sinclair/typebox").TInteger;
178
250
  quotas: import("@sinclair/typebox").TObject<{
@@ -181,10 +253,6 @@ export declare const FilesConfigSchema: import("@sinclair/typebox").TObject<{
181
253
  maxObjectCount: import("@sinclair/typebox").TInteger;
182
254
  }>;
183
255
  allowedContentTypes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
184
- contentScan: import("@sinclair/typebox").TObject<{
185
- enabled: import("@sinclair/typebox").TBoolean;
186
- quarantineOnFail: import("@sinclair/typebox").TBoolean;
187
- }>;
188
256
  sharedLinks: import("@sinclair/typebox").TObject<{
189
257
  enabled: import("@sinclair/typebox").TBoolean;
190
258
  maxTtl: import("@sinclair/typebox").TInteger;
@@ -268,9 +336,6 @@ export declare const McpConfigSchema: import("@sinclair/typebox").TObject<{
268
336
  enabled: import("@sinclair/typebox").TBoolean;
269
337
  exposureLevel: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"all">, import("@sinclair/typebox").TLiteral<"read-only">, import("@sinclair/typebox").TLiteral<"custom">]>;
270
338
  allowToolList: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
271
- scopedKey: import("@sinclair/typebox").TObject<{
272
- perAgentKeys: import("@sinclair/typebox").TBoolean;
273
- }>;
274
339
  rateLimits: import("@sinclair/typebox").TObject<{
275
340
  toolCallsPerMin: import("@sinclair/typebox").TInteger;
276
341
  }>;
@@ -328,9 +393,6 @@ export declare const ActivityFeedConfigSchema: import("@sinclair/typebox").TObje
328
393
  copyLimit: import("@sinclair/typebox").TInteger;
329
394
  maxFollowing: import("@sinclair/typebox").TInteger;
330
395
  }>;
331
- aggregation: import("@sinclair/typebox").TObject<{
332
- maxGroupActivities: import("@sinclair/typebox").TInteger;
333
- }>;
334
396
  realtime: import("@sinclair/typebox").TObject<{
335
397
  enabled: import("@sinclair/typebox").TBoolean;
336
398
  }>;
@@ -338,9 +400,6 @@ export declare const ActivityFeedConfigSchema: import("@sinclair/typebox").TObje
338
400
  enabled: import("@sinclair/typebox").TBoolean;
339
401
  digestCadence: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"off">, import("@sinclair/typebox").TLiteral<"hourly">, import("@sinclair/typebox").TLiteral<"daily">]>;
340
402
  }>;
341
- rateLimit: import("@sinclair/typebox").TObject<{
342
- addPerSec: import("@sinclair/typebox").TInteger;
343
- }>;
344
403
  }>;
345
404
  export type ActivityFeedConfig = Static<typeof ActivityFeedConfigSchema>;
346
405
  export declare const VectorSearchConfigSchema: import("@sinclair/typebox").TObject<{
@@ -458,6 +517,7 @@ export declare const PaymentsConfigSchema: import("@sinclair/typebox").TObject<{
458
517
  publicSdkKey: import("@sinclair/typebox").TString;
459
518
  secretApiKeyRef: import("@sinclair/typebox").TString;
460
519
  webhookSecretRef: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
520
+ acceptSandbox: import("@sinclair/typebox").TBoolean;
461
521
  }>>;
462
522
  paypal: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
463
523
  clientIdRef: import("@sinclair/typebox").TString;
@@ -466,9 +526,6 @@ export declare const PaymentsConfigSchema: import("@sinclair/typebox").TObject<{
466
526
  sandbox: import("@sinclair/typebox").TBoolean;
467
527
  }>>;
468
528
  returnUrl: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
469
- prices: import("@sinclair/typebox").TObject<{
470
- catalogRef: import("@sinclair/typebox").TString;
471
- }>;
472
529
  defaults: import("@sinclair/typebox").TObject<{
473
530
  currency: import("@sinclair/typebox").TString;
474
531
  trialDays: import("@sinclair/typebox").TInteger;
@@ -491,19 +548,19 @@ export declare const PaymentsConfigSchema: import("@sinclair/typebox").TObject<{
491
548
  }>>;
492
549
  priceMap: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TString>>;
493
550
  autoRefundOnJobFailure: import("@sinclair/typebox").TBoolean;
551
+ grace: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
552
+ pastDueDays: import("@sinclair/typebox").TInteger;
553
+ }>>;
554
+ enforcePeriodEnd: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
555
+ slackHours: import("@sinclair/typebox").TInteger;
556
+ }>>;
494
557
  }>>;
495
- webhooks: import("@sinclair/typebox").TObject<{
496
- forwardToTenantUrl: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
497
- }>;
498
558
  }>;
499
559
  export type PaymentsConfig = Static<typeof PaymentsConfigSchema>;
500
560
  export declare const FunctionsConfigSchema: import("@sinclair/typebox").TObject<{
501
561
  enabled: import("@sinclair/typebox").TBoolean;
502
- runtime: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"isolate">, import("@sinclair/typebox").TLiteral<"container">]>;
503
562
  defaultLimits: import("@sinclair/typebox").TObject<{
504
563
  cpuMs: import("@sinclair/typebox").TInteger;
505
- timeoutMs: import("@sinclair/typebox").TInteger;
506
- memoryMb: import("@sinclair/typebox").TInteger;
507
564
  }>;
508
565
  metering: import("@sinclair/typebox").TObject<{
509
566
  enabled: import("@sinclair/typebox").TBoolean;
@@ -567,14 +624,8 @@ export declare const CopilotConfigSchema: import("@sinclair/typebox").TObject<{
567
624
  guestToolAllow: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
568
625
  }>>;
569
626
  }>>;
570
- escalation: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
571
- enabled: import("@sinclair/typebox").TBoolean;
572
- handler: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"comments">, import("@sinclair/typebox").TLiteral<"notifications">]>;
573
- notifyTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
574
- }>>;
575
627
  limits: import("@sinclair/typebox").TObject<{
576
628
  consumeCredits: import("@sinclair/typebox").TBoolean;
577
- tokensPerUserPerDay: import("@sinclair/typebox").TInteger;
578
629
  }>;
579
630
  widget: import("@sinclair/typebox").TObject<{
580
631
  enabled: import("@sinclair/typebox").TBoolean;
@@ -590,6 +641,21 @@ export declare const CONFIG_FLAG_CAP = 15;
590
641
  * Per features/auth.md §4: an OPTIONAL object (e.g. a provider credential
591
642
  * block) counts as ONE flag — the tenant's decision is "configure it or
592
643
  * not", not each inner ref. */
644
+ /** The auth lifecycle events an `authHook` binding may name (F4-30) — the
645
+ * closed union `packages/config` types as AuthHookEvent; the control-plane
646
+ * reconciler maps each to its `auth.<event>` audit-event prefix. */
647
+ export declare const AUTH_HOOK_EVENTS: readonly ["user.created", "session.created", "session.revoked", "signin.failure"];
648
+ export type AuthHookEvent = (typeof AUTH_HOOK_EVENTS)[number];
649
+ /** F4-29 test-recipient entry grammar (shared by the validator and auth-v1's
650
+ * matcher): an exact email, a `*@domain` glob, or a +E.164 phone number. */
651
+ export declare const TEST_RECIPIENT_EMAIL_RE: RegExp;
652
+ export declare const TEST_RECIPIENT_GLOB_RE: RegExp;
653
+ export declare const TEST_RECIPIENT_E164_RE: RegExp;
654
+ export declare function isTestRecipientPattern(entry: string): boolean;
655
+ /** True iff `identifier` (an email today) matches one configured entry:
656
+ * exact (case-insensitive) or the `*@domain` glob. +E.164 entries never match
657
+ * an email. */
658
+ export declare function matchesTestRecipient(identifier: string, entries: readonly string[] | undefined): boolean;
593
659
  export declare function countLeaves(schema: TSchema): number;
594
660
  export interface ConfigValidation {
595
661
  ok: boolean;