@palbase/backend 10.1.0 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,12 +1,68 @@
1
- import { C as CacheClient, b as PalbaseDocsClient, c as PalbaseFlagsClient, L as Logger, d as PalbaseNotificationsClient, Q as QueueClient, e as PalbaseRealtimeClient, D as DBClient, f as PalbaseStorageClient, A as AuthSpec, R as RateLimitConfig, H as HttpError, U as User$1 } from './endpoint-Cn3ICGTf.cjs';
2
- export { g as AuthConfig, B as BadRequest, h as ClientInfo, i as Conflict, j as DBOps, E as ErrorDef, k as ErrorMap, l as ErrorThrowers, F as FileContext, m as Forbidden, n as HttpMethod, M as Middleware, o as MiddlewareContext, p as MiddlewareHandler, N as NotFound, P as PBRequest, q as PalError, r as PalbaseAnalyticsClient, s as PalbaseAnalyticsManagementNamespace, t as PalbaseAnalyticsProperties, u as PalbaseAnalyticsQueryNamespace, v as PalbaseAttestAndroidParams, w as PalbaseAttestAndroidResult, x as PalbaseAttestiOSParams, y as PalbaseAttestiOSResult, z as PalbaseAuthClient, G as PalbaseBatchOverrideOperation, I as PalbaseBatchSetOverridesResult, J as PalbaseBindDeviceParams, K as PalbaseBucketClient, O as PalbaseClearAllOverridesResult, S as PalbaseClearOverrideResult, T as PalbaseCohortQueryInput, V as PalbaseCohortResult, W as PalbaseCollectionRef, X as PalbaseCountQueryInput, Y as PalbaseCountResult, Z as PalbaseCreateLinkParams, _ as PalbaseDeviceInfo, $ as PalbaseDeviceTokenView, a0 as PalbaseDocumentRef, a1 as PalbaseDocumentSnapshot, a2 as PalbaseEmailClient, a3 as PalbaseEmailSendParams, a4 as PalbaseEmailSendResponse, a5 as PalbaseEventNamesResult, a6 as PalbaseEventsQueryInput, a7 as PalbaseEventsResult, a8 as PalbaseFileObject, a9 as PalbaseFlag, aa as PalbaseFlagContext, ab as PalbaseFlagSource, ac as PalbaseFlagValue, ad as PalbaseFlagVariant, ae as PalbaseFlagsServiceClient, af as PalbaseFunctionsClient, ag as PalbaseFunnelQueryInput, ah as PalbaseFunnelResult, ai as PalbaseIdentifyTraits, aj as PalbaseInboxClient, ak as PalbaseInboxListOptions, al as PalbaseInboxListResult, am as PalbaseInboxMessage, an as PalbaseInboxSendParams, ao as PalbaseInboxSendResponse, ap as PalbaseInitialLink, aq as PalbaseInvokeOptions, ar as PalbaseLink, as as PalbaseLinkAnalytics, at as PalbaseLinkDetails, au as PalbaseLinksClient, av as PalbaseListLinksOptions, aw as PalbaseListLinksResult, ax as PalbaseListOptions, ay as PalbaseMatchParams, az as PalbaseMultiChannelResponse, aA as PalbaseOverviewResult, aB as PalbasePreferences, aC as PalbasePreferencesClient, aD as PalbasePublicUrlResponse, aE as PalbasePushClient, aF as PalbasePushSendParams, aG as PalbasePushSendResponse, aH as PalbaseQrCodeOptions, aI as PalbaseQuerySnapshot, aJ as PalbaseRegisterDeviceParams, aK as PalbaseResult, aL as PalbaseRetentionQueryInput, aM as PalbaseRetentionResult, aN as PalbaseSession, aO as PalbaseSetOverrideResult, aP as PalbaseSetOverridesResult, aQ as PalbaseSignedUrlResponse, aR as PalbaseSmsClient, aS as PalbaseSmsSendParams, aT as PalbaseSmsSendResponse, aU as PalbaseTransformOptions, aV as PalbaseUpdateLinkParams, aW as PalbaseUploadOptions, aX as PalbaseUser, aY as PalbaseUserDetailResult, aZ as PalbaseUsersQueryInput, a_ as PalbaseUsersResult, a$ as PalbaseVerifyRequestSignatureParams, b0 as PalbaseWhereOperator, b1 as TooManyRequests, b2 as TxClient, b3 as Unauthorized, b4 as VerifiedDevice, b5 as defineMiddleware } from './endpoint-Cn3ICGTf.cjs';
1
+ import { C as CacheClient, b as PalbaseDocsClient, c as PalbaseFlagsClient, L as Logger, d as PalbaseNotificationsClient, Q as QueueClient, e as PalbaseRealtimeClient, D as DBClient, f as PalbaseStorageClient, A as AuthSpec, R as RateLimitConfig, H as HttpError, U as User$1 } from './endpoint-92kVepng.cjs';
2
+ export { g as AuthConfig, B as BadRequest, h as ClientInfo, i as Conflict, j as DBOps, E as ErrorDef, k as ErrorMap, l as ErrorThrowers, F as FileContext, m as Forbidden, n as HttpMethod, M as Middleware, o as MiddlewareContext, p as MiddlewareHandler, N as NotFound, P as PBRequest, q as PalError, r as PalbaseAnalyticsClient, s as PalbaseAnalyticsManagementNamespace, t as PalbaseAnalyticsProperties, u as PalbaseAnalyticsQueryNamespace, v as PalbaseAttestAndroidParams, w as PalbaseAttestAndroidResult, x as PalbaseAttestiOSParams, y as PalbaseAttestiOSResult, z as PalbaseAuthClient, G as PalbaseBatchOverrideOperation, I as PalbaseBatchSetOverridesResult, J as PalbaseBindDeviceParams, K as PalbaseBucketClient, O as PalbaseClearAllOverridesResult, S as PalbaseClearOverrideResult, T as PalbaseCohortQueryInput, V as PalbaseCohortResult, W as PalbaseCollectionRef, X as PalbaseCountQueryInput, Y as PalbaseCountResult, Z as PalbaseCreateLinkParams, _ as PalbaseDeviceInfo, $ as PalbaseDeviceTokenView, a0 as PalbaseDocumentRef, a1 as PalbaseDocumentSnapshot, a2 as PalbaseEmailClient, a3 as PalbaseEmailSendParams, a4 as PalbaseEmailSendResponse, a5 as PalbaseEventNamesResult, a6 as PalbaseEventsQueryInput, a7 as PalbaseEventsResult, a8 as PalbaseFileObject, a9 as PalbaseFlag, aa as PalbaseFlagContext, ab as PalbaseFlagSource, ac as PalbaseFlagValue, ad as PalbaseFlagVariant, ae as PalbaseFlagsServiceClient, af as PalbaseFunctionsClient, ag as PalbaseFunnelQueryInput, ah as PalbaseFunnelResult, ai as PalbaseIdentifyTraits, aj as PalbaseInboxClient, ak as PalbaseInboxListOptions, al as PalbaseInboxListResult, am as PalbaseInboxMessage, an as PalbaseInboxSendParams, ao as PalbaseInboxSendResponse, ap as PalbaseInitialLink, aq as PalbaseInvokeOptions, ar as PalbaseLink, as as PalbaseLinkAnalytics, at as PalbaseLinkDetails, au as PalbaseLinksClient, av as PalbaseListLinksOptions, aw as PalbaseListLinksResult, ax as PalbaseListOptions, ay as PalbaseMatchParams, az as PalbaseMultiChannelResponse, aA as PalbaseOverviewResult, aB as PalbasePreferences, aC as PalbasePreferencesClient, aD as PalbasePublicUrlResponse, aE as PalbasePushClient, aF as PalbasePushSendParams, aG as PalbasePushSendResponse, aH as PalbaseQrCodeOptions, aI as PalbaseQuerySnapshot, aJ as PalbaseRegisterDeviceParams, aK as PalbaseResult, aL as PalbaseRetentionQueryInput, aM as PalbaseRetentionResult, aN as PalbaseSession, aO as PalbaseSetOverrideResult, aP as PalbaseSetOverridesResult, aQ as PalbaseSignedUrlResponse, aR as PalbaseSmsClient, aS as PalbaseSmsSendParams, aT as PalbaseSmsSendResponse, aU as PalbaseTransformOptions, aV as PalbaseUpdateLinkParams, aW as PalbaseUploadOptions, aX as PalbaseUser, aY as PalbaseUserDetailResult, aZ as PalbaseUsersQueryInput, a_ as PalbaseUsersResult, a$ as PalbaseVerifyRequestSignatureParams, b0 as PalbaseWhereOperator, b1 as TooManyRequests, b2 as TxClient, b3 as Unauthorized, b4 as VerifiedDevice, b5 as defineMiddleware } from './endpoint-92kVepng.cjs';
3
3
  import { AsyncLocalStorage } from 'node:async_hooks';
