@classytic/repo-core 0.3.0 → 0.4.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 (73) hide show
  1. package/CHANGELOG.md +243 -0
  2. package/dist/adapter/index.d.mts +3 -0
  3. package/dist/adapter/index.mjs +2 -0
  4. package/dist/adapter/types.d.mts +222 -0
  5. package/dist/adapter/widen.d.mts +22 -0
  6. package/dist/adapter/widen.mjs +26 -0
  7. package/dist/aggregate/index.d.mts +3 -0
  8. package/dist/aggregate/index.mjs +3 -0
  9. package/dist/aggregate/keyset.d.mts +57 -0
  10. package/dist/aggregate/keyset.mjs +45 -0
  11. package/dist/aggregate/normalize.d.mts +24 -0
  12. package/dist/aggregate/normalize.mjs +28 -0
  13. package/dist/better-auth/index.d.mts +110 -0
  14. package/dist/better-auth/index.mjs +71 -0
  15. package/dist/cache/engine.d.mts +127 -0
  16. package/dist/cache/engine.mjs +235 -0
  17. package/dist/cache/envelope.mjs +32 -0
  18. package/dist/cache/index.d.mts +7 -2
  19. package/dist/cache/index.mjs +6 -2
  20. package/dist/cache/keys.mjs +131 -0
  21. package/dist/cache/memory-adapter.mjs +41 -7
  22. package/dist/cache/options.d.mts +112 -0
  23. package/dist/cache/options.mjs +25 -0
  24. package/dist/cache/plugin/context.d.mts +18 -0
  25. package/dist/cache/plugin/context.mjs +121 -0
  26. package/dist/cache/plugin/index.d.mts +86 -0
  27. package/dist/cache/plugin/index.mjs +78 -0
  28. package/dist/cache/plugin/invalidation-hooks.mjs +35 -0
  29. package/dist/cache/plugin/read-hooks.mjs +96 -0
  30. package/dist/cache/plugin/swr.mjs +20 -0
  31. package/dist/cache/runtime.d.mts +43 -0
  32. package/dist/cache/runtime.mjs +14 -0
  33. package/dist/cache/tag-index.mjs +84 -0
  34. package/dist/cache/timeout-adapter.d.mts +30 -0
  35. package/dist/cache/timeout-adapter.mjs +58 -0
  36. package/dist/cache/types.d.mts +45 -0
  37. package/dist/cache/version-store.mjs +57 -0
  38. package/dist/errors/index.d.mts +2 -1
  39. package/dist/errors/index.mjs +2 -1
  40. package/dist/errors/schema.d.mts +101 -0
  41. package/dist/errors/schema.mjs +78 -0
  42. package/dist/filter/match.mjs +38 -2
  43. package/dist/pagination/canonical.d.mts +8 -8
  44. package/dist/pagination/canonical.mjs +3 -9
  45. package/dist/pagination/cursor.mjs +4 -1
  46. package/dist/pagination/index.d.mts +2 -2
  47. package/dist/pagination/types.d.mts +17 -27
  48. package/dist/plugins/index.d.mts +2 -0
  49. package/dist/plugins/index.mjs +2 -0
  50. package/dist/plugins/tenant-helpers.d.mts +63 -0
  51. package/dist/plugins/tenant-helpers.mjs +84 -0
  52. package/dist/query-parser/index.d.mts +2 -1
  53. package/dist/query-parser/index.mjs +2 -1
  54. package/dist/query-parser/parse-url.mjs +13 -11
  55. package/dist/query-parser/reserved.d.mts +43 -0
  56. package/dist/query-parser/reserved.mjs +56 -0
  57. package/dist/repository/agg-output.d.mts +63 -0
  58. package/dist/repository/agg-output.mjs +89 -0
  59. package/dist/repository/index.d.mts +4 -2
  60. package/dist/repository/index.mjs +3 -1
  61. package/dist/repository/options.d.mts +62 -0
  62. package/dist/repository/options.mjs +57 -0
  63. package/dist/repository/types.d.mts +935 -48
  64. package/dist/schema/field-rules.d.mts +41 -1
  65. package/dist/schema/field-rules.mjs +92 -1
  66. package/dist/schema/index.d.mts +2 -2
  67. package/dist/schema/index.mjs +2 -2
  68. package/dist/schema/types.d.mts +21 -0
  69. package/dist/testing/conformance.mjs +666 -17
  70. package/dist/testing/index.d.mts +2 -2
  71. package/dist/testing/types.d.mts +99 -2
  72. package/package.json +19 -1
  73. package/dist/cache/stable-stringify.d.mts +0 -15
@@ -1,3 +1,3 @@
1
- import { ConformanceContext, ConformanceDoc, ConformanceFeatures, ConformanceHarness } from "./types.mjs";
1
+ import { AggregateOpsSupport, ConformanceContext, ConformanceDoc, ConformanceFeatures, ConformanceHarness } from "./types.mjs";
2
2
  import { runStandardRepoConformance } from "./conformance.mjs";
