cvx-kit 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +50 -2
  2. package/dist/errors.d.mts +3 -2
  3. package/dist/errors.mjs +1 -1
  4. package/dist/errors.mjs.map +1 -1
  5. package/dist/filters.d.mts +4 -0
  6. package/dist/filters.mjs +1 -0
  7. package/dist/index.d.mts +3 -3
  8. package/dist/modules/contracts/errors.d.mts +218 -0
  9. package/dist/modules/contracts/errors.mjs +1 -1
  10. package/dist/modules/contracts/errors.mjs.map +1 -1
  11. package/dist/modules/effect/crud.d.mts +28 -0
  12. package/dist/querying/cursors.d.mts +30 -4
  13. package/dist/querying/cursors.mjs +1 -1
  14. package/dist/querying/cursors.mjs.map +1 -1
  15. package/dist/querying/execution.d.mts +14 -3
  16. package/dist/querying/execution.mjs +1 -1
  17. package/dist/querying/execution.mjs.map +1 -1
  18. package/dist/querying/failures.d.mts +13 -0
  19. package/dist/querying/failures.mjs +2 -0
  20. package/dist/querying/failures.mjs.map +1 -0
  21. package/dist/querying/filters.d.mts +43 -44
  22. package/dist/querying/filters.mjs +1 -1
  23. package/dist/querying/filters.mjs.map +1 -1
  24. package/dist/querying/planner.d.mts +13 -16
  25. package/dist/querying/planner.mjs +1 -1
  26. package/dist/querying/planner.mjs.map +1 -1
  27. package/dist/querying/relationships.d.mts +24 -7
  28. package/dist/querying/relationships.mjs +1 -1
  29. package/dist/querying/relationships.mjs.map +1 -1
  30. package/dist/querying/schema-snapshot.d.mts +29 -0
  31. package/dist/querying/schema-snapshot.mjs +2 -0
  32. package/dist/querying/schema-snapshot.mjs.map +1 -0
  33. package/dist/querying/search.d.mts +69 -0
  34. package/dist/querying/search.mjs +2 -0
  35. package/dist/querying/search.mjs.map +1 -0
  36. package/dist/querying/security.d.mts +19 -2
  37. package/dist/querying/security.mjs +1 -1
  38. package/dist/querying/security.mjs.map +1 -1
  39. package/dist/querying/shared/ast.d.mts +28 -0
  40. package/dist/querying/shared/ast.mjs +2 -0
  41. package/dist/querying/shared/ast.mjs.map +1 -0
  42. package/dist/querying/shared/contract.d.mts +104 -0
  43. package/dist/querying/shared/contract.mjs +2 -0
  44. package/dist/querying/shared/contract.mjs.map +1 -0
  45. package/dist/querying/shared/evaluate.d.mts +7 -0
  46. package/dist/querying/shared/evaluate.mjs +2 -0
  47. package/dist/querying/shared/evaluate.mjs.map +1 -0
  48. package/dist/querying/shared/filters.d.mts +31 -0
  49. package/dist/querying/shared/filters.mjs +2 -0
  50. package/dist/querying/shared/filters.mjs.map +1 -0
  51. package/dist/querying.d.mts +47 -16
  52. package/dist/querying.mjs +1 -1
  53. package/dist/querying.mjs.map +1 -1
  54. package/dist/react/failures.d.mts +23 -0
  55. package/dist/react/failures.mjs +2 -0
  56. package/dist/react/failures.mjs.map +1 -0
  57. package/dist/react/optimistic.d.mts +47 -0
  58. package/dist/react/optimistic.mjs +2 -0
  59. package/dist/react/optimistic.mjs.map +1 -0
  60. package/dist/react/pagination-state.d.mts +40 -0
  61. package/dist/react/pagination-state.mjs +2 -0
  62. package/dist/react/pagination-state.mjs.map +1 -0
  63. package/dist/react/use-command.d.mts +23 -0
  64. package/dist/react/use-command.mjs +2 -0
  65. package/dist/react/use-command.mjs.map +1 -0
  66. package/dist/react/use-paginated-list.d.mts +32 -0
  67. package/dist/react/use-paginated-list.mjs +2 -0
  68. package/dist/react/use-paginated-list.mjs.map +1 -0
  69. package/dist/react.d.mts +6 -0
  70. package/dist/react.mjs +1 -0
  71. package/dist/relations-public.d.mts +2 -2
  72. package/dist/relations.d.mts +65 -22
  73. package/dist/relations.mjs +1 -1
  74. package/dist/relations.mjs.map +1 -1
  75. package/dist/zod-convex.mjs +1 -1
  76. package/dist/zod-convex.mjs.map +1 -1
  77. package/dist/zod-table.d.mts +15 -1
  78. package/dist/zod-table.mjs +1 -1
  79. package/dist/zod-table.mjs.map +1 -1
  80. package/package.json +21 -4
package/README.md CHANGED
@@ -156,6 +156,16 @@ public DTO or `null`. A many edge returns a bounded native page. Junction output
156
156
  preserves duplicate edge occurrences, even when target reads are memoized.
157
157
  Missing and unreadable targets have the same public result.
158
158
 
159
+ `defineRelations` captures table names, stored field semantics, public DTO
160
+ validation, variants, active indexes, and relation declarations. Complete schema
161
+ and index configuration before calling it. Later changes to source declarations
162
+ do not affect existing registries, helpers or engines. Create a new registry to
163
+ adopt changes. Caller-owned declarations remain mutable.
164
+
165
+ Relation DTOs support `.refine()`. They reject opaque `.superRefine()` and
166
+ `.check()` callbacks; express those validations with `.refine()` instead.
167
+ Captured callbacks still observe application state through their closures.
168
+
159
169
  Variant tables keep their discriminated public unions. Use the relation callback's
160
170
  second argument, `branch('payments', 'creditor_payment').creditorId`, for a
161
171
  branch-specific reference; its tag is checked before lookup. See the
@@ -220,7 +230,7 @@ usable equality prefix, then stable index-name order, with creation-time fallbac
220
230
  `order: 'desc'` reverses the chosen native index order. Native continuation,
221
231
  split/end cursors, and page status are preserved, with external tokens protected.
222
232
 
223
- Page sizes are positive integers up to 100; central `maxPageSize` may lower that
233
+ Browse page sizes are positive integers up to 100; central `maxPageSize` may lower that
224
234
  ceiling. Central budgets bound read operations, index ranges, raw candidates,
225
235
  observed/reserved document bytes, fan-out, depth, and memo entries across a
226
236
  request, including policy reads. Native request read limits may lower ceilings.
@@ -232,9 +242,47 @@ many reads. Application/helper accounting does not measure physical platform I/O
232
242
  Kit auth query contexts carry the engine's private read capability. Unknown
233
243
  contexts fail closed unless a centrally configured trusted adapter attests their
234
244
  query reader, effective policy, and security scope. Mutation/action contexts,
235
- search/vector optimization, arbitrary predicates/projections,
245
+ vector search, arbitrary predicates/projections,
236
246
  and custom sorts are outside this API.
237
247
 