4
- import { E as EnvTypedDatabase, S as SchemaDef } from './index-Bt7UHkAM.cjs';
5
- export { C as ColumnBuilder, a as ColumnDef, b as ColumnMap, c as ColumnType, d as EXTENSION_DEPENDENCIES, e as EnvServiceDatabase, f as EnvTables, g as EnvTypedTable, h as EnvTypedTx, I as InsertShape, O as OnDeleteAction, P as PALBASE_EXTENSIONS, i as PalbaseExtension, j as PolicyBuilder, k as PolicyCommand, l as PolicyDef, m as PolicyMode, R as RawConstraintDef, n as RowShape, o as SchemaInput, T as TableDef, p as TableInput, q as TypedDB, r as TypedTable, s as TypedTx, t as bigint, u as boolean, v as defineSchema, w as enumType, x as integer, y as isPalbaseExtension, z as jsonb, A as makeTypedDB, B as numeric, D as policy, F as raw, G as text, H as timestamp, J as uuid } from './index-Bt7UHkAM.cjs';
4
+ import { E as EnvTypedDatabase, S as SchemaDef } from './index-BaHs33jr.cjs';
5
+ export { C as ColumnBuilder, a as ColumnDef, b as ColumnMap, c as ColumnType, d as EXTENSION_DEPENDENCIES, e as EnvServiceDatabase, f as EnvTables, g as EnvTypedTable, h as EnvTypedTx, I as InsertShape, O as OnDeleteAction, P as PALBASE_EXTENSIONS, i as PalbaseExtension, j as PolicyBuilder, k as PolicyCommand, l as PolicyDef, m as PolicyMode, R as RawConstraintDef, n as RowShape, o as SchemaInput, T as TableDef, p as TableInput, q as TypedDB, r as TypedTable, s as TypedTx, t as bigint, u as boolean, v as defineSchema, w as enumType, x as integer, y as isPalbaseExtension, z as jsonb, A as makeTypedDB, B as numeric, D as policy, F as raw, G as text, H as timestamp, J as uuid } from './index-BaHs33jr.cjs';
6
+ import { EntitlementKey, LimitKey } from './purchases/keys.cjs';
6
7
  import { TableTypes, Tables } from './db/env.cjs';
