@classytic/repo-core 0.1.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 (84) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/LICENSE +21 -0
  3. package/README.md +154 -0
  4. package/dist/cache/index.d.mts +4 -0
  5. package/dist/cache/index.mjs +3 -0
  6. package/dist/cache/memory-adapter.d.mts +7 -0
  7. package/dist/cache/memory-adapter.mjs +37 -0
  8. package/dist/cache/stable-stringify.d.mts +15 -0
  9. package/dist/cache/stable-stringify.mjs +19 -0
  10. package/dist/cache/types.d.mts +59 -0
  11. package/dist/context/index.d.mts +2 -0
  12. package/dist/context/index.mjs +0 -0
  13. package/dist/context/types.d.mts +24 -0
  14. package/dist/errors/create-error.d.mts +19 -0
  15. package/dist/errors/create-error.mjs +23 -0
  16. package/dist/errors/duplicate-key.d.mts +38 -0
  17. package/dist/errors/duplicate-key.mjs +57 -0
  18. package/dist/errors/index.d.mts +4 -0
  19. package/dist/errors/index.mjs +3 -0
  20. package/dist/errors/types.d.mts +37 -0
  21. package/dist/filter/builders.d.mts +60 -0
  22. package/dist/filter/builders.mjs +172 -0
  23. package/dist/filter/guard.d.mts +13 -0
  24. package/dist/filter/guard.mjs +34 -0
  25. package/dist/filter/index.d.mts +7 -0
  26. package/dist/filter/index.mjs +6 -0
  27. package/dist/filter/match.d.mts +12 -0
  28. package/dist/filter/match.mjs +91 -0
  29. package/dist/filter/scope.d.mts +31 -0
  30. package/dist/filter/scope.mjs +54 -0
  31. package/dist/filter/types.d.mts +143 -0
  32. package/dist/filter/walk.d.mts +24 -0
  33. package/dist/filter/walk.mjs +77 -0
  34. package/dist/hooks/engine.d.mts +48 -0
  35. package/dist/hooks/engine.mjs +101 -0
  36. package/dist/hooks/events.d.mts +95 -0
  37. package/dist/hooks/events.mjs +93 -0
  38. package/dist/hooks/index.d.mts +5 -0
  39. package/dist/hooks/index.mjs +4 -0
  40. package/dist/hooks/priority.d.mts +23 -0
  41. package/dist/hooks/priority.mjs +21 -0
  42. package/dist/hooks/types.d.mts +37 -0
  43. package/dist/lookup/index.d.mts +2 -0
  44. package/dist/lookup/index.mjs +0 -0
  45. package/dist/lookup/types.d.mts +170 -0
  46. package/dist/operations/index.d.mts +3 -0
  47. package/dist/operations/index.mjs +2 -0
  48. package/dist/operations/registry.d.mts +41 -0
  49. package/dist/operations/registry.mjs +140 -0
  50. package/dist/operations/types.d.mts +49 -0
  51. package/dist/pagination/cursor.d.mts +44 -0
  52. package/dist/pagination/cursor.mjs +150 -0
  53. package/dist/pagination/index.d.mts +5 -0
  54. package/dist/pagination/index.mjs +4 -0
  55. package/dist/pagination/keyset.d.mts +25 -0
  56. package/dist/pagination/keyset.mjs +61 -0
  57. package/dist/pagination/offset.d.mts +26 -0
  58. package/dist/pagination/offset.mjs +47 -0
  59. package/dist/pagination/types.d.mts +136 -0
  60. package/dist/query-parser/coerce.d.mts +16 -0
  61. package/dist/query-parser/coerce.mjs +73 -0
  62. package/dist/query-parser/index.d.mts +4 -0
  63. package/dist/query-parser/index.mjs +3 -0
  64. package/dist/query-parser/parse-url.d.mts +7 -0
  65. package/dist/query-parser/parse-url.mjs +224 -0
  66. package/dist/query-parser/types.d.mts +104 -0
  67. package/dist/repository/base.d.mts +90 -0
  68. package/dist/repository/base.mjs +111 -0
  69. package/dist/repository/index.d.mts +5 -0
  70. package/dist/repository/index.mjs +3 -0
  71. package/dist/repository/plugin-types.d.mts +27 -0
  72. package/dist/repository/plugin-types.mjs +45 -0
  73. package/dist/repository/types.d.mts +470 -0
  74. package/dist/schema/field-rules.d.mts +62 -0
  75. package/dist/schema/field-rules.mjs +110 -0
  76. package/dist/schema/index.d.mts +3 -0
  77. package/dist/schema/index.mjs +2 -0
  78. package/dist/schema/types.d.mts +138 -0
  79. package/dist/testing/conformance.d.mts +6 -0
  80. package/dist/testing/conformance.mjs +481 -0
  81. package/dist/testing/index.d.mts +3 -0
  82. package/dist/testing/index.mjs +2 -0
  83. package/dist/testing/types.d.mts +113 -0
  84. package/package.json +130 -0