248
+ Root full-text search uses a declared native search index and keeps native relevance order:
249
+
250
+ ```ts
251
+ const page = await querying(ctx).tasks
252
+ .search('search_text', input.text)
253
+ .where({ status: 'open' })
254
+ .include({ finance: 'if-permitted' })
255
+ .with({ labels: true })
256
+ .paginate({ numItems: 25, cursor: input.cursor, scanLimit: 250 })
257
+ ```
258
+
259
+ Declare `search_text` on the exact native table registered in `relations`.
260
+ Search supports the existing root filters, field groups and nested output;
261
+ direct `.relation(...).search(...)` is unsupported. Nested relationship pages
262
+ remain ordinary browse pages. Root search returns `paginationMode: 'native-search'`,
263
+ null split metadata, and no caller-pinned `endCursor`, custom order or refill.
264
+ A short or denied-empty page can still have `isDone: false`: continue manually
265
+ with its `continueCursor` (or `usePaginatedList().fetchNextPage()`).
266
+
267
+ Search `numItems` requests a positive integer initial native target, clamped by
268
+ central limits and the remaining branch threshold. Live windows can return more raw rows.
269
+ `scanLimit` is a cursor-branch admission threshold: all raw candidates, including
270
+ denied rows, count in authenticated `seen` state. A whole window can overshoot
271
+ (e.g. seen 6 + 5 rows with limit 9 returns all authorized rows, then stops with
272
+ `result-cap`). Replaying a cursor repeats that branch; this is no global quota.
273
+ Root search payload accounting is separate from shared policy/relationship read,
274
+ byte, depth and fan-out budgets. Any unfinished nested read, policy exception or
275
+ DTO failure aborts the whole page and mints no new continuation.
276
+
277
+ Project errors explicitly at registered endpoint boundaries: ordinary `authQuery`
278
+ handlers catch and throw `new ConvexError(queryFailureContract.project(error))`
279
+ from `cvx-kit/querying`. Effect constructors configure
280
+ `query: { errors: queryFailureContract }` and wrap the promise in
281
+ `Effect.tryPromise({ try: () => /* search page */, catch: error => error })`.
282
+ The declared contract sanitizes unknown failures; do not transport raw exceptions.
283
+ See the [search contract](skills/cvx-kit/references/querying.md#root-full-text-search)
284
+ and [React list protocol](skills/cvx-kit/references/client-list.md).
285
+
238
286
  For manual projection, `tasks.toPublicDto(row)` still omits IDs and unselected
239
287
  storage fields. `tasks.toPublicDto(row, true)` or `{ _id: true }` adds the branded
240
288
  ID only. Computed extensions use matching schemas:
package/dist/errors.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { DeclaredError, DeclaredFailure, DecodedFailure, ErrorContract, ErrorDefinitions, ErrorDetailParser, UnknownFailure, defineErrorContract } from "./modules/contracts/errors.mjs";
1
+ import { DeclaredError, DeclaredFailure, DecodedFailure, ErrorContract, ErrorDefinitions, ErrorDetailParser, UnknownFailure, defineErrorContract, queryErrors } from "./modules/contracts/errors.mjs";
2
+ import { FailureHandlers, decodeFailure, matchFailure } from "./react/failures.mjs";
2
3
  //#region src/errors.d.ts
3
4
  /** Structured application error carried across Convex function boundaries. */
4
5
  export declare class KitError extends Error {
@@ -21,5 +22,5 @@ export type ErrorFactory = {
21
22
  };
22
23
  export declare const defaultErrors: ErrorFactory;
23
24
  //#endregion
24
- export { type DeclaredError, type DeclaredFailure, type DecodedFailure, type ErrorContract, type ErrorDefinitions, type ErrorDetailParser, type UnknownFailure, defineErrorContract };
25
+ export { type DeclaredError, type DeclaredFailure, type DecodedFailure, type ErrorContract, type ErrorDefinitions, type ErrorDetailParser, type FailureHandlers, type UnknownFailure, decodeFailure, defineErrorContract, matchFailure, queryErrors };
25
26
  //# sourceMappingURL=errors.d.mts.map
package/dist/errors.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{defineErrorContract as e}from"./modules/contracts/errors.mjs";var t=class extends Error{code;constructor(e){super(e.message??e.code),this.name=`KitError`,this.code=e.code}};const n={throw(e){throw new t(e)}};export{t as KitError,n as defaultErrors,e as defineErrorContract};
1
+ import{defineErrorContract as e,queryErrors as t}from"./modules/contracts/errors.mjs";import{decodeFailure as n,matchFailure as r}from"./react/failures.mjs";var i=class extends Error{code;constructor(e){super(e.message??e.code),this.name=`KitError`,this.code=e.code}};const a={throw(e){throw new i(e)}};export{i as KitError,n as decodeFailure,a as defaultErrors,e as defineErrorContract,r as matchFailure,t as queryErrors};
2
2
  //# sourceMappingURL=errors.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":["KitError","input","defaultErrors","defineErrorContract"],"sources":["../src/errors.ts"],"sourcesContent":["/** Structured application error carried across Convex function boundaries. */\nexport class KitError extends Error {\n\treadonly code: string\n\n\tconstructor(input: { code: string; message?: string }) {\n\t\tsuper(input.message ?? input.code)\n\t\tthis.name = 'KitError'\n\t\tthis.code = input.code\n\t}\n}\n\nexport {\n\tdefineErrorContract,\n\ttype ErrorContract,\n\ttype ErrorDefinitions,\n\ttype ErrorDetailParser,\n\ttype DeclaredError,\n\ttype DeclaredFailure,\n\ttype DecodedFailure,\n\ttype UnknownFailure,\n} from './modules/contracts/errors'\n\n/**\n * Injection seam for host error policy. Hosts with their own error taxonomy\n * (e.g. an App.errors singleton) adapt it to this shape; everyone else uses\n * the default factory below.\n */\nexport type ErrorFactory = {\n\tthrow(input: { code: string; message?: string }): never\n}\n\nexport const defaultErrors: ErrorFactory = {\n\tthrow(input) {\n\t\tthrow new KitError(input)\n\t},\n}\n"],"mappings":"qEACA,IAAaA,EAAb,cAA8B,KAAM,CACnC,KAEA,YAAY,EAA2C,CACtD,MAAMC,EAAM,SAAWA,EAAM,IAAI,EACjC,KAAK,KAAO,WACZ,KAAK,KAAOA,EAAM,IACnB,CACD,EAsBA,MAAaC,EAA8B,CAC1C,MAAM,EAAO,CACZ,MAAM,IAAIF,EAASC,CAAK,CACzB,CACD"}
1
+ {"version":3,"file":"errors.mjs","names":["KitError","input","defaultErrors","decodeFailure","defineErrorContract","matchFailure","queryErrors"],"sources":["../src/errors.ts"],"sourcesContent":["/** Structured application error carried across Convex function boundaries. */\nexport class KitError extends Error {\n\treadonly code: string\n\n\tconstructor(input: { code: string; message?: string }) {\n\t\tsuper(input.message ?? input.code)\n\t\tthis.name = 'KitError'\n\t\tthis.code = input.code\n\t}\n}\n\nexport {\n\tdefineErrorContract,\n\tqueryErrors,\n\ttype ErrorContract,\n\ttype ErrorDefinitions,\n\ttype ErrorDetailParser,\n\ttype DeclaredError,\n\ttype DeclaredFailure,\n\ttype DecodedFailure,\n\ttype UnknownFailure,\n} from './modules/contracts/errors'\n\n/**\n * Injection seam for host error policy. Hosts with their own error taxonomy\n * (e.g. an App.errors singleton) adapt it to this shape; everyone else uses\n * the default factory below.\n */\nexport type ErrorFactory = {\n\tthrow(input: { code: string; message?: string }): never\n}\n\nexport const defaultErrors: ErrorFactory = {\n\tthrow(input) {\n\t\tthrow new KitError(input)\n\t},\n}\n\n// Pure helpers do not import React or server query-engine modules.\nexport { decodeFailure, matchFailure, type FailureHandlers } from './react/failures'\n"],"mappings":"6JACA,IAAaA,EAAb,cAA8B,KAAM,CACnC,KAEA,YAAY,EAA2C,CACtD,MAAMC,EAAM,SAAWA,EAAM,IAAI,EACjC,KAAK,KAAO,WACZ,KAAK,KAAOA,EAAM,IACnB,CACD,EAuBA,MAAaC,EAA8B,CAC1C,MAAM,EAAO,CACZ,MAAM,IAAIF,EAASC,CAAK,CACzB,CACD"}
@@ -0,0 +1,4 @@
1
+ import { FilterContract, FilterDescriptor, SharedField, SharedFilters, defineFilterContract } from "./querying/shared/contract.mjs";
2
+ import { FilterAst, FilterOperator, FilterScalar } from "./querying/shared/ast.mjs";
3
+ import { evaluateFilter } from "./querying/shared/evaluate.mjs";
4
+ export { type FilterAst, type FilterContract, type FilterDescriptor, type FilterOperator, type FilterScalar, type SharedField, type SharedFilters, defineFilterContract, evaluateFilter };
@@ -0,0 +1 @@
1
+ import{evaluateFilter as e}from"./querying/shared/evaluate.mjs";import{defineFilterContract as t}from"./querying/shared/contract.mjs";export{t as defineFilterContract,e as evaluateFilter};
package/dist/index.d.mts CHANGED
@@ -1,17 +1,17 @@
1
1
  import { AgentToolHandlers, AgentToolRecord, createAgentTools } from "./agent-tools.mjs";
2
2
  import { ErrorFactory, KitError, defaultErrors } from "./errors.mjs";
3
+ import { ScalarFilters, ScalarSelection, ServerFilter } from "./querying/filters.mjs";
4
+ import { DtoOptions, TIMESTAMP_FIELDS, TableBoundaryOptions, createModule, createTable, jsonSafeZid, paginated, tenantTable, zodTable, zodVariantTable } from "./zod-table.mjs";
5
+ import { FieldReference, RelationBuilder, RelationDeclarations, RelationEdge, RelationFilterSelection, RelationFilters, RelationRegistry, RelationRegistryIdentity, RelationRow, RelationSelection, RelationTable, RelationTableHelpers, RelationTables, ThroughReference, defineRelations } from "./relations.mjs";
3
6
  import { RLSConfig, Rules, TENANT_FIELD, assertTenantOwned, composeRules, createTenantRules, requireTenantReference } from "./tenancy.mjs";
4
7
  import { Actor, AuthBundle, AuthFunctions, AuthFunctionsConfig, DefaultRole, Include, IncludedQuery, SecurityConfig, createAuthFunctions, createInclude, defaultRoleMap } from "./auth.mjs";
5
8
  import { AnyQueryMiddleware, QueryMiddleware } from "./components/foundation/modules/query/query.mjs";
6
9
  import { AnyCommandMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandConstructor, CommandDefaults, CommandMiddleware, Foundation, FoundationOptions, PermissionChecker } from "./components/foundation/client.mjs";
7
10
  import { CrudConfig, createCrudCommands } from "./crud.mjs";
8
- import { ScalarFilters, ScalarSelection, ServerFilter } from "./querying/filters.mjs";
9
- import { DtoOptions, TIMESTAMP_FIELDS, TableBoundaryOptions, createModule, createTable, jsonSafeZid, paginated, tenantTable, zodTable, zodVariantTable } from "./zod-table.mjs";
10
11
  import { RateLimiterLike, rateLimit } from "./middleware.mjs";
11
12
  import { WebhookBoundaryConfig, createWebhookBoundary, recordWebhookEvent, webhookEventsTable } from "./webhooks.mjs";
12
13
  import { createStateMachine } from "./state-machine.mjs";
13
14
  import { Change, Trigger, Triggers, appendOnly, createTriggers, noDelete, tenantOwnership, timestamps } from "./triggers.mjs";
14
15
  import { FoundationRegistry, FoundationRegistryOptions, createFoundation } from "./modules/foundation/foundation.mjs";
15
- import { FieldReference, RelationBuilder, RelationDeclarations, RelationEdge, RelationFilterSelection, RelationFilters, RelationRegistry, RelationRegistryIdentity, RelationRow, RelationSelection, RelationTable, RelationTableHelpers, RelationTables, ThroughReference, defineRelations } from "./relations.mjs";
16
16
  import { QueryEngineConfig, createQueryEngine } from "./querying.mjs";
17
17
  export { type Actor, type AgentToolHandlers, type AgentToolRecord, type AnyCommandMiddleware, type AnyQueryMiddleware, type ApplicationCommand, type AuditEntryInput, type AuditWriter, type AuditedOperation, type AuditedRegistry, type AuthBundle, type AuthFunctions, type AuthFunctionsConfig, type Change, type CommandConstructor, type CommandDefaults, type CommandMiddleware, type CrudConfig, type DefaultRole, type DtoOptions, type ErrorFactory, type FieldReference, Foundation, type FoundationOptions, type FoundationRegistry, type FoundationRegistryOptions, type Include, type IncludedQuery, KitError, type PermissionChecker, type QueryEngineConfig, type QueryMiddleware, type RLSConfig, type RateLimiterLike, type RelationBuilder, type RelationDeclarations, type RelationEdge, type RelationFilterSelection, type RelationFilters, type RelationRegistry, type RelationRegistryIdentity, type RelationRow, type RelationSelection, type RelationTable, type RelationTableHelpers, type RelationTables, type Rules, type ScalarFilters, type ScalarSelection, type SecurityConfig, type ServerFilter, TENANT_FIELD, TIMESTAMP_FIELDS, type TableBoundaryOptions, type ThroughReference, type Trigger, Triggers, type WebhookBoundaryConfig, appendOnly, assertTenantOwned, composeRules, createAgentTools, createAuthFunctions, createCrudCommands, createFoundation, createInclude, createModule, createQueryEngine, createStateMachine, createTable, createTenantRules, createTriggers, createWebhookBoundary, defaultErrors, defaultRoleMap, defineRelations, jsonSafeZid, noDelete, paginated, rateLimit, recordWebhookEvent, requireTenantReference, tenantOwnership, tenantTable, timestamps, webhookEventsTable, zodTable, zodVariantTable };
@@ -39,5 +39,223 @@ export type ErrorContract<Definitions extends ErrorDefinitions> = Readonly<{
39
39
  }>;
40
40
  /** Closed safe codes, allowlisted details, and a versioned checked transport union. */
41
41
  export declare function defineErrorContract<const Definitions extends ErrorDefinitions>(definitions: Definitions): ErrorContract<Definitions>;
42
+ /** Fixed, diagnostic-free transport shared by query endpoints and client recovery. */
43
+ export declare const queryErrors: Readonly<{
44
+ create: <Key extends Code<{
45
+ readonly 'invalid-cursor': {
46
+ readonly message: "Invalid cursor";
47
+ readonly details: {};
48
+ };
49
+ readonly FIELD_GROUP_DENIED: {
50
+ readonly message: "Field group access denied";
51
+ readonly details: {};
52
+ };
53
+ readonly QUERY_FANOUT: {
54
+ readonly message: "Query fan-out limit exceeded";
55
+ readonly details: {};
56
+ };
57
+ readonly SEARCH_TEXT_REQUIRED: {
58
+ readonly message: "Search text required";
59
+ readonly details: {};
60
+ };
61
+ readonly SEARCH_END_CURSOR_UNSUPPORTED: {
62
+ readonly message: "Search end cursor unsupported";
63
+ readonly details: {};
64
+ };
65
+ readonly SEARCH_ORDER_UNSUPPORTED: {
66
+ readonly message: "Search ordering unsupported";
67
+ readonly details: {};
68
+ };
69
+ readonly SEARCH_RELATION_UNSUPPORTED: {
70
+ readonly message: "Relation search unsupported";
71
+ readonly details: {};
72
+ };
73
+ readonly SEARCH_OPTIONS_UNSUPPORTED: {
74
+ readonly message: "Search options unsupported";
75
+ readonly details: {};
76
+ };
77
+ readonly SEARCH_PROTOCOL_UNSUPPORTED: {
78
+ readonly message: "Search pagination protocol unsupported";
79
+ readonly details: {};
80
+ };
81
+ }>>(code: Key, details: Details<{
82
+ readonly 'invalid-cursor': {
83
+ readonly message: "Invalid cursor";
84
+ readonly details: {};
85
+ };
86
+ readonly FIELD_GROUP_DENIED: {
87
+ readonly message: "Field group access denied";
88
+ readonly details: {};
89
+ };
90
+ readonly QUERY_FANOUT: {
91
+ readonly message: "Query fan-out limit exceeded";
92
+ readonly details: {};
93
+ };
94
+ readonly SEARCH_TEXT_REQUIRED: {
95
+ readonly message: "Search text required";
96
+ readonly details: {};
97
+ };
98
+ readonly SEARCH_END_CURSOR_UNSUPPORTED: {
99
+ readonly message: "Search end cursor unsupported";
100
+ readonly details: {};
101
+ };
102
+ readonly SEARCH_ORDER_UNSUPPORTED: {
103
+ readonly message: "Search ordering unsupported";
104
+ readonly details: {};
105
+ };
106
+ readonly SEARCH_RELATION_UNSUPPORTED: {
107
+ readonly message: "Relation search unsupported";
108
+ readonly details: {};
109
+ };
110
+ readonly SEARCH_OPTIONS_UNSUPPORTED: {
111
+ readonly message: "Search options unsupported";
112
+ readonly details: {};
113
+ };
114
+ readonly SEARCH_PROTOCOL_UNSUPPORTED: {
115
+ readonly message: "Search pagination protocol unsupported";
116
+ readonly details: {};
117
+ };
118
+ }[NoInfer<Key>]>) => Extract<Readonly<{
119
+ _tag: 'DeclaredError';
120
+ code: "FIELD_GROUP_DENIED";
121
+ details: {};
122
+ [declaredErrorBrand]: true;
123
+ }>, {
124
+ code: Key;
125
+ }> | Extract<Readonly<{
126
+ _tag: 'DeclaredError';
127
+ code: "QUERY_FANOUT";
128
+ details: {};
129
+ [declaredErrorBrand]: true;
130
+ }>, {
131
+ code: Key;
132
+ }> | Extract<Readonly<{
133
+ _tag: 'DeclaredError';
134
+ code: "SEARCH_END_CURSOR_UNSUPPORTED";
135
+ details: {};
136
+ [declaredErrorBrand]: true;
137
+ }>, {
138
+ code: Key;
139
+ }> | Extract<Readonly<{
140
+ _tag: 'DeclaredError';
141
+ code: "SEARCH_OPTIONS_UNSUPPORTED";
142
+ details: {};
143
+ [declaredErrorBrand]: true;
144
+ }>, {
145
+ code: Key;
146
+ }> | Extract<Readonly<{
147
+ _tag: 'DeclaredError';
148
+ code: "SEARCH_ORDER_UNSUPPORTED";
149
+ details: {};
150
+ [declaredErrorBrand]: true;
151
+ }>, {
152
+ code: Key;
153
+ }> | Extract<Readonly<{
154
+ _tag: 'DeclaredError';
155
+ code: "SEARCH_PROTOCOL_UNSUPPORTED";
156
+ details: {};
157
+ [declaredErrorBrand]: true;
158
+ }>, {
159
+ code: Key;
160
+ }> | Extract<Readonly<{
161
+ _tag: 'DeclaredError';
162
+ code: "SEARCH_RELATION_UNSUPPORTED";
163
+ details: {};
164
+ [declaredErrorBrand]: true;
165
+ }>, {
166
+ code: Key;
167
+ }> | Extract<Readonly<{
168
+ _tag: 'DeclaredError';
169
+ code: "SEARCH_TEXT_REQUIRED";
170
+ details: {};
171
+ [declaredErrorBrand]: true;
172
+ }>, {
173
+ code: Key;
174
+ }> | Extract<Readonly<{
175
+ _tag: 'DeclaredError';
176
+ code: "invalid-cursor";
177
+ details: {};
178
+ [declaredErrorBrand]: true;
179
+ }>, {
180
+ code: Key;
181
+ }>;
182
+ project: (error: unknown) => DecodedFailure<{
183
+ readonly 'invalid-cursor': {
184
+ readonly message: "Invalid cursor";
185
+ readonly details: {};
186
+ };
187
+ readonly FIELD_GROUP_DENIED: {
188
+ readonly message: "Field group access denied";
189
+ readonly details: {};
190
+ };
191
+ readonly QUERY_FANOUT: {
192
+ readonly message: "Query fan-out limit exceeded";
193
+ readonly details: {};
194
+ };
195
+ readonly SEARCH_TEXT_REQUIRED: {
196
+ readonly message: "Search text required";
197
+ readonly details: {};
198
+ };
199
+ readonly SEARCH_END_CURSOR_UNSUPPORTED: {
200
+ readonly message: "Search end cursor unsupported";
201
+ readonly details: {};
202
+ };
203
+ readonly SEARCH_ORDER_UNSUPPORTED: {
204
+ readonly message: "Search ordering unsupported";
205
+ readonly details: {};
206
+ };
207
+ readonly SEARCH_RELATION_UNSUPPORTED: {
208
+ readonly message: "Relation search unsupported";
209
+ readonly details: {};
210
+ };
211
+ readonly SEARCH_OPTIONS_UNSUPPORTED: {
212
+ readonly message: "Search options unsupported";
213
+ readonly details: {};
214
+ };
215
+ readonly SEARCH_PROTOCOL_UNSUPPORTED: {
216
+ readonly message: "Search pagination protocol unsupported";
217
+ readonly details: {};
218
+ };
219
+ }>;
220
+ /** Receives envelope data, e.g. ConvexError.data; never parses error messages. */
221
+ decode: (payload: unknown) => DecodedFailure<{
222
+ readonly 'invalid-cursor': {
223
+ readonly message: "Invalid cursor";
224
+ readonly details: {};
225
+ };
226
+ readonly FIELD_GROUP_DENIED: {
227
+ readonly message: "Field group access denied";
228
+ readonly details: {};
229
+ };
230
+ readonly QUERY_FANOUT: {
231
+ readonly message: "Query fan-out limit exceeded";
232
+ readonly details: {};
233
+ };
234
+ readonly SEARCH_TEXT_REQUIRED: {
235
+ readonly message: "Search text required";
236
+ readonly details: {};
237
+ };
238
+ readonly SEARCH_END_CURSOR_UNSUPPORTED: {
239
+ readonly message: "Search end cursor unsupported";
240
+ readonly details: {};
241
+ };
242
+ readonly SEARCH_ORDER_UNSUPPORTED: {
243
+ readonly message: "Search ordering unsupported";
244
+ readonly details: {};
245
+ };
246
+ readonly SEARCH_RELATION_UNSUPPORTED: {
247
+ readonly message: "Relation search unsupported";
248
+ readonly details: {};
249
+ };
250
+ readonly SEARCH_OPTIONS_UNSUPPORTED: {
251
+ readonly message: "Search options unsupported";
252
+ readonly details: {};
253
+ };
254
+ readonly SEARCH_PROTOCOL_UNSUPPORTED: {
255
+ readonly message: "Search pagination protocol unsupported";
256
+ readonly details: {};
257
+ };
258
+ }>;
259
+ }>;
42
260
  //#endregion
43
261
  //# sourceMappingURL=errors.d.mts.map
@@ -1,2 +1,2 @@
1
- const e=Object.freeze({_tag:`UnknownFailure`,version:1});function t(t){let a=Object.fromEntries(Object.entries(t).map(([e,t])=>{if(!e||!t.message||t.message.length>200)throw Error(`Declared errors require a code and a public message of 1–200 characters`);return[e,{message:t.message,details:{...t.details}}]})),o=/* @__PURE__ */ new WeakMap;return{create(e,t){let n;try{if(!Object.hasOwn(a,e))throw Error(`Undeclared code`);let r=a[e];n={_tag:`DeclaredFailure`,version:1,code:e,message:r.message,details:i(r,t,!1)}}catch{throw Error(`Invalid declared error details`)}let s=Object.freeze({_tag:`DeclaredError`,code:e,details:r(n.details)});return o.set(s,n),s},project(t){if(typeof t!=`object`||!t)return e;let n=o.get(t);return n?r(n):e},decode(t){try{if(!n(t)||t._tag!==`DeclaredFailure`||t.version!==1||typeof t.code!=`string`||!Object.hasOwn(a,t.code)||Object.keys(t).length!==5||![`message`,`details`,`_tag`,`version`,`code`].every(e=>Object.hasOwn(t,e)))return e;let o=a[t.code];return t.message===o.message?(r(t.details),{_tag:`DeclaredFailure`,version:1,code:t.code,message:o.message,details:i(o,t.details,!0)}):e}catch{return e}}}}function n(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function r(e,t=/* @__PURE__ */ new Set){if(e===null||typeof e==`string`||typeof e==`boolean`||typeof e==`number`&&Number.isFinite(e))return e;if(typeof e!=`object`||!e||t.has(e))throw Error(`Invalid JSON`);t.add(e);try{if(Array.isArray(e)){let n=[];for(let i=0;i<e.length;i++)n.push(r(e[i],t));return n}if(!n(e))throw Error(`Invalid JSON`);return Object.fromEntries(Object.entries(e).map(([e,n])=>[e,r(n,t)]))}finally{t.delete(e)}}function i(e,t,i){if(!n(t))throw Error(`Invalid details`);let a=Object.keys(e.details);if(i&&Object.keys(t).some(t=>!Object.hasOwn(e.details,t)))throw Error(`Undeclared detail`);return Object.fromEntries(a.map(n=>{if(!Object.hasOwn(t,n))throw Error(`Missing detail`);return[n,r(e.details[n].parse(t[n]))]}))}export{t as defineErrorContract};
1
+ const e=Object.freeze({_tag:`UnknownFailure`,version:1});function t(t){let n=Object.fromEntries(Object.entries(t).map(([e,t])=>{if(!e||!t.message||t.message.length>200)throw Error(`Declared errors require a code and a public message of 1–200 characters`);return[e,{message:t.message,details:{...t.details}}]})),o=/* @__PURE__ */ new WeakMap;return{create(e,t){let r;try{if(!Object.hasOwn(n,e))throw Error(`Undeclared code`);let i=n[e];r={_tag:`DeclaredFailure`,version:1,code:e,message:i.message,details:a(i,t,!1)}}catch{throw Error(`Invalid declared error details`)}let s=Object.freeze({_tag:`DeclaredError`,code:e,details:i(r.details)});return o.set(s,r),s},project(t){if(typeof t!=`object`||!t)return e;let n=o.get(t);return n?i(n):e},decode(t){try{if(!r(t)||t._tag!==`DeclaredFailure`||t.version!==1||typeof t.code!=`string`||!Object.hasOwn(n,t.code)||Object.keys(t).length!==5||![`message`,`details`,`_tag`,`version`,`code`].every(e=>Object.hasOwn(t,e)))return e;let o=n[t.code];return t.message===o.message?(i(t.details),{_tag:`DeclaredFailure`,version:1,code:t.code,message:o.message,details:a(o,t.details,!0)}):e}catch{return e}}}}const n=t({"invalid-cursor":{message:`Invalid cursor`,details:{}},FIELD_GROUP_DENIED:{message:`Field group access denied`,details:{}},QUERY_FANOUT:{message:`Query fan-out limit exceeded`,details:{}},SEARCH_TEXT_REQUIRED:{message:`Search text required`,details:{}},SEARCH_END_CURSOR_UNSUPPORTED:{message:`Search end cursor unsupported`,details:{}},SEARCH_ORDER_UNSUPPORTED:{message:`Search ordering unsupported`,details:{}},SEARCH_RELATION_UNSUPPORTED:{message:`Relation search unsupported`,details:{}},SEARCH_OPTIONS_UNSUPPORTED:{message:`Search options unsupported`,details:{}},SEARCH_PROTOCOL_UNSUPPORTED:{message:`Search pagination protocol unsupported`,details:{}}});function r(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function i(e,t=/* @__PURE__ */ new Set){if(e===null||typeof e==`string`||typeof e==`boolean`||typeof e==`number`&&Number.isFinite(e))return e;if(typeof e!=`object`||!e||t.has(e))throw Error(`Invalid JSON`);t.add(e);try{if(Array.isArray(e)){let n=[];for(let r=0;r<e.length;r++)n.push(i(e[r],t));return n}if(!r(e))throw Error(`Invalid JSON`);return Object.fromEntries(Object.entries(e).map(([e,n])=>[e,i(n,t)]))}finally{t.delete(e)}}function a(e,t,n){if(!r(t))throw Error(`Invalid details`);let a=Object.keys(e.details);if(n&&Object.keys(t).some(t=>!Object.hasOwn(e.details,t)))throw Error(`Undeclared detail`);return Object.fromEntries(a.map(n=>{if(!Object.hasOwn(t,n))throw Error(`Missing detail`);return[n,i(e.details[n].parse(t[n]))]}))}export{t as defineErrorContract,n as queryErrors};
2
2
  //# sourceMappingURL=errors.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":["unknownFailure","defineErrorContract","declarations","definitions","code","definition","trusted","envelope","parseDetails","details","error","copyJson","isRecord","payload","key","value","prototype","ancestors","result","index","child","fields","strict","field"],"sources":["../../../src/modules/contracts/errors.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters, anti-slop/no-unknown-returns, anti-slop/no-unknown-type-aliases, anti-slop/no-runtime-typeof, anti-slop/no-unsafe-dictionary-type -- This neutral contract validates unknown transport data and an explicitly keyed code/detail declaration. */\n\n/** Explicit synchronous validators for the final public detail representation. */\nexport type ErrorDetailParser<Output = unknown> = Readonly<{\n\tparse: (value: unknown) => Output\n}>\n\nexport type ErrorDefinitions = Readonly<\n\tRecord<\n\t\tstring,\n\t\tReadonly<{\n\t\t\tmessage: string\n\t\t\tdetails: Readonly<Record<string, ErrorDetailParser>>\n\t\t}>\n\t>\n>\n\ntype Code<Definitions extends ErrorDefinitions> = Extract<keyof Definitions, string>\ntype Details<Definition extends ErrorDefinitions[string]> =\n\tDefinition extends ErrorDefinitions[string]\n\t\t? { [Field in keyof Definition['details']]: ReturnType<Definition['details'][Field]['parse']> }\n\t\t: never\n\ndeclare const declaredErrorBrand: unique symbol\n\n/** Server failures are trusted only by the contract that created them. */\nexport type DeclaredError<Definitions extends ErrorDefinitions> = {\n\t[Key in Code<Definitions>]: Readonly<{\n\t\t_tag: 'DeclaredError'\n\t\tcode: Key\n\t\tdetails: Details<Definitions[Key]>\n\t\t[declaredErrorBrand]: true\n\t}>\n}[Code<Definitions>]\n\nexport type DeclaredFailure<Definitions extends ErrorDefinitions> = {\n\t[Key in Code<Definitions>]: Readonly<{\n\t\t_tag: 'DeclaredFailure'\n\t\tversion: 1\n\t\tcode: Key\n\t\tmessage: string\n\t\tdetails: Details<Definitions[Key]>\n\t}>\n}[Code<Definitions>]\n\nexport type UnknownFailure = Readonly<{ _tag: 'UnknownFailure'; version: 1 }>\nexport type DecodedFailure<Definitions extends ErrorDefinitions> =\n\t| DeclaredFailure<Definitions>\n\t| UnknownFailure\n\nexport type ErrorContract<Definitions extends ErrorDefinitions> = Readonly<{\n\tcreate: <Key extends Code<Definitions>>(\n\t\tcode: Key,\n\t\tdetails: Details<Definitions[NoInfer<Key>]>,\n\t) => Extract<DeclaredError<Definitions>, { code: Key }>\n\tproject: (error: unknown) => DecodedFailure<Definitions>\n\t/** Receives envelope data, e.g. ConvexError.data; never parses error messages. */\n\tdecode: (payload: unknown) => DecodedFailure<Definitions>\n}>\n\nconst unknownFailure: UnknownFailure = Object.freeze({ _tag: 'UnknownFailure', version: 1 })\ntype Json = null | boolean | number | string | Json[] | { [key: string]: Json }\n\n/** Closed safe codes, allowlisted details, and a versioned checked transport union. */\nexport function defineErrorContract<const Definitions extends ErrorDefinitions>(\n\tdefinitions: Definitions,\n): ErrorContract<Definitions> {\n\t// SAFETY: the snapshot copies every declaration without changing its code, message or paired parsers.\n\tconst declarations = Object.fromEntries(\n\t\tObject.entries(definitions).map(([code, definition]) => {\n\t\t\tif (!code || !definition.message || definition.message.length > 200) {\n\t\t\t\tthrow Error('Declared errors require a code and a public message of 1–200 characters')\n\t\t\t}\n\t\t\treturn [code, { message: definition.message, details: { ...definition.details } }]\n\t\t}),\n\t) as Definitions\n\tconst trusted = new WeakMap<object, DeclaredFailure<Definitions>>()\n\treturn {\n\t\tcreate(code, details) {\n\t\t\tlet envelope: DeclaredFailure<Definitions>\n\t\t\ttry {\n\t\t\t\tif (!Object.hasOwn(declarations, code)) throw Error('Undeclared code')\n\t\t\t\tconst definition = declarations[code]\n\t\t\t\t// SAFETY: the selected closed declaration validates every field before construction.\n\t\t\t\tenvelope = {\n\t\t\t\t\t_tag: 'DeclaredFailure',\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tcode,\n\t\t\t\t\tmessage: definition.message,\n\t\t\t\t\tdetails: parseDetails(definition, details, false),\n\t\t\t\t} as DeclaredFailure<Definitions>\n\t\t\t} catch {\n\t\t\t\t// Validator messages, issue paths and provider causes remain private.\n\t\t\t\tthrow Error('Invalid declared error details')\n\t\t\t}\n\t\t\t// SAFETY: identity is registered below; the inaccessible brand cannot confer trust by itself.\n\t\t\tconst error = Object.freeze({\n\t\t\t\t_tag: 'DeclaredError',\n\t\t\t\tcode,\n\t\t\t\tdetails: copyJson(envelope.details),\n\t\t\t}) as Extract<DeclaredError<Definitions>, { code: typeof code }>\n\t\t\ttrusted.set(error, envelope)\n\t\t\treturn error\n\t\t},\n\t\tproject(error) {\n\t\t\tif (error === null || typeof error !== 'object') return unknownFailure\n\t\t\tconst envelope = trusted.get(error)\n\t\t\t// SAFETY: only validated contract-owned envelopes enter the private map.\n\t\t\treturn envelope ? (copyJson(envelope) as DeclaredFailure<Definitions>) : unknownFailure\n\t\t},\n\t\tdecode(payload) {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\t!isRecord(payload) ||\n\t\t\t\t\tpayload._tag !== 'DeclaredFailure' ||\n\t\t\t\t\tpayload.version !== 1 ||\n\t\t\t\t\ttypeof payload.code !== 'string' ||\n\t\t\t\t\t!Object.hasOwn(declarations, payload.code) ||\n\t\t\t\t\tObject.keys(payload).length !== 5 ||\n\t\t\t\t\t!['message', 'details', '_tag', 'version', 'code'].every((key) =>\n\t\t\t\t\t\tObject.hasOwn(payload, key),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn unknownFailure\n\t\t\t\t}\n\t\t\t\tconst definition = declarations[payload.code]\n\t\t\t\tif (payload.message !== definition.message) return unknownFailure\n\t\t\t\t// Validate raw transport JSON too: parsers must not hide non-JSON payloads.\n\t\t\t\tcopyJson(payload.details)\n\t\t\t\t// SAFETY: code membership and every allowlisted field have been checked independently.\n\t\t\t\treturn {\n\t\t\t\t\t_tag: 'DeclaredFailure',\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tcode: payload.code,\n\t\t\t\t\tmessage: definition.message,\n\t\t\t\t\tdetails: parseDetails(definition, payload.details, true),\n\t\t\t\t} as DeclaredFailure<Definitions>\n\t\t\t} catch {\n\t\t\t\treturn unknownFailure\n\t\t\t}\n\t\t},\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst prototype = Object.getPrototypeOf(value)\n\treturn prototype === Object.prototype || prototype === null\n}\n\n/** Copy data without invoking toJSON or retaining mutable provider objects. */\nfunction copyJson(value: unknown, ancestors = new Set<object>()): Json {\n\tif (value === null || typeof value === 'string' || typeof value === 'boolean') return value\n\tif (typeof value === 'number' && Number.isFinite(value)) return value\n\tif (typeof value !== 'object' || value === null || ancestors.has(value))\n\t\tthrow Error('Invalid JSON')\n\tancestors.add(value)\n\ttry {\n\t\tif (Array.isArray(value)) {\n\t\t\tconst result: Json[] = []\n\t\t\tfor (let index = 0; index < value.length; index++)\n\t\t\t\tresult.push(copyJson(value[index], ancestors))\n\t\t\treturn result\n\t\t}\n\t\tif (!isRecord(value)) throw Error('Invalid JSON')\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value).map(([key, child]) => [key, copyJson(child, ancestors)]),\n\t\t)\n\t} finally {\n\t\tancestors.delete(value)\n\t}\n}\n\nfunction parseDetails<const Definition extends ErrorDefinitions[string]>(\n\tdefinition: Definition,\n\tvalue: unknown,\n\tstrict: boolean,\n): Details<Definition> {\n\tif (!isRecord(value)) throw Error('Invalid details')\n\tconst fields = Object.keys(definition.details)\n\tif (strict && Object.keys(value).some((key) => !Object.hasOwn(definition.details, key))) {\n\t\tthrow Error('Undeclared detail')\n\t}\n\t// SAFETY: each declared field is validated by its paired parser before its JSON-safe copy is returned.\n\treturn Object.fromEntries(\n\t\tfields.map((field) => {\n\t\t\tif (!Object.hasOwn(value, field)) throw Error('Missing detail')\n\t\t\treturn [field, copyJson(definition.details[field].parse(value[field]))]\n\t\t}),\n\t) as Details<Definition>\n}\n"],"mappings":"AA4DA,MAAMA,EAAiC,OAAO,OAAO,CAAE,KAAM,iBAAkB,QAAS,CAAE,CAAC,EAI3F,SAAgBC,EACf,EAC6B,CAE7B,IAAMC,EAAe,OAAO,YAC3B,OAAO,QAAQC,CAAW,CAAC,CAAC,KAAK,CAACC,EAAMC,KAAgB,CACvD,GAAI,CAACD,GAAQ,CAACC,EAAW,SAAWA,EAAW,QAAQ,OAAS,IAC/D,MAAM,MAAM,yEAAyE,EAEtF,MAAO,CAACD,EAAM,CAAE,QAASC,EAAW,QAAS,QAAS,CAAE,GAAGA,EAAW,OAAQ,CAAE,CAAC,CAClF,CAAC,CACF,EACMC,kBAAU,IAAI,QACpB,MAAO,CACN,OAAO,EAAM,EAAS,CACrB,IAAIC,EACJ,GAAI,CACH,GAAI,CAAC,OAAO,OAAOL,EAAcE,CAAI,EAAG,MAAM,MAAM,iBAAiB,EACrE,IAAMC,EAAaH,EAAaE,GAEhC,EAAW,CACV,KAAM,kBACN,QAAS,EACT,OACA,QAASC,EAAW,QACpB,QAASG,EAAaH,EAAYI,EAAS,EAAK,CACjD,CACD,MAAQ,CAEP,MAAM,MAAM,gCAAgC,CAC7C,CAEA,IAAMC,EAAQ,OAAO,OAAO,CAC3B,KAAM,gBACN,OACA,QAASC,EAASJ,EAAS,OAAO,CACnC,CAAC,EAED,OADAD,EAAQ,IAAII,EAAOH,CAAQ,EACpBG,CACR,EACA,QAAQ,EAAO,CACd,GAAsB,OAAOA,GAAU,WAAnCA,EAA6C,OAAOV,EACxD,IAAMO,EAAWD,EAAQ,IAAII,CAAK,EAElC,OAAOH,EAAYI,EAASJ,CAAQ,EAAqCP,CAC1E,EACA,OAAO,EAAS,CACf,GAAI,CACH,GACC,CAACY,EAASC,CAAO,GACjBA,EAAQ,OAAS,mBACjBA,EAAQ,UAAY,GACpB,OAAOA,EAAQ,MAAS,UACxB,CAAC,OAAO,OAAOX,EAAcW,EAAQ,IAAI,GACzC,OAAO,KAAKA,CAAO,CAAC,CAAC,SAAW,GAChC,CAAC,CAAC,UAAW,UAAW,OAAQ,UAAW,MAAM,CAAC,CAAC,MAAO,GACzD,OAAO,OAAOA,EAASC,CAAG,CAC3B,EAEA,OAAOd,EAER,IAAMK,EAAaH,EAAaW,EAAQ,MAKxC,OAJIA,EAAQ,UAAYR,EAAW,SAEnCM,EAASE,EAAQ,OAAO,EAEjB,CACN,KAAM,kBACN,QAAS,EACT,KAAMA,EAAQ,KACd,QAASR,EAAW,QACpB,QAASG,EAAaH,EAAYQ,EAAQ,QAAS,EAAI,CACxD,GAVmDb,CAWpD,MAAQ,CACP,OAAOA,CACR,CACD,CACD,CACD,CAEA,SAASY,EAAS,EAAkD,CACnE,GAAsB,OAAOG,GAAU,WAAnCA,GAA+C,MAAM,QAAQA,CAAK,EAAG,MAAO,GAChF,IAAMC,EAAY,OAAO,eAAeD,CAAK,EAC7C,OAAOC,IAAc,OAAO,WAAaA,IAAc,IACxD,CAGA,SAASL,EAAS,EAAgB,kBAAY,IAAI,IAAqB,CAEtE,GADII,IAAU,MAAQ,OAAOA,GAAU,UAAY,OAAOA,GAAU,WAChE,OAAOA,GAAU,UAAY,OAAO,SAASA,CAAK,EAAG,OAAOA,EAChE,GAAI,OAAOA,GAAU,WAAYA,GAAkBE,EAAU,IAAIF,CAAK,EACrE,MAAM,MAAM,cAAc,EAC3BE,EAAU,IAAIF,CAAK,EACnB,GAAI,CACH,GAAI,MAAM,QAAQA,CAAK,EAAG,CACzB,IAAMG,EAAiB,CAAC,EACxB,IAAK,IAAIC,EAAQ,EAAGA,EAAQJ,EAAM,OAAQ,IACzCG,EAAO,KAAKP,EAASI,EAAMI,GAAQF,CAAS,CAAC,EAC9C,OAAOC,CACR,CACA,GAAI,CAACN,EAASG,CAAK,EAAG,MAAM,MAAM,cAAc,EAChD,OAAO,OAAO,YACb,OAAO,QAAQA,CAAK,CAAC,CAAC,KAAK,CAACD,EAAKM,KAAW,CAACN,EAAKH,EAASS,EAAOH,CAAS,CAAC,CAAC,CAC9E,CACD,QAAU,CACTA,EAAU,OAAOF,CAAK,CACvB,CACD,CAEA,SAASP,EACR,EACA,EACA,EACsB,CACtB,GAAI,CAACI,EAASG,CAAK,EAAG,MAAM,MAAM,iBAAiB,EACnD,IAAMM,EAAS,OAAO,KAAKhB,EAAW,OAAO,EAC7C,GAAIiB,GAAU,OAAO,KAAKP,CAAK,CAAC,CAAC,KAAM,GAAQ,CAAC,OAAO,OAAOV,EAAW,QAASS,CAAG,CAAC,EACrF,MAAM,MAAM,mBAAmB,EAGhC,OAAO,OAAO,YACbO,EAAO,IAAK,GAAU,CACrB,GAAI,CAAC,OAAO,OAAON,EAAOQ,CAAK,EAAG,MAAM,MAAM,gBAAgB,EAC9D,MAAO,CAACA,EAAOZ,EAASN,EAAW,QAAQkB,EAAM,CAAC,MAAMR,EAAMQ,EAAM,CAAC,CAAC,CACvE,CAAC,CACF,CACD"}
1
+ {"version":3,"file":"errors.mjs","names":["unknownFailure","defineErrorContract","declarations","definitions","code","definition","trusted","envelope","parseDetails","details","error","copyJson","isRecord","payload","key","queryErrors","value","prototype","ancestors","result","index","child","fields","strict","field"],"sources":["../../../src/modules/contracts/errors.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters, anti-slop/no-unknown-returns, anti-slop/no-unknown-type-aliases, anti-slop/no-runtime-typeof, anti-slop/no-unsafe-dictionary-type -- This neutral contract validates unknown transport data and an explicitly keyed code/detail declaration. */\n\n/** Explicit synchronous validators for the final public detail representation. */\nexport type ErrorDetailParser<Output = unknown> = Readonly<{\n\tparse: (value: unknown) => Output\n}>\n\nexport type ErrorDefinitions = Readonly<\n\tRecord<\n\t\tstring,\n\t\tReadonly<{\n\t\t\tmessage: string\n\t\t\tdetails: Readonly<Record<string, ErrorDetailParser>>\n\t\t}>\n\t>\n>\n\ntype Code<Definitions extends ErrorDefinitions> = Extract<keyof Definitions, string>\ntype Details<Definition extends ErrorDefinitions[string]> =\n\tDefinition extends ErrorDefinitions[string]\n\t\t? { [Field in keyof Definition['details']]: ReturnType<Definition['details'][Field]['parse']> }\n\t\t: never\n\ndeclare const declaredErrorBrand: unique symbol\n\n/** Server failures are trusted only by the contract that created them. */\nexport type DeclaredError<Definitions extends ErrorDefinitions> = {\n\t[Key in Code<Definitions>]: Readonly<{\n\t\t_tag: 'DeclaredError'\n\t\tcode: Key\n\t\tdetails: Details<Definitions[Key]>\n\t\t[declaredErrorBrand]: true\n\t}>\n}[Code<Definitions>]\n\nexport type DeclaredFailure<Definitions extends ErrorDefinitions> = {\n\t[Key in Code<Definitions>]: Readonly<{\n\t\t_tag: 'DeclaredFailure'\n\t\tversion: 1\n\t\tcode: Key\n\t\tmessage: string\n\t\tdetails: Details<Definitions[Key]>\n\t}>\n}[Code<Definitions>]\n\nexport type UnknownFailure = Readonly<{ _tag: 'UnknownFailure'; version: 1 }>\nexport type DecodedFailure<Definitions extends ErrorDefinitions> =\n\tDeclaredFailure<Definitions> | UnknownFailure\n\nexport type ErrorContract<Definitions extends ErrorDefinitions> = Readonly<{\n\tcreate: <Key extends Code<Definitions>>(\n\t\tcode: Key,\n\t\tdetails: Details<Definitions[NoInfer<Key>]>,\n\t) => Extract<DeclaredError<Definitions>, { code: Key }>\n\tproject: (error: unknown) => DecodedFailure<Definitions>\n\t/** Receives envelope data, e.g. ConvexError.data; never parses error messages. */\n\tdecode: (payload: unknown) => DecodedFailure<Definitions>\n}>\n\nconst unknownFailure: UnknownFailure = Object.freeze({\n\t_tag: 'UnknownFailure',\n\tversion: 1,\n})\ntype Json = null | boolean | number | string | Json[] | { [key: string]: Json }\n\n/** Closed safe codes, allowlisted details, and a versioned checked transport union. */\nexport function defineErrorContract<const Definitions extends ErrorDefinitions>(\n\tdefinitions: Definitions,\n): ErrorContract<Definitions> {\n\t// SAFETY: the snapshot copies every declaration without changing its code, message or paired parsers.\n\tconst declarations = Object.fromEntries(\n\t\tObject.entries(definitions).map(([code, definition]) => {\n\t\t\tif (!code || !definition.message || definition.message.length > 200) {\n\t\t\t\tthrow Error('Declared errors require a code and a public message of 1–200 characters')\n\t\t\t}\n\t\t\treturn [code, { message: definition.message, details: { ...definition.details } }]\n\t\t}),\n\t) as Definitions\n\tconst trusted = new WeakMap<object, DeclaredFailure<Definitions>>()\n\treturn {\n\t\tcreate(code, details) {\n\t\t\tlet envelope: DeclaredFailure<Definitions>\n\t\t\ttry {\n\t\t\t\tif (!Object.hasOwn(declarations, code)) throw Error('Undeclared code')\n\t\t\t\tconst definition = declarations[code]\n\t\t\t\t// SAFETY: the selected closed declaration validates every field before construction.\n\t\t\t\tenvelope = {\n\t\t\t\t\t_tag: 'DeclaredFailure',\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tcode,\n\t\t\t\t\tmessage: definition.message,\n\t\t\t\t\tdetails: parseDetails(definition, details, false),\n\t\t\t\t} as DeclaredFailure<Definitions>\n\t\t\t} catch {\n\t\t\t\t// Validator messages, issue paths and provider causes remain private.\n\t\t\t\tthrow Error('Invalid declared error details')\n\t\t\t}\n\t\t\t// SAFETY: identity is registered below; the inaccessible brand cannot confer trust by itself.\n\t\t\tconst error = Object.freeze({\n\t\t\t\t_tag: 'DeclaredError',\n\t\t\t\tcode,\n\t\t\t\tdetails: copyJson(envelope.details),\n\t\t\t}) as Extract<DeclaredError<Definitions>, { code: typeof code }>\n\t\t\ttrusted.set(error, envelope)\n\t\t\treturn error\n\t\t},\n\t\tproject(error) {\n\t\t\tif (error === null || typeof error !== 'object') return unknownFailure\n\t\t\tconst envelope = trusted.get(error)\n\t\t\t// SAFETY: only validated contract-owned envelopes enter the private map.\n\t\t\treturn envelope ? (copyJson(envelope) as DeclaredFailure<Definitions>) : unknownFailure\n\t\t},\n\t\tdecode(payload) {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\t!isRecord(payload) ||\n\t\t\t\t\tpayload._tag !== 'DeclaredFailure' ||\n\t\t\t\t\tpayload.version !== 1 ||\n\t\t\t\t\ttypeof payload.code !== 'string' ||\n\t\t\t\t\t!Object.hasOwn(declarations, payload.code) ||\n\t\t\t\t\tObject.keys(payload).length !== 5 ||\n\t\t\t\t\t!['message', 'details', '_tag', 'version', 'code'].every((key) =>\n\t\t\t\t\t\tObject.hasOwn(payload, key),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn unknownFailure\n\t\t\t\t}\n\t\t\t\tconst definition = declarations[payload.code]\n\t\t\t\tif (payload.message !== definition.message) return unknownFailure\n\t\t\t\t// Validate raw transport JSON too: parsers must not hide non-JSON payloads.\n\t\t\t\tcopyJson(payload.details)\n\t\t\t\t// SAFETY: code membership and every allowlisted field have been checked independently.\n\t\t\t\treturn {\n\t\t\t\t\t_tag: 'DeclaredFailure',\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tcode: payload.code,\n\t\t\t\t\tmessage: definition.message,\n\t\t\t\t\tdetails: parseDetails(definition, payload.details, true),\n\t\t\t\t} as DeclaredFailure<Definitions>\n\t\t\t} catch {\n\t\t\t\treturn unknownFailure\n\t\t\t}\n\t\t},\n\t}\n}\n\n/** Fixed, diagnostic-free transport shared by query endpoints and client recovery. */\nexport const queryErrors = defineErrorContract({\n\t'invalid-cursor': { message: 'Invalid cursor', details: {} },\n\tFIELD_GROUP_DENIED: { message: 'Field group access denied', details: {} },\n\tQUERY_FANOUT: { message: 'Query fan-out limit exceeded', details: {} },\n\tSEARCH_TEXT_REQUIRED: { message: 'Search text required', details: {} },\n\tSEARCH_END_CURSOR_UNSUPPORTED: {\n\t\tmessage: 'Search end cursor unsupported',\n\t\tdetails: {},\n\t},\n\tSEARCH_ORDER_UNSUPPORTED: {\n\t\tmessage: 'Search ordering unsupported',\n\t\tdetails: {},\n\t},\n\tSEARCH_RELATION_UNSUPPORTED: {\n\t\tmessage: 'Relation search unsupported',\n\t\tdetails: {},\n\t},\n\tSEARCH_OPTIONS_UNSUPPORTED: {\n\t\tmessage: 'Search options unsupported',\n\t\tdetails: {},\n\t},\n\tSEARCH_PROTOCOL_UNSUPPORTED: {\n\t\tmessage: 'Search pagination protocol unsupported',\n\t\tdetails: {},\n\t},\n})\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst prototype = Object.getPrototypeOf(value)\n\treturn prototype === Object.prototype || prototype === null\n}\n\n/** Copy data without invoking toJSON or retaining mutable provider objects. */\nfunction copyJson(value: unknown, ancestors = new Set<object>()): Json {\n\tif (value === null || typeof value === 'string' || typeof value === 'boolean') return value\n\tif (typeof value === 'number' && Number.isFinite(value)) return value\n\tif (typeof value !== 'object' || value === null || ancestors.has(value))\n\t\tthrow Error('Invalid JSON')\n\tancestors.add(value)\n\ttry {\n\t\tif (Array.isArray(value)) {\n\t\t\tconst result: Json[] = []\n\t\t\tfor (let index = 0; index < value.length; index++)\n\t\t\t\tresult.push(copyJson(value[index], ancestors))\n\t\t\treturn result\n\t\t}\n\t\tif (!isRecord(value)) throw Error('Invalid JSON')\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value).map(([key, child]) => [key, copyJson(child, ancestors)]),\n\t\t)\n\t} finally {\n\t\tancestors.delete(value)\n\t}\n}\n\nfunction parseDetails<const Definition extends ErrorDefinitions[string]>(\n\tdefinition: Definition,\n\tvalue: unknown,\n\tstrict: boolean,\n): Details<Definition> {\n\tif (!isRecord(value)) throw Error('Invalid details')\n\tconst fields = Object.keys(definition.details)\n\tif (strict && Object.keys(value).some((key) => !Object.hasOwn(definition.details, key))) {\n\t\tthrow Error('Undeclared detail')\n\t}\n\t// SAFETY: each declared field is validated by its paired parser before its JSON-safe copy is returned.\n\treturn Object.fromEntries(\n\t\tfields.map((field) => {\n\t\t\tif (!Object.hasOwn(value, field)) throw Error('Missing detail')\n\t\t\treturn [field, copyJson(definition.details[field].parse(value[field]))]\n\t\t}),\n\t) as Details<Definition>\n}\n"],"mappings":"AA2DA,MAAMA,EAAiC,OAAO,OAAO,CACpD,KAAM,iBACN,QAAS,CACV,CAAC,EAID,SAAgBC,EACf,EAC6B,CAE7B,IAAMC,EAAe,OAAO,YAC3B,OAAO,QAAQC,CAAW,CAAC,CAAC,KAAK,CAACC,EAAMC,KAAgB,CACvD,GAAI,CAACD,GAAQ,CAACC,EAAW,SAAWA,EAAW,QAAQ,OAAS,IAC/D,MAAM,MAAM,yEAAyE,EAEtF,MAAO,CAACD,EAAM,CAAE,QAASC,EAAW,QAAS,QAAS,CAAE,GAAGA,EAAW,OAAQ,CAAE,CAAC,CAClF,CAAC,CACF,EACMC,kBAAU,IAAI,QACpB,MAAO,CACN,OAAO,EAAM,EAAS,CACrB,IAAIC,EACJ,GAAI,CACH,GAAI,CAAC,OAAO,OAAOL,EAAcE,CAAI,EAAG,MAAM,MAAM,iBAAiB,EACrE,IAAMC,EAAaH,EAAaE,GAEhC,EAAW,CACV,KAAM,kBACN,QAAS,EACT,OACA,QAASC,EAAW,QACpB,QAASG,EAAaH,EAAYI,EAAS,EAAK,CACjD,CACD,MAAQ,CAEP,MAAM,MAAM,gCAAgC,CAC7C,CAEA,IAAMC,EAAQ,OAAO,OAAO,CAC3B,KAAM,gBACN,OACA,QAASC,EAASJ,EAAS,OAAO,CACnC,CAAC,EAED,OADAD,EAAQ,IAAII,EAAOH,CAAQ,EACpBG,CACR,EACA,QAAQ,EAAO,CACd,GAAsB,OAAOA,GAAU,WAAnCA,EAA6C,OAAOV,EACxD,IAAMO,EAAWD,EAAQ,IAAII,CAAK,EAElC,OAAOH,EAAYI,EAASJ,CAAQ,EAAqCP,CAC1E,EACA,OAAO,EAAS,CACf,GAAI,CACH,GACC,CAACY,EAASC,CAAO,GACjBA,EAAQ,OAAS,mBACjBA,EAAQ,UAAY,GACpB,OAAOA,EAAQ,MAAS,UACxB,CAAC,OAAO,OAAOX,EAAcW,EAAQ,IAAI,GACzC,OAAO,KAAKA,CAAO,CAAC,CAAC,SAAW,GAChC,CAAC,CAAC,UAAW,UAAW,OAAQ,UAAW,MAAM,CAAC,CAAC,MAAO,GACzD,OAAO,OAAOA,EAASC,CAAG,CAC3B,EAEA,OAAOd,EAER,IAAMK,EAAaH,EAAaW,EAAQ,MAKxC,OAJIA,EAAQ,UAAYR,EAAW,SAEnCM,EAASE,EAAQ,OAAO,EAEjB,CACN,KAAM,kBACN,QAAS,EACT,KAAMA,EAAQ,KACd,QAASR,EAAW,QACpB,QAASG,EAAaH,EAAYQ,EAAQ,QAAS,EAAI,CACxD,GAVmDb,CAWpD,MAAQ,CACP,OAAOA,CACR,CACD,CACD,CACD,CAGA,MAAae,EAAcd,EAAoB,CAC9C,iBAAkB,CAAE,QAAS,iBAAkB,QAAS,CAAC,CAAE,EAC3D,mBAAoB,CAAE,QAAS,4BAA6B,QAAS,CAAC,CAAE,EACxE,aAAc,CAAE,QAAS,+BAAgC,QAAS,CAAC,CAAE,EACrE,qBAAsB,CAAE,QAAS,uBAAwB,QAAS,CAAC,CAAE,EACrE,8BAA+B,CAC9B,QAAS,gCACT,QAAS,CAAC,CACX,EACA,yBAA0B,CACzB,QAAS,8BACT,QAAS,CAAC,CACX,EACA,4BAA6B,CAC5B,QAAS,8BACT,QAAS,CAAC,CACX,EACA,2BAA4B,CAC3B,QAAS,6BACT,QAAS,CAAC,CACX,EACA,4BAA6B,CAC5B,QAAS,yCACT,QAAS,CAAC,CACX,CACD,CAAC,EAED,SAASW,EAAS,EAAkD,CACnE,GAAsB,OAAOI,GAAU,WAAnCA,GAA+C,MAAM,QAAQA,CAAK,EAAG,MAAO,GAChF,IAAMC,EAAY,OAAO,eAAeD,CAAK,EAC7C,OAAOC,IAAc,OAAO,WAAaA,IAAc,IACxD,CAGA,SAASN,EAAS,EAAgB,kBAAY,IAAI,IAAqB,CAEtE,GADIK,IAAU,MAAQ,OAAOA,GAAU,UAAY,OAAOA,GAAU,WAChE,OAAOA,GAAU,UAAY,OAAO,SAASA,CAAK,EAAG,OAAOA,EAChE,GAAI,OAAOA,GAAU,WAAYA,GAAkBE,EAAU,IAAIF,CAAK,EACrE,MAAM,MAAM,cAAc,EAC3BE,EAAU,IAAIF,CAAK,EACnB,GAAI,CACH,GAAI,MAAM,QAAQA,CAAK,EAAG,CACzB,IAAMG,EAAiB,CAAC,EACxB,IAAK,IAAIC,EAAQ,EAAGA,EAAQJ,EAAM,OAAQ,IACzCG,EAAO,KAAKR,EAASK,EAAMI,GAAQF,CAAS,CAAC,EAC9C,OAAOC,CACR,CACA,GAAI,CAACP,EAASI,CAAK,EAAG,MAAM,MAAM,cAAc,EAChD,OAAO,OAAO,YACb,OAAO,QAAQA,CAAK,CAAC,CAAC,KAAK,CAACF,EAAKO,KAAW,CAACP,EAAKH,EAASU,EAAOH,CAAS,CAAC,CAAC,CAC9E,CACD,QAAU,CACTA,EAAU,OAAOF,CAAK,CACvB,CACD,CAEA,SAASR,EACR,EACA,EACA,EACsB,CACtB,GAAI,CAACI,EAASI,CAAK,EAAG,MAAM,MAAM,iBAAiB,EACnD,IAAMM,EAAS,OAAO,KAAKjB,EAAW,OAAO,EAC7C,GAAIkB,GAAU,OAAO,KAAKP,CAAK,CAAC,CAAC,KAAM,GAAQ,CAAC,OAAO,OAAOX,EAAW,QAASS,CAAG,CAAC,EACrF,MAAM,MAAM,mBAAmB,EAGhC,OAAO,OAAO,YACbQ,EAAO,IAAK,GAAU,CACrB,GAAI,CAAC,OAAO,OAAON,EAAOQ,CAAK,EAAG,MAAM,MAAM,gBAAgB,EAC9D,MAAO,CAACA,EAAOb,EAASN,EAAW,QAAQmB,EAAM,CAAC,MAAMR,EAAMQ,EAAM,CAAC,CAAC,CACvE,CAAC,CACF,CACD"}
@@ -361,6 +361,7 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
361
361
  }, z.core.$strict>;
362
362
  result: z.ZodObject<{
363
363
  page: z.ZodArray<Table["publicDto"]>;
364
+ paginationMode: z.ZodOptional<z.ZodLiteral<"native-search">>;
364
365
  isDone: z.ZodBoolean;
365
366
  continueCursor: z.ZodString;
366
367
  splitCursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -368,6 +369,13 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
368
369
  SplitRecommended: "SplitRecommended";
369
370
  SplitRequired: "SplitRequired";
370
371
  }>>>;
372
+ completeness: z.ZodOptional<z.ZodEnum<{
373
+ "byte-budget": "byte-budget";
374
+ complete: "complete";
375
+ "native-limit": "native-limit";
376
+ "result-cap": "result-cap";
377
+ "row-budget": "row-budget";
378
+ }>>;
371
379
  }, z.core.$strip>;
372
380
  handler: (input: {
373
381
  numItems: number;
@@ -392,10 +400,12 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
392
400
  schema: ContractSchema;
393
401
  project: (value: {
394
402
  page: z.infer<Table["publicDto"]>[];
403
+ paginationMode?: "native-search" | undefined;
395
404
  isDone: boolean;
396
405
  continueCursor: string;
397
406
  splitCursor?: string | null | undefined;
398
407
  pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined;
408
+ completeness?: "byte-budget" | "complete" | "native-limit" | "result-cap" | "row-budget" | undefined;
399
409
  }) => unknown;
400
410
  } | undefined;
401
411
  guard?: ((context: EffectValue<ContextReturned> & Record<never, never>, input: {
@@ -425,10 +435,12 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
425
435
  };
426
436
  result: {
427
437
  page: z.infer<Table["publicDto"]>[];
438
+ paginationMode?: "native-search" | undefined;
428
439
  isDone: boolean;
429
440
  continueCursor: string;
430
441
  splitCursor?: string | null | undefined;
431
442
  pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined;
443
+ completeness?: "byte-budget" | "complete" | "native-limit" | "result-cap" | "row-budget" | undefined;
432
444
  };
433
445
  }, context: EffectValue<ContextReturned>) => never) | undefined;
434
446
  } & {
@@ -441,10 +453,12 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
441
453
  maximumBytesRead?: number | undefined;
442
454
  }, {
443
455
  page: z.core.input<Table["publicDto"]>[];
456
+ paginationMode?: "native-search" | undefined;
444
457
  isDone: boolean;
445
458
  continueCursor: string;
446
459
  splitCursor?: string | null | undefined;
447
460
  pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined;
461
+ completeness?: "byte-budget" | "complete" | "native-limit" | "result-cap" | "row-budget" | undefined;
448
462
  }, Record<never, never>, CallbackError<List> | CallbackError<ListGuard> | CallbackError<Policy> | CallbackError<Project>, CallbackRequirements<List> | CallbackRequirements<ListGuard> | CallbackRequirements<Policy> | CallbackRequirements<Project>>) => never) | undefined;
