@classytic/repo-core 0.8.1 → 0.10.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/adapter/types.d.mts +0 -1
  3. package/dist/adapter/widen.d.mts +0 -1
  4. package/dist/aggregate/normalize.d.mts +0 -1
  5. package/dist/better-auth/index.mjs +1 -1
  6. package/dist/cache/engine.d.mts +0 -1
  7. package/dist/cache/memory-adapter.d.mts +0 -1
  8. package/dist/cache/memory-adapter.mjs +13 -0
  9. package/dist/cache/plugin/context.mjs +1 -1
  10. package/dist/cache/plugin/index.d.mts +3 -4
  11. package/dist/cache/plugin/index.mjs +1 -1
  12. package/dist/cache/timeout-adapter.d.mts +0 -1
  13. package/dist/context/index.d.mts +1 -1
  14. package/dist/context/types.d.mts +0 -1
  15. package/dist/errors/contract.d.mts +4 -4
  16. package/dist/errors/contract.mjs +7 -4
  17. package/dist/errors/create-error.d.mts +0 -1
  18. package/dist/errors/duplicate-key.d.mts +0 -1
  19. package/dist/errors/schema.d.mts +27 -27
  20. package/dist/errors/types.d.mts +24 -11
  21. package/dist/events/emit.d.mts +0 -1
  22. package/dist/filter/builders.d.mts +2 -3
  23. package/dist/filter/builders.mjs +1 -1
  24. package/dist/filter/from-record.d.mts +0 -1
  25. package/dist/filter/from-record.mjs +2 -2
  26. package/dist/filter/guard.d.mts +0 -1
  27. package/dist/filter/guard.mjs +1 -1
  28. package/dist/filter/index.d.mts +1 -1
  29. package/dist/filter/index.mjs +1 -1
  30. package/dist/filter/match.d.mts +0 -1
  31. package/dist/filter/scope.d.mts +0 -1
  32. package/dist/filter/walk.d.mts +0 -1
  33. package/dist/hooks/engine.d.mts +0 -1
  34. package/dist/hooks/events.d.mts +51 -51
  35. package/dist/hooks/priority.d.mts +8 -4
  36. package/dist/hooks/priority.mjs +9 -0
  37. package/dist/lookup/index.d.mts +1 -1
  38. package/dist/lookup/types.d.mts +0 -1
  39. package/dist/operations/registry.d.mts +0 -1
  40. package/dist/pagination/canonical.d.mts +0 -1
  41. package/dist/pagination/cursor.d.mts +0 -1
  42. package/dist/pagination/keyset.d.mts +0 -1
  43. package/dist/pagination/offset.d.mts +0 -1
  44. package/dist/plugins/tenant-helpers.d.mts +0 -1
  45. package/dist/query-parser/coerce.d.mts +1 -2
  46. package/dist/query-parser/coerce.mjs +2 -2
  47. package/dist/query-parser/parse-url.d.mts +0 -1
  48. package/dist/query-parser/parse-url.mjs +2 -2
  49. package/dist/query-parser/reserved.mjs +1 -1
  50. package/dist/query-parser/types.d.mts +0 -1
  51. package/dist/repository/archive.d.mts +0 -1
  52. package/dist/repository/base.d.mts +0 -1
  53. package/dist/repository/distribution.d.mts +0 -1
  54. package/dist/repository/options.d.mts +1 -1
  55. package/dist/repository/plugin-types.d.mts +0 -1
  56. package/dist/repository/purge.d.mts +0 -1
  57. package/dist/repository/types.d.mts +38 -10
  58. package/dist/schema/field-rules.d.mts +0 -1
  59. package/dist/schema/field-rules.mjs +1 -1
  60. package/dist/schema/generator.d.mts +0 -1
  61. package/dist/schema/types.d.mts +14 -6
  62. package/dist/sync/index.d.mts +1 -1
  63. package/dist/sync/index.mjs +2 -0
  64. package/dist/tenant/resolve.d.mts +1 -2
  65. package/dist/testing/conformance.d.mts +0 -1
  66. package/dist/testing/conformance.mjs +1 -1
  67. package/dist/testing/lock-conformance.d.mts +0 -1
  68. package/dist/testing/types.d.mts +1 -2
  69. package/dist/update/builders.d.mts +0 -1
  70. package/dist/update/compile.d.mts +0 -1
  71. package/dist/update/guard.d.mts +0 -1
  72. package/package.json +3 -3
