@mongrov/data-access 0.1.0-alpha.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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +266 -0
  3. package/dist/context.d.ts +56 -0
  4. package/dist/context.d.ts.map +1 -0
  5. package/dist/context.js +51 -0
  6. package/dist/context.js.map +1 -0
  7. package/dist/define.d.ts +26 -0
  8. package/dist/define.d.ts.map +1 -0
  9. package/dist/define.js +60 -0
  10. package/dist/define.js.map +1 -0
  11. package/dist/dispatcher.d.ts +62 -0
  12. package/dist/dispatcher.d.ts.map +1 -0
  13. package/dist/dispatcher.js +91 -0
  14. package/dist/dispatcher.js.map +1 -0
  15. package/dist/errors.d.ts +27 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +35 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/eslint/index.d.ts +52 -0
  20. package/dist/eslint/index.d.ts.map +1 -0
  21. package/dist/eslint/index.js +45 -0
  22. package/dist/eslint/index.js.map +1 -0
  23. package/dist/eslint/rules/no-storage-engine-imports.d.ts +22 -0
  24. package/dist/eslint/rules/no-storage-engine-imports.d.ts.map +1 -0
  25. package/dist/eslint/rules/no-storage-engine-imports.js +145 -0
  26. package/dist/eslint/rules/no-storage-engine-imports.js.map +1 -0
  27. package/dist/hooks.d.ts +67 -0
  28. package/dist/hooks.d.ts.map +1 -0
  29. package/dist/hooks.js +234 -0
  30. package/dist/hooks.js.map +1 -0
  31. package/dist/index.d.ts +16 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +16 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/invalidation.d.ts +29 -0
  36. package/dist/invalidation.d.ts.map +1 -0
  37. package/dist/invalidation.js +106 -0
  38. package/dist/invalidation.js.map +1 -0
  39. package/dist/tenant.d.ts +18 -0
  40. package/dist/tenant.d.ts.map +1 -0
  41. package/dist/tenant.js +31 -0
  42. package/dist/tenant.js.map +1 -0
  43. package/dist/types.d.ts +127 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +11 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +91 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mongrov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,266 @@