449
463
  } & {
450
464
  prepare?: never;
@@ -686,6 +700,7 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
686
700
  }, z.core.$strict>;
687
701
  result: z.ZodObject<{
688
702
  page: z.ZodArray<Table["publicDto"]>;
703
+ paginationMode: z.ZodOptional<z.ZodLiteral<"native-search">>;
689
704
  isDone: z.ZodBoolean;
690
705
  continueCursor: z.ZodString;
691
706
  splitCursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -693,6 +708,13 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
693
708
  SplitRecommended: "SplitRecommended";
694
709
  SplitRequired: "SplitRequired";
695
710
  }>>>;
711
+ completeness: z.ZodOptional<z.ZodEnum<{
712
+ "byte-budget": "byte-budget";
713
+ complete: "complete";
714
+ "native-limit": "native-limit";
715
+ "result-cap": "result-cap";
716
+ "row-budget": "row-budget";
717
+ }>>;
696
718
  }, z.core.$strip>;
697
719
  handler: (input: {
698
720
  numItems: number;
@@ -717,10 +739,12 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
717
739
  schema: ContractSchema;
718
740
  project: (value: {
719
741
  page: z.infer<Table["publicDto"]>[];
742
+ paginationMode?: "native-search" | undefined;
720
743
  isDone: boolean;
721
744
  continueCursor: string;
722
745
  splitCursor?: string | null | undefined;
723
746
  pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined;
747
+ completeness?: "byte-budget" | "complete" | "native-limit" | "result-cap" | "row-budget" | undefined;
724
748
  }) => unknown;
725
749
  } | undefined;
726
750
  guard?: ((context: EffectValue<ContextReturned> & Record<never, never>, input: {
@@ -750,10 +774,12 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
750
774
  };
751
775
  result: {
752
776
  page: z.infer<Table["publicDto"]>[];
777
+ paginationMode?: "native-search" | undefined;
753
778
  isDone: boolean;
754
779
  continueCursor: string;
755
780
  splitCursor?: string | null | undefined;
756
781
  pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined;
782
+ completeness?: "byte-budget" | "complete" | "native-limit" | "result-cap" | "row-budget" | undefined;
757
783
  };
758
784
  }, context: EffectValue<ContextReturned>) => never) | undefined;