7
8
  import { ZodTypeAny, z } from 'zod';
8
9
  export { z } from 'zod';
9
10
 
11
+ /**
12
+ * The purchases surface the decorators need, declared as a NARROW STRUCTURAL
13
+ * interface rather than an import of `@palstore/purchases`.
14
+ *
15
+ * `PurchasesClient` (palstore's `sk_` backend SDK) satisfies this shape as-is,
16
+ * so the runtime injects the real client with no adapter — but `@palbase/backend`
17
+ * itself gains no dependency on it. That matters: this package is published
18
+ * public and baked into the br-pod image from a tarball, so a dependency on an
19
+ * unpublished sibling would break `npm install palbase-backend.tgz`. It is also
20
+ * the pattern `withSpend` already uses for the same reason (`SpendCapableClient`
21
+ * in palstore's own spend.ts: "a narrow structural interface … so this file has
22
+ * no dependency on client.ts").
23
+ *
24
+ * The methods here are a SUBSET of `PurchasesClient` — only what the two
25
+ * decorators call. Grants, refunds, credits and customer-info reads stay off
26
+ * this interface: a tenant that wants them imports the palstore SDK directly.
27
+ */
28
+ /** Store environment a subject is fixed to. Mirrors `StoreEnv` in `@palstore/purchases`. */
29
+ type StoreEnv = "production" | "sandbox";
30
+ /** Quota/credit state carried by a 429. Mirrors `LimitState` in `@palstore/purchases`
31
+ * (SPEC-purchases-v1 §11) — re-declared, not imported, for the reason above. */
32
+ interface LimitState {
33
+ key: string;
34
+ scope: string;
35
+ window: string;
36
+ used: number;
37
+ reserved: number;
38
+ max: number;
39
+ remaining: number;
40
+ resetAt: string;
41
+ }
42
+ /** Options for one spend. `idempotencyKey` is required by the server (§9). */
43
+ interface SpendOptions {
44
+ /** Defaults to 1. */
45
+ count?: number;
46
+ idempotencyKey: string;
47
+ }
48
+ interface PurchasesService {
49
+ /** Map a tenant-side user reference to its palstore subject, creating one on
50
+ * first sight. Server-authoritative — the caller never names a subject. */
51
+ resolveSubject(input: {
52
+ userRef: string;
53
+ storeEnv: StoreEnv;
54
+ }): Promise<{
55
+ subjectId: string;
56
+ }>;
57
+ /** Resolve silently when `entitlementKey` is active for `subjectId`; throw
58
+ * `EntitlementRequiredError` otherwise. Consumes nothing. */
59
+ require(subjectId: string, entitlementKey: string): Promise<void>;
60
+ /** Reserve → run `handler` → commit on success, cancel on throw, always
61
+ * rethrowing the handler's own error. The whole reason the decorators are a
62
+ * thin layer: this lifecycle is already written and tested in palstore's SDK. */
63
+ withSpend<T>(subjectId: string, key: string, opts: SpendOptions, handler: () => Promise<T>): Promise<T>;
64
+ }
65
+
10
66
  /**
11
67
  * runtime.ts — request-scoped service singletons.
12
68
  *
@@ -66,6 +122,28 @@ interface RuntimeServices {
66
122
  Notifications: PalbaseNotificationsClient;
67
123
  Flags: PalbaseFlagsClient;
68
124
  Realtime: PalbaseRealtimeClient;
125
+ Purchases: PurchasesService;
126
+ }
127
+ /**
128
+ * The per-request ALS box.
129
+ *
130
+ * `runtime` is the service bundle. `userId` is the request's authenticated user
131
+ * id, written by the runtime immediately after it assembles the request object
132
+ * (worker.js: `requestALS.getStore().userId = pbReq.user?.id || null`) and
133
+ * `null` on an anonymous request. It was already being written there for the
134
+ * Flags client's auto-bind; declaring it here makes the existing contract typed
135
+ * instead of implicit, which is what lets `currentSubjectId()` read a
136
+ * server-owned identity rather than trusting anything the caller sent.
137
+ */
138
+ interface RequestStore {
139
+ runtime: RuntimeServices;
140
+ userId?: string | null;
141
+ /** This request's id (`req_…`), written by the runtime. Used as the spend
142
+ * idempotency scope when the caller supplied no `Idempotency-Key`. */
143
+ requestId?: string;
144
+ /** The caller's `Idempotency-Key` header, or `null` when absent. What makes a
145
+ * client's retry replay its first result instead of charging twice. */
146
+ idempotencyKey?: string | null;
69
147
  }
