@classytic/repo-core 0.31.1 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/_virtual/_rolldown/runtime.mjs +1 -2
- package/dist/adapter/index.mjs +1 -2
- package/dist/adapter/types.d.mts +0 -2
- package/dist/adapter/widen.d.mts +0 -2
- package/dist/adapter/widen.mjs +1 -2
- package/dist/aggregate/keyset.d.mts +0 -2
- package/dist/aggregate/keyset.mjs +1 -2
- package/dist/aggregate/normalize.d.mts +0 -2
- package/dist/aggregate/normalize.mjs +1 -2
- package/dist/better-auth/index.d.mts +0 -2
- package/dist/better-auth/index.mjs +1 -2
- package/dist/cache/engine.d.mts +0 -2
- package/dist/cache/engine.mjs +1 -2
- package/dist/cache/envelope.mjs +1 -2
- package/dist/cache/keys.mjs +1 -2
- package/dist/cache/memo.d.mts +0 -2
- package/dist/cache/memo.mjs +1 -2
- package/dist/cache/memory-adapter.d.mts +0 -2
- package/dist/cache/memory-adapter.mjs +1 -2
- package/dist/cache/options.d.mts +0 -2
- package/dist/cache/options.mjs +1 -2
- package/dist/cache/plugin/context.d.mts +0 -2
- package/dist/cache/plugin/context.mjs +1 -2
- package/dist/cache/plugin/index.d.mts +0 -2
- package/dist/cache/plugin/index.mjs +1 -2
- package/dist/cache/plugin/invalidation-hooks.mjs +1 -2
- package/dist/cache/plugin/read-hooks.mjs +1 -2
- package/dist/cache/plugin/swr.mjs +1 -2
- package/dist/cache/runtime.d.mts +0 -2
- package/dist/cache/runtime.mjs +0 -1
- package/dist/cache/stable-stringify.d.mts +0 -2
- package/dist/cache/stable-stringify.mjs +1 -2
- package/dist/cache/tag-index.mjs +1 -2
- package/dist/cache/timeout-adapter.d.mts +0 -2
- package/dist/cache/timeout-adapter.mjs +1 -2
- package/dist/cache/types.d.mts +0 -2
- package/dist/cache/version-store.mjs +1 -2
- package/dist/cleanup/define-purge-step.d.mts +0 -2
- package/dist/cleanup/define-purge-step.mjs +1 -2
- package/dist/cleanup/index.mjs +1 -2
- package/dist/cleanup/types.d.mts +0 -2
- package/dist/context/types.d.mts +0 -2
- package/dist/errors/codes.d.mts +0 -2
- package/dist/errors/codes.mjs +1 -2
- package/dist/errors/conflict.d.mts +0 -2
- package/dist/errors/conflict.mjs +1 -2
- package/dist/errors/contract.d.mts +0 -2
- package/dist/errors/contract.mjs +1 -2
- package/dist/errors/create-error.d.mts +0 -2
- package/dist/errors/create-error.mjs +1 -2
- package/dist/errors/duplicate-key.d.mts +0 -2
- package/dist/errors/duplicate-key.mjs +1 -2
- package/dist/errors/schema.d.mts +0 -2
- package/dist/errors/schema.mjs +1 -2
- package/dist/errors/types.d.mts +0 -2
- package/dist/events/emit.d.mts +0 -2
- package/dist/events/emit.mjs +1 -2
- package/dist/events/index.mjs +1 -2
- package/dist/events/types.d.mts +0 -2
- package/dist/filter/builders.d.mts +0 -2
- package/dist/filter/builders.mjs +1 -2
- package/dist/filter/coerce-dates.d.mts +0 -2
- package/dist/filter/coerce-dates.mjs +1 -2
- package/dist/filter/from-record.d.mts +0 -2
- package/dist/filter/from-record.mjs +1 -2
- package/dist/filter/guard.d.mts +0 -2
- package/dist/filter/guard.mjs +1 -2
- package/dist/filter/match-record.d.mts +0 -2
- package/dist/filter/match-record.mjs +1 -2
- package/dist/filter/match.d.mts +0 -2
- package/dist/filter/match.mjs +1 -2
- package/dist/filter/scope.d.mts +0 -2
- package/dist/filter/scope.mjs +1 -2
- package/dist/filter/types.d.mts +0 -2
- package/dist/filter/walk.d.mts +0 -2
- package/dist/filter/walk.mjs +1 -2
- package/dist/hash/index.d.mts +0 -2
- package/dist/hash/index.mjs +1 -2
- package/dist/hooks/engine.d.mts +0 -2
- package/dist/hooks/engine.mjs +1 -2
- package/dist/hooks/events.d.mts +0 -2
- package/dist/hooks/events.mjs +1 -2
- package/dist/hooks/priority.d.mts +0 -2
- package/dist/hooks/priority.mjs +1 -2
- package/dist/hooks/types.d.mts +0 -2
- package/dist/idempotency/index.d.mts +0 -2
- package/dist/idempotency/index.mjs +1 -2
- package/dist/loader/batch-loader.d.mts +0 -2
- package/dist/loader/batch-loader.mjs +1 -2
- package/dist/loader/index.mjs +1 -2
- package/dist/lock/index.d.mts +0 -2
- package/dist/lock/index.mjs +1 -2
- package/dist/lookup/types.d.mts +0 -2
- package/dist/operations/index.mjs +1 -2
- package/dist/operations/registry.d.mts +0 -2
- package/dist/operations/registry.mjs +1 -2
- package/dist/operations/types.d.mts +0 -2
- package/dist/pagination/canonical.d.mts +0 -2
- package/dist/pagination/canonical.mjs +1 -2
- package/dist/pagination/cursor.d.mts +0 -2
- package/dist/pagination/cursor.mjs +1 -2
- package/dist/pagination/keyset.d.mts +0 -2
- package/dist/pagination/keyset.mjs +1 -2
- package/dist/pagination/merge-keyset.d.mts +0 -2
- package/dist/pagination/merge-keyset.mjs +1 -2
- package/dist/pagination/offset.d.mts +0 -2
- package/dist/pagination/offset.mjs +1 -2
- package/dist/pagination/types.d.mts +0 -2
- package/dist/plugins/index.mjs +1 -2
- package/dist/plugins/tenant-helpers.d.mts +0 -2
- package/dist/plugins/tenant-helpers.mjs +1 -2
- package/dist/query-parser/coerce.d.mts +0 -2
- package/dist/query-parser/coerce.mjs +1 -2
- package/dist/query-parser/errors.d.mts +0 -2
- package/dist/query-parser/errors.mjs +1 -2
- package/dist/query-parser/format-query.d.mts +30 -0
- package/dist/query-parser/format-query.mjs +90 -0
- package/dist/query-parser/grammar.d.mts +0 -2
- package/dist/query-parser/grammar.mjs +1 -2
- package/dist/query-parser/index.d.mts +2 -1
- package/dist/query-parser/index.mjs +2 -1
- package/dist/query-parser/parse-url.d.mts +0 -2
- package/dist/query-parser/parse-url.mjs +1 -2
- package/dist/query-parser/regex-safety.d.mts +0 -2
- package/dist/query-parser/regex-safety.mjs +1 -2
- package/dist/query-parser/reserved.d.mts +0 -2
- package/dist/query-parser/reserved.mjs +1 -2
- package/dist/query-parser/types.d.mts +0 -2
- package/dist/repository/agg-output.d.mts +0 -2
- package/dist/repository/agg-output.mjs +1 -2
- package/dist/repository/archive.d.mts +0 -2
- package/dist/repository/archive.mjs +1 -2
- package/dist/repository/base.d.mts +0 -2
- package/dist/repository/base.mjs +1 -2
- package/dist/repository/bulk-upsert.d.mts +0 -2
- package/dist/repository/bulk-upsert.mjs +1 -2
- package/dist/repository/capabilities.d.mts +0 -2
- package/dist/repository/distribution.d.mts +0 -2
- package/dist/repository/distribution.mjs +1 -2
- package/dist/repository/options.d.mts +0 -2
- package/dist/repository/options.mjs +1 -2
- package/dist/repository/plugin-types.d.mts +0 -2
- package/dist/repository/plugin-types.mjs +1 -2
- package/dist/repository/purge.d.mts +0 -2
- package/dist/repository/purge.mjs +1 -2
- package/dist/repository/read-only.d.mts +0 -2
- package/dist/repository/read-only.mjs +1 -2
- package/dist/repository/resilience.d.mts +0 -2
- package/dist/repository/resilience.mjs +1 -2
- package/dist/repository/retrying-transaction.d.mts +0 -2
- package/dist/repository/retrying-transaction.mjs +1 -2
- package/dist/repository/types.d.mts +0 -2
- package/dist/schema/field-rules.d.mts +0 -2
- package/dist/schema/field-rules.mjs +1 -2
- package/dist/schema/generator.d.mts +0 -2
- package/dist/schema/generator.mjs +1 -2
- package/dist/schema/standard-schema.d.mts +0 -2
- package/dist/schema/standard-schema.mjs +1 -2
- package/dist/schema/types.d.mts +0 -2
- package/dist/sync/index.d.mts +0 -2
- package/dist/sync/index.mjs +0 -0
- package/dist/tenant/index.mjs +1 -2
- package/dist/tenant/resolve.d.mts +0 -2
- package/dist/tenant/resolve.mjs +1 -2
- package/dist/tenant/types.d.mts +0 -2
- package/dist/testing/change-log-conformance.d.mts +0 -2
- package/dist/testing/change-log-conformance.mjs +1 -2
- package/dist/testing/command-stream-conformance.d.mts +0 -2
- package/dist/testing/command-stream-conformance.mjs +1 -2
- package/dist/testing/conformance.d.mts +0 -2
- package/dist/testing/conformance.mjs +1 -2
- package/dist/testing/data-access-conformance.d.mts +0 -2
- package/dist/testing/data-access-conformance.mjs +1 -2
- package/dist/testing/idempotency-conformance.d.mts +0 -2
- package/dist/testing/idempotency-conformance.mjs +1 -2
- package/dist/testing/lock-conformance.d.mts +0 -2
- package/dist/testing/lock-conformance.mjs +1 -2
- package/dist/testing/merge-keyset-conformance.d.mts +0 -2
- package/dist/testing/merge-keyset-conformance.mjs +1 -2
- package/dist/testing/pagination-property-conformance.d.mts +0 -2
- package/dist/testing/pagination-property-conformance.mjs +1 -2
- package/dist/testing/purge-conformance.d.mts +0 -2
- package/dist/testing/purge-conformance.mjs +1 -2
- package/dist/testing/query-grammar-conformance.d.mts +0 -2
- package/dist/testing/query-grammar-conformance.mjs +1 -2
- package/dist/testing/read-model-property-conformance.d.mts +0 -2
- package/dist/testing/read-model-property-conformance.mjs +1 -2
- package/dist/testing/types.d.mts +0 -2
- package/dist/testing/usage-conformance.d.mts +0 -2
- package/dist/testing/usage-conformance.mjs +1 -2
- package/dist/update/builders.d.mts +0 -2
- package/dist/update/builders.mjs +1 -2
- package/dist/update/compile.d.mts +0 -2
- package/dist/update/compile.mjs +1 -2
- package/dist/update/guard.d.mts +0 -2
- package/dist/update/guard.mjs +1 -2
- package/dist/update/types.d.mts +0 -2
- package/dist/usage/index.d.mts +0 -2
- package/dist/usage/index.mjs +1 -2
- package/llms.txt +29 -0
- package/package.json +7 -4
- package/skills/repo-core/SKILL.md +108 -0
- package/skills/repo-core/references/contracts.md +69 -0
- package/skills/repo-core/references/kit-authoring.md +75 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { SortSpec } from "./types.mjs";
|
|
2
|
-
//#region src/pagination/keyset.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Normalize a sort object so non-`_id` fields come first, `_id` last.
|
|
5
4
|
* Stable ordering is required for cursor comparability across requests.
|
|
@@ -20,5 +19,4 @@ declare function validateKeysetSort(sort: SortSpec, allowedPrimaryFields?: reado
|
|
|
20
19
|
declare function invertSort(sort: SortSpec): SortSpec;
|
|
21
20
|
/** Primary (first non-`_id`) sort field; falls back to `_id`. */
|
|
22
21
|
declare function getPrimaryField(sort: SortSpec): string;
|
|
23
|
-
//#endregion
|
|
24
22
|
export { getPrimaryField, invertSort, normalizeSort, validateKeysetSort };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/pagination/keyset.ts
|
|
2
2
|
/**
|
|
3
3
|
* Normalize a sort object so non-`_id` fields come first, `_id` last.
|
|
4
4
|
* Stable ordering is required for cursor comparability across requests.
|
|
@@ -55,5 +55,4 @@ function getPrimaryField(sort) {
|
|
|
55
55
|
for (const key of Object.keys(sort)) if (key !== "_id") return key;
|
|
56
56
|
return "_id";
|
|
57
57
|
}
|
|
58
|
-
//#endregion
|
|
59
58
|
export { getPrimaryField, invertSort, normalizeSort, validateKeysetSort };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/pagination/merge-keyset.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* `mergeKeysetPages`: one keyset page over SEVERAL sources, each already ordered by the same total
|
|
4
3
|
* order, merged by the sort key with source order as the final tiebreaker. Each call reads at most
|
|
@@ -46,5 +45,4 @@ interface MergedKeysetPage<T> {
|
|
|
46
45
|
hasNext: boolean;
|
|
47
46
|
}
|
|
48
47
|
declare function mergeKeysetPages<T>(sources: readonly KeysetSource<T>[], options: MergeKeysetOptions<T>): Promise<MergedKeysetPage<T>>;
|
|
49
|
-
//#endregion
|
|
50
48
|
export { KeysetSource, MergeKeysetOptions, MergedKeysetPage, mergeKeysetPages };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CURSOR_ERROR_CODES } from "../errors/codes.mjs";
|
|
2
2
|
import { createError } from "../errors/create-error.mjs";
|
|
3
3
|
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
4
|
-
|
|
4
|
+
// src/pagination/merge-keyset.ts
|
|
5
5
|
/**
|
|
6
6
|
* `mergeKeysetPages`: one keyset page over SEVERAL sources, each already ordered by the same total
|
|
7
7
|
* order, merged by the sort key with source order as the final tiebreaker. Each call reads at most
|
|
@@ -135,5 +135,4 @@ async function mergeKeysetPages(sources, options) {
|
|
|
135
135
|
}, secret) : null
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
|
-
//#endregion
|
|
139
138
|
export { mergeKeysetPages };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { PaginationConfig } from "./types.mjs";
|
|
2
|
-
//#region src/pagination/offset.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Parse, clamp, and sanitize a `limit` value. Accepts string or number
|
|
5
4
|
* input (URL params arrive as strings). Returns the configured default
|
|
@@ -21,5 +20,4 @@ declare function shouldWarnDeepPagination(page: number, threshold: number): bool
|
|
|
21
20
|
declare function calculateSkip(page: number, limit: number): number;
|
|
22
21
|
/** Total page count from total rows + per-page limit. Zero-safe. */
|
|
23
22
|
declare function calculateTotalPages(total: number, limit: number): number;
|
|
24
|
-
//#endregion
|
|
25
23
|
export { calculateSkip, calculateTotalPages, shouldWarnDeepPagination, validateLimit, validatePage };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/pagination/offset.ts
|
|
2
2
|
const DEFAULT_LIMIT = 10;
|
|
3
3
|
const DEFAULT_MAX_LIMIT = 100;
|
|
4
4
|
const DEFAULT_MAX_PAGE = 1e4;
|
|
@@ -43,5 +43,4 @@ function calculateTotalPages(total, limit) {
|
|
|
43
43
|
if (limit <= 0) return 0;
|
|
44
44
|
return Math.ceil(total / limit);
|
|
45
45
|
}
|
|
46
|
-
//#endregion
|
|
47
46
|
export { calculateSkip, calculateTotalPages, shouldWarnDeepPagination, validateLimit, validatePage };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/pagination/types.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Pagination primitives — driver-agnostic type surface.
|
|
4
3
|
*
|
|
@@ -204,5 +203,4 @@ interface BareListResult<TDoc> {
|
|
|
204
203
|
* (HTTP status >= 400 → `ErrorContract`).
|
|
205
204
|
*/
|
|
206
205
|
type PaginatedResult<TDoc, TExtra extends Record<string, unknown> = {}> = OffsetPaginationResult<TDoc, TExtra> | KeysetPaginationResult<TDoc, TExtra> | AggregatePaginationResult<TDoc, TExtra> | BareListResult<TDoc>;
|
|
207
|
-
//#endregion
|
|
208
206
|
export { AggregatePaginationResult, AggregatePaginationResultCore, AnyPaginationResult, BareListResult, CursorPayload, DecodedCursor, KeysetPaginationResult, KeysetPaginationResultCore, OffsetPaginationResult, OffsetPaginationResultCore, PaginatedResult, PaginationConfig, SortDirection, SortSpec, ValueType };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { adminBypass, payloadHasTenantField };
|
|
1
|
+
export { adminBypass, payloadHasTenantField } from "./tenant-helpers.mjs";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { PolicyKey } from "../operations/types.mjs";
|
|
2
|
-
//#region src/plugins/tenant-helpers.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Minimal context shape this module reads. Kits' richer
|
|
5
4
|
* `RepositoryContext` types extend this — by accepting only the slots
|
|
@@ -58,5 +57,4 @@ declare function adminBypass(options: {
|
|
|
58
57
|
roleField?: string;
|
|
59
58
|
adminRoles: readonly string[];
|
|
60
59
|
}): (context: Record<string, unknown>, operation: string) => boolean;
|
|
61
|
-
//#endregion
|
|
62
60
|
export { TenantPolicyContext, adminBypass, payloadHasTenantField };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/plugins/tenant-helpers.ts
|
|
2
2
|
/**
|
|
3
3
|
* True when the op's policy target already has `tenantField` set by
|
|
4
4
|
* the caller. Used to decide whether the plugin can safely skip
|
|
@@ -80,5 +80,4 @@ function adminBypass(options) {
|
|
|
80
80
|
return typeof role === "string" && allowed.has(role);
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
-
//#endregion
|
|
84
83
|
export { adminBypass, payloadHasTenantField };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/query-parser/coerce.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* URL value → typed scalar. A declared field type wins, and a value that does not fit it is
|
|
4
3
|
* refused. Without one, a conservative heuristic applies: never a leading-zero code (`007`), never a
|
|
@@ -18,5 +17,4 @@ interface CoerceOptions {
|
|
|
18
17
|
param: string;
|
|
19
18
|
}
|
|
20
19
|
declare function coerceQueryValue(raw: string, options: CoerceOptions): QueryScalar;
|
|
21
|
-
//#endregion
|
|
22
20
|
export { CoerceOptions, QueryFieldType, QueryScalar, coerceQueryValue };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ISO_DATE_PATTERN } from "../filter/coerce-dates.mjs";
|
|
2
2
|
import { QueryGrammarError } from "./errors.mjs";
|
|
3
|
-
|
|
3
|
+
// src/query-parser/coerce.ts
|
|
4
4
|
/**
|
|
5
5
|
* URL value → typed scalar. A declared field type wins, and a value that does not fit it is
|
|
6
6
|
* refused. Without one, a conservative heuristic applies: never a leading-zero code (`007`), never a
|
|
@@ -44,5 +44,4 @@ function toDate(raw) {
|
|
|
44
44
|
function fail(param, reason) {
|
|
45
45
|
throw new QueryGrammarError(param, reason);
|
|
46
46
|
}
|
|
47
|
-
//#endregion
|
|
48
47
|
export { coerceQueryValue };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { HttpError, ValidationErrorMeta } from "../errors/types.mjs";
|
|
2
|
-
//#region src/query-parser/errors.d.ts
|
|
3
2
|
/** The code every query-grammar refusal carries — stable across parsers and kits. */
|
|
4
3
|
declare const INVALID_QUERY_INPUT = "INVALID_QUERY_INPUT";
|
|
5
4
|
/**
|
|
@@ -26,5 +25,4 @@ declare class QueryGrammarError extends Error implements HttpError {
|
|
|
26
25
|
constructor(param: string, reason: string, kind?: QueryErrorKind);
|
|
27
26
|
}
|
|
28
27
|
declare function isQueryGrammarError(value: unknown): value is QueryGrammarError;
|
|
29
|
-
//#endregion
|
|
30
28
|
export { INVALID_QUERY_INPUT, QueryErrorKind, QueryGrammarError, isQueryGrammarError };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/query-parser/errors.ts
|
|
2
2
|
/** The code every query-grammar refusal carries — stable across parsers and kits. */
|
|
3
3
|
const INVALID_QUERY_INPUT = "INVALID_QUERY_INPUT";
|
|
4
4
|
/**
|
|
@@ -28,5 +28,4 @@ var QueryGrammarError = class extends Error {
|
|
|
28
28
|
function isQueryGrammarError(value) {
|
|
29
29
|
return value instanceof Error && value.code === "INVALID_QUERY_INPUT";
|
|
30
30
|
}
|
|
31
|
-
//#endregion
|
|
32
31
|
export { INVALID_QUERY_INPUT, QueryGrammarError, isQueryGrammarError };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BracketOperator } from "./types.mjs";
|
|
2
|
+
/** Operators a filter term can use: every URL operator except `options` (regex flags ride on `flags`). */
|
|
3
|
+
type FilterOperator = Exclude<BracketOperator, 'options'>;
|
|
4
|
+
/** One typed filter. `op` defaults to `eq`. */
|
|
5
|
+
interface FilterTerm {
|
|
6
|
+
field: string;
|
|
7
|
+
op?: FilterOperator;
|
|
8
|
+
/**
|
|
9
|
+
* `in`/`nin`: array. `between`: `[from, to]` (either side may be empty). `exists`: boolean.
|
|
10
|
+
* Otherwise a string, number, boolean, `Date` or `null`.
|
|
11
|
+
*/
|
|
12
|
+
value?: unknown;
|
|
13
|
+
/** `regex` only: flags from `i`, `m`, `s`, `x`. */
|
|
14
|
+
flags?: string;
|
|
15
|
+
}
|
|
16
|
+
interface FormatQueryInput {
|
|
17
|
+
filters?: readonly FilterTerm[];
|
|
18
|
+
/** `'-createdAt'`, `'name'` or a list of them. */
|
|
19
|
+
sort?: string | readonly string[];
|
|
20
|
+
page?: number;
|
|
21
|
+
limit?: number;
|
|
22
|
+
/** Free-text search, trimmed; empty is omitted. */
|
|
23
|
+
search?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Flat params in the shape `parseUrl` takes and Fastify/qs produce. */
|
|
26
|
+
type QueryParams = Record<string, string>;
|
|
27
|
+
declare function formatQuery(input: FormatQueryInput): QueryParams;
|
|
28
|
+
/** {@link formatQuery} as a query string, without the leading `?`. */
|
|
29
|
+
declare function stringifyQuery(input: FormatQueryInput): string;
|
|
30
|
+
export { FilterOperator, FilterTerm, FormatQueryInput, QueryParams, formatQuery, stringifyQuery };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { QueryGrammarError } from "./errors.mjs";
|
|
2
|
+
import { URL_OPERATORS } from "./grammar.mjs";
|
|
3
|
+
// src/query-parser/format-query.ts
|
|
4
|
+
/**
|
|
5
|
+
* Typed filters -> the URL params {@link parseUrl} accepts: the inverse of the grammar in
|
|
6
|
+
* `./grammar.ts`, client-safe, built from the same operator constants.
|
|
7
|
+
*
|
|
8
|
+
* formatQuery({ filters: [{ field: 'age', op: 'gte', value: 18 }], sort: '-createdAt' })
|
|
9
|
+
* -> { 'age[gte]': '18', sort: '-createdAt' }
|
|
10
|
+
*
|
|
11
|
+
* An empty value (`''`, `undefined`, `[]`) is no filter, as the parser reads it. `between` is sent
|
|
12
|
+
* as `gte` + `lte`. A value the grammar cannot carry is refused, never sent mangled.
|
|
13
|
+
*/
|
|
14
|
+
const FIELD_RE = /^[A-Za-z_][A-Za-z0-9_-]*(?:\.[A-Za-z0-9_-]+)*$/;
|
|
15
|
+
function formatQuery(input) {
|
|
16
|
+
const out = {};
|
|
17
|
+
const put = (key, value) => {
|
|
18
|
+
if (key in out) throw new QueryGrammarError(key, "sent twice (one term per field and operator)", "syntax");
|
|
19
|
+
out[key] = value;
|
|
20
|
+
};
|
|
21
|
+
for (const term of input.filters ?? []) writeTerm(term, put);
|
|
22
|
+
if (input.sort !== void 0) {
|
|
23
|
+
const sort = (Array.isArray(input.sort) ? input.sort : [input.sort]).join(",");
|
|
24
|
+
if (sort) out["sort"] = sort;
|
|
25
|
+
}
|
|
26
|
+
if (input.page !== void 0) out["page"] = positive("page", input.page);
|
|
27
|
+
if (input.limit !== void 0) out["limit"] = positive("limit", input.limit);
|
|
28
|
+
const search = input.search?.trim();
|
|
29
|
+
if (search) out["search"] = search;
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/** {@link formatQuery} as a query string, without the leading `?`. */
|
|
33
|
+
function stringifyQuery(input) {
|
|
34
|
+
return new URLSearchParams(formatQuery(input)).toString();
|
|
35
|
+
}
|
|
36
|
+
function writeTerm(term, put) {
|
|
37
|
+
const { field, value } = term;
|
|
38
|
+
const op = term.op ?? "eq";
|
|
39
|
+
if (!FIELD_RE.test(field)) throw new QueryGrammarError(field, "not a field path", "syntax");
|
|
40
|
+
if (!URL_OPERATORS.includes(op) || op === "options") throw new QueryGrammarError(field, `unknown operator "${String(op)}"`, "syntax");
|
|
41
|
+
const key = (o) => o === "eq" ? field : `${field}[${o}]`;
|
|
42
|
+
if (value === void 0 || value === "" || Array.isArray(value) && value.length === 0) return;
|
|
43
|
+
switch (op) {
|
|
44
|
+
case "in":
|
|
45
|
+
case "nin": {
|
|
46
|
+
if (!Array.isArray(value)) throw new QueryGrammarError(key(op), `${op} takes an array`);
|
|
47
|
+
const items = value.map((v) => scalar(v, key(op)));
|
|
48
|
+
if (items.some((item) => item.includes(","))) throw new QueryGrammarError(key(op), "a list value cannot contain a comma");
|
|
49
|
+
put(key(op), items.join(","));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
case "between": {
|
|
53
|
+
if (!Array.isArray(value) || value.length !== 2) throw new QueryGrammarError(key(op), "between takes [from, to]");
|
|
54
|
+
const [from, to] = value;
|
|
55
|
+
if (from !== void 0 && from !== "" && from !== null) put(key("gte"), scalar(from, key("gte")));
|
|
56
|
+
if (to !== void 0 && to !== "" && to !== null) put(key("lte"), scalar(to, key("lte")));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
case "exists":
|
|
60
|
+
if (typeof value !== "boolean") throw new QueryGrammarError(key(op), "exists takes a boolean");
|
|
61
|
+
put(key(op), String(value));
|
|
62
|
+
return;
|
|
63
|
+
default:
|
|
64
|
+
put(key(op), scalar(value, key(op)));
|
|
65
|
+
if (op === "regex" && term.flags) {
|
|
66
|
+
if (!/^[imsx]+$/.test(term.flags)) throw new QueryGrammarError(`${field}[options]`, "flags must be from i, m, s, x");
|
|
67
|
+
put(`${field}[options]`, term.flags);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function scalar(value, param) {
|
|
72
|
+
if (value === null) return "null";
|
|
73
|
+
if (value instanceof Date) {
|
|
74
|
+
if (Number.isNaN(value.getTime())) throw new QueryGrammarError(param, "invalid Date");
|
|
75
|
+
return value.toISOString();
|
|
76
|
+
}
|
|
77
|
+
if (typeof value === "number") {
|
|
78
|
+
if (!Number.isFinite(value)) throw new QueryGrammarError(param, "not a finite number");
|
|
79
|
+
return String(value);
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === "boolean") return String(value);
|
|
82
|
+
if (typeof value !== "string") throw new QueryGrammarError(param, "a filter value is a string, number, boolean, Date or null");
|
|
83
|
+
if (value === "null") throw new QueryGrammarError(param, "the text \"null\" reads as null");
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
function positive(param, value) {
|
|
87
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new QueryGrammarError(param, `"${String(value)}" is not a positive integer`);
|
|
88
|
+
return String(value);
|
|
89
|
+
}
|
|
90
|
+
export { formatQuery, stringifyQuery };
|
|
@@ -2,7 +2,6 @@ import { Filter } from "../filter/types.mjs";
|
|
|
2
2
|
import { QueryFieldType, QueryScalar } from "./coerce.mjs";
|
|
3
3
|
import { QueryGrammarError } from "./errors.mjs";
|
|
4
4
|
import { BracketOperator, ParsedSelect, ParsedSort } from "./types.mjs";
|
|
5
|
-
//#region src/query-parser/grammar.d.ts
|
|
6
5
|
declare const DEFAULT_LIMIT = 20;
|
|
7
6
|
declare const DEFAULT_MAX_LIMIT = 1000;
|
|
8
7
|
declare const DEFAULT_MAX_TEXT_LENGTH = 500;
|
|
@@ -109,5 +108,4 @@ declare function readSearch(raw: unknown, options?: {
|
|
|
109
108
|
}): string | undefined;
|
|
110
109
|
/** Every input shape → one record: repeated URL keys become arrays, qs objects stay nested. */
|
|
111
110
|
declare function toRecord(input: QueryInput | null | undefined): Record<string, unknown>;
|
|
112
|
-
//#endregion
|
|
113
111
|
export { CORE_OPERATORS, CoreOperator, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_MAX_SEARCH_LENGTH, DEFAULT_MAX_TEXT_LENGTH, ExtensionClause, FilterClause, FilterReadResult, OPERATOR_DESCRIPTIONS, PageOptions, PageRequest, QueryGrammarOptions, QueryInput, URL_OPERATORS, clausesToFilter, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, toRecord };
|
|
@@ -3,7 +3,7 @@ import { QueryGrammarError } from "./errors.mjs";
|
|
|
3
3
|
import { coerceQueryValue } from "./coerce.mjs";
|
|
4
4
|
import { assessRegex } from "./regex-safety.mjs";
|
|
5
5
|
import { STANDARD_RESERVED_PARAMS } from "./reserved.mjs";
|
|
6
|
-
|
|
6
|
+
// src/query-parser/grammar.ts
|
|
7
7
|
const DEFAULT_LIMIT = 20;
|
|
8
8
|
const DEFAULT_MAX_LIMIT = 1e3;
|
|
9
9
|
const DEFAULT_MAX_TEXT_LENGTH = 500;
|
|
@@ -475,5 +475,4 @@ function cursorValue(raw) {
|
|
|
475
475
|
const value = String(raw).trim();
|
|
476
476
|
return value === "" ? void 0 : value;
|
|
477
477
|
}
|
|
478
|
-
//#endregion
|
|
479
478
|
export { CORE_OPERATORS, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_MAX_SEARCH_LENGTH, DEFAULT_MAX_TEXT_LENGTH, OPERATOR_DESCRIPTIONS, URL_OPERATORS, clausesToFilter, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, toRecord };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CoerceOptions, QueryFieldType, QueryScalar, coerceQueryValue } from "./coerce.mjs";
|
|
2
2
|
import { INVALID_QUERY_INPUT, QueryErrorKind, QueryGrammarError, isQueryGrammarError } from "./errors.mjs";
|
|
3
3
|
import { BracketOperator, ParsedPopulate, ParsedQuery, ParsedSelect, ParsedSort, ParsedSortDirection, QueryParserInput, QueryParserOptions } from "./types.mjs";
|
|
4
|
+
import { FilterOperator, FilterTerm, FormatQueryInput, QueryParams, formatQuery, stringifyQuery } from "./format-query.mjs";
|
|
4
5
|
import { CORE_OPERATORS, CoreOperator, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_MAX_SEARCH_LENGTH, DEFAULT_MAX_TEXT_LENGTH, ExtensionClause, FilterClause, FilterReadResult, OPERATOR_DESCRIPTIONS, PageOptions, PageRequest, QueryGrammarOptions, QueryInput, URL_OPERATORS, clausesToFilter, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, toRecord } from "./grammar.mjs";
|
|
5
6
|
import { parseUrl } from "./parse-url.mjs";
|
|
6
7
|
import { MAX_UNBOUNDED_QUANTIFIERS, RegexRisk, assessRegex, escapeRegex } from "./regex-safety.mjs";
|
|
7
8
|
import { STANDARD_RESERVED_PARAMS, isControlParam } from "./reserved.mjs";
|
|
8
|
-
export { type BracketOperator, CORE_OPERATORS, type CoerceOptions, type CoreOperator, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_MAX_SEARCH_LENGTH, DEFAULT_MAX_TEXT_LENGTH, type ExtensionClause, type FilterClause, type FilterReadResult, INVALID_QUERY_INPUT, MAX_UNBOUNDED_QUANTIFIERS, OPERATOR_DESCRIPTIONS, type PageOptions, type PageRequest, type ParsedPopulate, type ParsedQuery, type ParsedSelect, type ParsedSort, type ParsedSortDirection, type QueryErrorKind, type QueryFieldType, QueryGrammarError, type QueryGrammarOptions, type QueryInput, type QueryParserInput, type QueryParserOptions, type QueryScalar, type RegexRisk, STANDARD_RESERVED_PARAMS, URL_OPERATORS, assessRegex, clausesToFilter, coerceQueryValue, escapeRegex, isControlParam, isQueryGrammarError, parseUrl, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, toRecord };
|
|
9
|
+
export { type BracketOperator, CORE_OPERATORS, type CoerceOptions, type CoreOperator, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_MAX_SEARCH_LENGTH, DEFAULT_MAX_TEXT_LENGTH, type ExtensionClause, type FilterClause, type FilterOperator, type FilterReadResult, type FilterTerm, type FormatQueryInput, INVALID_QUERY_INPUT, MAX_UNBOUNDED_QUANTIFIERS, OPERATOR_DESCRIPTIONS, type PageOptions, type PageRequest, type ParsedPopulate, type ParsedQuery, type ParsedSelect, type ParsedSort, type ParsedSortDirection, type QueryErrorKind, type QueryFieldType, QueryGrammarError, type QueryGrammarOptions, type QueryInput, type QueryParams, type QueryParserInput, type QueryParserOptions, type QueryScalar, type RegexRisk, STANDARD_RESERVED_PARAMS, URL_OPERATORS, assessRegex, clausesToFilter, coerceQueryValue, escapeRegex, formatQuery, isControlParam, isQueryGrammarError, parseUrl, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, stringifyQuery, toRecord };
|
|
@@ -3,5 +3,6 @@ import { coerceQueryValue } from "./coerce.mjs";
|
|
|
3
3
|
import { MAX_UNBOUNDED_QUANTIFIERS, assessRegex, escapeRegex } from "./regex-safety.mjs";
|
|
4
4
|
import { STANDARD_RESERVED_PARAMS, isControlParam } from "./reserved.mjs";
|
|
5
5
|
import { CORE_OPERATORS, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_MAX_SEARCH_LENGTH, DEFAULT_MAX_TEXT_LENGTH, OPERATOR_DESCRIPTIONS, URL_OPERATORS, clausesToFilter, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, toRecord } from "./grammar.mjs";
|
|
6
|
+
import { formatQuery, stringifyQuery } from "./format-query.mjs";
|
|
6
7
|
import { parseUrl } from "./parse-url.mjs";
|
|
7
|
-
export { CORE_OPERATORS, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_MAX_SEARCH_LENGTH, DEFAULT_MAX_TEXT_LENGTH, INVALID_QUERY_INPUT, MAX_UNBOUNDED_QUANTIFIERS, OPERATOR_DESCRIPTIONS, QueryGrammarError, STANDARD_RESERVED_PARAMS, URL_OPERATORS, assessRegex, clausesToFilter, coerceQueryValue, escapeRegex, isControlParam, isQueryGrammarError, parseUrl, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, toRecord };
|
|
8
|
+
export { CORE_OPERATORS, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_MAX_SEARCH_LENGTH, DEFAULT_MAX_TEXT_LENGTH, INVALID_QUERY_INPUT, MAX_UNBOUNDED_QUANTIFIERS, OPERATOR_DESCRIPTIONS, QueryGrammarError, STANDARD_RESERVED_PARAMS, URL_OPERATORS, assessRegex, clausesToFilter, coerceQueryValue, escapeRegex, formatQuery, isControlParam, isQueryGrammarError, parseUrl, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, stringifyQuery, toRecord };
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { ParsedQuery, QueryParserInput, QueryParserOptions } from "./types.mjs";
|
|
2
|
-
//#region src/query-parser/parse-url.d.ts
|
|
3
2
|
/** Parse URL search params into a driver-agnostic ParsedQuery. Invalid input throws a 400. */
|
|
4
3
|
declare function parseUrl(input: QueryParserInput, options?: QueryParserOptions): ParsedQuery;
|
|
5
|
-
//#endregion
|
|
6
4
|
export { parseUrl };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { QueryGrammarError } from "./errors.mjs";
|
|
2
2
|
import { clausesToFilter, readFilterClauses, readPageRequest, readSearch, readSelect, readSort, toRecord } from "./grammar.mjs";
|
|
3
|
-
|
|
3
|
+
// src/query-parser/parse-url.ts
|
|
4
4
|
/**
|
|
5
5
|
* URL → {@link ParsedQuery} with a portable Filter IR. The grammar (keys, operators, coercion,
|
|
6
6
|
* paging) is `./grammar.ts`'s; this adds only the IR emission and the populate spec.
|
|
@@ -89,5 +89,4 @@ function readMatch(path, match) {
|
|
|
89
89
|
for (const [field, value] of Object.entries(match)) if (!FIELD_RE.test(field) || value !== null && typeof value === "object") throw new QueryGrammarError(`populate[${path}][match]`, `field equality only: "${field}"`, "syntax");
|
|
90
90
|
return match;
|
|
91
91
|
}
|
|
92
|
-
//#endregion
|
|
93
92
|
export { parseUrl };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/query-parser/regex-safety.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* ReDoS assessment for a user-supplied regex — the ONE analyser every parser uses.
|
|
4
3
|
*
|
|
@@ -27,5 +26,4 @@ declare function assessRegex(pattern: string, options?: {
|
|
|
27
26
|
}): RegexRisk;
|
|
28
27
|
/** Escape every regex metacharacter — the literal-match fallback for an unsafe pattern. */
|
|
29
28
|
declare function escapeRegex(value: string): string;
|
|
30
|
-
//#endregion
|
|
31
29
|
export { MAX_UNBOUNDED_QUANTIFIERS, RegexRisk, assessRegex, escapeRegex };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/query-parser/regex-safety.ts
|
|
2
2
|
/**
|
|
3
3
|
* ReDoS assessment for a user-supplied regex — the ONE analyser every parser uses.
|
|
4
4
|
*
|
|
@@ -147,5 +147,4 @@ function assessRegex(pattern, options = {}) {
|
|
|
147
147
|
function escapeRegex(value) {
|
|
148
148
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
149
149
|
}
|
|
150
|
-
//#endregion
|
|
151
150
|
export { MAX_UNBOUNDED_QUANTIFIERS, assessRegex, escapeRegex };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/query-parser/reserved.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* URL parameter keys reserved by the framework — never parsed as
|
|
4
3
|
* filter predicates. Two categories:
|
|
@@ -39,5 +38,4 @@ declare const STANDARD_RESERVED_PARAMS: ReadonlySet<string>;
|
|
|
39
38
|
* ```
|
|
40
39
|
*/
|
|
41
40
|
declare function isControlParam(key: string): boolean;
|
|
42
|
-
//#endregion
|
|
43
41
|
export { STANDARD_RESERVED_PARAMS, isControlParam };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/query-parser/reserved.ts
|
|
2
2
|
/**
|
|
3
3
|
* URL parameter keys reserved by the framework — never parsed as
|
|
4
4
|
* filter predicates. Two categories:
|
|
@@ -55,5 +55,4 @@ const STANDARD_RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
55
55
|
function isControlParam(key) {
|
|
56
56
|
return STANDARD_RESERVED_PARAMS.has(key);
|
|
57
57
|
}
|
|
58
|
-
//#endregion
|
|
59
58
|
export { STANDARD_RESERVED_PARAMS, isControlParam };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Filter } from "../filter/types.mjs";
|
|
2
2
|
import { QueryFieldType } from "./coerce.mjs";
|
|
3
|
-
//#region src/query-parser/types.d.ts
|
|
4
3
|
/** Sort direction on a single field. */
|
|
5
4
|
type ParsedSortDirection = 1 | -1;
|
|
6
5
|
/** Sort spec after parsing — array so field order is preserved. */
|
|
@@ -89,5 +88,4 @@ type BracketOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin'
|
|
|
89
88
|
* or a compatible iterable. The parser normalizes.
|
|
90
89
|
*/
|
|
91
90
|
type QueryParserInput = URLSearchParams | Record<string, string | string[] | undefined> | Iterable<[string, string]>;
|
|
92
|
-
//#endregion
|
|
93
91
|
export { BracketOperator, ParsedPopulate, ParsedQuery, ParsedSelect, ParsedSort, ParsedSortDirection, QueryParserInput, QueryParserOptions };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/repository/agg-output.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Cross-kit AggResult row-shape normalization.
|
|
4
3
|
*
|
|
@@ -59,5 +58,4 @@ declare function nestDottedKeys<T extends Record<string, unknown>>(row: T): Reco
|
|
|
59
58
|
* normalized rows; the input is not modified.
|
|
60
59
|
*/
|
|
61
60
|
declare function nestDottedKeysAll<T extends Record<string, unknown>>(rows: readonly T[]): Record<string, unknown>[];
|
|
62
|
-
//#endregion
|
|
63
61
|
export { nestDottedKeys, nestDottedKeysAll };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/repository/agg-output.ts
|
|
2
2
|
/**
|
|
3
3
|
* Cross-kit AggResult row-shape normalization.
|
|
4
4
|
*
|
|
@@ -85,5 +85,4 @@ function setDeep(target, path, value) {
|
|
|
85
85
|
}
|
|
86
86
|
cursor[path[path.length - 1]] = value;
|
|
87
87
|
}
|
|
88
|
-
//#endregion
|
|
89
88
|
export { nestDottedKeys, nestDottedKeysAll };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RetryPolicy } from "./resilience.mjs";
|
|
2
|
-
//#region src/repository/archive.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Destination for archived documents — implemented by the HOST, not the
|
|
5
4
|
* kit. One method so anything writable fits: an archive collection/table,
|
|
@@ -102,5 +101,4 @@ interface ArchiveResult {
|
|
|
102
101
|
* @param port Kit-specific driver glue (readChunk / deleteChunk).
|
|
103
102
|
*/
|
|
104
103
|
declare function runChunkedArchive<TDoc = unknown>(options: ArchiveOptions, sink: ArchiveSink<TDoc>, port: ArchivePort<TDoc>): Promise<ArchiveResult>;
|
|
105
|
-
//#endregion
|
|
106
104
|
export { ArchiveOptions, ArchivePort, ArchiveProgress, ArchiveResult, ArchiveSink, runChunkedArchive };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { withRetry } from "./resilience.mjs";
|
|
2
|
-
|
|
2
|
+
// src/repository/archive.ts
|
|
3
3
|
/**
|
|
4
4
|
* Drive a chunked archive to completion. Returns an `ArchiveResult`
|
|
5
5
|
* envelope — never throws for in-run errors (those wrap into
|
|
@@ -60,5 +60,4 @@ async function runChunkedArchive(options, sink, port) {
|
|
|
60
60
|
durationMs: Date.now() - start
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
//#endregion
|
|
64
63
|
export { runChunkedArchive };
|
|
@@ -4,7 +4,6 @@ import { HookListener, HookMode } from "../hooks/types.mjs";
|
|
|
4
4
|
import { HookEngine } from "../hooks/engine.mjs";
|
|
5
5
|
import { StandardSchemaV1 } from "../schema/standard-schema.mjs";
|
|
6
6
|
import { PluginType } from "./plugin-types.mjs";
|
|
7
|
-
//#region src/repository/base.d.ts
|
|
8
7
|
/** Construction options common to every kit. */
|
|
9
8
|
interface RepositoryBaseOptions {
|
|
10
9
|
/** Stable identifier (model/table name) — surfaces in hook contexts and ordering errors. */
|
|
@@ -117,5 +116,4 @@ declare abstract class RepositoryBase {
|
|
|
117
116
|
*/
|
|
118
117
|
_cachedValue<T>(context: RepositoryContext): T | undefined;
|
|
119
118
|
}
|
|
120
|
-
//#endregion
|
|
121
119
|
export { RepositoryBase, RepositoryBaseOptions };
|
package/dist/repository/base.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { HOOK_PRIORITY } from "../hooks/priority.mjs";
|
|
|
3
3
|
import { registerRepositoryEvents } from "../events/emit.mjs";
|
|
4
4
|
import { validateStandardSchema } from "../schema/standard-schema.mjs";
|
|
5
5
|
import { validatePluginOrder } from "./plugin-types.mjs";
|
|
6
|
-
|
|
6
|
+
// src/repository/base.ts
|
|
7
7
|
/**
|
|
8
8
|
* Base class every driver kit extends. Exposes the hook surface
|
|
9
9
|
* (`on` / `off` / `emit` / `emitAsync`), plugin installation (`use`), and
|
|
@@ -153,5 +153,4 @@ function assertValidPlugin(plugin, repoName, index) {
|
|
|
153
153
|
}
|
|
154
154
|
if (typeof plugin.apply !== "function") throw new TypeError(`[repo-core] Repository "${repoName}": ${where} is an object but missing \`apply(repo)\`. Expected \`{ name: string, apply: (repo) => void }\`.`);
|
|
155
155
|
}
|
|
156
|
-
//#endregion
|
|
157
156
|
export { RepositoryBase };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { WriteOptions } from "./types.mjs";
|
|
2
|
-
//#region src/repository/bulk-upsert.d.ts
|
|
3
2
|
type BulkUpsertOutcome = 'inserted' | 'updated' | 'unchanged' | 'failed';
|
|
4
3
|
declare const BULK_UPSERT_FAILURE_CODES: {
|
|
5
4
|
/** A unique index other than (or racing on) the key refused the row. */
|
|
@@ -55,5 +54,4 @@ interface BulkUpsertResult {
|
|
|
55
54
|
declare function bulkUpsertOptionsError(options: BulkUpsertOptions): string | undefined;
|
|
56
55
|
/** Tally a positional result list into the summary counts. */
|
|
57
56
|
declare function summarizeBulkUpsert(results: readonly BulkUpsertRowResult[]): BulkUpsertResult;
|
|
58
|
-
//#endregion
|
|
59
57
|
export { BULK_UPSERT_ERROR_CODES, BULK_UPSERT_FAILURE_CODES, BulkUpsertFailureCode, BulkUpsertOptions, BulkUpsertOutcome, BulkUpsertResult, BulkUpsertRowResult, bulkUpsertOptionsError, summarizeBulkUpsert };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/repository/bulk-upsert.ts
|
|
2
2
|
const BULK_UPSERT_FAILURE_CODES = {
|
|
3
3
|
/** A unique index other than (or racing on) the key refused the row. */
|
|
4
4
|
DUPLICATE: "duplicate",
|
|
@@ -46,5 +46,4 @@ function summarizeBulkUpsert(results) {
|
|
|
46
46
|
for (const r of results) out[r.outcome] += 1;
|
|
47
47
|
return out;
|
|
48
48
|
}
|
|
49
|
-
//#endregion
|
|
50
49
|
export { BULK_UPSERT_ERROR_CODES, BULK_UPSERT_FAILURE_CODES, bulkUpsertOptionsError, summarizeBulkUpsert };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/repository/capabilities.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Runtime capability descriptor — the feature-detection contract every
|
|
4
3
|
* kit declares so hosts (and arc) can branch on backend support at boot
|
|
@@ -244,5 +243,4 @@ interface RepoCapabilities {
|
|
|
244
243
|
/** Kit sources compose with `mergeKeysetPages` (scope-bound per-row cursors). Gates `runMergeKeysetConformance`. */
|
|
245
244
|
mergeKeyset?: boolean;
|
|
246
245
|
}
|
|
247
|
-
//#endregion
|
|
248
246
|
export { AggregateOpsSupport, RepoCapabilities };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { FilterInput } from "./types.mjs";
|
|
2
|
-
//#region src/repository/distribution.d.ts
|
|
3
2
|
/** Per-repo declaration of how the underlying table/collection is distributed. */
|
|
4
3
|
interface DistributionConfig {
|
|
5
4
|
/**
|
|
@@ -43,5 +42,4 @@ type DistributionMissHandler = (info: {
|
|
|
43
42
|
* once per operation name per guard instance to keep logs readable.
|
|
44
43
|
*/
|
|
45
44
|
declare function createDistributionGuard(config: DistributionConfig, onMiss?: DistributionMissHandler): (operation: string, filter: FilterInput | undefined) => void;
|
|
46
|
-
//#endregion
|
|
47
45
|
export { DistributionConfig, DistributionMissHandler, createDistributionGuard, filterReferencesKey };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isFilter } from "../filter/guard.mjs";
|
|
2
2
|
import { collectFields } from "../filter/walk.mjs";
|
|
3
|
-
|
|
3
|
+
// src/repository/distribution.ts
|
|
4
4
|
/**
|
|
5
5
|
* Distribution-key awareness — the lean, industry-standard slice of
|
|
6
6
|
* "sharding support" that belongs in an access layer.
|
|
@@ -69,5 +69,4 @@ function createDistributionGuard(config, onMiss) {
|
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
-
//#endregion
|
|
73
72
|
export { createDistributionGuard, filterReferencesKey };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/repository/options.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Canonical option keys forwarded into every `MinimalRepo` /
|
|
4
3
|
* `StandardRepo` method call.
|
|
@@ -62,5 +61,4 @@ declare const STANDARD_REPO_OPTION_KEYS: readonly ['organizationId', 'userId', '
|
|
|
62
61
|
* ```
|
|
63
62
|
*/
|
|
64
63
|
type StandardRepoOptionKey = (typeof STANDARD_REPO_OPTION_KEYS)[number];
|
|
65
|
-
//#endregion
|
|
66
64
|
export { STANDARD_REPO_OPTION_KEYS, StandardRepoOptionKey };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/repository/options.ts
|
|
2
2
|
/**
|
|
3
3
|
* Canonical option keys forwarded into every `MinimalRepo` /
|
|
4
4
|
* `StandardRepo` method call.
|
|
@@ -58,5 +58,4 @@ const STANDARD_REPO_OPTION_KEYS = [
|
|
|
58
58
|
"requestId",
|
|
59
59
|
"traceId"
|
|
60
60
|
];
|
|
61
|
-
//#endregion
|
|
62
61
|
export { STANDARD_REPO_OPTION_KEYS };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RepositoryBase } from "./base.mjs";
|
|
2
|
-
//#region src/repository/plugin-types.d.ts
|
|
3
2
|
/** Object-style plugin — preferred form. Carries a stable `name` for ordering checks. */
|
|
4
3
|
interface Plugin<TRepo extends RepositoryBase = RepositoryBase> {
|
|
5
4
|
/** Stable identifier used by plugin-order validators. */
|
|
@@ -22,5 +21,4 @@ declare const PLUGIN_ORDER_CONSTRAINTS: readonly (readonly [string, string, stri
|
|
|
22
21
|
* mode. Plain-function plugins (no `name`) are skipped — no false positives.
|
|
23
22
|
*/
|
|
24
23
|
declare function validatePluginOrder(plugins: readonly PluginType[], repoName: string, mode?: 'warn' | 'throw' | 'off', onWarn?: (message: string) => void): void;
|
|
25
|
-
//#endregion
|
|
26
24
|
export { PLUGIN_ORDER_CONSTRAINTS, Plugin, PluginFunction, PluginType, validatePluginOrder };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/repository/plugin-types.ts
|
|
2
2
|
/**
|
|
3
3
|
* Ordered pairs that produce wrong behavior when installed out of order.
|
|
4
4
|
* Each entry: `[mustComeFirst, mustComeAfter, reason]`.
|
|
@@ -41,5 +41,4 @@ function validatePluginOrder(plugins, repoName, mode = "warn", onWarn = (m) => {
|
|
|
41
41
|
onWarn(message);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
//#endregion
|
|
45
44
|
export { PLUGIN_ORDER_CONSTRAINTS, validatePluginOrder };
|