@@ -0,0 +1,113 @@
1
+ import { MinimalRepo, StandardRepo } from "../repository/types.mjs";
2
+
3
+ //#region src/testing/types.d.ts
4
+ /**
5
+ * The conformance scenarios operate on a minimal doc shape common to
6
+ * every backend:
7
+ *
8
+ * - `name`, `email`, `category`, `active`, `count`, `notes` — plain
9
+ * scalar fields every driver supports.
10
+ * - `createdAt` — ISO string. Avoids Date/epoch coercion divergence.
11
+ * - `_id` / `id` — the harness decides which one its backend uses;
12
+ * scenarios read it via `harness.idField`.
13
+ *
14
+ * The intent is to keep the schema lowest-common-denominator, not to
15
+ * exercise backend-specific features (JSON mode, BSON subdocs, vector
16
+ * fields). Those stay per-kit.
17
+ */
18
+ interface ConformanceDoc {
19
+ id?: string;
20
+ _id?: string;
21
+ name: string;
22
+ email: string;
23
+ category: string | null;
24
+ count: number;
25
+ active: boolean;
26
+ notes: string | null;
27
+ createdAt: string;
28
+ }
29
+ /**
30
+ * Per-backend feature flags. Scenarios that exercise a non-universal
31
+ * capability (transactions in D1, upsert in narrow stores) check the
32
+ * flag and `it.skip` on the off branch — so the suite runs on every
33
+ * environment without "optional test failed" noise.
34
+ */
35
+ interface ConformanceFeatures {
36
+ /** `withTransaction(fn)` — D1 throws, standalone Mongo throws 263. */
37
+ transactions: boolean;
38
+ /**
39
+ * True if calling `withTransaction` inside another `withTransaction`
40
+ * callback is expected to work. Mongo's driver supports it via the
41
+ * same session; SQL drivers typically reject it. Either behavior is
42
+ * valid — the scenario asserts whichever the harness declares.
43
+ */
44
+ nestedTransactions: boolean;
45
+ /** `findOneAndUpdate` with upsert: true. */
46
+ upsert: boolean;
47
+ /** `isDuplicateKeyError(err)` classifier. */
48
+ duplicateKeyError: boolean;
49
+ /** `distinct(field)`. */
50
+ distinct: boolean;
51
+ /** Portable `aggregate({ measures, groupBy, having })`. */
52
+ aggregate: boolean;
53
+ /** `getOrCreate(filter, data)`. */
54
+ getOrCreate: boolean;
55
+ /** `count(filter)` and `exists(filter)`. */
56
+ countAndExists: boolean;
57
+ }
58
+ /**
59
+ * One-shot context produced by `harness.setup()`. Scenarios receive a
60
+ * fresh context per test — the harness is responsible for isolation
61
+ * (new collection name on mongo, fresh `:memory:` db on sqlite).
62
+ */
63
+ interface ConformanceContext<TDoc extends ConformanceDoc = ConformanceDoc> {
64
+ /** Repository under test. */
65
+ repo: StandardRepo<TDoc> & MinimalRepo<TDoc>;
66
+ /**
67
+ * Optional secondary repository pointing at a DIFFERENT collection /
68
+ * table — scenarios that exercise cross-repo atomicity use it. May
69
+ * be omitted; scenarios that need it will skip if missing.
70
+ */
71
+ secondaryRepo?: StandardRepo<TDoc> & MinimalRepo<TDoc>;
72
+ /** Release resources (close db, drop collection). Must be idempotent. */
73
+ cleanup(): Promise<void>;
74
+ }
75
+ /**
76
+ * Harness plugged into `runStandardRepoConformance`.
77
+ *
78
+ * The harness owns:
79
+ * - Backend bootstrap (mongo connection, sqlite db + migrations).
80
+ * - Doc factory — returns a partial doc with default values filled in.
81
+ * Scenarios call `makeDoc({ name: 'override' })` to produce inserts.
82
+ * - Feature flags — declares what the backend supports.
83
+ * - idField — `'id'` for sqlitekit, `'_id'` for mongokit. Scenarios
84
+ * read this to project the primary key off returned docs.
85
+ */
86
+ interface ConformanceHarness<TDoc extends ConformanceDoc = ConformanceDoc> {
87
+ /** Human-readable kit name, used as the top-level describe() label. */
88
+ name: string;
89
+ /**
90
+ * Name of the primary-key field on docs the repo returns. `'id'` for
91
+ * sqlitekit, `'_id'` for mongokit, could be `'uuid'` or anything else
92
+ * for future kits. Scenarios read ids via `doc[idField]`.
93
+ */
94
+ idField: string;
95
+ /** Feature support flags — skipped scenarios show as `skipped` in vitest output. */
96
+ features: ConformanceFeatures;
97
+ /** Create a fresh, isolated repo + cleanup closure. Called per test. */
98
+ setup(): Promise<ConformanceContext<TDoc>>;
99
+ /**
100
+ * Build a partial doc with fixture-safe defaults. Passed-in overrides
101
+ * merge on top. Do NOT include `id`/`_id` unless the caller overrides
102
+ * (scenarios usually let the backend assign or set one explicitly).
103
+ *
104
+ * Overrides are typed against `ConformanceDoc` (not `TDoc`) because
105
+ * the shared scenarios only know the common doc shape. Kits that use
106
+ * a wider `TDoc` with extra required fields fill those in inside
107
+ * their harness's `makeDoc` implementation — the scenarios never
108
+ * reference them.
109
+ */
110
+ makeDoc(overrides?: Partial<ConformanceDoc>): Partial<TDoc>;
111
+ }
112
+ //#endregion
113
+ export { ConformanceContext, ConformanceDoc, ConformanceFeatures, ConformanceHarness };
package/package.json ADDED
@@ -0,0 +1,130 @@
1
+ {
2
+ "name": "@classytic/repo-core",
3
+ "version": "0.1.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
+ "./query-parser": {
42
+ "types": "./dist/query-parser/index.d.mts",
43
+ "default": "./dist/query-parser/index.mjs"
44
+ },
45
+ "./context": {
46
+ "types": "./dist/context/index.d.mts",
47
+ "default": "./dist/context/index.mjs"
48
+ },
49
+ "./cache": {
50
+ "types": "./dist/cache/index.d.mts",
51
+ "default": "./dist/cache/index.mjs"
52
+ },
53
+ "./schema": {
54
+ "types": "./dist/schema/index.d.mts",
55
+ "default": "./dist/schema/index.mjs"
56
+ },
57
+ "./testing": {
58
+ "types": "./dist/testing/index.d.mts",
59
+ "default": "./dist/testing/index.mjs"
60
+ },
61
+ "./lookup": {
62
+ "types": "./dist/lookup/index.d.mts",
63
+ "default": "./dist/lookup/index.mjs"
64
+ },
65
+ "./package.json": "./package.json"
66
+ },
67
+ "keywords": [
68
+ "repository",
69
+ "repository-pattern",
70
+ "data-access",
71
+ "hooks",
72
+ "filter-ir",
73
+ "pagination",
74
+ "cursor-pagination",
75
+ "keyset-pagination",
76
+ "plugin-based",
77
+ "driver-agnostic",
78
+ "typescript",
79
+ "esm"
80
+ ],
81
+ "author": "Classytic <classytic.dev@gmail.com> (https://github.com/classytic)",
82
+ "license": "MIT",
83
+ "repository": {
84
+ "type": "git",
85
+ "url": "git+https://github.com/classytic/repo-core.git"
86
+ },
87
+ "bugs": {
88
+ "url": "https://github.com/classytic/repo-core/issues"
89
+ },
90
+ "homepage": "https://github.com/classytic/repo-core#readme",
91
+ "scripts": {
92
+ "build": "tsdown",
93
+ "dev": "tsdown --watch",
94
+ "test": "vitest run --project unit --project integration",
95
+ "test:unit": "vitest run --project unit",
96
+ "test:integration": "vitest run --project integration",
97
+ "test:e2e": "vitest run --project e2e",
98
+ "test:all": "vitest run",
99
+ "test:watch": "vitest --project unit --project integration",
100
+ "test:coverage": "vitest run --coverage",
101
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
102
+ "lint": "biome check src tests",
103
+ "lint:fix": "biome check src tests --write",
104
+ "format": "biome format src tests --write",
105
+ "check": "biome ci src tests --diagnostic-level=error",
106
+ "knip": "knip",
107
+ "prepublishOnly": "npm run check && npm run build && npm run typecheck && npm test",
108
+ "publish:dry": "npm publish --dry-run --access public",
109
+ "publish:npm": "npm publish --access public"
110
+ },
111
+ "devDependencies": {
112
+ "@arethetypeswrong/cli": "^0.18.2",
113
+ "@biomejs/biome": "^2.4.12",
114
+ "@types/node": "^22.0.0",
115
+ "@vitest/coverage-v8": "^4.1.4",
116
+ "knip": "^6.3.0",
117
+ "publint": "^0.3.18",
118
+ "tsdown": "^0.21.8",
119
+ "typescript": "^6.0.3",
120
+ "vitest": "^4.1.4"
121
+ },
122
+ "peerDependencies": {
123
+ "vitest": "^3.0.0 || ^4.0.0"
124
+ },
125
+ "peerDependenciesMeta": {
126
+ "vitest": {
127
+ "optional": true
128
+ }
129
+ }
130
+ }