70
148
  /**
71
149
  * Per-request store. The persistent runtime runs each request inside
@@ -76,9 +154,7 @@ interface RuntimeServices {
76
154
  * instance across the one module instance — two ALS instances would silently
77
155
  * not see each other's stores. NOT part of the public author-facing API.
78
156
  */
79
- declare const __requestALS: AsyncLocalStorage<{
80
- runtime: RuntimeServices;
81
- }>;
157
+ declare const __requestALS: AsyncLocalStorage<RequestStore>;
82
158
  /** Install the live clients in the process-global fallback slot.
83
159
  *
84
160
  * Persistent-server requests should use {@link __runWithRuntime} instead; this
@@ -129,6 +205,15 @@ declare const Queue: QueueClient;
129
205
  declare const Log: Logger;
130
206
  /** Push / email / SMS / in-app notifications. */
131
207
  declare const Notifications: PalbaseNotificationsClient;
208
+ /**
209
+ * Palstore purchases (entitlements + quota/credit spend).
210
+ *
211
+ * Reached by handlers through the `@RequireEntitlement` / `@Spend` decorators
212
+ * rather than called directly in the common case; exposed as a singleton for
213
+ * the cases the decorators deliberately do not cover (a dynamic spend count,
214
+ * which must run BEFORE the billable side-effect).
215
+ */
216
+ declare const Purchases: PurchasesService;
132
217
  /**
133
218
  * Feature flags.
134
219
  *
@@ -161,11 +246,120 @@ declare const Flags: PalbaseFlagsClient;
161
246
  */
162
247
  declare const Realtime: PalbaseRealtimeClient;
163
248
 
