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.
- package/README.md +50 -2
- package/dist/errors.d.mts +3 -2
- package/dist/errors.mjs +1 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/filters.d.mts +4 -0
- package/dist/filters.mjs +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/modules/contracts/errors.d.mts +218 -0
- package/dist/modules/contracts/errors.mjs +1 -1
- package/dist/modules/contracts/errors.mjs.map +1 -1
- package/dist/modules/effect/crud.d.mts +28 -0
- package/dist/querying/cursors.d.mts +30 -4
- package/dist/querying/cursors.mjs +1 -1
- package/dist/querying/cursors.mjs.map +1 -1
- package/dist/querying/execution.d.mts +14 -3
- package/dist/querying/execution.mjs +1 -1
- package/dist/querying/execution.mjs.map +1 -1
- package/dist/querying/failures.d.mts +13 -0
- package/dist/querying/failures.mjs +2 -0
- package/dist/querying/failures.mjs.map +1 -0
- package/dist/querying/filters.d.mts +43 -44
- package/dist/querying/filters.mjs +1 -1
- package/dist/querying/filters.mjs.map +1 -1
- package/dist/querying/planner.d.mts +13 -16
- package/dist/querying/planner.mjs +1 -1
- package/dist/querying/planner.mjs.map +1 -1
- package/dist/querying/relationships.d.mts +24 -7
- package/dist/querying/relationships.mjs +1 -1
- package/dist/querying/relationships.mjs.map +1 -1
- package/dist/querying/schema-snapshot.d.mts +29 -0
- package/dist/querying/schema-snapshot.mjs +2 -0
- package/dist/querying/schema-snapshot.mjs.map +1 -0
- package/dist/querying/search.d.mts +69 -0
- package/dist/querying/search.mjs +2 -0
- package/dist/querying/search.mjs.map +1 -0
- package/dist/querying/security.d.mts +19 -2
- package/dist/querying/security.mjs +1 -1
- package/dist/querying/security.mjs.map +1 -1
- package/dist/querying/shared/ast.d.mts +28 -0
- package/dist/querying/shared/ast.mjs +2 -0
- package/dist/querying/shared/ast.mjs.map +1 -0
- package/dist/querying/shared/contract.d.mts +104 -0
- package/dist/querying/shared/contract.mjs +2 -0
- package/dist/querying/shared/contract.mjs.map +1 -0
- package/dist/querying/shared/evaluate.d.mts +7 -0
- package/dist/querying/shared/evaluate.mjs +2 -0
- package/dist/querying/shared/evaluate.mjs.map +1 -0
- package/dist/querying/shared/filters.d.mts +31 -0
- package/dist/querying/shared/filters.mjs +2 -0
- package/dist/querying/shared/filters.mjs.map +1 -0
- package/dist/querying.d.mts +47 -16
- package/dist/querying.mjs +1 -1
- package/dist/querying.mjs.map +1 -1
- package/dist/react/failures.d.mts +23 -0
- package/dist/react/failures.mjs +2 -0
- package/dist/react/failures.mjs.map +1 -0
- package/dist/react/optimistic.d.mts +47 -0
- package/dist/react/optimistic.mjs +2 -0
- package/dist/react/optimistic.mjs.map +1 -0
- package/dist/react/pagination-state.d.mts +40 -0
- package/dist/react/pagination-state.mjs +2 -0
- package/dist/react/pagination-state.mjs.map +1 -0
- package/dist/react/use-command.d.mts +23 -0
- package/dist/react/use-command.mjs +2 -0
- package/dist/react/use-command.mjs.map +1 -0
- package/dist/react/use-paginated-list.d.mts +32 -0
- package/dist/react/use-paginated-list.mjs +2 -0
- package/dist/react/use-paginated-list.mjs.map +1 -0
- package/dist/react.d.mts +6 -0
- package/dist/react.mjs +1 -0
- package/dist/relations-public.d.mts +2 -2
- package/dist/relations.d.mts +65 -22
- package/dist/relations.mjs +1 -1
- package/dist/relations.mjs.map +1 -1
- package/dist/zod-convex.mjs +1 -1
- package/dist/zod-convex.mjs.map +1 -1
- package/dist/zod-table.d.mts +15 -1
- package/dist/zod-table.mjs +1 -1
- package/dist/zod-table.mjs.map +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
package/dist/errors.mjs.map
CHANGED
|
@@ -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":"
|
|
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 };
|
package/dist/filters.mjs
ADDED
|
@@ -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
|
|
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":"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"}
|
|
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":"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"}
|
|
@@ -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)
|
|
37
|
-
open(token: string, binding: CursorBinding)
|
|
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)
|
|
47
|
-
open(token: string, binding: CursorBinding)
|
|
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
|