759
785
  } & {
@@ -766,10 +792,12 @@ export declare function createEffectCrud<const Options extends EffectFoundationO
766
792
  maximumBytesRead?: number | undefined;
767
793
  }, {
768
794
  page: z.core.input<Table["publicDto"]>[];
795
+ paginationMode?: "native-search" | undefined;
769
796
  isDone: boolean;
770
797
  continueCursor: string;
771
798
  splitCursor?: string | null | undefined;
772
799
  pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined;
800
+ completeness?: "byte-budget" | "complete" | "native-limit" | "result-cap" | "row-budget" | undefined;
773
801
  }, Record<never, never>, CallbackError<List> | CallbackError<ListGuard> | CallbackError<Policy> | CallbackError<Project>, CallbackRequirements<List> | CallbackRequirements<ListGuard> | CallbackRequirements<Policy> | CallbackRequirements<Project>>) => never) | undefined;
774
802
  } & {
775
803
  prepare?: never;
@@ -1,3 +1,5 @@
1
+ import { ManagedScope } from "./security.mjs";
2
+ import { z } from "zod";
1
3
  import { Value } from "convex/values";
2
4
  //#region src/querying/cursors.d.ts
3
5
  /** Private engine contract. Bindings must come from trusted normalized engine state, never query args. */
@@ -33,8 +35,12 @@ export declare class CursorConfigurationError extends Error {
33
35
  }
34
36
  /** Private lazy server configuration. Call only at the first pagination boundary. */
35
37
  export declare function createEnvironmentCursorCodec(): Readonly<{
36
- seal(raw: string, binding: CursorBinding): string;
37
- open(token: string, binding: CursorBinding): string;
38
+ seal: (raw: string, binding: CursorBinding) => string;
39
+ open: (token: string, binding: CursorBinding) => string;
40
+ custom: Readonly<{
41
+ seal(raw: string, binding: CursorBinding): string;
42
+ open(token: string, binding: CursorBinding): string;
43
+ }>;
38
44
  }>;
39
45
  /**
40
46
  * Deterministic RFC 5297 AES-SIV: equal plaintext and binding leak equality.
@@ -43,8 +49,28 @@ export declare function createEnvironmentCursorCodec(): Readonly<{
43
49
  * must recheck current policy on every future request, including nested continuations.
44
50
  */
45
51
  export declare function createCursorCodec(config: CursorCodecConfig): Readonly<{
46
- seal(raw: string, binding: CursorBinding): string;
47
- open(token: string, binding: CursorBinding): string;
52
+ seal: (raw: string, binding: CursorBinding) => string;
53
+ open: (token: string, binding: CursorBinding) => string;
54
+ custom: Readonly<{
55
+ seal(raw: string, binding: CursorBinding): string;
56
+ open(token: string, binding: CursorBinding): string;
57
+ }>;
48
58
  }>;
59
+ /** Server-derived binding supplements managed scope; opening never grants row access. */
60
+ export interface ScopedCursorOptions<S extends z.ZodType<Value>> {
61
+ readonly domain: string;
62
+ readonly bind: Value;
63
+ /** Synchronous schema, applied on seal AND open. Outputs must be bounded Convex values. */
64
+ readonly schema: S;
65
+ }
66
+ /** Integrity only: consumers must recheck current authorization on every request. */
67
+ export interface ScopedCursorCodec<S extends z.ZodType<Value>> {
68
+ seal(payload: z.input<S>): string;
69
+ open(token: string): z.output<S>;
70
+ }
71
+ /** Internal construction uses the configured engine's already captured key ring. */
72
+ export declare function createScopedCursorCodec<S extends z.ZodType<Value>>(transport: ReturnType<typeof createCursorCodec>['custom'], scope: ManagedScope & {
73
+ readonly policyVersion: string;
74
+ }, options: ScopedCursorOptions<S>): ScopedCursorCodec<S>;
49
75
  //#endregion
50
76
  //# sourceMappingURL=cursors.d.mts.map