249
+ /**
250
+ * `@RequireEntitlement` and `@Spend` — access and metering at the decorator
251
+ * level, so a controller method says what it costs instead of wiring it.
252
+ *
253
+ * TWO decorators, deliberately, because not every subscription is limit-based.
254
+ * Most products just say "pro users may call this" and consume nothing; a
255
+ * zero-count spend would express that badly and run the whole reserve/commit
256
+ * machine for no reason.
257
+ *
258
+ * @RequireEntitlement('pro') pure gate → 403, consumes nothing
259
+ * @Spend('bookCreate') consumption → 429 when exhausted
260
+ *
261
+ * ORDER IS FIXED, and not by where you wrote them. Both decorators record
262
+ * metadata and share ONE installed wrapper that always runs the entitlement
263
+ * gate before it reserves quota. Reserving for someone who is about to get a
264
+ * 403 is wasted work and a race window, so the source order of the two
265
+ * decorators cannot introduce it.
266
+ */
267
+
268
+ type MethodDecorator$2 = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
269
+ /**
270
+ * Require an active entitlement. Answers 403 `entitlement_required` when it is
271
+ * absent and consumes nothing.
272
+ *
273
+ * The key is a member of the generated `EntitlementKey` union, so a typo is a
274
+ * compile error rather than a gate that silently never matches.
275
+ */
276
+ declare function RequireEntitlement(key: EntitlementKey): MethodDecorator$2;
277
+ /**
278
+ * Consume one unit of a limit or credit for the request's subject.
279
+ *
280
+ * Reserves before the handler runs and commits only if it returns; a throw
281
+ * cancels the hold, so a user is never charged for work that did not complete.
282
+ * Answers 429 `quota_exceeded` (carrying the `LimitState`) when exhausted.
283
+ *
284
+ * The count is fixed at 1. A DYNAMIC amount is deliberately not expressible
285
+ * here: it has to be decided inside the handler and spent BEFORE the billable
286
+ * side-effect, which is an explicit `Purchases.withSpend(...)` call, not a
287
+ * decorator.
288
+ */
289
+ declare function Spend(key: LimitKey): MethodDecorator$2;
290
+
291
+ /**
292
+ * keys-gen.ts — generate the `palbase-purchases.d.ts` text from a catalog
293
+ * manifest.
294
+ *
295
+ * The twin of `db/env-gen.ts`: the CLI / deploy pipeline calls
296
+ * {@link makePurchasesDts} with the project's catalog revision and writes the
297
+ * result to `palbase-purchases.d.ts` at the project root. That file augments
298
+ * the `@palbase/backend/purchases` `Entitlements` / `Limits` interfaces, so
299
+ * `@RequireEntitlement(...)` and `@Spend(...)` accept the project's real keys
300
+ * and nothing else.
301
+ *
302
+ * Same catalog, two languages: this is the TypeScript half of what swiftgen
303
+ * already emits for iOS. Neither side is hand-maintained, so the two cannot
304
+ * drift from each other or from the manifest the server validated.
305
+ *
306
+ * The emitted file ends in `export {};` — same as `makeEnvDts`. Without it the
307
+ * `.d.ts` is a global script, and `declare module "…"` there DECLARES an
308
+ * ambient module (shadowing the real one, so every key silently becomes
309
+ * invalid) instead of AUGMENTING it.
310
+ */
311
+ /** A catalog entry that may be tombstoned. */
312
+ interface Removable {
313
+ removed?: boolean;
314
+ }
315
+ /**
316
+ * The slice of `pur_catalogs.manifest` this generator reads. Deliberately
317
+ * partial and permissive: the server already validated the manifest (its models
318
+ * are `extra="forbid"`), so re-checking here would be a second, weaker copy of
319
+ * that authority.
320
+ */
321
+ interface PurchasesManifest {
322
+ limits?: Record<string, Removable>;
323
+ credits?: Record<string, Removable>;
324
+ entitlements?: Record<string, Removable>;
325
+ }
326
+ /**
327
+ * Render the project's `palbase-purchases.d.ts`.
328
+ *
329
+ * `Limits` carries BOTH limits and credits: `@Spend` consumes either, and the
330
+ * server answers both with a 429 (`quota_exceeded` / `credit_insufficient`), so
331
+ * splitting them into two unions would make the author pick the right decorator
332
+ * for a distinction the spend path does not make.
333
+ */
334
+ declare function makePurchasesDts(manifest: PurchasesManifest): string;
335
+
336
+ /**
337
+ * Per-method purchases metadata, stored on the controller class the same way
338
+ * `decorators/registry.ts` stores routes: a symbol-keyed static, plain data, no
339
+ * `reflect-metadata`.
340
+ *
341
+ * Its only consumer is the OpenAPI step, which needs to know that a route is
342
+ * gated (declare 403) or metered (declare 429) so the iOS client codegen emits
343
+ * TYPED errors instead of a bare status number. Enforcement does NOT read this
344
+ * — the decorators wrap the method directly — so a stale registry can never
345
+ * cause a missed gate.
346
+ */
347
+
348
+ /** What `@Spend` recorded for one method. */
349
+ interface SpendMeta {
350
+ key: LimitKey;
351
+ count: number;
352
+ }
353
+ /** The entitlement `fnName` is gated on, or undefined when it is not gated. */
354
+ declare function entitlementFor(ctor: object, fnName: string): EntitlementKey | undefined;
355
+ /** What `fnName` spends, or undefined when it spends nothing. */
356
+ declare function spendFor(ctor: object, fnName: string): SpendMeta | undefined;
357
+
164
358
  /**
165
359
  * env-gen.ts — generate the `palbase-env.d.ts` text from a `defineSchema()`
166
360
  * result.
167
361
  *
168
- * The CLI (`palbase serve` / codegen) and the deploy pipeline call
362
+ * The CLI (`palbase db types` / codegen) and the deploy pipeline call
169
363
  * {@link makeEnvDts} with the project's schema and write the returned string to
170
364
  * `palbase-env.d.ts` at the project root. That file AUGMENTS the
171
365
  * `@palbase/backend/env` `Tables` interface (controlled global augmentation,
@@ -808,6 +1002,7 @@ declare function defineTestUsers(input: TestUsersInput): TestUsersConfig;
808
1002
  * new_dashboard: flag({ type: "boolean", default: false, description: "Roll out the new dashboard" }),
809
1003
  * max_uploads: flag({ type: "number", default: 10 }),
810
1004
  * theme: flag({ type: "string", default: "light", variants: ["light", "dark", "system"] }),
1005
+ * limits: flag({ type: "json", default: { daily: 10, burst: 50 } }),
811
1006
  * },
812
1007
  * });
813
1008
  *
@@ -816,47 +1011,80 @@ declare function defineTestUsers(input: TestUsersInput): TestUsersConfig;
816
1011
  * { __config: "flags", flags: { <key>: { type, default, variants, description }, ... } }
817
1012
  * `variants` is the allowed-values list for a string flag, or `null` (any
818
1013
  * string). `description` is the doc string, or `null`. The apply step maps the
819
- * author-facing `type` ("boolean") to PalFlags' `value_type` ("bool").
1014
+ * author-facing `type` to PalFlags' `value_type`: "boolean" → "bool" and
1015
+ * "json" → "object"; "number"/"string" pass through.
820
1016
  */
