@classytic/repo-core 0.2.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 (87) hide show
  1. package/CHANGELOG.md +363 -0
  2. package/README.md +28 -7
  3. package/dist/adapter/index.d.mts +3 -0
  4. package/dist/adapter/index.mjs +2 -0
  5. package/dist/adapter/types.d.mts +222 -0
  6. package/dist/adapter/widen.d.mts +22 -0
  7. package/dist/adapter/widen.mjs +26 -0
  8. package/dist/aggregate/index.d.mts +3 -0
  9. package/dist/aggregate/index.mjs +3 -0
  10. package/dist/aggregate/keyset.d.mts +57 -0
  11. package/dist/aggregate/keyset.mjs +45 -0
  12. package/dist/aggregate/normalize.d.mts +24 -0
  13. package/dist/aggregate/normalize.mjs +28 -0
  14. package/dist/better-auth/index.d.mts +110 -0
  15. package/dist/better-auth/index.mjs +71 -0
  16. package/dist/cache/engine.d.mts +127 -0
  17. package/dist/cache/engine.mjs +235 -0
  18. package/dist/cache/envelope.mjs +32 -0
  19. package/dist/cache/index.d.mts +7 -2
  20. package/dist/cache/index.mjs +6 -2
  21. package/dist/cache/keys.mjs +131 -0
  22. package/dist/cache/memory-adapter.mjs +41 -7
  23. package/dist/cache/options.d.mts +112 -0
  24. package/dist/cache/options.mjs +25 -0
  25. package/dist/cache/plugin/context.d.mts +18 -0
  26. package/dist/cache/plugin/context.mjs +121 -0
  27. package/dist/cache/plugin/index.d.mts +86 -0
  28. package/dist/cache/plugin/index.mjs +78 -0
  29. package/dist/cache/plugin/invalidation-hooks.mjs +35 -0
  30. package/dist/cache/plugin/read-hooks.mjs +96 -0
  31. package/dist/cache/plugin/swr.mjs +20 -0
  32. package/dist/cache/runtime.d.mts +43 -0
  33. package/dist/cache/runtime.mjs +14 -0
  34. package/dist/cache/tag-index.mjs +84 -0
  35. package/dist/cache/timeout-adapter.d.mts +30 -0
  36. package/dist/cache/timeout-adapter.mjs +58 -0
  37. package/dist/cache/types.d.mts +45 -0
  38. package/dist/cache/version-store.mjs +57 -0
  39. package/dist/errors/contract.d.mts +37 -0
  40. package/dist/errors/contract.mjs +75 -0
  41. package/dist/errors/index.d.mts +4 -2
  42. package/dist/errors/index.mjs +4 -1
  43. package/dist/errors/schema.d.mts +101 -0
  44. package/dist/errors/schema.mjs +78 -0
  45. package/dist/errors/types.d.mts +113 -8
  46. package/dist/errors/types.mjs +29 -0
  47. package/dist/filter/match.mjs +38 -2
  48. package/dist/pagination/canonical.d.mts +35 -0
  49. package/dist/pagination/canonical.mjs +26 -0
  50. package/dist/pagination/cursor.mjs +4 -1
  51. package/dist/pagination/index.d.mts +3 -2
  52. package/dist/pagination/index.mjs +2 -1
  53. package/dist/pagination/types.d.mts +57 -3
  54. package/dist/plugins/index.d.mts +2 -0
  55. package/dist/plugins/index.mjs +2 -0
  56. package/dist/plugins/tenant-helpers.d.mts +63 -0
  57. package/dist/plugins/tenant-helpers.mjs +84 -0
  58. package/dist/query-parser/index.d.mts +2 -1
  59. package/dist/query-parser/index.mjs +2 -1
  60. package/dist/query-parser/parse-url.mjs +13 -11
  61. package/dist/query-parser/reserved.d.mts +43 -0
  62. package/dist/query-parser/reserved.mjs +56 -0
  63. package/dist/repository/agg-output.d.mts +63 -0
  64. package/dist/repository/agg-output.mjs +89 -0
  65. package/dist/repository/base.mjs +21 -0
  66. package/dist/repository/index.d.mts +4 -2
  67. package/dist/repository/index.mjs +3 -1
  68. package/dist/repository/options.d.mts +62 -0
  69. package/dist/repository/options.mjs +57 -0
  70. package/dist/repository/types.d.mts +935 -48
  71. package/dist/schema/field-rules.d.mts +60 -9
  72. package/dist/schema/field-rules.mjs +121 -10
  73. package/dist/schema/generator.d.mts +72 -0
  74. package/dist/schema/generator.mjs +16 -0
  75. package/dist/schema/index.d.mts +3 -2
  76. package/dist/schema/index.mjs +3 -2
  77. package/dist/schema/types.d.mts +77 -3
  78. package/dist/tenant/index.d.mts +3 -0
  79. package/dist/tenant/index.mjs +2 -0
  80. package/dist/tenant/resolve.d.mts +27 -0
  81. package/dist/tenant/resolve.mjs +69 -0
  82. package/dist/tenant/types.d.mts +142 -0
  83. package/dist/testing/conformance.mjs +666 -17
  84. package/dist/testing/index.d.mts +2 -2
  85. package/dist/testing/types.d.mts +99 -2
  86. package/package.json +27 -1
  87. package/dist/cache/stable-stringify.d.mts +0 -15
