@classytic/repo-core 0.8.0 → 0.9.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 +42 -0
- package/dist/adapter/types.d.mts +19 -2
- package/dist/adapter/widen.d.mts +0 -1
- package/dist/aggregate/normalize.d.mts +0 -1
- package/dist/better-auth/index.mjs +1 -1
- package/dist/cache/engine.d.mts +0 -1
- package/dist/cache/memory-adapter.d.mts +0 -1
- package/dist/cache/memory-adapter.mjs +13 -0
- package/dist/cache/plugin/context.mjs +1 -1
- package/dist/cache/plugin/index.d.mts +3 -4
- package/dist/cache/plugin/index.mjs +1 -1
- package/dist/cache/timeout-adapter.d.mts +0 -1
- package/dist/context/index.d.mts +1 -1
- package/dist/context/types.d.mts +0 -1
- package/dist/errors/contract.d.mts +0 -1
- package/dist/errors/create-error.d.mts +0 -1
- package/dist/errors/duplicate-key.d.mts +0 -1
- package/dist/errors/schema.d.mts +27 -27
- package/dist/errors/types.d.mts +11 -11
- package/dist/events/emit.d.mts +0 -1
- package/dist/filter/builders.d.mts +2 -3
- package/dist/filter/builders.mjs +1 -1
- package/dist/filter/from-record.d.mts +0 -1
- package/dist/filter/from-record.mjs +2 -2
- package/dist/filter/guard.d.mts +0 -1
- package/dist/filter/guard.mjs +1 -1
- package/dist/filter/index.d.mts +1 -1
- package/dist/filter/index.mjs +1 -1
- package/dist/filter/match.d.mts +0 -1
- package/dist/filter/scope.d.mts +0 -1
- package/dist/filter/walk.d.mts +0 -1
- package/dist/hooks/engine.d.mts +0 -1
- package/dist/hooks/events.d.mts +51 -51
- package/dist/hooks/priority.d.mts +8 -4
- package/dist/hooks/priority.mjs +9 -0
- package/dist/lookup/index.d.mts +1 -1
- package/dist/lookup/types.d.mts +0 -1
- package/dist/operations/registry.d.mts +0 -1
- package/dist/pagination/canonical.d.mts +0 -1
- package/dist/pagination/cursor.d.mts +0 -1
- package/dist/pagination/keyset.d.mts +0 -1
- package/dist/pagination/offset.d.mts +0 -1
- package/dist/plugins/tenant-helpers.d.mts +0 -1
- package/dist/query-parser/coerce.d.mts +1 -2
- package/dist/query-parser/coerce.mjs +2 -2
- package/dist/query-parser/parse-url.d.mts +0 -1
- package/dist/query-parser/parse-url.mjs +2 -2
- package/dist/query-parser/reserved.mjs +1 -1
- package/dist/query-parser/types.d.mts +0 -1
- package/dist/repository/archive.d.mts +0 -1
- package/dist/repository/base.d.mts +0 -1
- package/dist/repository/distribution.d.mts +0 -1
- package/dist/repository/options.d.mts +1 -1
- package/dist/repository/plugin-types.d.mts +0 -1
- package/dist/repository/purge.d.mts +0 -1
- package/dist/repository/types.d.mts +38 -10
- package/dist/schema/field-rules.d.mts +0 -1
- package/dist/schema/field-rules.mjs +1 -1
- package/dist/schema/generator.d.mts +0 -1
- package/dist/schema/types.d.mts +14 -6
- package/dist/sync/index.d.mts +1 -1
- package/dist/sync/index.mjs +2 -0
- package/dist/tenant/resolve.d.mts +1 -2
- package/dist/testing/conformance.d.mts +0 -1
- package/dist/testing/conformance.mjs +1 -1
- package/dist/testing/lock-conformance.d.mts +0 -1
- package/dist/testing/types.d.mts +1 -2
- package/dist/update/builders.d.mts +0 -1
- package/dist/update/compile.d.mts +0 -1
- package/dist/update/guard.d.mts +0 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,48 @@ All notable changes to `@classytic/repo-core` are documented here.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.9.0] - 2026-07-11
|
|
8
|
+
|
|
9
|
+
### Added — `StandardRepo.getByIds` (batch point-read)
|
|
10
|
+
|
|
11
|
+
- **`getByIds?(ids: readonly string[], options?: FindAllOptions): Promise<Map<string, TDoc>>`**
|
|
12
|
+
added to `StandardRepo` — the `$in` / `IN (...)` counterpart to `getById`. The N+1
|
|
13
|
+
killer for callers resolving many ids in one tick (order-line snapshotting, dashboard
|
|
14
|
+
row enrichment, reference expansion).
|
|
15
|
+
|
|
16
|
+
**Contract:**
|
|
17
|
+
- Kit de-duplicates ids before the driver round-trip.
|
|
18
|
+
- Ids with no matching doc are ABSENT from the returned `Map` — never throw on a miss.
|
|
19
|
+
- Structurally invalid ids are treated as misses (one malformed id must never fail the
|
|
20
|
+
batch).
|
|
21
|
+
- Tenant scoping, soft-delete, and hooks apply exactly as they would to `getById`.
|
|
22
|
+
- Empty input → empty Map, no round-trip.
|
|
23
|
+
- Optional: kits adopt incrementally. Callers feature-detect
|
|
24
|
+
(`repo.getByIds ?? per-id fallback`) until their kit floor ships it.
|
|
25
|
+
- Sizing is backend-bound (Mongo: 16 MB query document); keep batches ≤ ~10k ids and
|
|
26
|
+
chunk beyond.
|
|
27
|
+
|
|
28
|
+
- **`events.ts` clarification** — `getByIds` has no `BEFORE_GET_BY_IDS` event by design.
|
|
29
|
+
It routes through `findAll`, so observers see the batch as `before:findAll` / `after:findAll`
|
|
30
|
+
with an `$in` filter. The comment guards against accidentally adding a separate event.
|
|
31
|
+
|
|
32
|
+
- **New type-level contract test** (`tests/unit/repository/get-by-ids-contract.test.ts`) —
|
|
33
|
+
pins the `getByIds` shape for kits implementing it: `Map<string, TDoc>` keyed by
|
|
34
|
+
stringified id, `readonly string[]` input. Verifies that kits without `getByIds` still
|
|
35
|
+
conform (the member is optional).
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- `ChangesPage.changes` type: `ReadonlyArray<ChangeEntry<TDoc>>` → `readonly ChangeEntry<TDoc>[]`
|
|
40
|
+
(canonical form — functionally identical).
|
|
41
|
+
- devDeps: `tsdown ^0.21.8 → ^0.22.5`, `typescript ^6.0.3 → ^7.0.2`.
|
|
42
|
+
|
|
43
|
+
## [0.8.1] - 2026-07-11
|
|
44
|
+
|
|
45
|
+
### Docs — `DataAdapter.close()` ownership rule
|
|
46
|
+
|
|
47
|
+
- Clarified the `close?()` contract (doc-only, no type/runtime change): the adapter does **not** own the database connection — the host does, and `close()` must release only adapter/kit-owned resources (TTL/vacuum timers, change streams), never disconnect a shared client/pool. Kits that genuinely own a connection gate its disposal behind an explicit opt-in. This standardizes cleanup semantics across mongokit/sqlitekit/pgkit/prismakit.
|
|
48
|
+
|
|
7
49
|
## [0.8.0] - 2026-07-08
|
|
8
50
|
|
|
9
51
|
### Added — data-lifecycle contract (archive, streaming, distribution awareness)
|
package/dist/adapter/types.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { DeleteResult, MinimalRepo, StandardRepo } from "../repository/types.mjs";
|
|
2
2
|
import { SchemaBuilderOptions } from "../schema/types.mjs";
|
|
3
|
-
|
|
4
3
|
//#region src/adapter/types.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Cross-kit repository contract.
|
|
@@ -191,7 +190,25 @@ interface DataAdapter<TDoc = unknown> {
|
|
|
191
190
|
* SQL adapters, non-Mongo operators, or kits that compile Filter IR.
|
|
192
191
|
*/
|
|
193
192
|
matchesFilter?: (item: unknown, filters: Record<string, unknown>) => boolean;
|
|
194
|
-
/**
|
|
193
|
+
/**
|
|
194
|
+
* Release resources this ADAPTER (or its repository) allocated —
|
|
195
|
+
* background timers (TTL / vacuum sweepers), open change streams,
|
|
196
|
+
* kit-internal caches. Safe to call more than once.
|
|
197
|
+
*
|
|
198
|
+
* **Ownership rule (load-bearing).** The adapter does NOT own the
|
|
199
|
+
* database connection — the HOST does. The host wrote
|
|
200
|
+
* `new PrismaClient()` / `new Pool()` / `mongoose.connect()`, so the
|
|
201
|
+
* host closes it (typically in its own `onClose`). `close()` MUST NOT
|
|
202
|
+
* disconnect a connection it didn't open: several resources commonly
|
|
203
|
+
* share one client/pool, so tearing it down from one adapter would
|
|
204
|
+
* break every sibling resource. Kits that must dispose a connection
|
|
205
|
+
* they genuinely own gate it behind an explicit opt-in (e.g.
|
|
206
|
+
* prismakit's `ownsClient`).
|
|
207
|
+
*
|
|
208
|
+
* Kits with nothing kit-owned to release still MAY implement this as a
|
|
209
|
+
* documented no-op so hosts can call `adapter.close()` uniformly across
|
|
210
|
+
* every backend without feature-detecting.
|
|
211
|
+
*/
|
|
195
212
|
close?(): Promise<void>;
|
|
196
213
|
/**
|
|
197
214
|
* Optional: does the underlying schema declare a path with this name?
|
package/dist/adapter/widen.d.mts
CHANGED
|
@@ -53,7 +53,7 @@ function pluralizeBetterAuthCollection(name) {
|
|
|
53
53
|
*/
|
|
54
54
|
function resolveBetterAuthCollections(options = {}) {
|
|
55
55
|
const { plugins = [], extraCollections = [], usePlural = false, modelOverrides = {} } = options;
|
|
56
|
-
const pluginSet = new Set(["core", ...plugins]);
|
|
56
|
+
const pluginSet = /* @__PURE__ */ new Set(["core", ...plugins]);
|
|
57
57
|
const collected = [];
|
|
58
58
|
for (const key of pluginSet) for (const name of BA_COLLECTIONS_BY_PLUGIN[key]) collected.push(name);
|
|
59
59
|
for (const name of extraCollections) collected.push(name);
|
package/dist/cache/engine.d.mts
CHANGED
|
@@ -36,6 +36,13 @@ function createMemoryCacheAdapter() {
|
|
|
36
36
|
const prefix = pattern.endsWith("*") ? pattern.slice(0, -1) : pattern;
|
|
37
37
|
for (const key of store.keys()) if (key.startsWith(prefix)) store.delete(key);
|
|
38
38
|
},
|
|
39
|
+
/**
|
|
40
|
+
* Atomic add-to-set — `O(M)` per call where M is the number of
|
|
41
|
+
* new members. Stores the value as `Set<string>` internally for
|
|
42
|
+
* `O(1)` membership checks; `get` exposes it back as an array for
|
|
43
|
+
* cross-adapter portability. JS's single-thread guarantee makes
|
|
44
|
+
* the read-mutate-write race-free without locks.
|
|
45
|
+
*/
|
|
39
46
|
addToSet(key, members, ttlSeconds = 60) {
|
|
40
47
|
const existing = readUnexpired(key);
|
|
41
48
|
let set;
|
|
@@ -55,6 +62,12 @@ function createMemoryCacheAdapter() {
|
|
|
55
62
|
}
|
|
56
63
|
return added;
|
|
57
64
|
},
|
|
65
|
+
/**
|
|
66
|
+
* Atomic increment — JS's single-threaded execution model makes
|
|
67
|
+
* this race-free without locks. Mirrors Redis's `INCRBY key by` +
|
|
68
|
+
* `EXPIRE key ttlSeconds NX`: TTL is applied only on key creation,
|
|
69
|
+
* existing keys keep their original expiry.
|
|
70
|
+
*/
|
|
58
71
|
increment(key, by = 1, ttlSeconds = 60) {
|
|
59
72
|
const existing = readUnexpired(key);
|
|
60
73
|
const next = (existing && typeof existing.value === "number" && Number.isFinite(existing.value) ? existing.value : 0) + by;
|
|
@@ -99,7 +99,7 @@ function stripAggOperationalSlots(value) {
|
|
|
99
99
|
return rest;
|
|
100
100
|
}
|
|
101
101
|
/** Aggregate ops carry their cache slot inside the AggRequest IR. */
|
|
102
|
-
const AGGREGATE_OPS = new Set(["aggregate", "aggregatePaginate"]);
|
|
102
|
+
const AGGREGATE_OPS = /* @__PURE__ */ new Set(["aggregate", "aggregatePaginate"]);
|
|
103
103
|
/**
|
|
104
104
|
* Extract per-call `CacheOptions` from a hook context. Three valid
|
|
105
105
|
* locations covered (kit-dependent):
|
|
@@ -3,12 +3,11 @@ import { CacheOptions, CacheReadResult } from "../options.mjs";
|
|
|
3
3
|
import { CacheAdapter } from "../types.mjs";
|
|
4
4
|
import { CacheEngine } from "../engine.mjs";
|
|
5
5
|
import { DEFAULT_SHAPE_KEYS_BY_OP } from "./context.mjs";
|
|
6
|
-
|
|
7
6
|
//#region src/cache/plugin/index.d.ts
|
|
8
7
|
/** Default read ops the plugin caches. Kits may override per resource. */
|
|
9
|
-
declare const DEFAULT_CACHEABLE_OPS: readonly [
|
|
8
|
+
declare const DEFAULT_CACHEABLE_OPS: readonly ['getById', 'getAll', 'getOne', 'getByQuery', 'count', 'exists', 'distinct', 'aggregate', 'aggregatePaginate'];
|
|
10
9
|
/** Default mutating ops that bump version + invalidate tags on success. */
|
|
11
|
-
declare const DEFAULT_INVALIDATING_OPS: readonly [
|
|
10
|
+
declare const DEFAULT_INVALIDATING_OPS: readonly ['create', 'createMany', 'update', 'updateMany', 'findOneAndUpdate', 'upsert', 'delete', 'deleteMany', 'restore', 'claim', 'claimVersion', 'increment', 'bulkWrite'];
|
|
12
11
|
/** Observability callbacks — pipe to metrics, traces, or stdout. */
|
|
13
12
|
interface LogCallbacks {
|
|
14
13
|
onHit?: (key: string, op: string, ageSeconds: number) => void;
|
|
@@ -83,4 +82,4 @@ interface RepositoryCacheHandle {
|
|
|
83
82
|
*/
|
|
84
83
|
declare function cachePlugin(options: RepositoryCachePluginOptions): Plugin;
|
|
85
84
|
//#endregion
|
|
86
|
-
export { DEFAULT_CACHEABLE_OPS, DEFAULT_INVALIDATING_OPS, LogCallbacks, RepositoryCacheHandle, RepositoryCachePluginOptions, cachePlugin };
|
|
85
|
+
export { DEFAULT_CACHEABLE_OPS, DEFAULT_INVALIDATING_OPS, DEFAULT_SHAPE_KEYS_BY_OP, LogCallbacks, RepositoryCacheHandle, RepositoryCachePluginOptions, cachePlugin };
|
package/dist/context/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { RepositoryContext } from "./types.mjs";
|
|
2
|
-
export {
|
|
2
|
+
export type { RepositoryContext };
|
package/dist/context/types.d.mts
CHANGED
package/dist/errors/schema.d.mts
CHANGED
|
@@ -17,24 +17,24 @@
|
|
|
17
17
|
* {@link ./types.ts}.
|
|
18
18
|
*/
|
|
19
19
|
declare const errorDetailSchema: {
|
|
20
|
-
readonly type:
|
|
20
|
+
readonly type: 'object';
|
|
21
21
|
readonly properties: {
|
|
22
22
|
readonly path: {
|
|
23
|
-
readonly type:
|
|
23
|
+
readonly type: 'string';
|
|
24
24
|
readonly description: "Dot-path to the offending field, e.g. 'lines.0.quantity'.";
|
|
25
25
|
};
|
|
26
26
|
readonly code: {
|
|
27
|
-
readonly type:
|
|
27
|
+
readonly type: 'string';
|
|
28
28
|
};
|
|
29
29
|
readonly message: {
|
|
30
|
-
readonly type:
|
|
30
|
+
readonly type: 'string';
|
|
31
31
|
};
|
|
32
32
|
readonly meta: {
|
|
33
|
-
readonly type:
|
|
34
|
-
readonly description:
|
|
33
|
+
readonly type: 'object';
|
|
34
|
+
readonly description: 'Non-PII per-detail diagnostics (safe to log + return).';
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
|
-
readonly required: readonly [
|
|
37
|
+
readonly required: readonly ['code', 'message'];
|
|
38
38
|
};
|
|
39
39
|
/**
|
|
40
40
|
* Canonical error response. Mirrors `ErrorContract` in {@link ./types.ts}.
|
|
@@ -48,54 +48,54 @@ declare const errorDetailSchema: {
|
|
|
48
48
|
* a separate path from success — HTTP status discriminates.
|
|
49
49
|
*/
|
|
50
50
|
declare const errorContractSchema: {
|
|
51
|
-
readonly type:
|
|
51
|
+
readonly type: 'object';
|
|
52
52
|
readonly properties: {
|
|
53
53
|
readonly code: {
|
|
54
|
-
readonly type:
|
|
54
|
+
readonly type: 'string';
|
|
55
55
|
readonly description: "Hierarchical machine-readable code (e.g. 'arc.not_found').";
|
|
56
56
|
};
|
|
57
57
|
readonly message: {
|
|
58
|
-
readonly type:
|
|
59
|
-
readonly description:
|
|
58
|
+
readonly type: 'string';
|
|
59
|
+
readonly description: 'Human-readable, safe-for-client message.';
|
|
60
60
|
};
|
|
61
61
|
readonly status: {
|
|
62
|
-
readonly type:
|
|
63
|
-
readonly description:
|
|
62
|
+
readonly type: 'integer';
|
|
63
|
+
readonly description: 'Suggested HTTP status code (hosts may override).';
|
|
64
64
|
};
|
|
65
65
|
readonly details: {
|
|
66
|
-
readonly type:
|
|
67
|
-
readonly description:
|
|
66
|
+
readonly type: 'array';
|
|
67
|
+
readonly description: 'Field-scoped structured details (validation failures, duplicate keys, multi-code domain errors).';
|
|
68
68
|
readonly items: {
|
|
69
|
-
readonly type:
|
|
69
|
+
readonly type: 'object';
|
|
70
70
|
readonly properties: {
|
|
71
71
|
readonly path: {
|
|
72
|
-
readonly type:
|
|
72
|
+
readonly type: 'string';
|
|
73
73
|
readonly description: "Dot-path to the offending field, e.g. 'lines.0.quantity'.";
|
|
74
74
|
};
|
|
75
75
|
readonly code: {
|
|
76
|
-
readonly type:
|
|
76
|
+
readonly type: 'string';
|
|
77
77
|
};
|
|
78
78
|
readonly message: {
|
|
79
|
-
readonly type:
|
|
79
|
+
readonly type: 'string';
|
|
80
80
|
};
|
|
81
81
|
readonly meta: {
|
|
82
|
-
readonly type:
|
|
83
|
-
readonly description:
|
|
82
|
+
readonly type: 'object';
|
|
83
|
+
readonly description: 'Non-PII per-detail diagnostics (safe to log + return).';
|
|
84
84
|
};
|
|
85
85
|
};
|
|
86
|
-
readonly required: readonly [
|
|
86
|
+
readonly required: readonly ['code', 'message'];
|
|
87
87
|
};
|
|
88
88
|
};
|
|
89
89
|
readonly correlationId: {
|
|
90
|
-
readonly type:
|
|
91
|
-
readonly description:
|
|
90
|
+
readonly type: 'string';
|
|
91
|
+
readonly description: 'Request id for support lookups.';
|
|
92
92
|
};
|
|
93
93
|
readonly meta: {
|
|
94
|
-
readonly type:
|
|
95
|
-
readonly description:
|
|
94
|
+
readonly type: 'object';
|
|
95
|
+
readonly description: 'Non-PII diagnostics (safe to log + return).';
|
|
96
96
|
};
|
|
97
97
|
};
|
|
98
|
-
readonly required: readonly [
|
|
98
|
+
readonly required: readonly ['code', 'message'];
|
|
99
99
|
};
|
|
100
100
|
//#endregion
|
|
101
101
|
export { errorContractSchema, errorDetailSchema };
|
package/dist/errors/types.d.mts
CHANGED
|
@@ -125,17 +125,17 @@ interface ErrorDetail {
|
|
|
125
125
|
* codes on the wire so existing client switches keep working.
|
|
126
126
|
*/
|
|
127
127
|
declare const ERROR_CODES: {
|
|
128
|
-
readonly VALIDATION:
|
|
129
|
-
readonly NOT_FOUND:
|
|
130
|
-
readonly CONFLICT:
|
|
131
|
-
readonly UNAUTHORIZED:
|
|
132
|
-
readonly FORBIDDEN:
|
|
133
|
-
readonly RATE_LIMITED:
|
|
134
|
-
readonly IDEMPOTENCY_CONFLICT:
|
|
135
|
-
readonly PRECONDITION_FAILED:
|
|
136
|
-
readonly INTERNAL:
|
|
137
|
-
readonly UNAVAILABLE:
|
|
138
|
-
readonly TIMEOUT:
|
|
128
|
+
readonly VALIDATION: 'validation_error';
|
|
129
|
+
readonly NOT_FOUND: 'not_found';
|
|
130
|
+
readonly CONFLICT: 'conflict';
|
|
131
|
+
readonly UNAUTHORIZED: 'unauthorized';
|
|
132
|
+
readonly FORBIDDEN: 'forbidden';
|
|
133
|
+
readonly RATE_LIMITED: 'rate_limited';
|
|
134
|
+
readonly IDEMPOTENCY_CONFLICT: 'idempotency_conflict';
|
|
135
|
+
readonly PRECONDITION_FAILED: 'precondition_failed';
|
|
136
|
+
readonly INTERNAL: 'internal_error';
|
|
137
|
+
readonly UNAVAILABLE: 'service_unavailable';
|
|
138
|
+
readonly TIMEOUT: 'timeout';
|
|
139
139
|
};
|
|
140
140
|
type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
141
141
|
//#endregion
|
package/dist/events/emit.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Filter, FilterEq, FilterExists, FilterFalse, FilterGt, FilterGte, FilterIn, FilterLike, FilterLt, FilterLte, FilterNe, FilterNin, FilterRaw, FilterRegex, FilterTrue } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/filter/builders.d.ts
|
|
4
3
|
declare const eq: (field: string, value: unknown) => FilterEq;
|
|
5
4
|
declare const ne: (field: string, value: unknown) => FilterNe;
|
|
@@ -11,7 +10,7 @@ declare const lte: (field: string, value: unknown) => FilterLte;
|
|
|
11
10
|
declare const in_: (field: string, values: readonly unknown[]) => FilterIn;
|
|
12
11
|
/** Non-membership (`$nin` / `NOT IN (...)`). Aliased as `noneOf`. */
|
|
13
12
|
declare const nin: (field: string, values: readonly unknown[]) => FilterNin;
|
|
14
|
-
declare const like: (field: string, pattern: string, caseSensitivity?:
|
|
13
|
+
declare const like: (field: string, pattern: string, caseSensitivity?: 'sensitive' | 'insensitive') => FilterLike;
|
|
15
14
|
declare const regex: (field: string, pattern: string, flags?: string) => FilterRegex;
|
|
16
15
|
declare const exists: (field: string, present?: boolean) => FilterExists;
|
|
17
16
|
/**
|
|
@@ -57,4 +56,4 @@ declare function isNotNull(field: string): FilterExists;
|
|
|
57
56
|
*/
|
|
58
57
|
declare function raw(sql: string, params?: readonly unknown[]): FilterRaw;
|
|
59
58
|
//#endregion
|
|
60
|
-
export { FALSE, TRUE, and, between, contains, endsWith, eq, exists, gt, gte, iEq,
|
|
59
|
+
export { FALSE, type FilterRaw, TRUE, and, in_ as anyOf, in_, between, contains, endsWith, eq, exists, gt, gte, iEq, not as invert, not, isNotNull, isNull, like, lt, lte, ne, nin, nin as noneOf, or, raw, regex, startsWith };
|
package/dist/filter/builders.mjs
CHANGED
|
@@ -169,4 +169,4 @@ function escapeLikePattern(value) {
|
|
|
169
169
|
return value.replace(/[%_]/g, (c) => `\\${c}`);
|
|
170
170
|
}
|
|
171
171
|
//#endregion
|
|
172
|
-
export { FALSE, TRUE, and, between, contains, endsWith, eq, exists, gt, gte, iEq,
|
|
172
|
+
export { FALSE, TRUE, and, in_ as anyOf, in_, between, contains, endsWith, eq, exists, gt, gte, iEq, not as invert, not, isNotNull, isNull, like, lt, lte, ne, nin, nin as noneOf, or, raw, regex, startsWith };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TRUE, and, contains, endsWith, eq, gt, gte,
|
|
1
|
+
import { TRUE, and, anyOf as in_, contains, endsWith, eq, gt, gte, isNotNull, isNull, like, lt, lte, ne, nin, startsWith } from "./builders.mjs";
|
|
2
2
|
//#region src/filter/from-record.ts
|
|
3
3
|
/**
|
|
4
4
|
* Plain-record → Filter IR conversion — the canonical normalizer every
|
|
@@ -40,7 +40,7 @@ function isOperatorObject(value) {
|
|
|
40
40
|
if (keys.length === 0) return false;
|
|
41
41
|
return keys.every((k) => KNOWN_OPS.has(k));
|
|
42
42
|
}
|
|
43
|
-
const KNOWN_OPS = new Set([
|
|
43
|
+
const KNOWN_OPS = /* @__PURE__ */ new Set([
|
|
44
44
|
"eq",
|
|
45
45
|
"ne",
|
|
46
46
|
"gt",
|
package/dist/filter/guard.d.mts
CHANGED
package/dist/filter/guard.mjs
CHANGED
package/dist/filter/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Filter, FilterAnd, FilterEq, FilterExists, FilterFalse, FilterGt, FilterGte, FilterIn, FilterLike, FilterLt, FilterLte, FilterNe, FilterNin, FilterNot, FilterOp, FilterOr, FilterRaw, FilterRegex, FilterTrue } from "./types.mjs";
|
|
2
|
-
import { FALSE, TRUE, and, between, contains, endsWith, eq, exists, gt, gte, iEq,
|
|
2
|
+
import { FALSE, TRUE, and, anyOf as in_, between, contains, endsWith, eq, exists, gt, gte, iEq, invert as not, isNotNull, isNull, like, lt, lte, ne, nin, or, raw, regex, startsWith } from "./builders.mjs";
|
|
3
3
|
import { recordToFilter } from "./from-record.mjs";
|
|
4
4
|
import { isFilter } from "./guard.mjs";
|
|
5
5
|
import { asPredicate, matchFilter } from "./match.mjs";
|
package/dist/filter/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isFilter } from "./guard.mjs";
|
|
2
2
|
import { collectFields, mapFilter, walkFilter } from "./walk.mjs";
|
|
3
|
-
import { FALSE, TRUE, and, between, contains, endsWith, eq, exists, gt, gte, iEq,
|
|
3
|
+
import { FALSE, TRUE, and, anyOf as in_, between, contains, endsWith, eq, exists, gt, gte, iEq, invert as not, isNotNull, isNull, like, lt, lte, ne, nin, or, raw, regex, startsWith } from "./builders.mjs";
|
|
4
4
|
import { recordToFilter } from "./from-record.mjs";
|
|
5
5
|
import { asPredicate, matchFilter } from "./match.mjs";
|
|
6
6
|
import { SCOPE_ANY, buildTenantScope, mergeScope } from "./scope.mjs";
|
package/dist/filter/match.d.mts
CHANGED
package/dist/filter/scope.d.mts
CHANGED
package/dist/filter/walk.d.mts
CHANGED
package/dist/hooks/engine.d.mts
CHANGED
package/dist/hooks/events.d.mts
CHANGED
|
@@ -37,57 +37,57 @@
|
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
39
|
declare const HOOK_EVENTS: {
|
|
40
|
-
readonly BEFORE_CREATE:
|
|
41
|
-
readonly AFTER_CREATE:
|
|
42
|
-
readonly ERROR_CREATE:
|
|
43
|
-
readonly BEFORE_UPDATE:
|
|
44
|
-
readonly AFTER_UPDATE:
|
|
45
|
-
readonly ERROR_UPDATE:
|
|
46
|
-
readonly BEFORE_DELETE:
|
|
47
|
-
readonly AFTER_DELETE:
|
|
48
|
-
readonly ERROR_DELETE:
|
|
49
|
-
readonly BEFORE_GET_BY_ID:
|
|
50
|
-
readonly AFTER_GET_BY_ID:
|
|
51
|
-
readonly ERROR_GET_BY_ID:
|
|
52
|
-
readonly BEFORE_GET_ALL:
|
|
53
|
-
readonly AFTER_GET_ALL:
|
|
54
|
-
readonly ERROR_GET_ALL:
|
|
55
|
-
readonly BEFORE_CREATE_MANY:
|
|
56
|
-
readonly AFTER_CREATE_MANY:
|
|
57
|
-
readonly ERROR_CREATE_MANY:
|
|
58
|
-
readonly BEFORE_UPDATE_MANY:
|
|
59
|
-
readonly AFTER_UPDATE_MANY:
|
|
60
|
-
readonly ERROR_UPDATE_MANY:
|
|
61
|
-
readonly BEFORE_DELETE_MANY:
|
|
62
|
-
readonly AFTER_DELETE_MANY:
|
|
63
|
-
readonly ERROR_DELETE_MANY:
|
|
64
|
-
readonly BEFORE_FIND_ONE_AND_UPDATE:
|
|
65
|
-
readonly AFTER_FIND_ONE_AND_UPDATE:
|
|
66
|
-
readonly ERROR_FIND_ONE_AND_UPDATE:
|
|
67
|
-
readonly BEFORE_RESTORE:
|
|
68
|
-
readonly AFTER_RESTORE:
|
|
69
|
-
readonly ERROR_RESTORE:
|
|
70
|
-
readonly BEFORE_GET_BY_QUERY:
|
|
71
|
-
readonly AFTER_GET_BY_QUERY:
|
|
72
|
-
readonly ERROR_GET_BY_QUERY:
|
|
73
|
-
readonly BEFORE_GET_ONE:
|
|
74
|
-
readonly AFTER_GET_ONE:
|
|
75
|
-
readonly ERROR_GET_ONE:
|
|
76
|
-
readonly BEFORE_FIND_ALL:
|
|
77
|
-
readonly AFTER_FIND_ALL:
|
|
78
|
-
readonly ERROR_FIND_ALL:
|
|
79
|
-
readonly BEFORE_GET_OR_CREATE:
|
|
80
|
-
readonly AFTER_GET_OR_CREATE:
|
|
81
|
-
readonly ERROR_GET_OR_CREATE:
|
|
82
|
-
readonly BEFORE_COUNT:
|
|
83
|
-
readonly AFTER_COUNT:
|
|
84
|
-
readonly ERROR_COUNT:
|
|
85
|
-
readonly BEFORE_EXISTS:
|
|
86
|
-
readonly AFTER_EXISTS:
|
|
87
|
-
readonly ERROR_EXISTS:
|
|
88
|
-
readonly BEFORE_DISTINCT:
|
|
89
|
-
readonly AFTER_DISTINCT:
|
|
90
|
-
readonly ERROR_DISTINCT:
|
|
40
|
+
readonly BEFORE_CREATE: 'before:create';
|
|
41
|
+
readonly AFTER_CREATE: 'after:create';
|
|
42
|
+
readonly ERROR_CREATE: 'error:create';
|
|
43
|
+
readonly BEFORE_UPDATE: 'before:update';
|
|
44
|
+
readonly AFTER_UPDATE: 'after:update';
|
|
45
|
+
readonly ERROR_UPDATE: 'error:update';
|
|
46
|
+
readonly BEFORE_DELETE: 'before:delete';
|
|
47
|
+
readonly AFTER_DELETE: 'after:delete';
|
|
48
|
+
readonly ERROR_DELETE: 'error:delete';
|
|
49
|
+
readonly BEFORE_GET_BY_ID: 'before:getById';
|
|
50
|
+
readonly AFTER_GET_BY_ID: 'after:getById';
|
|
51
|
+
readonly ERROR_GET_BY_ID: 'error:getById';
|
|
52
|
+
readonly BEFORE_GET_ALL: 'before:getAll';
|
|
53
|
+
readonly AFTER_GET_ALL: 'after:getAll';
|
|
54
|
+
readonly ERROR_GET_ALL: 'error:getAll';
|
|
55
|
+
readonly BEFORE_CREATE_MANY: 'before:createMany';
|
|
56
|
+
readonly AFTER_CREATE_MANY: 'after:createMany';
|
|
57
|
+
readonly ERROR_CREATE_MANY: 'error:createMany';
|
|
58
|
+
readonly BEFORE_UPDATE_MANY: 'before:updateMany';
|
|
59
|
+
readonly AFTER_UPDATE_MANY: 'after:updateMany';
|
|
60
|
+
readonly ERROR_UPDATE_MANY: 'error:updateMany';
|
|
61
|
+
readonly BEFORE_DELETE_MANY: 'before:deleteMany';
|
|
62
|
+
readonly AFTER_DELETE_MANY: 'after:deleteMany';
|
|
63
|
+
readonly ERROR_DELETE_MANY: 'error:deleteMany';
|
|
64
|
+
readonly BEFORE_FIND_ONE_AND_UPDATE: 'before:findOneAndUpdate';
|
|
65
|
+
readonly AFTER_FIND_ONE_AND_UPDATE: 'after:findOneAndUpdate';
|
|
66
|
+
readonly ERROR_FIND_ONE_AND_UPDATE: 'error:findOneAndUpdate';
|
|
67
|
+
readonly BEFORE_RESTORE: 'before:restore';
|
|
68
|
+
readonly AFTER_RESTORE: 'after:restore';
|
|
69
|
+
readonly ERROR_RESTORE: 'error:restore';
|
|
70
|
+
readonly BEFORE_GET_BY_QUERY: 'before:getByQuery';
|
|
71
|
+
readonly AFTER_GET_BY_QUERY: 'after:getByQuery';
|
|
72
|
+
readonly ERROR_GET_BY_QUERY: 'error:getByQuery';
|
|
73
|
+
readonly BEFORE_GET_ONE: 'before:getOne';
|
|
74
|
+
readonly AFTER_GET_ONE: 'after:getOne';
|
|
75
|
+
readonly ERROR_GET_ONE: 'error:getOne';
|
|
76
|
+
readonly BEFORE_FIND_ALL: 'before:findAll';
|
|
77
|
+
readonly AFTER_FIND_ALL: 'after:findAll';
|
|
78
|
+
readonly ERROR_FIND_ALL: 'error:findAll';
|
|
79
|
+
readonly BEFORE_GET_OR_CREATE: 'before:getOrCreate';
|
|
80
|
+
readonly AFTER_GET_OR_CREATE: 'after:getOrCreate';
|
|
81
|
+
readonly ERROR_GET_OR_CREATE: 'error:getOrCreate';
|
|
82
|
+
readonly BEFORE_COUNT: 'before:count';
|
|
83
|
+
readonly AFTER_COUNT: 'after:count';
|
|
84
|
+
readonly ERROR_COUNT: 'error:count';
|
|
85
|
+
readonly BEFORE_EXISTS: 'before:exists';
|
|
86
|
+
readonly AFTER_EXISTS: 'after:exists';
|
|
87
|
+
readonly ERROR_EXISTS: 'error:exists';
|
|
88
|
+
readonly BEFORE_DISTINCT: 'before:distinct';
|
|
89
|
+
readonly AFTER_DISTINCT: 'after:distinct';
|
|
90
|
+
readonly ERROR_DISTINCT: 'error:distinct';
|
|
91
91
|
};
|
|
92
92
|
/** String-literal union of every canonical hook event name. */
|
|
93
93
|
type HookEventName = (typeof HOOK_EVENTS)[keyof typeof HOOK_EVENTS];
|
|
@@ -12,15 +12,19 @@
|
|
|
12
12
|
* compose identically.
|
|
13
13
|
*/
|
|
14
14
|
declare const HOOK_PRIORITY: {
|
|
15
|
-
/** Policy enforcement — tenant isolation, soft-delete filtering. */
|
|
15
|
+
/** Policy enforcement — tenant isolation, soft-delete filtering. */
|
|
16
|
+
readonly POLICY: 100;
|
|
16
17
|
/**
|
|
17
18
|
* Input validation — Standard Schema / custom validators. Runs after
|
|
18
19
|
* policy (so tenant-stamped fields are present on the payload) and
|
|
19
20
|
* before cache (an invalid request must never claim a cache slot).
|
|
20
21
|
*/
|
|
21
|
-
readonly VALIDATION: 150;
|
|
22
|
-
|
|
23
|
-
readonly
|
|
22
|
+
readonly VALIDATION: 150;
|
|
23
|
+
/** Cache lookup / store — must run after policy so filters are in the key. */
|
|
24
|
+
readonly CACHE: 200;
|
|
25
|
+
/** Observability — audit logging, metrics, telemetry. Must not mutate context. */
|
|
26
|
+
readonly OBSERVABILITY: 300;
|
|
27
|
+
/** Default priority for user-registered hooks with no explicit priority. */
|
|
24
28
|
readonly DEFAULT: 500;
|
|
25
29
|
};
|
|
26
30
|
/** The numeric type of any `HOOK_PRIORITY` value. */
|
package/dist/hooks/priority.mjs
CHANGED
|
@@ -12,10 +12,19 @@
|
|
|
12
12
|
* compose identically.
|
|
13
13
|
*/
|
|
14
14
|
const HOOK_PRIORITY = {
|
|
15
|
+
/** Policy enforcement — tenant isolation, soft-delete filtering. */
|
|
15
16
|
POLICY: 100,
|
|
17
|
+
/**
|
|
18
|
+
* Input validation — Standard Schema / custom validators. Runs after
|
|
19
|
+
* policy (so tenant-stamped fields are present on the payload) and
|
|
20
|
+
* before cache (an invalid request must never claim a cache slot).
|
|
21
|
+
*/
|
|
16
22
|
VALIDATION: 150,
|
|
23
|
+
/** Cache lookup / store — must run after policy so filters are in the key. */
|
|
17
24
|
CACHE: 200,
|
|
25
|
+
/** Observability — audit logging, metrics, telemetry. Must not mutate context. */
|
|
18
26
|
OBSERVABILITY: 300,
|
|
27
|
+
/** Default priority for user-registered hooks with no explicit priority. */
|
|
19
28
|
DEFAULT: 500
|
|
20
29
|
};
|
|
21
30
|
//#endregion
|
package/dist/lookup/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { LookupPopulateOptions, LookupPopulateResult, LookupRow, LookupSpec } from "./types.mjs";
|
|
2
|
-
export {
|
|
2
|
+
export type { LookupPopulateOptions, LookupPopulateResult, LookupRow, LookupSpec };
|
package/dist/lookup/types.d.mts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { QueryParserOptions } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/query-parser/coerce.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Coerce a single URL value to its field-declared type, or to a best-guess
|
|
6
5
|
* scalar when no hint exists. Always returns `string`, `number`, `boolean`,
|
|
7
6
|
* `Date`, or `null` — never `undefined`.
|
|
8
7
|
*/
|
|
9
|
-
declare function coerceValue(rawValue: string, fieldType: QueryParserOptions['fieldTypes'] extends infer T ? T extends Record<string, infer V> ? V | undefined : undefined : undefined): unknown;
|
|
8
|
+
declare function coerceValue(rawValue: string, fieldType: QueryParserOptions['fieldTypes'] extends (infer T) ? T extends Record<string, infer V> ? V | undefined : undefined : undefined): unknown;
|
|
10
9
|
/**
|
|
11
10
|
* Split a comma-separated URL value into an array of coerced scalars.
|
|
12
11
|
* Used by `in`/`nin`/`between` which accept `field[in]=a,b,c`.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
//#region src/query-parser/coerce.ts
|
|
2
|
-
const BOOLEAN_STRINGS = new Set([
|
|
2
|
+
const BOOLEAN_STRINGS = /* @__PURE__ */ new Set([
|
|
3
3
|
"true",
|
|
4
4
|
"1",
|
|
5
5
|
"yes",
|
|
6
6
|
"on"
|
|
7
7
|
]);
|
|
8
|
-
const FALSEY_STRINGS = new Set([
|
|
8
|
+
const FALSEY_STRINGS = /* @__PURE__ */ new Set([
|
|
9
9
|
"false",
|
|
10
10
|
"0",
|
|
11
11
|
"no",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ParsedQuery, QueryParserInput, QueryParserOptions } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/query-parser/parse-url.d.ts
|
|
4
3
|
/** Parse URL search params into a driver-agnostic ParsedQuery. */
|
|
5
4
|
declare function parseUrl(input: QueryParserInput, options?: QueryParserOptions): ParsedQuery;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TRUE, and, between, contains, endsWith, eq, gt, gte, iEq,
|
|
1
|
+
import { TRUE, and, anyOf as in_, between, contains, endsWith, eq, gt, gte, iEq, isNotNull, isNull, like, lt, lte, ne, nin, regex, startsWith } from "../filter/builders.mjs";
|
|
2
2
|
import { coerceList, coerceValue } from "./coerce.mjs";
|
|
3
3
|
import { isControlParam } from "./reserved.mjs";
|
|
4
4
|
//#region src/query-parser/parse-url.ts
|
|
@@ -16,7 +16,7 @@ const DEFAULT_MAX_SEARCH = 200;
|
|
|
16
16
|
* approach this bound.
|
|
17
17
|
*/
|
|
18
18
|
const MAX_PARAM_KEY_LENGTH = 256;
|
|
19
|
-
const ALL_OPERATORS = new Set([
|
|
19
|
+
const ALL_OPERATORS = /* @__PURE__ */ new Set([
|
|
20
20
|
"eq",
|
|
21
21
|
"ne",
|
|
22
22
|
"gt",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* Pagination + sort + select + populate + search + resource-dispatch
|
|
24
24
|
* verbs (`_count`, `_distinct`, `_exists`).
|
|
25
25
|
*/
|
|
26
|
-
const STANDARD_RESERVED_PARAMS = new Set([
|
|
26
|
+
const STANDARD_RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
27
27
|
"page",
|
|
28
28
|
"limit",
|
|
29
29
|
"after",
|
|
@@ -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
|
-
|
|
8
7
|
//#region src/repository/base.d.ts
|
|
9
8
|
/** Construction options common to every kit. */
|
|
10
9
|
interface RepositoryBaseOptions {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
* options bag, and let kit plugins read what they implement. Unknown
|
|
51
51
|
* ctx keys do NOT forward — the bag stays narrow.
|
|
52
52
|
*/
|
|
53
|
-
declare const STANDARD_REPO_OPTION_KEYS: readonly [
|
|
53
|
+
declare const STANDARD_REPO_OPTION_KEYS: readonly ['organizationId', 'userId', 'user', 'session', 'requestId', 'traceId'];
|
|
54
54
|
/**
|
|
55
55
|
* Type-level union of canonical option keys. Use to constrain
|
|
56
56
|
* framework helpers that thread request context into repo options:
|
|
@@ -6,7 +6,6 @@ import { RetryPolicy } from "./resilience.mjs";
|
|
|
6
6
|
import { ArchiveOptions, ArchiveResult, ArchiveSink } from "./archive.mjs";
|
|
7
7
|
import { RepoCapabilities } from "./capabilities.mjs";
|
|
8
8
|
import { CacheOptions } from "../cache/options.mjs";
|
|
9
|
-
|
|
10
9
|
//#region src/repository/types.d.ts
|
|
11
10
|
/**
|
|
12
11
|
* Accepted filter input across every repository method. A repository
|
|
@@ -146,31 +145,33 @@ type TenantPurgeStrategy =
|
|
|
146
145
|
*/
|
|
147
146
|
{
|
|
148
147
|
type: 'hard';
|
|
149
|
-
}
|
|
148
|
+
} |
|
|
150
149
|
/**
|
|
151
150
|
* Mark every matching row as deleted via the soft-delete convention.
|
|
152
151
|
* Pair with `softDeletePlugin` or a TTL index for eventual cleanup.
|
|
153
152
|
*/
|
|
154
|
-
|
|
155
|
-
type: 'soft';
|
|
156
|
-
|
|
153
|
+
{
|
|
154
|
+
type: 'soft';
|
|
155
|
+
/** Boolean flag field set to `true`. Default `'deleted'`. */
|
|
156
|
+
deletedField?: string;
|
|
157
|
+
/** Timestamp field set to purge-time. Default `'deletedAt'`. */
|
|
157
158
|
deletedAtField?: string;
|
|
158
|
-
}
|
|
159
|
+
} |
|
|
159
160
|
/**
|
|
160
161
|
* Retain every matching row but overwrite the declared fields. Field
|
|
161
162
|
* values can be static (preferred — statically inspectable for audit)
|
|
162
163
|
* or per-row functions when deterministic transforms are needed
|
|
163
164
|
* (hashing, derived identifiers).
|
|
164
165
|
*/
|
|
165
|
-
|
|
166
|
+
{
|
|
166
167
|
type: 'anonymize';
|
|
167
168
|
fields: Record<string, unknown | ((doc: Record<string, unknown>) => unknown)>;
|
|
168
|
-
}
|
|
169
|
+
} |
|
|
169
170
|
/**
|
|
170
171
|
* Take no action. `reason` is required — an undocumented skip is a
|
|
171
172
|
* silent compliance leak. Surfaces in audit reports + introspection.
|
|
172
173
|
*/
|
|
173
|
-
|
|
174
|
+
{
|
|
174
175
|
type: 'skip';
|
|
175
176
|
reason: string;
|
|
176
177
|
};
|
|
@@ -827,7 +828,9 @@ type AggDateBucketUnit = 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter
|
|
|
827
828
|
* order without parsing the string back into a date.
|
|
828
829
|
*/
|
|
829
830
|
type AggDateBucketInterval = AggDateBucketUnit | {
|
|
830
|
-
/** Number of `unit`-sized chunks per bin. Must be a positive integer. */
|
|
831
|
+
/** Number of `unit`-sized chunks per bin. Must be a positive integer. */
|
|
832
|
+
every: number;
|
|
833
|
+
/** Base unit. `quarter` and `year` aren't supported in custom-bin form. */
|
|
831
834
|
unit: Exclude<AggDateBucketUnit, 'quarter' | 'year'>;
|
|
832
835
|
};
|
|
833
836
|
/**
|
|
@@ -1421,6 +1424,31 @@ interface StandardRepo<TDoc> extends MinimalRepo<TDoc> {
|
|
|
1421
1424
|
getOne?(filter: FilterInput, options?: QueryOptions): Promise<TDoc | null>;
|
|
1422
1425
|
/** Alias many kits expose alongside `getOne`. Arc checks both names. */
|
|
1423
1426
|
getByQuery?(filter: FilterInput, options?: QueryOptions): Promise<TDoc | null>;
|
|
1427
|
+
/**
|
|
1428
|
+
* Batch point-read — the `$in` / `IN (...)` counterpart to `getById`.
|
|
1429
|
+
* Fetches every doc whose primary key is in `ids` in ONE query and
|
|
1430
|
+
* returns a Map keyed by the STRINGIFIED id (`String(doc.id)`) for O(1)
|
|
1431
|
+
* lookup. The N+1 killer for callers resolving many ids in one tick
|
|
1432
|
+
* (order-line snapshotting, dashboard row enrichment, reference
|
|
1433
|
+
* expansion).
|
|
1434
|
+
*
|
|
1435
|
+
* **Contract:**
|
|
1436
|
+
* - Input ids are de-duplicated by the kit.
|
|
1437
|
+
* - Ids with no matching doc are simply ABSENT from the Map — never
|
|
1438
|
+
* throw on a miss (mirrors `getById`'s null-on-miss form).
|
|
1439
|
+
* - Structurally invalid ids (impossible for the backend's key type)
|
|
1440
|
+
* are treated as misses and dropped — one malformed id must never
|
|
1441
|
+
* fail the batch.
|
|
1442
|
+
* - Same policy surface as a single read: tenant scoping, soft-delete,
|
|
1443
|
+
* and hooks apply to the batch exactly as they would to `getById`.
|
|
1444
|
+
* - Empty input → empty Map, no driver round-trip.
|
|
1445
|
+
*
|
|
1446
|
+
* Optional: kits adopt incrementally. Callers feature-detect
|
|
1447
|
+
* (`repo.getByIds ?? per-id fallback`) until their kit floor ships it.
|
|
1448
|
+
* Sizing is backend-bound (e.g. Mongo's 16 MB query document); keep
|
|
1449
|
+
* batches ≤ ~10k ids and chunk beyond.
|
|
1450
|
+
*/
|
|
1451
|
+
getByIds?(ids: readonly string[], options?: FindAllOptions): Promise<Map<string, TDoc>>;
|
|
1424
1452
|
count?(filter?: FilterInput, options?: QueryOptions): Promise<number>;
|
|
1425
1453
|
exists?(filter: FilterInput, options?: QueryOptions): Promise<boolean | {
|
|
1426
1454
|
_id: unknown;
|
package/dist/schema/types.d.mts
CHANGED
|
@@ -149,9 +149,13 @@ interface SchemaBuilderOptions {
|
|
|
149
149
|
dateAs?: 'date' | 'datetime';
|
|
150
150
|
/** Create-schema overrides. */
|
|
151
151
|
create?: {
|
|
152
|
-
/** Fields to omit from the create body. */
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
/** Fields to omit from the create body. */
|
|
153
|
+
omitFields?: string[];
|
|
154
|
+
/** Force field to required (merged with auto-detected required). */
|
|
155
|
+
requiredOverrides?: Record<string, boolean>;
|
|
156
|
+
/** Force field to optional (even if DB-level required). */
|
|
157
|
+
optionalOverrides?: Record<string, boolean>;
|
|
158
|
+
/** Replace the generated schema for a specific field. */
|
|
155
159
|
schemaOverrides?: Record<string, unknown>;
|
|
156
160
|
};
|
|
157
161
|
/**
|
|
@@ -165,12 +169,15 @@ interface SchemaBuilderOptions {
|
|
|
165
169
|
softRequiredFields?: string[];
|
|
166
170
|
/** Update-schema overrides. */
|
|
167
171
|
update?: {
|
|
168
|
-
/** Fields to omit from the update body. */
|
|
172
|
+
/** Fields to omit from the update body. */
|
|
173
|
+
omitFields?: string[];
|
|
174
|
+
/** When `true`, reject empty update bodies (`minProperties: 1`). */
|
|
169
175
|
requireAtLeastOne?: boolean;
|
|
170
176
|
};
|
|
171
177
|
/** List-query schema overrides. */
|
|
172
178
|
query?: {
|
|
173
|
-
/** Extra filterable fields exposed on the list-query schema. */
|
|
179
|
+
/** Extra filterable fields exposed on the list-query schema. */
|
|
180
|
+
filterableFields?: Record<string, {
|
|
174
181
|
type: string;
|
|
175
182
|
} | unknown>;
|
|
176
183
|
};
|
|
@@ -185,7 +192,8 @@ interface SchemaBuilderOptions {
|
|
|
185
192
|
* scoring you want kept in update bodies but stripped from list reads).
|
|
186
193
|
*/
|
|
187
194
|
response?: {
|
|
188
|
-
/** Extra fields to omit from the response shape. */
|
|
195
|
+
/** Extra fields to omit from the response shape. */
|
|
196
|
+
omitFields?: string[];
|
|
189
197
|
};
|
|
190
198
|
/**
|
|
191
199
|
* Emit OpenAPI vendor extensions (`x-*` keywords like `x-ref` for populated
|
package/dist/sync/index.d.mts
CHANGED
|
@@ -68,7 +68,7 @@ interface ChangesSinceOptions {
|
|
|
68
68
|
readonly tenantId?: string;
|
|
69
69
|
}
|
|
70
70
|
interface ChangesPage<TDoc = unknown> {
|
|
71
|
-
readonly changes:
|
|
71
|
+
readonly changes: readonly ChangeEntry<TDoc>[];
|
|
72
72
|
/** Checkpoint AFTER applying this page — echo into the next `since`. */
|
|
73
73
|
readonly cursor: string;
|
|
74
74
|
/** True → more entries exist; pull again immediately. */
|
package/dist/sync/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
//#region src/sync/index.ts
|
|
2
2
|
/** Client checkpoint older than the store's compaction horizon → full resync. */
|
|
3
3
|
var CursorExpiredError = class extends Error {
|
|
4
|
+
cursor;
|
|
5
|
+
horizon;
|
|
4
6
|
constructor(cursor, horizon) {
|
|
5
7
|
super(`[repo-core:sync] cursor "${cursor}" predates the compaction horizon — full resync required.`);
|
|
6
8
|
this.cursor = cursor;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ResolvedTenantConfig, TenantConfig } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/tenant/resolve.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Sensible defaults for a freshly-built package (field strategy).
|
|
@@ -10,7 +9,7 @@ import { ResolvedTenantConfig, TenantConfig } from "./types.mjs";
|
|
|
10
9
|
* runtime default — `Pick<TenantConfig, 'fieldType'>` extension preserves
|
|
11
10
|
* type-level alignment without forcing a runtime default change.
|
|
12
11
|
*/
|
|
13
|
-
type TenantDefaults = { [K in 'strategy' | 'enabled' | 'tenantField' | 'fieldType' | 'ref' | 'contextKey' | 'required']-?: Exclude<TenantConfig[K], undefined
|
|
12
|
+
type TenantDefaults = { [K in 'strategy' | 'enabled' | 'tenantField' | 'fieldType' | 'ref' | 'contextKey' | 'required']-?: Exclude<TenantConfig[K], undefined>; };
|
|
14
13
|
declare const DEFAULT_TENANT_CONFIG: TenantDefaults;
|
|
15
14
|
declare function resolveTenantConfig(config?: TenantConfig | boolean): ResolvedTenantConfig;
|
|
16
15
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and, eq, gt,
|
|
1
|
+
import { and, anyOf as in_, eq, gt, isNull, like, ne, or } from "../filter/builders.mjs";
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
3
|
//#region src/testing/conformance.ts
|
|
4
4
|
/**
|
package/dist/testing/types.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AggregateOpsSupport, RepoCapabilities } from "../repository/capabilities.mjs";
|
|
2
2
|
import { MinimalRepo, StandardRepo } from "../repository/types.mjs";
|
|
3
|
-
|
|
4
3
|
//#region src/testing/types.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* The conformance scenarios operate on a minimal doc shape common to
|
|
@@ -114,4 +113,4 @@ interface ConformanceHarness<TDoc extends ConformanceDoc = ConformanceDoc> {
|
|
|
114
113
|
makeDoc(overrides?: Partial<ConformanceDoc>): Partial<TDoc>;
|
|
115
114
|
}
|
|
116
115
|
//#endregion
|
|
117
|
-
export { ConformanceContext, ConformanceDoc, ConformanceFeatures, ConformanceHarness };
|
|
116
|
+
export { type AggregateOpsSupport, ConformanceContext, ConformanceDoc, ConformanceFeatures, ConformanceHarness };
|
package/dist/update/guard.d.mts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/repo-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Driver-agnostic repository primitives: hooks, Filter IR, operations, pagination, cache contract. Foundation for mongokit, sqlitekit, pgkit, and prismakit. Lean by design — no plugins ship here; each kit owns its own.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -157,8 +157,8 @@
|
|
|
157
157
|
"fast-check": "^4.7.0",
|
|
158
158
|
"knip": "^6.3.0",
|
|
159
159
|
"publint": "^0.3.18",
|
|
160
|
-
"tsdown": "^0.
|
|
161
|
-
"typescript": "^
|
|
160
|
+
"tsdown": "^0.22.5",
|
|
161
|
+
"typescript": "^7.0.2",
|
|
162
162
|
"vitest": "^4.1.4"
|
|
163
163
|
},
|
|
164
164
|
"peerDependencies": {
|