@classytic/repo-core 0.9.0 → 0.11.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 CHANGED
@@ -4,6 +4,34 @@ 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.11.0] - 2026-07-15
8
+
9
+ ### Added — `./usage` subpath (period-bucketed counter contract)
10
+
11
+ - **`UsageStore`** — driver-agnostic interface for atomic period-bucketed counters: `increment(bucket, amount)` + `summary(actor, period)`. The storage seam under platform accounting (quotas, plan enforcement, usage-based billing). One cell = `(actor, period, kind)`; one write = atomic upsert; one read = all counters for an actor-period pair. Kits ship adapters (`@classytic/mongokit/usage`, `@classytic/sqlitekit/usage`, …) without depending on arc; `@classytic/arc/usage` consumes this contract structurally.
12
+ - **`UsageBucket`** — `{ actor, period, kind }` tuple. `kind` is dot-namespaced: `api.requests`, `ai.tokens.input`, `storage.egress.bytes`.
13
+ - **`usagePeriod(date?)`** — canonical UTC calendar-month key (`"2026-07"`). Monthly is the billing-native granularity.
14
+ - **`createMemoryUsageStore()`** — in-process reference implementation for tests and single-instance apps. Returns `UsageStore & { clear() }`. Multi-replica deployments need a shared adapter.
15
+
16
+ ### Added — `runUsageStoreContract` in `./testing`
17
+
18
+ - **`runUsageStoreContract(harness)`** — cross-kit conformance suite. Kits import once and pass their adapter; all canonical scenarios run automatically. Same pattern as `runLockAdapterConformance`. `UsageConformanceHarness` is exported from `./testing` for harness typing.
19
+
20
+ ## [0.10.0] - 2026-07-13
21
+
22
+ ### Added — `ValidationErrorMeta.path` + `.meta` (field-scoped validation errors)
23
+
24
+ - **`ValidationErrorMeta.path?: string`** — dot-path to the offending field
25
+ (e.g. `'journalItems.2.account'`). Set by kits with field-scoped validation
26
+ (ledger's `FieldError`, Mongoose `ValidationError`). Absent when the kit doesn't
27
+ have a field path.
28
+ - **`ValidationErrorMeta.meta?: Readonly<Record<string, unknown>>`** — non-PII
29
+ structured extra for the field (e.g. `{ value: 'bad' }`). Never include secrets.
30
+ - **`toErrorContract`** now forwards `path` and `meta` from each `validationErrors`
31
+ entry onto the wire `ErrorDetail`. Previously only `code` and `message` were mapped;
32
+ field paths were silently dropped. No breaking change — both fields are optional and
33
+ additive. Kits that don't set them produce the same wire shape as before.
34
+
7
35
  ## [0.9.0] - 2026-07-11
8
36
 
9
37
  ### Added — `StandardRepo.getByIds` (batch point-read)
@@ -23,9 +23,10 @@ declare function statusToErrorCode(status: number): ErrorCode;
23
23
  * `validationErrors` (mongokit-shaped throwable field) is mapped into the
24
24
  * canonical `details` array so wire consumers see one shape regardless
25
25
  * of which kit threw the error. Each `validationErrors[i]` becomes an
26
- * `ErrorDetail` with `code: validator`, `message: error`, `path` left
27
- * unset (kits that have field paths set them in their own ErrorDetail
28
- * mapping).
26
+ * `ErrorDetail` with `code: validator`, `message: error`, plus `path` and
27
+ * `meta` when the throwing kit populates them (field-scoped validators
28
+ * like ledger's `FieldError` — so a kernel's rich field errors reach the
29
+ * wire natively, with NO per-host errorMapper).
29
30
  *
30
31
  * `duplicate.fields` is similarly flattened into `details` with the
31
32
  * duplicate-key code so unique-constraint failures look uniform on the
@@ -36,9 +36,10 @@ function statusToErrorCode(status) {
36
36
  * `validationErrors` (mongokit-shaped throwable field) is mapped into the
37
37
  * canonical `details` array so wire consumers see one shape regardless
38
38
  * of which kit threw the error. Each `validationErrors[i]` becomes an
39
- * `ErrorDetail` with `code: validator`, `message: error`, `path` left
40
- * unset (kits that have field paths set them in their own ErrorDetail
41
- * mapping).
39
+ * `ErrorDetail` with `code: validator`, `message: error`, plus `path` and
40
+ * `meta` when the throwing kit populates them (field-scoped validators
41
+ * like ledger's `FieldError` — so a kernel's rich field errors reach the
42
+ * wire natively, with NO per-host errorMapper).
42
43
  *
43
44
  * `duplicate.fields` is similarly flattened into `details` with the
44
45
  * duplicate-key code so unique-constraint failures look uniform on the
@@ -59,8 +60,10 @@ function toErrorContract(error) {
59
60
  };
60
61
  const details = [];
61
62
  if (Array.isArray(e.validationErrors)) for (const v of e.validationErrors) details.push({
63
+ ...v.path ? { path: v.path } : {},
62
64
  code: v.validator,
63
- message: v.error
65
+ message: v.error,
66
+ ...v.meta ? { meta: v.meta } : {}
64
67
  });
65
68
  if (e.duplicate?.fields?.length) for (const field of e.duplicate.fields) details.push({
66
69
  path: field,
@@ -39,6 +39,19 @@ interface DuplicateKeyMeta {
39
39
  interface ValidationErrorMeta {
40
40
  validator: string;
41
41
  error: string;
42
+ /**
43
+ * Dot-path to the offending field (e.g. `'journalItems.2.account'`).
44
+ * Optional — kits with field-scoped validation (ledger's `FieldError`,
45
+ * mongoose ValidationError) set it so it flows onto the wire
46
+ * {@link ErrorDetail.path} via {@link toErrorContract} instead of being
47
+ * dropped. Kits without a path leave it unset.
48
+ */
49
+ path?: string;
50
+ /**
51
+ * Non-PII structured extra for this field (e.g. the offending value under
52
+ * `{ value }`). Flows onto {@link ErrorDetail.meta}. Never include secrets.
53
+ */
54
+ meta?: Readonly<Record<string, unknown>>;
42
55
  }
43
56
  /**
44
57
  * HTTP-shaped error — the throwable envelope every repository error and
@@ -2,4 +2,5 @@ import { AggregateOpsSupport } from "../repository/capabilities.mjs";
2
2
  import { ConformanceContext, ConformanceDoc, ConformanceFeatures, ConformanceHarness } from "./types.mjs";
3
3
  import { runStandardRepoConformance } from "./conformance.mjs";
4
4
  import { LockConformanceHarness, runLockAdapterConformance } from "./lock-conformance.mjs";
5
- export { type AggregateOpsSupport, type ConformanceContext, type ConformanceDoc, type ConformanceFeatures, type ConformanceHarness, type LockConformanceHarness, runLockAdapterConformance, runStandardRepoConformance };
5
+ import { UsageConformanceHarness, runUsageStoreContract } from "./usage-conformance.mjs";
6
+ export { type AggregateOpsSupport, type ConformanceContext, type ConformanceDoc, type ConformanceFeatures, type ConformanceHarness, type LockConformanceHarness, type UsageConformanceHarness, runLockAdapterConformance, runStandardRepoConformance, runUsageStoreContract };
@@ -1,3 +1,4 @@
1
1
  import { runStandardRepoConformance } from "./conformance.mjs";
2
2
  import { runLockAdapterConformance } from "./lock-conformance.mjs";
3
- export { runLockAdapterConformance, runStandardRepoConformance };
3
+ import { runUsageStoreContract } from "./usage-conformance.mjs";
4
+ export { runLockAdapterConformance, runStandardRepoConformance, runUsageStoreContract };
@@ -0,0 +1,18 @@
1
+ import { UsageStore } from "../usage/index.mjs";
2
+ //#region src/testing/usage-conformance.d.ts
3
+ interface UsageConformanceHarness {
4
+ /**
5
+ * Construct the store under test. May be async (SQL migrations,
6
+ * index creation). The same instance is shared by every test —
7
+ * `beforeEach` clears residual counters.
8
+ */
9
+ createStore(): UsageStore | Promise<UsageStore>;
10
+ /**
11
+ * Wipe every counter between tests. Mongo: `deleteMany({})`.
12
+ * SQL: `DELETE FROM kit_usage`. Memory: `clear()`.
13
+ */
14
+ beforeEach?(store: UsageStore): void | Promise<void>;
15
+ }
16
+ declare function runUsageStoreContract(harness: UsageConformanceHarness): void;
17
+ //#endregion
18
+ export { UsageConformanceHarness, runUsageStoreContract };
@@ -0,0 +1,101 @@
1
+ import { beforeEach, describe, expect, it } from "vitest";
2
+ //#region src/testing/usage-conformance.ts
3
+ /**
4
+ * `runUsageStoreContract` — cross-kit usage-store contract suite.
5
+ *
6
+ * Wires a kit-specific harness to the canonical scenarios every
7
+ * `UsageStore` implementation must pass — so "swap mongokit/usage for
8
+ * sqlitekit/usage" is a provable claim and drift shows up here before
9
+ * it ships. Written ONCE here; kits import it instead of hand-writing
10
+ * conformance (same shape as `runLockAdapterConformance`).
11
+ *
12
+ * ## Usage from a kit
13
+ *
14
+ * import { runUsageStoreContract } from '@classytic/repo-core/testing';
15
+ * import { createMongoUsageStore } from '../../src/usage/index.js';
16
+ *
17
+ * describe('mongokit/usage conformance', () => {
18
+ * runUsageStoreContract({
19
+ * createStore: () => createMongoUsageStore({ connection }),
20
+ * async beforeEach() { await clearCounters(); },
21
+ * });
22
+ * });
23
+ */
24
+ function runUsageStoreContract(harness) {
25
+ describe("UsageStore contract", () => {
26
+ let store;
27
+ beforeEach(async () => {
28
+ store = await harness.createStore();
29
+ await harness.beforeEach?.(store);
30
+ });
31
+ it("accumulates increments per (actor, period, kind)", async () => {
32
+ const bucket = {
33
+ actor: "org-1",
34
+ period: "2026-07",
35
+ kind: "api.requests"
36
+ };
37
+ await store.increment(bucket, 1);
38
+ await store.increment(bucket, 2);
39
+ await store.increment({
40
+ ...bucket,
41
+ kind: "ai.tokens.input"
42
+ }, 500);
43
+ expect(await store.summary("org-1", "2026-07")).toEqual({
44
+ "api.requests": 3,
45
+ "ai.tokens.input": 500
46
+ });
47
+ });
48
+ it("treats a missing bucket as 0 (first increment creates it)", async () => {
49
+ await store.increment({
50
+ actor: "a",
51
+ period: "2026-07",
52
+ kind: "k"
53
+ }, 7);
54
+ expect(await store.summary("a", "2026-07")).toEqual({ k: 7 });
55
+ });
56
+ it("isolates actors and periods", async () => {
57
+ await store.increment({
58
+ actor: "org-1",
59
+ period: "2026-06",
60
+ kind: "k"
61
+ }, 5);
62
+ await store.increment({
63
+ actor: "org-1",
64
+ period: "2026-07",
65
+ kind: "k"
66
+ }, 7);
67
+ await store.increment({
68
+ actor: "org-2",
69
+ period: "2026-07",
70
+ kind: "k"
71
+ }, 11);
72
+ expect(await store.summary("org-1", "2026-06")).toEqual({ k: 5 });
73
+ expect(await store.summary("org-1", "2026-07")).toEqual({ k: 7 });
74
+ expect(await store.summary("org-2", "2026-07")).toEqual({ k: 11 });
75
+ });
76
+ it("dotted kind names round-trip exactly (no path nesting)", async () => {
77
+ await store.increment({
78
+ actor: "a",
79
+ period: "2026-07",
80
+ kind: "storage.egress.bytes"
81
+ }, 42);
82
+ const summary = await store.summary("a", "2026-07");
83
+ expect(summary["storage.egress.bytes"]).toBe(42);
84
+ expect(Object.keys(summary)).toEqual(["storage.egress.bytes"]);
85
+ });
86
+ it("returns {} for unknown actors/periods (never throws)", async () => {
87
+ expect(await store.summary("nobody", "2099-01")).toEqual({});
88
+ });
89
+ it("increment is atomic per bucket under concurrency", async () => {
90
+ const bucket = {
91
+ actor: "org-c",
92
+ period: "2026-07",
93
+ kind: "api.requests"
94
+ };
95
+ await Promise.all(Array.from({ length: 50 }, () => store.increment(bucket, 1)));
96
+ expect((await store.summary("org-c", "2026-07"))["api.requests"]).toBe(50);
97
+ });
98
+ });
99
+ }
100
+ //#endregion
101
+ export { runUsageStoreContract };
@@ -0,0 +1,77 @@
1
+ //#region src/usage/index.d.ts
2
+ /**
3
+ * Usage-counter contract for the @classytic ecosystem.
4
+ *
5
+ * Period-bucketed counters per actor — the storage seam under
6
+ * platform accounting (quotas, plan enforcement, usage-based
7
+ * billing). One cell = `(actor, period, kind)`; one write op =
8
+ * atomic increment; one read op = all counters for an actor-period.
9
+ *
10
+ * ## Why this lives in repo-core
11
+ *
12
+ * Same reasoning as `./lock`: the contract is driver-free — any
13
+ * store with an atomic increment-upsert implements it (Mongo `$inc`
14
+ * upsert, SQL `ON CONFLICT ... DO UPDATE SET n = n + ?`, Redis
15
+ * `HINCRBY`). Kits ship their adapters (`@classytic/mongokit/usage`,
16
+ * `@classytic/sqlitekit/usage`, ...) WITHOUT depending on arc;
17
+ * `@classytic/arc/usage`'s `usagePlugin` consumes the contract
18
+ * structurally (its local `UsageStore` mirrors this shape the same
19
+ * way its `ScheduleLockLike` mirrors `LockAdapter`) so arc's
20
+ * repo-core peer floor never bumps for it.
21
+ *
22
+ * Distinct from itemized event/usage RECORD stores (e.g.
23
+ * `@classytic/arc-ai/usage`'s per-run records): this contract holds
24
+ * AGGREGATES only. Itemized layers sink into it; they don't replace it.
25
+ *
26
+ * ## Semantics
27
+ *
28
+ * - `increment` MUST be atomic per bucket (concurrent writers never
29
+ * lose counts) and MUST treat a missing bucket as `0`.
30
+ * - `summary` returns `{}` (never throws) for unknown actors/periods.
31
+ * - `period` keys are opaque strings to the store; `usagePeriod()`
32
+ * is the ecosystem's canonical key (UTC calendar month, `2026-07`).
33
+ * Stores wanting finer windows shard internally without changing
34
+ * the contract.
35
+ * - Sync-or-async: memory adapter is sync; DB adapters async;
36
+ * consumers `await` either way.
37
+ *
38
+ * ## Why one file, not a barrel
39
+ *
40
+ * Contract + reference memory adapter + the period helper are under
41
+ * 100 LOC with no internal seams — same single-file rule as `./lock`.
42
+ */
43
+ /** One counter cell: (actor, period, kind). */
44
+ interface UsageBucket {
45
+ /** Who consumed — org / user / client id, or a host-chosen fallback key. */
46
+ actor: string;
47
+ /** Aggregation period key — canonically `usagePeriod()`'s `YYYY-MM`. */
48
+ period: string;
49
+ /** Namespaced counter, dot-separated: `api.requests`, `ai.tokens.input`, `storage.egress.bytes`. */
50
+ kind: string;
51
+ }
52
+ /** Period-bucketed usage counters. See module header for semantics. */
53
+ interface UsageStore {
54
+ /** Store name for diagnostics (e.g. 'memory', 'mongo', 'redis'). */
55
+ readonly name: string;
56
+ /** Atomically add `amount` to the bucket's counter, creating it at 0. */
57
+ increment(bucket: UsageBucket, amount: number): Promise<void> | void;
58
+ /** Every counter for an actor in a period: `{ 'api.requests': 40231, ... }`. */
59
+ summary(actor: string, period: string): Promise<Record<string, number>>;
60
+ /** Optional cleanup hook (connections, timers). */
61
+ close?(): Promise<void>;
62
+ }
63
+ /**
64
+ * Canonical period key for a date — calendar month, UTC: `2026-07`.
65
+ * Monthly is the billing-native granularity.
66
+ */
67
+ declare function usagePeriod(date?: Date): string;
68
+ /**
69
+ * In-memory reference implementation — tests and single-instance
70
+ * apps. Counters are per-process; multi-replica deployments need a
71
+ * shared adapter (kit- or Redis-backed).
72
+ */
73
+ declare function createMemoryUsageStore(): UsageStore & {
74
+ clear(): void;
75
+ };
76
+ //#endregion
77
+ export { UsageBucket, UsageStore, createMemoryUsageStore, usagePeriod };
@@ -0,0 +1,36 @@
1
+ //#region src/usage/index.ts
2
+ /**
3
+ * Canonical period key for a date — calendar month, UTC: `2026-07`.
4
+ * Monthly is the billing-native granularity.
5
+ */
6
+ function usagePeriod(date = /* @__PURE__ */ new Date()) {
7
+ return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}`;
8
+ }
9
+ /**
10
+ * In-memory reference implementation — tests and single-instance
11
+ * apps. Counters are per-process; multi-replica deployments need a
12
+ * shared adapter (kit- or Redis-backed).
13
+ */
14
+ function createMemoryUsageStore() {
15
+ /** actor → period → kind → count */
16
+ const counters = /* @__PURE__ */ new Map();
17
+ return {
18
+ name: "memory",
19
+ increment(bucket, amount) {
20
+ const periods = counters.get(bucket.actor) ?? /* @__PURE__ */ new Map();
21
+ const kinds = periods.get(bucket.period) ?? /* @__PURE__ */ new Map();
22
+ kinds.set(bucket.kind, (kinds.get(bucket.kind) ?? 0) + amount);
23
+ periods.set(bucket.period, kinds);
24
+ counters.set(bucket.actor, periods);
25
+ },
26
+ async summary(actor, period) {
27
+ const kinds = counters.get(actor)?.get(period);
28
+ return kinds ? Object.fromEntries(kinds) : {};
29
+ },
30
+ clear() {
31
+ counters.clear();
32
+ }
33
+ };
34
+ }
35
+ //#endregion
36
+ export { createMemoryUsageStore, usagePeriod };
package/package.json CHANGED
@@ -1,172 +1,176 @@
1
- {
2
- "name": "@classytic/repo-core",
3
- "version": "0.9.0",
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
- "type": "module",
6
- "sideEffects": false,
7
- "files": [
8
- "dist",
9
- "README.md",
10
- "LICENSE",
11
- "CHANGELOG.md"
12
- ],
13
- "engines": {
14
- "node": ">=22"
15
- },
16
- "exports": {
17
- "./hooks": {
18
- "types": "./dist/hooks/index.d.mts",
19
- "default": "./dist/hooks/index.mjs"
20
- },
21
- "./operations": {
22
- "types": "./dist/operations/index.d.mts",
23
- "default": "./dist/operations/index.mjs"
24
- },
25
- "./errors": {
26
- "types": "./dist/errors/index.d.mts",
27
- "default": "./dist/errors/index.mjs"
28
- },
29
- "./pagination": {
30
- "types": "./dist/pagination/index.d.mts",
31
- "default": "./dist/pagination/index.mjs"
32
- },
33
- "./repository": {
34
- "types": "./dist/repository/index.d.mts",
35
- "default": "./dist/repository/index.mjs"
36
- },
37
- "./filter": {
38
- "types": "./dist/filter/index.d.mts",
39
- "default": "./dist/filter/index.mjs"
40
- },
41
- "./update": {
42
- "types": "./dist/update/index.d.mts",
43
- "default": "./dist/update/index.mjs"
44
- },
45
- "./query-parser": {
46
- "types": "./dist/query-parser/index.d.mts",
47
- "default": "./dist/query-parser/index.mjs"
48
- },
49
- "./context": {
50
- "types": "./dist/context/index.d.mts",
51
- "default": "./dist/context/index.mjs"
52
- },
53
- "./cache": {
54
- "types": "./dist/cache/index.d.mts",
55
- "default": "./dist/cache/index.mjs"
56
- },
57
- "./events": {
58
- "types": "./dist/events/index.d.mts",
59
- "default": "./dist/events/index.mjs"
60
- },
61
- "./schema": {
62
- "types": "./dist/schema/index.d.mts",
63
- "default": "./dist/schema/index.mjs"
64
- },
65
- "./testing": {
66
- "types": "./dist/testing/index.d.mts",
67
- "default": "./dist/testing/index.mjs"
68
- },
69
- "./tenant": {
70
- "types": "./dist/tenant/index.d.mts",
71
- "default": "./dist/tenant/index.mjs"
72
- },
73
- "./lookup": {
74
- "types": "./dist/lookup/index.d.mts",
75
- "default": "./dist/lookup/index.mjs"
76
- },
77
- "./adapter": {
78
- "types": "./dist/adapter/index.d.mts",
79
- "default": "./dist/adapter/index.mjs"
80
- },
81
- "./better-auth": {
82
- "types": "./dist/better-auth/index.d.mts",
83
- "default": "./dist/better-auth/index.mjs"
84
- },
85
- "./aggregate": {
86
- "types": "./dist/aggregate/index.d.mts",
87
- "default": "./dist/aggregate/index.mjs"
88
- },
89
- "./plugins": {
90
- "types": "./dist/plugins/index.d.mts",
91
- "default": "./dist/plugins/index.mjs"
92
- },
93
- "./lock": {
94
- "types": "./dist/lock/index.d.mts",
95
- "default": "./dist/lock/index.mjs"
96
- },
97
- "./package.json": "./package.json",
98
- "./sync": {
99
- "types": "./dist/sync/index.d.mts",
100
- "default": "./dist/sync/index.mjs"
101
- }
102
- },
103
- "keywords": [
104
- "repository",
105
- "repository-pattern",
106
- "data-access",
107
- "hooks",
108
- "filter-ir",
109
- "pagination",
110
- "cursor-pagination",
111
- "keyset-pagination",
112
- "plugin-based",
113
- "driver-agnostic",
114
- "typescript",
115
- "esm"
116
- ],
117
- "author": "Classytic <classytic.dev@gmail.com> (https://github.com/classytic)",
118
- "license": "MIT",
119
- "repository": {
120
- "type": "git",
121
- "url": "git+https://github.com/classytic/repo-core.git"
122
- },
123
- "bugs": {
124
- "url": "https://github.com/classytic/repo-core/issues"
125
- },
126
- "homepage": "https://github.com/classytic/repo-core#readme",
127
- "scripts": {
128
- "build": "tsdown",
129
- "dev": "tsdown --watch",
130
- "test": "vitest run --project unit --project integration",
131
- "test:unit": "vitest run --project unit",
132
- "test:integration": "vitest run --project integration",
133
- "test:e2e": "vitest run --project e2e",
134
- "test:all": "vitest run",
135
- "test:watch": "vitest --project unit --project integration",
136
- "bench": "vitest bench --run --project bench",
137
- "test:coverage": "vitest run --coverage",
138
- "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
139
- "lint": "biome check src tests",
140
- "lint:fix": "biome check src tests --write",
141
- "format": "biome format src tests --write",
142
- "check": "biome ci src tests --diagnostic-level=error",
143
- "knip": "knip",
144
- "push": "classytic-push",
145
- "prepublishOnly": "npm run check && npm run build && npm run typecheck && npm test",
146
- "release:tag": "node -e \"require('child_process').execSync('npm run push -- v'+require('./package.json').version,{stdio:'inherit'})\"",
147
- "release": "npm run push -- main && npm run release:tag && npm publish",
148
- "publish:dry": "npm publish --dry-run --access public",
149
- "publish:npm": "npm publish --access public"
150
- },
151
- "devDependencies": {
152
- "@arethetypeswrong/cli": "^0.18.2",
153
- "@biomejs/biome": "^2.4.12",
154
- "@classytic/dev-tools": "^0.2.0",
155
- "@types/node": "^22.0.0",
156
- "@vitest/coverage-v8": "^4.1.4",
157
- "fast-check": "^4.7.0",
158
- "knip": "^6.3.0",
159
- "publint": "^0.3.18",
160
- "tsdown": "^0.22.5",
161
- "typescript": "^7.0.2",
162
- "vitest": "^4.1.4"
163
- },
164
- "peerDependencies": {
165
- "vitest": "^3.0.0 || ^4.0.0"
166
- },
167
- "peerDependenciesMeta": {
168
- "vitest": {
169
- "optional": true
170
- }
171
- }
172
- }
1
+ {
2
+ "name": "@classytic/repo-core",
3
+ "version": "0.11.0",
4
+ "description": "Driver-agnostic repository primitives: hooks, Filter IR, operations, pagination, cache contract. Foundation for mongokit, sqlitekit, pgkit, and prismakit. Lean by design \u00e2\u20ac\u201d no plugins ship here; each kit owns its own.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "LICENSE",
11
+ "CHANGELOG.md"
12
+ ],
13
+ "engines": {
14
+ "node": ">=22"
15
+ },
16
+ "exports": {
17
+ "./hooks": {
18
+ "types": "./dist/hooks/index.d.mts",
19
+ "default": "./dist/hooks/index.mjs"
20
+ },
21
+ "./operations": {
22
+ "types": "./dist/operations/index.d.mts",
23
+ "default": "./dist/operations/index.mjs"
24
+ },
25
+ "./errors": {
26
+ "types": "./dist/errors/index.d.mts",
27
+ "default": "./dist/errors/index.mjs"
28
+ },
29
+ "./pagination": {
30
+ "types": "./dist/pagination/index.d.mts",
31
+ "default": "./dist/pagination/index.mjs"
32
+ },
33
+ "./repository": {
34
+ "types": "./dist/repository/index.d.mts",
35
+ "default": "./dist/repository/index.mjs"
36
+ },
37
+ "./filter": {
38
+ "types": "./dist/filter/index.d.mts",
39
+ "default": "./dist/filter/index.mjs"
40
+ },
41
+ "./update": {
42
+ "types": "./dist/update/index.d.mts",
43
+ "default": "./dist/update/index.mjs"
44
+ },
45
+ "./query-parser": {
46
+ "types": "./dist/query-parser/index.d.mts",
47
+ "default": "./dist/query-parser/index.mjs"
48
+ },
49
+ "./context": {
50
+ "types": "./dist/context/index.d.mts",
51
+ "default": "./dist/context/index.mjs"
52
+ },
53
+ "./cache": {
54
+ "types": "./dist/cache/index.d.mts",
55
+ "default": "./dist/cache/index.mjs"
56
+ },
57
+ "./events": {
58
+ "types": "./dist/events/index.d.mts",
59
+ "default": "./dist/events/index.mjs"
60
+ },
61
+ "./schema": {
62
+ "types": "./dist/schema/index.d.mts",
63
+ "default": "./dist/schema/index.mjs"
64
+ },
65
+ "./testing": {
66
+ "types": "./dist/testing/index.d.mts",
67
+ "default": "./dist/testing/index.mjs"
68
+ },
69
+ "./tenant": {
70
+ "types": "./dist/tenant/index.d.mts",
71
+ "default": "./dist/tenant/index.mjs"
72
+ },
73
+ "./lookup": {
74
+ "types": "./dist/lookup/index.d.mts",
75
+ "default": "./dist/lookup/index.mjs"
76
+ },
77
+ "./adapter": {
78
+ "types": "./dist/adapter/index.d.mts",
79
+ "default": "./dist/adapter/index.mjs"
80
+ },
81
+ "./better-auth": {
82
+ "types": "./dist/better-auth/index.d.mts",
83
+ "default": "./dist/better-auth/index.mjs"
84
+ },
85
+ "./aggregate": {
86
+ "types": "./dist/aggregate/index.d.mts",
87
+ "default": "./dist/aggregate/index.mjs"
88
+ },
89
+ "./plugins": {
90
+ "types": "./dist/plugins/index.d.mts",
91
+ "default": "./dist/plugins/index.mjs"
92
+ },
93
+ "./lock": {
94
+ "types": "./dist/lock/index.d.mts",
95
+ "default": "./dist/lock/index.mjs"
96
+ },
97
+ "./usage": {
98
+ "types": "./dist/usage/index.d.mts",
99
+ "default": "./dist/usage/index.mjs"
100
+ },
101
+ "./package.json": "./package.json",
102
+ "./sync": {
103
+ "types": "./dist/sync/index.d.mts",
104
+ "default": "./dist/sync/index.mjs"
105
+ }
106
+ },
107
+ "keywords": [
108
+ "repository",
109
+ "repository-pattern",
110
+ "data-access",
111
+ "hooks",
112
+ "filter-ir",
113
+ "pagination",
114
+ "cursor-pagination",
115
+ "keyset-pagination",
116
+ "plugin-based",
117
+ "driver-agnostic",
118
+ "typescript",
119
+ "esm"
120
+ ],
121
+ "author": "Classytic <classytic.dev@gmail.com> (https://github.com/classytic)",
122
+ "license": "MIT",
123
+ "repository": {
124
+ "type": "git",
125
+ "url": "git+https://github.com/classytic/repo-core.git"
126
+ },
127
+ "bugs": {
128
+ "url": "https://github.com/classytic/repo-core/issues"
129
+ },
130
+ "homepage": "https://github.com/classytic/repo-core#readme",
131
+ "scripts": {
132
+ "build": "tsdown",
133
+ "dev": "tsdown --watch",
134
+ "test": "vitest run --project unit --project integration",
135
+ "test:unit": "vitest run --project unit",
136
+ "test:integration": "vitest run --project integration",
137
+ "test:e2e": "vitest run --project e2e",
138
+ "test:all": "vitest run",
139
+ "test:watch": "vitest --project unit --project integration",
140
+ "bench": "vitest bench --run --project bench",
141
+ "test:coverage": "vitest run --coverage",
142
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
143
+ "lint": "biome check src tests",
144
+ "lint:fix": "biome check src tests --write",
145
+ "format": "biome format src tests --write",
146
+ "check": "biome ci src tests --diagnostic-level=error",
147
+ "knip": "knip",
148
+ "push": "classytic-push",
149
+ "prepublishOnly": "npm run check && npm run build && npm run typecheck && npm test",
150
+ "release:tag": "node -e \"require('child_process').execSync('npm run push -- v'+require('./package.json').version,{stdio:'inherit'})\"",
151
+ "release": "npm run push -- main && npm run release:tag && npm publish",
152
+ "publish:dry": "npm publish --dry-run --access public",
153
+ "publish:npm": "npm publish --access public"
154
+ },
155
+ "devDependencies": {
156
+ "@arethetypeswrong/cli": "^0.18.2",
157
+ "@biomejs/biome": "^2.4.12",
158
+ "@classytic/dev-tools": "^0.2.0",
159
+ "@types/node": "^22.0.0",
160
+ "@vitest/coverage-v8": "^4.1.4",
161
+ "fast-check": "^4.7.0",
162
+ "knip": "^6.3.0",
163
+ "publint": "^0.3.18",
164
+ "tsdown": "^0.22.5",
165
+ "typescript": "^7.0.2",
166
+ "vitest": "^4.1.4"
167
+ },
168
+ "peerDependencies": {
169
+ "vitest": "^3.0.0 || ^4.0.0"
170
+ },
171
+ "peerDependenciesMeta": {
172
+ "vitest": {
173
+ "optional": true
174
+ }
175
+ }
176
+ }