@@ -0,0 +1,142 @@
1
+ //#region src/tenant/types.d.ts
2
+ /**
3
+ * Tenant scope configuration — canonical static contract for the org.
4
+ *
5
+ * **`@classytic/repo-core/tenant` is the single source of truth.** Every
6
+ * multi-tenant-capable package (`@classytic/mongokit`, `@classytic/sqlitekit`,
7
+ * future kits, arc presets, services) consumes {@link TenantConfig} for its
8
+ * static fields and extends with kit-specific runtime callbacks via
9
+ * `Pick<TenantConfig, ...>` to lock the field vocabulary by structural typing.
10
+ *
11
+ * Three strategies are supported:
12
+ * - `'field'` (default) — filter every query by a scalar field on documents.
13
+ * The common case; used by `multiTenantPlugin` in mongokit and sqlitekit.
14
+ * - `'none'` — disable scoping entirely (single-tenant app). Equivalent to
15
+ * `enabled: false`; `strategy: 'none'` is the explicit form.
16
+ * - `'custom'` — caller supplies a `resolve(ctx)` function that returns the
17
+ * filter shape to inject. **The escape hatch for custom systems** —
18
+ * covers multi-field composite tenants, context-derived filters
19
+ * (region + partner id), non-scalar scope keys, or any tenancy model that
20
+ * doesn't fit the simple `field === id` pattern.
21
+ *
22
+ * **Why this layer is static-only.** Runtime callbacks (`skipWhen(ctx, op)`,
23
+ * `resolveContext()`, `resolveTenantId(ctx)`) genuinely differ across kits
24
+ * because their `RepositoryContext` shapes differ — mongokit's resolver
25
+ * returns just an id, sqlitekit's takes a richer context object. Each kit
26
+ * extends `TenantConfig` with its own runtime-callback fields. Hosts who
27
+ * need a single config object can compose: pass the static `TenantConfig`
28
+ * through {@link resolveTenantConfig} once, then forward the resolved
29
+ * static fields into each kit's runtime options alongside the kit-specific
30
+ * callbacks.
31
+ */
32
+ /**
33
+ * Storage / cast strategy for the tenant identifier on documents.
34
+ *
35
+ * - `'objectId'` (recommended for new packages) — `Schema.Types.ObjectId`
36
+ * with `ref`. Enables `$lookup`, `.populate()`, QueryParser `?lookup=...`
37
+ * on Mongo-shaped kits. SQL kits typically ignore this and rely on
38
+ * schema-defined column types instead.
39
+ * - `'string'` — plain string. Use when the host auth system issues UUIDs
40
+ * or slugs rather than ObjectIds.
41
+ */
42
+ type TenantFieldType = 'objectId' | 'string';
43
+ /** Scope resolution strategy. */
44
+ type TenantStrategy = 'field' | 'none' | 'custom';
45
+ interface TenantConfig {
46
+ /**
47
+ * Scope strategy. Omit for the common `'field'` case — explicit `'none'`
48
+ * / `'custom'` lets packages collapse what used to live in a separate
49
+ * `ScopeConfig` type.
50
+ *
51
+ * @default 'field'
52
+ */
53
+ strategy?: TenantStrategy;
54
+ /**
55
+ * Whether tenant scoping is active. When `false`, the package runs in
56
+ * single-tenant mode — no filter injection, no tenant field on documents.
57
+ * Equivalent to `strategy: 'none'`.
58
+ *
59
+ * @default true
60
+ */
61
+ enabled?: boolean;
62
+ /**
63
+ * Document / column field name that stores the tenant id. Used when
64
+ * `strategy === 'field'`.
65
+ *
66
+ * @default 'organizationId'
67
+ */
68
+ tenantField?: string;
69
+ /**
70
+ * How to store / cast the tenant id.
71
+ *
72
+ * @default 'objectId'
73
+ */
74
+ fieldType?: TenantFieldType;
75
+ /**
76
+ * Mongoose ref for `'objectId'` types. Ignored by SQL kits and when
77
+ * `fieldType === 'string'`.
78
+ *
79
+ * @default 'organization'
80
+ */
81
+ ref?: string;
82
+ /**
83
+ * Which key on the repository context to read the tenant id from.
84
+ *
85
+ * Defaults cascade: if omitted, falls back to the caller's `tenantField`
86
+ * (if supplied), else to `'organizationId'`. Rationale: when a host renames
87
+ * `tenantField` to e.g. `'branchId'`, their context almost always carries
88
+ * the value under the same key — mirroring `tenantField` is the
89
+ * least-surprise behavior. Override explicitly if the context key diverges
90
+ * from the document field (e.g. `tenantField: 'branchId'`,
91
+ * `contextKey: 'organizationId'`).
92
+ *
93
+ * @default tenantField ?? 'organizationId'
94
+ */
95
+ contextKey?: string;
96
+ /**
97
+ * Whether the field is required. When `false`, the package permits
98
+ * unscoped / cross-tenant reads (typically only for admin paths).
99
+ *
100
+ * @default true
101
+ */
102
+ required?: boolean;
103
+ /**
104
+ * Custom resolver — called when `strategy === 'custom'` to produce the
105
+ * filter object injected into queries. Packages pass the request /
106
+ * repository context; the resolver returns the filter shape.
107
+ *
108
+ * Use for tenancy models that don't fit the simple `field === id`
109
+ * pattern: multi-field composites, context-derived filters
110
+ * (region + partner id), hash-derived shards, etc.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * {
115
+ * strategy: 'custom',
116
+ * resolve: (ctx) => ({
117
+ * organizationId: ctx.organizationId,
118
+ * region: ctx.region,
119
+ * partnerId: ctx.partnerId,
120
+ * }),
121
+ * }
122
+ * ```
123
+ */
124
+ resolve?: (ctx: Record<string, unknown>) => Record<string, unknown>;
125
+ }
126
+ /**
127
+ * Resolved shape returned by `resolveTenantConfig`. Always includes the
128
+ * field defaults (so packages can inspect field names even when
129
+ * `enabled: false`) and threads `resolve` when `strategy === 'custom'`.
130
+ */
131
+ type ResolvedTenantConfig = {
132
+ strategy: TenantStrategy;
133
+ enabled: boolean;
134
+ tenantField: string;
135
+ fieldType: TenantFieldType;
136
+ ref: string;
137
+ contextKey: string;
138
+ required: boolean;
139
+ resolve?: TenantConfig['resolve'];
140
+ };
141
+ //#endregion
142
+ export { ResolvedTenantConfig, TenantConfig, TenantFieldType, TenantStrategy };