821
1017
  /** The `__config` discriminant the eval/apply reads to confirm a config/*.ts is
822
1018
  * a flags config. Flags is `"flags"`. */
823
1019
  declare const FLAGS_CONFIG_KIND: "flags";
824
1020
  /**
825
1021
  * A flag's type. The author-facing vocabulary is `"boolean" | "number" |
826
- * "string"`. (The br-pod maps `"boolean"` to PalFlags' `value_type: "bool"` on
827
- * apply — the SDK keeps the JS-natural `"boolean"` so the DSL reads cleanly.)
1022
+ * "string" | "json"`; the apply step maps it to PalFlags' `value_type` enum —
1023
+ * `"boolean"` → `"bool"`, `"json"` → `"object"`, the other two pass through. The
1024
+ * SDK keeps the JS-natural names so the DSL reads cleanly.
828
1025
  *
829
- * PalFlags also supports an `object` value type, but config-as-code flags are
830
- * deliberately scoped to the three SCALAR types — an object default is awkward
831
- * to author on the CLI and rare for a feature flag, so it is intentionally
832
- * omitted here (set an object flag via the Studio / API directly if needed).
1026
+ * `"json"` is a structured (object) flag. It is named for what an author types —
1027
+ * a JSON object literal — and matches the `--type json` the CLI's per-user
1028
+ * override commands already use for this same server type.
1029
+ */
1030
+ type FlagType = "boolean" | "number" | "string" | "json";
1031
+ /** Any value expressible in JSON — what may appear INSIDE a `"json"` flag's default. */
1032
+ type FlagJsonValue = boolean | number | string | null | FlagJsonValue[] | {
1033
+ [key: string]: FlagJsonValue;
1034
+ };
1035
+ /**
1036
+ * The default of a `"json"` flag: a JSON OBJECT. Not an array and not a scalar —
1037
+ * PalFlags' `object` value type accepts a JSON object at the top level only
1038
+ * (`validate.ValidateValueTypeMatch`), and it caps nesting depth at 3.
833
1039
  */
834
- type FlagType = "boolean" | "number" | "string";
1040
+ type FlagJsonObject = {
1041
+ [key: string]: FlagJsonValue;
1042
+ };
835
1043
  /** The runtime JSON value a flag's `default` may hold (matches {@link FlagType}). */
836
- type FlagValue = boolean | number | string;
1044
+ type FlagValue = boolean | number | string | FlagJsonObject;
837
1045
  /**
838
- * The author-facing options for a single flag.
1046
+ * The author-facing options for a single flag — a UNION keyed on `type`, so
1047
+ * `default` is checked against the declared type at compile time and `variants`
1048
+ * is only accepted where it means something.
839
1049
  *
840
- * - `type`: the flag's type — `"boolean" | "number" | "string"`.
1050
+ * - `type`: the flag's type — `"boolean" | "number" | "string" | "json"`.
841
1051
  * - `default`: the project-wide default value. MUST match `type` (a `number`
842
- * default with `type: "boolean"` throws).
1052
+ * default with `type: "boolean"` is a type error, and throws at runtime).
843
1053
  * - `variants`: ONLY valid for `type: "string"` — the allowed values the string
844
1054
  * flag may take (a DEFINITION, not a per-user assignment). When given, the
845
- * `default` MUST be one of the variants. Supplying `variants` on a non-string
846
- * type throws.
1055
+ * `default` MUST be one of the variants. Supplying `variants` on any other
1056
+ * type is a type error, and throws at runtime. (PalFlags rejects variants for
1057
+ * every non-string `value_type`, so accepting them here would only produce a
1058
+ * config the server refuses.)
847
1059
  * - `description`: an optional human description of what the flag controls.
848
1060
  */