3
- export { type ConformanceContext, type ConformanceDoc, type ConformanceFeatures, type ConformanceHarness, runStandardRepoConformance };
3
+ export { type AggregateOpsSupport, type ConformanceContext, type ConformanceDoc, type ConformanceFeatures, type ConformanceHarness, runStandardRepoConformance };
@@ -26,6 +26,70 @@ interface ConformanceDoc {
26
26
  notes: string | null;
27
27
  createdAt: string;
28
28
  }
29
+ /**
30
+ * Per-aggregate-op support matrix. Some aggregate ops aren't
31
+ * portable across every backend — `percentile` requires Mongo 7+'s
32
+ * `$percentile` accumulator or SQL's `PERCENTILE_CONT`, neither of
33
+ * which sqlitekit ships. Scenarios that exercise a non-universal
34
+ * op gate on the matching flag and `it.skip` on the off branch so
35
+ * the suite runs cleanly across every environment.
36
+ *
37
+ * **Stability contract.** Adding a flag here is additive — kits
38
+ * that don't declare the new key default to `false`, which is the
39
+ * conservative choice. Renaming or removing a flag is a breaking
40
+ * change.
41
+ *
42
+ * **Naming convention.** Flag names match the IR field they gate
43
+ * (`percentile` → `AggMeasure.op === 'percentile'`). When in doubt,
44
+ * grep the IR types and use the same identifier.
45
+ */
46
+ interface AggregateOpsSupport {
47
+ /**
48
+ * `{ op: 'percentile', field, p }` measure. Mongokit (Mongo 7+)
49
+ * supports it; sqlitekit throws by design (no native function).
50
+ * Hosts targeting percentile dashboards pin to a kit that supports it.
51
+ */
52
+ percentile?: boolean;
53
+ /**
54
+ * `{ op: 'stddev', field }` / `{ op: 'stddevPop', field }` measures.
55
+ * Mongokit supports both via native `$stdDevSamp` / `$stdDevPop`
56
+ * (Welford). Sqlitekit throws — SQLite has no native STDDEV and
57
+ * the computational formula is numerically unstable. Hosts pin
58
+ * to mongokit / future pgkit when stddev is load-bearing.
59
+ */
60
+ stddev?: boolean;
61
+ /**
62
+ * `topN: { partitionBy, sortBy, limit, ties }` filter. Both
63
+ * mongokit and sqlitekit support it as of repo-core 0.4.x; the
64
+ * flag exists for future kits that may not ship window-function
65
+ * equivalents.
66
+ */
67
+ topN?: boolean;
68
+ /**
69
+ * `dateBuckets: { ..., interval: { every, unit } }` custom-bin
70
+ * form. Kits that only support named-bucket form can leave this
71
+ * `false`; tests for `'minute'` / `'hour'` named intervals are
72
+ * gated separately via `dateBucketSubMinute`.
73
+ */
74
+ customDateBuckets?: boolean;
75
+ /**
76
+ * Sub-day-granularity named buckets (`'minute'` / `'hour'`).
77
+ * Older kits may only support day+ named intervals; flag exists
78
+ * to gate those scenarios cleanly.
79
+ */
80
+ dateBucketSubMinute?: boolean;
81
+ /**
82
+ * Per-request `cache?: AggCacheOptions` slot — TTL / tags / SWR /
83
+ * bypass / `repo.invalidateAggregateCache(tags)`. Both mongokit
84
+ * and sqlitekit support it as of repo-core 0.4.x. Future kits
85
+ * without the wiring can leave this false to skip cache scenarios.
86
+ *
87
+ * Independent of which CACHE BACKEND the harness wires — test
88
+ * scenarios construct their own `createMemoryCacheAdapter()` so
89
+ * this flag is purely "does the kit honour the request slot".
90
+ */
91
+ cache?: boolean;
92
+ }
29
93
  /**
30
94
  * Per-backend feature flags. Scenarios that exercise a non-universal
31
95
  * capability (transactions in D1, upsert in narrow stores) check the
@@ -48,8 +112,22 @@ interface ConformanceFeatures {
48
112
  duplicateKeyError: boolean;
49
113
  /** `distinct(field)`. */
50
114
  distinct: boolean;
51
- /** Portable `aggregate({ measures, groupBy, having })`. */
115
+ /**
116
+ * Portable `aggregate({ measures, groupBy, having })`. Coarse
117
+ * top-level flag — gates the entire `describe('aggregate')` block.
118
+ * Per-op flags live on `aggregateOps` for asymmetric capabilities
119
+ * (percentile, custom date bins, etc.) that some kits skip while
120
+ * still supporting the core aggregate surface.
121
+ */
52
122
  aggregate: boolean;