@@ -37,57 +37,57 @@
37
37
  * ```
38
38
  */
39
39
  declare const HOOK_EVENTS: {
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";
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. */readonly POLICY: 100;
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; /** Cache lookup / store — must run after policy so filters are in the key. */
22
- readonly CACHE: 200; /** Observabilityaudit logging, metrics, telemetry. Must not mutate context. */
23
- readonly OBSERVABILITY: 300; /** Default priority for user-registered hooks with no explicit priority. */
22
+ readonly VALIDATION: 150;
23
+ /** Cache lookup / storemust 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. */
@@ -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
@@ -1,2 +1,2 @@
1
1
  import { LookupPopulateOptions, LookupPopulateResult, LookupRow, LookupSpec } from "./types.mjs";
2
- export { type LookupPopulateOptions, type LookupPopulateResult, type LookupRow, type LookupSpec };
2
+ export type { LookupPopulateOptions, LookupPopulateResult, LookupRow, LookupSpec };
@@ -1,6 +1,5 @@
1
1
  import { Filter } from "../filter/types.mjs";
2
2
  import { KeysetPaginationResultCore, OffsetPaginationResultCore } from "../pagination/types.mjs";
3
-
4
3
  //#region src/lookup/types.d.ts
5
4
  /**
6
5
  * Single lookup join. Reads like a `LEFT JOIN from ON from.foreignField
@@ -1,5 +1,4 @@
1
1
  import { CoreRepositoryOperation, OperationDescriptor, OperationRegistry, PolicyKey, RepositoryOperation } from "./types.mjs";
2
-
3
2
  //#region src/operations/registry.d.ts
4
3
  /**
5
4
  * The core registry. Driver kits merge their own descriptors on top of this
@@ -1,5 +1,4 @@
1
1
  import { AnyPaginationResult, BareListResult, PaginatedResult } from "./types.mjs";
2
-
3
2
  //#region src/pagination/canonical.d.ts
4
3
  /**
5
4
  * Type guard: is this value a paginated result envelope (vs a bare array
@@ -1,5 +1,4 @@
1
1
  import { DecodedCursor, SortSpec } from "./types.mjs";
2
-
3
2
  //#region src/pagination/cursor.d.ts
4
3
  /**
5
4
  * Encode a document's sort values and id into a base64url cursor token.
@@ -1,5 +1,4 @@
1
1
  import { SortSpec } from "./types.mjs";
2
-
3
2
  //#region src/pagination/keyset.d.ts
4
3
  /**
5
4
  * Normalize a sort object so non-`_id` fields come first, `_id` last.
@@ -1,5 +1,4 @@
1
1
  import { PaginationConfig } from "./types.mjs";
2
-
3
2
  //#region src/pagination/offset.d.ts
4
3
  /**
5
4
  * Parse, clamp, and sanitize a `limit` value. Accepts string or number
@@ -1,5 +1,4 @@
1
1
  import { PolicyKey } from "../operations/types.mjs";
2
-
3
2
  //#region src/plugins/tenant-helpers.d.ts
4
3
  /**
5
4
  * Minimal context shape this module reads. Kits' richer
@@ -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, in_, isNotNull, isNull, like, lt, lte, ne, nin, regex, startsWith } from "../filter/builders.mjs";
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",
@@ -1,5 +1,4 @@
1
1
  import { Filter } from "../filter/types.mjs";
2
-
3
2
  //#region src/query-parser/types.d.ts
4
3
  /** Sort direction on a single field. */
5
4
  type ParsedSortDirection = 1 | -1;
@@ -1,5 +1,4 @@
1
1
  import { RetryPolicy } from "./resilience.mjs";
2
-
3
2
  //#region src/repository/archive.d.ts
4
3
  /**
5
4
  * Destination for archived documents — implemented by the HOST, not the
@@ -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 {
@@ -1,5 +1,4 @@
1
1
  import { FilterInput } from "./types.mjs";
2
-
3
2
  //#region src/repository/distribution.d.ts
4
3
  /** Per-repo declaration of how the underlying table/collection is distributed. */