849
- interface FlagOptions {
850
- type: FlagType;
851
- default: FlagValue;
1061
+ type FlagOptions = {
1062
+ type: "boolean";
1063
+ default: boolean;
1064
+ variants?: never;
1065
+ description?: string;
1066
+ } | {
1067
+ type: "number";
1068
+ default: number;
1069
+ variants?: never;
1070
+ description?: string;
1071
+ } | {
1072
+ type: "string";
1073
+ default: string;
852
1074
  variants?: string[];
853
1075
  description?: string;
854
- }
1076
+ } | {
1077
+ type: "json";
1078
+ default: FlagJsonObject;
1079
+ variants?: never;
1080
+ description?: string;
1081
+ };
855
1082
  /**
856
1083
  * The compiled, serializable flag definition — the EXACT shape emitted to JSON
857
1084
  * and consumed by the Go apply step.
858
1085
  *
859
- * - `type`: the author-facing type verbatim (`"boolean" | "number" | "string"`).
1086
+ * - `type`: the author-facing type verbatim (`"boolean" | "number" | "string" |
1087
+ * "json"`) — the apply step maps it to PalFlags' `value_type`.
860
1088
  * - `default`: the default value (matches `type`).
861
1089
  * - `variants`: the allowed-values list for a string flag, or `null` (any).
862
1090
  * - `description`: the doc string, or `null`.
@@ -885,12 +1113,12 @@ interface FlagsInput {
885
1113
  * options.
886
1114
  *
887
1115
  * Validates eagerly (at config-author time):
888
- * - `type` is one of `"boolean" | "number" | "string"`.
1116
+ * - `type` is one of `"boolean" | "number" | "string" | "json"`.
889
1117
  * - `default` matches `type` (e.g. a `number` default with `type: "boolean"`
890
- * throws).
891
- * - `variants` is ONLY allowed for a string flag (variants on a non-string
892
- * type throws); each entry is a non-empty string; the `default` must be one
893
- * of the variants.
1118
+ * throws; a `"json"` default must be a plain object, not an array or scalar).
1119
+ * - `variants` is ONLY allowed for a string flag (variants on any other type
1120
+ * throws); each entry is a non-empty string; the `default` must be one of the
1121
+ * variants.
894
1122
  *
895
1123
  * Returns a normalized {@link FlagDef}: `variants` deduped-or-null,
896
1124
  * `description` trimmed-or-null.
@@ -1620,4 +1848,4 @@ declare const documents: {
1620
1848
  onDocumentDeleted(handler: HookHandler<DocumentDeletedEvent>): ResolvedHook<DocumentDeletedEvent>;
1621
1849
  };
1622
1850
 
1623
- export { type AcsOptions, type ApnsOptions, type BackoffStrategy, Body, type BucketDef, type BucketOptions, Cache, CacheClient, Client, Controller, type ControllerOptions, type CustomWebhookConfig, DBClient, Database, type DefinedError, type DefinedErrorWithData, Delete, type DocumentCreatedEvent, type DocumentDeletedEvent, type DocumentUpdatedEvent, Documents, EGRESS_CONFIG_KIND, type EgressConfig, type EgressInput, type EnvSecretRef, EnvTypedDatabase, FLAGS_CONFIG_KIND, type FcmOptions, type FileDeletedEvent, type FileUploadedEvent, type FlagDef, type FlagOptions, type FlagType, type FlagValue, Flags, type FlagsConfig, type FlagsInput, Get, Headers, type HookHandler, type HookMeta, HttpError, type HttpMethodUpper, type JobConfig, type JobMeta, Log, Logger, NOTIFICATIONS_CONFIG_KIND, Notifications, type NotificationsConfig, type NotificationsInput, OptionalUser, type OwnedTablesOf, PROVIDER_CATALOG, PalbaseDocsClient, PalbaseFlagsClient, PalbaseNotificationsClient, PalbaseRealtimeClient, PalbaseStorageClient, Param, type ParamKind, type ParamMeta, type PasswordResetEvent, Patch, Post, type ProviderCatalogEntry, type ProviderDef, type ProviderEventMap, type ProviderName, type ProviderOptions, type ProviderWebhookConfig, Put, Query, QueryParams, Queue, QueueClient, RESERVED_SECRET_PREFIX, RateLimitConfig, Realtime, type RegisteredError, Req, RequestId, type ResolvedCustomWebhook, type ResolvedHook, type ResolvedJobConfig, type ResolvedProviderWebhook, type ResolvedWebhookConfig, type ResolvedWorkerConfig, Resource, type ResourceEnv, type RouteMeta, type RouteOptions, type RuntimeServices, STORAGE_CONFIG_KIND, SchemaDef, type Seed, type SeedFor, type SeedJson, type SeedRowFor, type SendgridOptions, type SesOptions, type SignInEvent, type SignOutEvent, type SmtpOptions, Storage, type StorageConfig, type StorageInput, TEST_USERS_CONFIG_KIND, type TableGraph, TableTypes, Tables, type TestUserDef, type TestUserOptions, type TestUsersConfig, type TestUsersInput, type ThrowDescriptor, TraceId, type TwilioOptions, Upload, type UploadConfig, UploadedObject, User, type UserCreatedEvent, User$1 as UserT, type WebhookEventHandler, type WebhookMeta, type WebhookProvider, type WebhookRequest, type WorkerConfig, type WorkerMeta, __getRuntime, __registerResource, __requestALS, __runResourceBoot, __runWithRuntime, __setRuntime, __shutdownResources, auth, bucket, buildProvider, defineEgress, defineError, defineFlags, defineJob, defineNotifications, defineStorage, defineTestUsers, defineWebhook, defineWorker, documents, flag, getErrorRegistry, getRoutes, makeEnvDts, parseFileSizeLimit, recordThrows, reservedSecretKey, storage, testUser, validateUploadAgainstStorage };
1851
+ export { type AcsOptions, type ApnsOptions, type BackoffStrategy, Body, type BucketDef, type BucketOptions, Cache, CacheClient, Client, Controller, type ControllerOptions, type CustomWebhookConfig, DBClient, Database, type DefinedError, type DefinedErrorWithData, Delete, type DocumentCreatedEvent, type DocumentDeletedEvent, type DocumentUpdatedEvent, Documents, EGRESS_CONFIG_KIND, type EgressConfig, type EgressInput, EntitlementKey, type EnvSecretRef, EnvTypedDatabase, FLAGS_CONFIG_KIND, type FcmOptions, type FileDeletedEvent, type FileUploadedEvent, type FlagDef, type FlagJsonObject, type FlagJsonValue, type FlagOptions, type FlagType, type FlagValue, Flags, type FlagsConfig, type FlagsInput, Get, Headers, type HookHandler, type HookMeta, HttpError, type HttpMethodUpper, type JobConfig, type JobMeta, LimitKey, type LimitState, Log, Logger, NOTIFICATIONS_CONFIG_KIND, Notifications, type NotificationsConfig, type NotificationsInput, OptionalUser, type OwnedTablesOf, PROVIDER_CATALOG, PalbaseDocsClient, PalbaseFlagsClient, PalbaseNotificationsClient, PalbaseRealtimeClient, PalbaseStorageClient, Param, type ParamKind, type ParamMeta, type PasswordResetEvent, Patch, Post, type ProviderCatalogEntry, type ProviderDef, type ProviderEventMap, type ProviderName, type ProviderOptions, type ProviderWebhookConfig, Purchases, type PurchasesManifest, type PurchasesService, Put, Query, QueryParams, Queue, QueueClient, RESERVED_SECRET_PREFIX, RateLimitConfig, Realtime, type RegisteredError, Req, RequestId, type RequestStore, RequireEntitlement, type ResolvedCustomWebhook, type ResolvedHook, type ResolvedJobConfig, type ResolvedProviderWebhook, type ResolvedWebhookConfig, type ResolvedWorkerConfig, Resource, type ResourceEnv, type RouteMeta, type RouteOptions, type RuntimeServices, STORAGE_CONFIG_KIND, SchemaDef, type Seed, type SeedFor, type SeedJson, type SeedRowFor, type SendgridOptions, type SesOptions, type SignInEvent, type SignOutEvent, type SmtpOptions, Spend, type SpendMeta, type SpendOptions, Storage, type StorageConfig, type StorageInput, type StoreEnv, TEST_USERS_CONFIG_KIND, type TableGraph, TableTypes, Tables, type TestUserDef, type TestUserOptions, type TestUsersConfig, type TestUsersInput, type ThrowDescriptor, TraceId, type TwilioOptions, Upload, type UploadConfig, UploadedObject, User, type UserCreatedEvent, User$1 as UserT, type WebhookEventHandler, type WebhookMeta, type WebhookProvider, type WebhookRequest, type WorkerConfig, type WorkerMeta, __getRuntime, __registerResource, __requestALS, __runResourceBoot, __runWithRuntime, __setRuntime, __shutdownResources, auth, bucket, buildProvider, defineEgress, defineError, defineFlags, defineJob, defineNotifications, defineStorage, defineTestUsers, defineWebhook, defineWorker, documents, entitlementFor, flag, getErrorRegistry, getRoutes, makeEnvDts, makePurchasesDts, parseFileSizeLimit, recordThrows, reservedSecretKey, spendFor, storage, testUser, validateUploadAgainstStorage };