123
+ /**
124
+ * Per-op feature matrix for the aggregate surface. Optional —
125
+ * absent matrix or absent key both mean "not supported", so kits
126
+ * opt INTO scenarios for ops they implement. This avoids the
127
+ * trap where a future kit silently fails percentile tests because
128
+ * it forgot to set the flag.
129
+ */
130
+ aggregateOps?: AggregateOpsSupport;
53
131
  /** `getOrCreate(filter, data)`. */
54
132
  getOrCreate: boolean;
55
133
  /** `count(filter)` and `exists(filter)`. */
@@ -69,6 +147,25 @@ interface ConformanceContext<TDoc extends ConformanceDoc = ConformanceDoc> {
69
147
  * be omitted; scenarios that need it will skip if missing.
70
148
  */
71
149
  secondaryRepo?: StandardRepo<TDoc> & MinimalRepo<TDoc>;
150
+ /**
151
+ * Optional repo-with-cache for cache-suite scenarios. Required iff
152
+ * the harness declares `features.aggregateOps.cache === true`.
153
+ *
154
+ * **Why a separate repo** rather than reconfiguring the primary
155
+ * one: cache state survives across calls within a test, and the
156
+ * primary repo is shared by every other suite. A dedicated cached
157
+ * repo keeps the cache scenarios hermetic.
158
+ *
159
+ * The bound type widens to include `invalidateAggregateCache` —
160
+ * cast at the use site since the contract type
161
+ * (`StandardRepo<TDoc>`) doesn't currently surface it. This
162
+ * intentionally leaves the contract narrower than the
163
+ * implementation; consumers pin to a specific kit when they need
164
+ * the invalidate method on their `RepositoryLike` slot.
165
+ */
166
+ cachedRepo?: StandardRepo<TDoc> & MinimalRepo<TDoc> & {
167
+ invalidateAggregateCache(tags?: readonly string[]): Promise<number>;
168
+ };
72
169
  /** Release resources (close db, drop collection). Must be idempotent. */
73
170
  cleanup(): Promise<void>;
74
171
  }
@@ -110,4 +207,4 @@ interface ConformanceHarness<TDoc extends ConformanceDoc = ConformanceDoc> {
110
207
  makeDoc(overrides?: Partial<ConformanceDoc>): Partial<TDoc>;
111
208
  }
112
209
  //#endregion
113
- export { ConformanceContext, ConformanceDoc, ConformanceFeatures, ConformanceHarness };
210
+ export { AggregateOpsSupport, ConformanceContext, ConformanceDoc, ConformanceFeatures, ConformanceHarness };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/repo-core",
3
- "version": "0.3.0",
3
+ "version": "0.4.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,
@@ -70,6 +70,22 @@
70
70
  "types": "./dist/lookup/index.d.mts",
71
71
  "default": "./dist/lookup/index.mjs"
72
72
  },
73
+ "./adapter": {
74
+ "types": "./dist/adapter/index.d.mts",
75
+ "default": "./dist/adapter/index.mjs"
76
+ },
77
+ "./better-auth": {
78
+ "types": "./dist/better-auth/index.d.mts",
79
+ "default": "./dist/better-auth/index.mjs"
80
+ },
81
+ "./aggregate": {
82
+ "types": "./dist/aggregate/index.d.mts",
83
+ "default": "./dist/aggregate/index.mjs"
84
+ },
85
+ "./plugins": {
86
+ "types": "./dist/plugins/index.d.mts",
87
+ "default": "./dist/plugins/index.mjs"
88
+ },
73
89
  "./package.json": "./package.json"
74
90
  },
75
91
  "keywords": [
@@ -105,6 +121,7 @@
105
121
  "test:e2e": "vitest run --project e2e",
106
122
  "test:all": "vitest run",
107
123
  "test:watch": "vitest --project unit --project integration",
124
+ "bench": "vitest bench --run --project bench",
108
125
  "test:coverage": "vitest run --coverage",
109
126
  "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
110
127
  "lint": "biome check src tests",
@@ -125,6 +142,7 @@
125
142
  "@classytic/dev-tools": "^0.2.0",
126
143
  "@types/node": "^22.0.0",
127
144
  "@vitest/coverage-v8": "^4.1.4",
145
+ "fast-check": "^4.7.0",
128
146
  "knip": "^6.3.0",
129
147
  "publint": "^0.3.18",
130
148
  "tsdown": "^0.21.8",
@@ -1,15 +0,0 @@
1
- //#region src/cache/stable-stringify.d.ts
2
- /**
3
- * Deterministic JSON stringify — equivalent values produce identical output.
4
- *
5
- * Used by kits that build cache keys from hook contexts so
6
- * `{ b: 1, a: 2 }` and `{ a: 2, b: 1 }` hash to the same bucket. Arrays
7
- * preserve order (order is part of array identity).
8
- *
9
- * Extracted from the mongokit / sqlitekit cache plugins into repo-core so
10
- * every kit's cachePlugin uses the same keying rule — cross-kit caches
11
- * remain bucket-compatible.
12
- */
13
- declare function stableStringify(value: unknown): string;
14
- //#endregion
15
- export { stableStringify };