5
4
  interface DistributionConfig {
@@ -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 ["organizationId", "userId", "user", "session", "requestId", "traceId"];
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:
@@ -1,5 +1,4 @@
1
1
  import { RepositoryBase } from "./base.mjs";
2
-
3
2
  //#region src/repository/plugin-types.d.ts
4
3
  /** Object-style plugin — preferred form. Carries a stable `name` for ordering checks. */
5
4
  interface Plugin<TRepo extends RepositoryBase = RepositoryBase> {
@@ -1,5 +1,4 @@
1
1
  import { TenantPurgeOptions, TenantPurgeResult, TenantPurgeStrategy } from "./types.mjs";
2
-
3
2
  //#region src/repository/purge.d.ts
4
3
  /**
5
4
  * Strategies that perform a write. `skip` is handled by the orchestrator
@@ -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'; /** Boolean flag field set to `true`. Default `'deleted'`. */
156
- deletedField?: string; /** Timestamp field set to purge-time. Default `'deletedAt'`. */
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. */every: number; /** Base unit. `quarter` and `year` aren't supported in custom-bin form. */
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;
@@ -1,5 +1,4 @@
1
1
  import { JsonSchema, SchemaBuilderOptions, ValidationResult } from "./types.mjs";
2
-
3
2
  //#region src/schema/field-rules.d.ts
4
3
  /**
5
4
  * Collect the set of fields that must NOT appear in a generated schema.
@@ -33,7 +33,7 @@ function collectFieldsToOmit(options, purpose) {
33
33
  if (explicit) for (const f of explicit) result.add(f);
34
34
  return result;
35
35
  }
36
- const result = new Set([
36
+ const result = /* @__PURE__ */ new Set([
37
37
  "createdAt",
38
38
  "updatedAt",
39
39
  "__v",
@@ -1,5 +1,4 @@
1
1
  import { CrudSchemas, SchemaBuilderOptions } from "./types.mjs";
2
-
3
2
  //#region src/schema/generator.d.ts
4
3
  /**
5
4
  * Resource-level context threaded into the generator at boot. Lets the
@@ -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. */omitFields?: string[]; /** Force field to required (merged with auto-detected required). */
153
- requiredOverrides?: Record<string, boolean>; /** Force field to optional (even if DB-level required). */
154
- optionalOverrides?: Record<string, boolean>; /** Replace the generated schema for a specific field. */
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. */omitFields?: string[]; /** When `true`, reject empty update bodies (`minProperties: 1`). */
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. */filterableFields?: Record<string, {
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. */omitFields?: string[];
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
@@ -68,7 +68,7 @@ interface ChangesSinceOptions {
68
68
  readonly tenantId?: string;
69
69
  }
70
70
  interface ChangesPage<TDoc = unknown> {
71
- readonly changes: ReadonlyArray<ChangeEntry<TDoc>>;
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. */
@@ -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,5 +1,4 @@
1
1
  import { ConformanceDoc, ConformanceHarness } from "./types.mjs";
2
-
3
2
  //#region src/testing/conformance.d.ts
4
3
  declare function runStandardRepoConformance<TDoc extends ConformanceDoc = ConformanceDoc>(harness: ConformanceHarness<TDoc>): void;
5
4
  //#endregion
@@ -1,4 +1,4 @@
1
- import { and, eq, gt, in_, isNull, like, ne, or } from "../filter/builders.mjs";
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
  /**
@@ -1,5 +1,4 @@
1
1
  import { LockAdapter } from "../lock/index.mjs";
2
-
3
2
  //#region src/testing/lock-conformance.d.ts
4
3
  interface LockConformanceHarness {
5
4
  /**
@@ -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 };
@@ -1,5 +1,4 @@
1
1
  import { UpdateSpec } from "./types.mjs";
2
-
3
2
  //#region src/update/builders.d.ts
4
3
  /**
5
4
  * Compose an `UpdateSpec` from the four primitive mutations.
@@ -1,5 +1,4 @@
1
1
  import { UpdateSpec } from "./types.mjs";
2
-
3
2
  //#region src/update/compile.d.ts
4
3
  /**
5
4
  * Compile an `UpdateSpec` to a Mongo operator record.
@@ -1,5 +1,4 @@
1
1
  import { UpdateSpec } from "./types.mjs";
2
-
3
2
  //#region src/update/guard.d.ts
4
3
  /**
5
4
  * True when `value` is an `UpdateSpec` — i.e. the portable, compile-to-native
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/repo-core",
3
- "version": "0.8.1",
3
+ "version": "0.10.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.21.8",
161
- "typescript": "^6.0.3",
160
+ "tsdown": "^0.22.5",
161
+ "typescript": "^7.0.2",
162
162
  "vitest": "^4.1.4"
163
163
  },
164
164
  "peerDependencies": {