1
+ # @mongrov/data-access
2
+
3
+ Named-query / mutation / event registry with engine dispatch for `@mongrov` apps.
4
+
5
+ Screens never import storage engines. Instead, they read via `useAppQuery(name, input)` and write via `useAppMutation(name)` against a single **registry** the app assembles at startup. The provider dispatches to the right engine (DuckDB, RxDB, or KV) and Zod-parses the result before it reaches the UI.
6
+
7
+ ## Features
8
+
9
+ - **Define API** — `defineQuery`, `defineMutation`, `defineEvent` produce typed handles with a `__kind` discriminator and engine-specific config (SQL for DuckDB, observable factory for RxDB, key builder for KV).
10
+ - **Engine dispatch** — `duckdb` (analytics), `rxdb` (reactive stream), `kv` (single-key read). Apps wire concrete adapters into `DataAccessProvider`.
11
+ - **Authorize gate** — optional `authorize(input, ctx)` runs before every query and mutation; `false` throws `AuthorizationError`.
12
+ - **Tenant auto-binding** — `{ brand, familyId }` from the active `RequestContext` are merged into DuckDB params automatically. Screens never pass tenant.
13
+ - **Invalidation event bus** — `mitt`-backed exact-match dispatch plus a glob wrapper (`hrv:*`, `sleep:**`); mutations declare `invalidates`, queries declare `invalidatedBy`, refetches happen automatically.
14
+ - **Optimistic mutations** — `optimistic(input, ctx)` surfaces immediately as `data`; reverts on failure.
15
+ - **Cache defaults** — 30s `staleTime`, 5min `gcTime`, overridable per query.
16
+ - **ESLint plugin** — `no-storage-engine-imports` bans direct `@mongrov/db`, `@mongrov/analytics`, `@mongrov/collab` imports outside the registry directory.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pnpm add @mongrov/data-access
22
+ # Peer deps
23
+ pnpm add @tanstack/react-query zod
24
+ # Optional peers (only what you use)
25
+ pnpm add @mongrov/db @mongrov/analytics react react-native eslint
26
+ ```
27
+
28
+ ## Registry pattern
29
+
30
+ Assemble a registry once — typically under `src/data/` in your app.
31
+
32
+ ```ts
33
+ // src/data/queries.ts
34
+ import { defineQuery } from '@mongrov/data-access';
35
+ import { z } from 'zod';
36
+
37
+ export const hrvLast7Days = defineQuery({
38
+ engine: 'duckdb',
39
+ input: z.object({ userId: z.string() }),
40
+ output: z.array(z.object({ day: z.string(), rmssd: z.number() })),
41
+ sql: 'SELECT day, rmssd FROM hrv WHERE user_id = $userId AND brand = $brand AND family_id = $familyId ORDER BY day DESC LIMIT 7',
42
+ invalidatedBy: ['hrv:*'],
43
+ staleTime: 60_000,
44
+ });
45
+ ```
46
+
47
+ ```ts
48
+ // src/data/mutations.ts
49
+ import { defineMutation } from '@mongrov/data-access';
50
+ import { z } from 'zod';
51
+
52
+ export const recordHrvSample = defineMutation({
53
+ input: z.object({ rmssd: z.number(), ts: z.string() }),
54
+ output: z.object({ id: z.string() }),
55
+ exec: async (input, ctx) => {
56
+ // Direct engine access is legal here because this file lives in
57
+ // src/data/, which the ESLint rule's `allowedDirs` grants an
58
+ // exception to.
59
+ return await analytics.insert('hrv', { ...input, userId: ctx.requesterUserId });
60
+ },
61
+ invalidates: ['hrv:sample:added'],
62
+ });
63
+ ```
64
+
65
+ ```ts
66
+ // src/data/registry.ts
67
+ import { hrvLast7Days } from './queries';
68
+ import { recordHrvSample } from './mutations';
69
+
70
+ export const registry = {
71
+ queries: { 'hrv.last7Days': hrvLast7Days },
72
+ mutations: { 'hrv.record': recordHrvSample },
73
+ events: {},
74
+ } as const;
75
+ ```
76
+
77
+ Mount the provider at the app root:
78
+
79
+ ```tsx
80
+ // src/app/_layout.tsx
81
+ import { DataAccessProvider } from '@mongrov/data-access';
82
+ import { registry } from '@/data/registry';
83
+ import { analyticsEngine } from '@/data/engines';
84
+
85
+ export default function RootLayout() {
86
+ return (
87
+ <DataAccessProvider
88
+ registry={registry}
89
+ engines={{ duckdb: analyticsEngine }}
90
+ context={() => ({
91
+ requesterUserId: session.userId,
92
+ brand: 'zivaone',
93
+ familyId: session.familyId,
94
+ now: () => new Date(),
95
+ })}
96
+ >
97
+ <Slot />
98
+ </DataAccessProvider>
99
+ );
100
+ }
101
+ ```
102
+
103
+ Screens consume via hooks — no engine imports:
104
+
105
+ ```tsx
106
+ // src/features/hrv/screen.tsx
107
+ import { useAppQuery, useAppMutation } from '@mongrov/data-access';
108
+
109
+ export function HrvScreen() {
110
+ const { data, loading, error } = useAppQuery<
111
+ { userId: string },
112
+ Array<{ day: string; rmssd: number }>
113
+ >('hrv.last7Days', { userId: 'me' });
114
+
115
+ const record = useAppMutation<{ rmssd: number; ts: string }, { id: string }>('hrv.record');
116
+
117
+ if (loading) return <ActivityIndicator />;
118
+ if (error) return <Text>{error.message}</Text>;
119
+
120
+ return (
121
+ <>
122
+ <List data={data} />
123
+ <Button
124
+ title="Record sample"
125
+ onPress={() => record.mutate({ rmssd: 42, ts: new Date().toISOString() })}
126
+ />
127
+ </>
128
+ );
129
+ }
130
+ ```
131
+
132
+ ## API
133
+
134
+ ### `defineQuery(config)`
135
+
136
+ Returns a `QueryDefinition<Input, Output>` handle with `__kind: 'query'`. Config union by `engine`:
137
+
138
+ | Field | duckdb | rxdb | kv |
139
+ | --------------- | ------------- | ----------------------------------- | --------------------------- |
140
+ | Required | `sql: string` | `query: (db, input) => Observable` | `keyBuilder: (input) => string` |
141
+ | Common | `output: ZodType` (required), `input?: ZodType`, `invalidatedBy?: string[]`, `authorize?`, `staleTime?`, `gcTime?` |||
142
+
143
+ - **duckdb** — params include Zod-parsed input **plus** `brand` and `familyId` from `RequestContext`, injected as `$brand` / `$familyId` for `analytics.execute(sql, params)`.
144
+ - **rxdb** — returned observable is subscribed in `useAppQuery`. Each emission is Zod-parsed. Unsubscribes on unmount.
145
+ - **kv** — `keyBuilder(input)` produces the storage key; the engine's `get(key)` returns the value. No automatic invalidation.
146
+
147
+ ### `defineMutation(config)`
148
+
149
+ Returns a `MutationDefinition<Input, Output>` handle with `__kind: 'mutation'`.
150
+
151
+ - `exec(input, ctx) => Promise<Output>` — required.
152
+ - `invalidates?: string[]` — event bus patterns fired on success.
153
+ - `authorize?(input, ctx) => boolean | Promise<boolean>` — false throws `AuthorizationError`.
154
+ - `optimistic?(input, ctx) => Output` — surfaces as `data` while pending; reverts on failure.
155
+ - `input?` / `output?` — Zod schemas; validation is applied on both sides.
156
+
157
+ ### `defineEvent<TPayload>()`
158
+
159
+ Returns an `EventDefinition<TPayload>` handle with `__kind: 'event'`. Payload type flows through `useAppEvent`.
160
+
161
+ ### `useAppQuery<Input, Output>(name, input?)`
162
+
163
+ Returns `{ data, loading, error, refetch, isStale }`.
164
+
165
+ - For **duckdb** / **kv**, backed by TanStack `useQuery`. Subscribes to every `invalidatedBy` pattern on mount and invalidates its own cache when a match fires.
166
+ - For **rxdb**, subscribes to the observable via `useEffect`. `refetch()` is a no-op — the stream drives updates. Zod-parses on every emission; parse failure surfaces as `error`.
167
+ - Cache defaults: `staleTime = 30_000`, `gcTime = 300_000`, both overridable in the query definition.
168
+
169
+ ### `useAppMutation<Input, Output>(name)`
170
+
171
+ Returns `{ mutate, mutateAsync, loading, error, data, reset }`. On success, emits every entry in `invalidates` on the bus. On failure, clears optimistic state so subsequent renders see `data === undefined`.
172
+
173
+ ### `useAppEvent<TPayload>(name, handler)`
174
+
175
+ Subscribes to exact-match `name`. Handler ref stays fresh across renders. Unsubscribes on unmount. For glob semantics, call `bus.subscribePattern` directly via `useDataAccessRuntime`.
176
+
177
+ ### `useRequestContext(): RequestContext`
178
+
179
+ Reads the provider's `context()` callback on every render, so session updates propagate without remount.
180
+
181
+ ### `DataAccessProvider`
182
+
183
+ ```ts
184
+ interface DataAccessProviderConfig {
185
+ registry: Registry;
186
+ engines: { duckdb?: DuckdbEngine; rxdb?: RxdbEngine; kv?: KvEngine };
187
+ context: () => RequestContext;
188
+ bus?: EventBus; // optional; one is minted per provider
189
+ queryClient?: QueryClient; // optional; one is minted per provider
190
+ }
191
+ ```
192
+
193
+ Nesting providers is not supported in v0.1 — mount exactly one at the app root.
194
+
195
+ ### `createEventBus(): EventBus`
196
+
197
+ Standalone factory for tests or non-React callers. Exposes `emit`, `subscribe`, `subscribePattern`. Pattern semantics:
198
+
199
+ - `hrv:*` — matches one `:`-delimited segment.
200
+ - `hrv:**` — matches one or more segments.
201
+ - Empty pattern rejected. Case-sensitive.
202
+
203
+ ### Errors
204
+
205
+ - `DataAccessError` — base class with `.code`. Codes: `engine_missing`, `zod_parse_failed`, `authorization_denied`, `not_implemented`.
206
+ - `AuthorizationError` extends `DataAccessError` (`code: 'authorization_denied'`).
207
+ - `NotImplementedError` extends `DataAccessError` (`code: 'not_implemented'`); reserved for stubs.
208
+
209
+ ## ESLint plugin
210
+
211
+ Blocks direct storage-engine imports outside the registry directory.
212
+
213
+ ```js
214
+ // eslint.config.mjs
215
+ import dataAccessPlugin from '@mongrov/data-access/eslint';
216
+
217
+ export default [
218
+ {
219
+ plugins: { '@mongrov/data-access': dataAccessPlugin },
220
+ rules: {
221
+ '@mongrov/data-access/no-storage-engine-imports': ['error', {
222
+ allowedDirs: ['src/data'],
223
+ }],
224
+ },
225
+ },
226
+ ];
227
+ ```
228
+
229
+ - Default block list: `@mongrov/analytics`, `@mongrov/collab`, `@mongrov/db` (subpaths included — `@mongrov/db/kv` is blocked by the same rule).
230
+ - `allowedDirs` — path fragments that suppress the rule; the registry directory typically goes here.
231
+ - `blockedPackages` — override the default block list.
232
+ - Covers `import`, dynamic `import()`, and CommonJS `require()`.
233
+
234
+ ## Testing patterns
235
+
236
+ The package ships with a full vitest suite you can mirror in-app.
237
+
238
+ - **In-memory event bus** — `createEventBus()` is standalone and needs no engine wiring.
239
+ - **Fake DuckDB engine** — implement `DuckdbEngine.execute(sql, params)` in a couple of lines to drive query tests.
240
+ - **Fake RxDB observable** — a `{ subscribe(observer) }` object with `next` / `error` is enough for `useAppQuery('name.rxdb', ...)`.
241
+ - **QueryClient with `retry: false`** — avoid retry timeouts in error-surface tests:
242
+
243
+ ```ts
244
+ const client = new QueryClient({
245
+ defaultOptions: {
246
+ queries: { retry: false, gcTime: 0 },
247
+ mutations: { retry: false },
248
+ },
249
+ });
250
+ ```
251
+
252
+ See `src/__tests__/integration.test.tsx` for a full smoke test wiring registry + provider + hooks together.
253
+
254
+ ## Version
255
+
256
+ `0.1.0-alpha.0` — first cut. Follow-ups tracked in [`.specifica/features/data-access/tasks.md`](../../../.specifica/features/data-access/tasks.md):
257
+
258
+ - Row-level invalidation
259
+ - Streaming query results
260
+ - Persistent cache (AsyncStorage / MMKV)
261
+ - Devtools panel
262
+ - Custom engine adapters (GraphQL, REST)
263
+
264
+ ## License
265
+
266
+ MIT
@@ -0,0 +1,56 @@
1
+ /**
2
+ * DataAccessProvider (T-16) + internal context.
3
+ *
4
+ * The provider is the single injection point for the registry, engine
5
+ * adapters, RequestContext factory, event bus, and TanStack QueryClient.
6
+ * If no bus / queryClient is supplied, one is created lazily and shared
7
+ * across the entire tree.
8
+ *
9
+ * See data-access/spec.md §Registry pattern.
10
+ */
11
+ import { QueryClient } from '@tanstack/react-query';
12
+ import type { ReactNode } from 'react';
13
+ import * as React from 'react';
14
+ import type { EngineAdapters } from './dispatcher';
15
+ import type { EventBus, Registry, RequestContext } from './types';
16
+ /**
17
+ * Concrete provider props. Diverges from DataAccessProviderProps in
18
+ * ./types (which stays engine-agnostic to avoid a circular import) by
19
+ * accepting a strongly typed EngineAdapters bundle.
20
+ */
21
+ export interface DataAccessProviderConfig {
22
+ registry: Registry;
23
+ engines: EngineAdapters;
24
+ context: () => RequestContext;
25
+ /**
26
+ * Optional override — when omitted the provider mints its own bus.
27
+ * Sharing a bus across nested providers is a v0.2.0 concern.
28
+ */
29
+ bus?: EventBus;
30
+ /**
31
+ * Optional override — when omitted the provider mints its own
32
+ * QueryClient with default options. Supply your own to share a
33
+ * client across nested trees or to configure retry/staleTime.
34
+ */
35
+ queryClient?: QueryClient;
36
+ children?: ReactNode;
37
+ }
38
+ /**
39
+ * Runtime shape exposed via context. Kept internal so app code goes
40
+ * through hooks (useRequestContext / useAppEvent / …) — no direct
41
+ * consumers of the context value.
42
+ */
43
+ export interface DataAccessRuntime {
44
+ registry: Registry;
45
+ engines: EngineAdapters;
46
+ bus: EventBus;
47
+ queryClient: QueryClient;
48
+ getContext: () => RequestContext;
49
+ }
50
+ export declare function DataAccessProvider(props: DataAccessProviderConfig): React.ReactElement;
51
+ /**
52
+ * Internal — used by every hook to reach the runtime. Throws a typed
53
+ * error when a hook is used outside a DataAccessProvider.
54
+ */
55
+ export declare function useDataAccessRuntime(): DataAccessRuntime;
56
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAA;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,cAAc,EACf,MAAM,SAAS,CAAA;AAEhB;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,MAAM,cAAc,CAAA;IAC7B;;;OAGG;IACH,GAAG,CAAC,EAAE,QAAQ,CAAA;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,EAAE,cAAc,CAAA;IACvB,GAAG,EAAE,QAAQ,CAAA;IACb,WAAW,EAAE,WAAW,CAAA;IACxB,UAAU,EAAE,MAAM,cAAc,CAAA;CACjC;AAMD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,wBAAwB,GAC9B,KAAK,CAAC,YAAY,CAyCpB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CASxD"}
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * DataAccessProvider (T-16) + internal context.
4
+ *
5
+ * The provider is the single injection point for the registry, engine
6
+ * adapters, RequestContext factory, event bus, and TanStack QueryClient.
7
+ * If no bus / queryClient is supplied, one is created lazily and shared
8
+ * across the entire tree.
9
+ *
10
+ * See data-access/spec.md §Registry pattern.
11
+ */
12
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
13
+ import * as React from 'react';
14
+ import { DataAccessError } from './errors';
15
+ import { createEventBus } from './invalidation';
16
+ const DataAccessContext = React.createContext(null);
17
+ DataAccessContext.displayName = 'DataAccessContext';
18
+ export function DataAccessProvider(props) {
19
+ const { registry, engines, context, bus: suppliedBus, queryClient: suppliedClient, children, } = props;
20
+ // Bus / client are created once per provider mount. Deliberately not
21
+ // memoized on the supplied identity — swapping either mid-lifetime is
22
+ // unsupported.
23
+ const busRef = React.useRef(null);
24
+ if (busRef.current === null) {
25
+ busRef.current = suppliedBus ?? createEventBus();
26
+ }
27
+ const clientRef = React.useRef(null);
28
+ if (clientRef.current === null) {
29
+ clientRef.current = suppliedClient ?? new QueryClient();
30
+ }
31
+ const runtime = React.useMemo(() => ({
32
+ registry,
33
+ engines,
34
+ bus: busRef.current,
35
+ queryClient: clientRef.current,
36
+ getContext: context,
37
+ }), [registry, engines, context]);
38
+ return (_jsx(DataAccessContext.Provider, { value: runtime, children: _jsx(QueryClientProvider, { client: runtime.queryClient, children: children }) }));
39
+ }
40
+ /**
41
+ * Internal — used by every hook to reach the runtime. Throws a typed
42
+ * error when a hook is used outside a DataAccessProvider.
43
+ */
44
+ export function useDataAccessRuntime() {
45
+ const runtime = React.useContext(DataAccessContext);
46
+ if (runtime === null) {
47
+ throw new DataAccessError('engine_missing', 'data-access hook called outside <DataAccessProvider>');
48
+ }
49
+ return runtime;
50
+ }
51
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAExE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AA2C/C,MAAM,iBAAiB,GAAG,KAAK,CAAC,aAAa,CAA2B,IAAI,CAAC,CAAA;AAE7E,iBAAiB,CAAC,WAAW,GAAG,mBAAmB,CAAA;AAEnD,MAAM,UAAU,kBAAkB,CAChC,KAA+B;IAE/B,MAAM,EACJ,QAAQ,EACR,OAAO,EACP,OAAO,EACP,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,cAAc,EAC3B,QAAQ,GACT,GAAG,KAAK,CAAA;IAET,qEAAqE;IACrE,sEAAsE;IACtE,eAAe;IACf,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAkB,IAAI,CAAC,CAAA;IAClD,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC5B,MAAM,CAAC,OAAO,GAAG,WAAW,IAAI,cAAc,EAAE,CAAA;IAClD,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAqB,IAAI,CAAC,CAAA;IACxD,IAAI,SAAS,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC/B,SAAS,CAAC,OAAO,GAAG,cAAc,IAAI,IAAI,WAAW,EAAE,CAAA;IACzD,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAC3B,GAAG,EAAE,CAAC,CAAC;QACL,QAAQ;QACR,OAAO;QACP,GAAG,EAAE,MAAM,CAAC,OAAmB;QAC/B,WAAW,EAAE,SAAS,CAAC,OAAsB;QAC7C,UAAU,EAAE,OAAO;KACpB,CAAC,EACF,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAC7B,CAAA;IAED,OAAO,CACL,KAAC,iBAAiB,CAAC,QAAQ,IAAC,KAAK,EAAE,OAAO,YACxC,KAAC,mBAAmB,IAAC,MAAM,EAAE,OAAO,CAAC,WAAW,YAC7C,QAAQ,GACW,GACK,CAC9B,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAA;IACnD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,eAAe,CACvB,gBAAgB,EAChB,sDAAsD,CACvD,CAAA;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Registry primitives — defineQuery / defineMutation / defineEvent.
3
+ *
4
+ * The Define API is entirely local: no engine access, no React, no
5
+ * dispatcher. It hands back typed, discriminated handles that the
6
+ * dispatcher (arriving in a later task) can safely inspect and route.
7
+ *
8
+ * See data-access/spec.md §Define API.
9
+ */
10
+ import type { EventDefinition, MutationConfig, MutationDefinition, QueryConfig, QueryDefinition } from './types';
11
+ /**
12
+ * Register a typed query. Runtime enforces the engine-specific required
13
+ * field so JS callers get a loud error even when TypeScript is skipped.
14
+ */
15
+ export declare function defineQuery<TInput, TOutput>(config: QueryConfig<TInput, TOutput>): QueryDefinition<TInput, TOutput>;
16
+ /**
17
+ * Register a typed mutation. `exec` is required; `invalidates` patterns
18
+ * fire through the invalidation bus on success.
19
+ */
20
+ export declare function defineMutation<TInput, TOutput>(config: MutationConfig<TInput, TOutput>): MutationDefinition<TInput, TOutput>;
21
+ /**
22
+ * Register a typed event. Payload type is inferred at the point of use
23
+ * (via useAppEvent). No runtime state is captured here.
24
+ */
25
+ export declare function defineEvent<TPayload>(): EventDefinition<TPayload>;
26
+ //# sourceMappingURL=define.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,eAAe,EAChB,MAAM,SAAS,CAAA;AAEhB;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,EACzC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,CAGlC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAC5C,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAQrC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,KAAK,eAAe,CAAC,QAAQ,CAAC,CAEjE"}
package/dist/define.js ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Registry primitives — defineQuery / defineMutation / defineEvent.
3
+ *
4
+ * The Define API is entirely local: no engine access, no React, no
5
+ * dispatcher. It hands back typed, discriminated handles that the
6
+ * dispatcher (arriving in a later task) can safely inspect and route.
7
+ *
8
+ * See data-access/spec.md §Define API.
9
+ */
10
+ import { DataAccessError } from './errors';
11
+ /**
12
+ * Register a typed query. Runtime enforces the engine-specific required
13
+ * field so JS callers get a loud error even when TypeScript is skipped.
14
+ */
15
+ export function defineQuery(config) {
16
+ assertQueryEngineField(config);
17
+ return { __kind: 'query', config };
18
+ }
19
+ /**
20
+ * Register a typed mutation. `exec` is required; `invalidates` patterns
21
+ * fire through the invalidation bus on success.
22
+ */
23
+ export function defineMutation(config) {
24
+ if (typeof config.exec !== 'function') {
25
+ throw new DataAccessError('define_config_invalid', 'defineMutation requires an `exec` function');
26
+ }
27
+ return { __kind: 'mutation', config };
28
+ }
29
+ /**
30
+ * Register a typed event. Payload type is inferred at the point of use
31
+ * (via useAppEvent). No runtime state is captured here.
32
+ */
33
+ export function defineEvent() {
34
+ return { __kind: 'event' };
35
+ }
36
+ function assertQueryEngineField(config) {
37
+ switch (config.engine) {
38
+ case 'duckdb':
39
+ if (typeof config.sql !== 'string' || config.sql.length === 0) {
40
+ throw new DataAccessError('define_config_invalid', "defineQuery: engine 'duckdb' requires a non-empty `sql` string");
41
+ }
42
+ return;
43
+ case 'rxdb':
44
+ if (typeof config.query !== 'function') {
45
+ throw new DataAccessError('define_config_invalid', "defineQuery: engine 'rxdb' requires a `query` function");
46
+ }
47
+ return;
48
+ case 'kv':
49
+ if (typeof config.keyBuilder !== 'function') {
50
+ throw new DataAccessError('define_config_invalid', "defineQuery: engine 'kv' requires a `keyBuilder` function");
51
+ }
52
+ return;
53
+ default: {
54
+ // Exhaustiveness: if a new engine is added, TS surfaces this at compile time.
55
+ const exhaustive = config;
56
+ throw new DataAccessError('define_config_invalid', `defineQuery: unknown engine ${JSON.stringify(exhaustive)}`);
57
+ }
58
+ }
59
+ }
60
+ //# sourceMappingURL=define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAS1C;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,MAAoC;IAEpC,sBAAsB,CAAC,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAA;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAuC;IAEvC,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACtC,MAAM,IAAI,eAAe,CACvB,uBAAuB,EACvB,4CAA4C,CAC7C,CAAA;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAA;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAC5B,CAAC;AAED,SAAS,sBAAsB,CAC7B,MAAoC;IAEpC,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;QACtB,KAAK,QAAQ;YACX,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9D,MAAM,IAAI,eAAe,CACvB,uBAAuB,EACvB,gEAAgE,CACjE,CAAA;YACH,CAAC;YACD,OAAM;QACR,KAAK,MAAM;YACT,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBACvC,MAAM,IAAI,eAAe,CACvB,uBAAuB,EACvB,wDAAwD,CACzD,CAAA;YACH,CAAC;YACD,OAAM;QACR,KAAK,IAAI;YACP,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;gBAC5C,MAAM,IAAI,eAAe,CACvB,uBAAuB,EACvB,2DAA2D,CAC5D,CAAA;YACH,CAAC;YACD,OAAM;QACR,OAAO,CAAC,CAAC,CAAC;YACR,8EAA8E;YAC9E,MAAM,UAAU,GAAU,MAAM,CAAA;YAChC,MAAM,IAAI,eAAe,CACvB,uBAAuB,EACvB,+BAA+B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAC5D,CAAA;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Engine dispatcher (T-06 · T-07 · T-08).
3
+ *
4
+ * `executeQuery` resolves a registered query definition against a set of
5
+ * engine adapters and returns typed output. This is the read-path used
6
+ * both by useAppQuery (arriving in a later task) and by direct callers
7
+ * that want dispatcher semantics without React.
8
+ *
9
+ * Concerns wired here:
10
+ *
11
+ * - T-06 — dispatch by `def.config.engine`
12
+ * - T-07 — run `authorize` gate before execution
13
+ * - T-08 — merge tenant pair into DuckDB params
14
+ *
15
+ * Engines are provided as adapter objects (see EngineAdapters below).
16
+ * The dispatcher never imports @mongrov/analytics or @mongrov/db.
17
+ */
18
+ import type { QueryDefinition, RequestContext, RxdbQueryConfig } from './types';
19
+ /**
20
+ * DuckDB adapter — analytics engine surface consumed by the dispatcher.
21
+ */
22
+ export interface DuckdbEngine {
23
+ execute(sql: string, params: Record<string, unknown>): Promise<unknown>;
24
+ }
25
+ /**
26
+ * RxDB adapter — the app-provided runner takes the raw QueryConfig and
27
+ * caller input, executes `config.query(db, input)`, converts the returned
28
+ * Observable to a promise (`firstValueFrom`), and yields the first value.
29
+ *
30
+ * This keeps rxjs out of the data-access peer graph — the adapter is
31
+ * responsible for the rxjs boundary.
32
+ */
33
+ export interface RxdbEngine {
34
+ db: unknown;
35
+ execute<TInput>(config: RxdbQueryConfig<TInput, unknown>, input: TInput): Promise<unknown>;
36
+ }
37
+ /**
38
+ * KV adapter — the dispatcher builds the key via `config.keyBuilder(input)`
39
+ * and reads through the app-provided store.
40
+ */
41
+ export interface KvEngine {
42
+ get(key: string): Promise<unknown> | unknown;
43
+ }
44
+ /**
45
+ * Concrete engine bundle. Any subset may be omitted; the dispatcher
46
+ * throws `engine_missing` if a query targets an omitted engine.
47
+ */
48
+ export interface EngineAdapters {
49
+ duckdb?: DuckdbEngine;
50
+ rxdb?: RxdbEngine;
51
+ kv?: KvEngine;
52
+ }
53
+ /**
54
+ * T-06 core — resolve a registered query and return typed output.
55
+ */
56
+ export declare function executeQuery<TInput, TOutput>(def: QueryDefinition<TInput, TOutput>, input: TInput, ctx: RequestContext, engines: EngineAdapters): Promise<TOutput>;
57
+ /**
58
+ * T-07 — authorization gate. Runs the definition's `authorize` if
59
+ * present; a `false` result throws `AuthorizationError`.
60
+ */
61
+ export declare function runAuthorize<TInput>(authorize: ((input: TInput, ctx: RequestContext) => boolean | Promise<boolean>) | undefined, input: TInput, ctx: RequestContext): Promise<void>;
62
+ //# sourceMappingURL=dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AASH,OAAO,KAAK,EAGV,eAAe,EACf,cAAc,EACd,eAAe,EAChB,MAAM,SAAS,CAAA;AAEhB;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACxE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAA;IACX,OAAO,CAAC,MAAM,EACZ,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;CAC7C;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,EAAE,CAAC,EAAE,QAAQ,CAAA;CACd;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,OAAO,EAChD,GAAG,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,CAAC,CAQlB;AAiCD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EACvC,SAAS,EACL,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GACpE,SAAS,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CAMf"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Engine dispatcher (T-06 · T-07 · T-08).
3
+ *
4
+ * `executeQuery` resolves a registered query definition against a set of
5
+ * engine adapters and returns typed output. This is the read-path used
6
+ * both by useAppQuery (arriving in a later task) and by direct callers
7
+ * that want dispatcher semantics without React.
8
+ *
9
+ * Concerns wired here:
10
+ *
11
+ * - T-06 — dispatch by `def.config.engine`
12
+ * - T-07 — run `authorize` gate before execution
13
+ * - T-08 — merge tenant pair into DuckDB params
14
+ *
15
+ * Engines are provided as adapter objects (see EngineAdapters below).
16
+ * The dispatcher never imports @mongrov/analytics or @mongrov/db.
17
+ */
18
+ import { AuthorizationError, DataAccessError, } from './errors';
19
+ import { mergeTenantParams } from './tenant';
20
+ /**
21
+ * T-06 core — resolve a registered query and return typed output.
22
+ */
23
+ export async function executeQuery(def, input, ctx, engines) {
24
+ const parsedInput = parseInput(def.config.input, input);
25
+ await runAuthorize(def.config.authorize, parsedInput, ctx);
26
+ const raw = await dispatchByEngine(def.config, parsedInput, ctx, engines);
27
+ return parseOutput(def.config.output, raw);
28
+ }
29
+ function parseInput(schema, input) {
30
+ if (!schema)
31
+ return input;
32
+ const result = schema.safeParse(input);
33
+ if (!result.success) {
34
+ throw new DataAccessError('zod_parse_failed', `input failed schema validation: ${result.error.message}`, result.error);
35
+ }
36
+ return result.data;
37
+ }
38
+ function parseOutput(schema, raw) {
39
+ const result = schema.safeParse(raw);
40
+ if (!result.success) {
41
+ throw new DataAccessError('zod_parse_failed', `output failed schema validation: ${result.error.message}`, result.error);
42
+ }
43
+ return result.data;
44
+ }
45
+ /**
46
+ * T-07 — authorization gate. Runs the definition's `authorize` if
47
+ * present; a `false` result throws `AuthorizationError`.
48
+ */
49
+ export async function runAuthorize(authorize, input, ctx) {
50
+ if (!authorize)
51
+ return;
52
+ const allowed = await Promise.resolve(authorize(input, ctx));
53
+ if (!allowed) {
54
+ throw new AuthorizationError('authorization denied by query authorize hook');
55
+ }
56
+ }
57
+ async function dispatchByEngine(config, input, ctx, engines) {
58
+ switch (config.engine) {
59
+ case 'duckdb':
60
+ return dispatchDuckdb(config, input, ctx, engines.duckdb);
61
+ case 'rxdb':
62
+ return dispatchRxdb(config, input, engines.rxdb);
63
+ case 'kv':
64
+ return dispatchKv(config, input, engines.kv);
65
+ default: {
66
+ const exhaustive = config;
67
+ throw new DataAccessError('engine_missing', `unknown engine: ${JSON.stringify(exhaustive)}`);
68
+ }
69
+ }
70
+ }
71
+ function dispatchDuckdb(config, input, ctx, engine) {
72
+ if (!engine) {
73
+ throw new DataAccessError('engine_missing', "engine 'duckdb' is not wired in this dispatcher");
74
+ }
75
+ const params = mergeTenantParams(input, ctx);
76
+ return engine.execute(config.sql, params);
77
+ }
78
+ function dispatchRxdb(config, input, engine) {
79
+ if (!engine) {
80
+ throw new DataAccessError('engine_missing', "engine 'rxdb' is not wired in this dispatcher");
81
+ }
82
+ return engine.execute(config, input);
83
+ }
84
+ async function dispatchKv(config, input, engine) {
85
+ if (!engine) {
86
+ throw new DataAccessError('engine_missing', "engine 'kv' is not wired in this dispatcher");
87
+ }
88
+ const key = config.keyBuilder(input);
89
+ return engine.get(key);
90
+ }
91
+ //# sourceMappingURL=dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"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"}