reflectdb 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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1260 -0
  3. package/dist/cjs/client/index.cjs +1252 -0
  4. package/dist/cjs/client/index.d.cts +629 -0
  5. package/dist/cjs/client/storage/indexeddb.cjs +253 -0
  6. package/dist/cjs/client/storage/indexeddb.d.cts +85 -0
  7. package/dist/cjs/core/index.cjs +202 -0
  8. package/dist/cjs/core/index.d.cts +473 -0
  9. package/dist/cjs/react/index.cjs +1512 -0
  10. package/dist/cjs/react/index.d.cts +748 -0
  11. package/dist/cjs/server/drizzle.cjs +413 -0
  12. package/dist/cjs/server/drizzle.d.cts +233 -0
  13. package/dist/cjs/server/index.cjs +4486 -0
  14. package/dist/cjs/server/index.d.cts +1988 -0
  15. package/dist/cjs/svelte/index.cjs +1414 -0
  16. package/dist/cjs/svelte/index.d.cts +645 -0
  17. package/dist/cjs/transport/bun-ws.cjs +244 -0
  18. package/dist/cjs/transport/bun-ws.d.cts +215 -0
  19. package/dist/cjs/transport/polling.cjs +285 -0
  20. package/dist/cjs/transport/polling.d.cts +210 -0
  21. package/dist/cjs/transport/sse.cjs +312 -0
  22. package/dist/cjs/transport/sse.d.cts +205 -0
  23. package/dist/cjs/transport/ws.cjs +330 -0
  24. package/dist/cjs/transport/ws.d.cts +236 -0
  25. package/dist/cjs/vanilla/index.cjs +1430 -0
  26. package/dist/cjs/vanilla/index.d.cts +657 -0
  27. package/dist/client/index.d.ts +629 -0
  28. package/dist/client/index.js +106 -0
  29. package/dist/client/storage/indexeddb.d.ts +85 -0
  30. package/dist/client/storage/indexeddb.js +213 -0
  31. package/dist/core/index.d.ts +473 -0
  32. package/dist/core/index.js +56 -0
  33. package/dist/react/index.d.ts +748 -0
  34. package/dist/react/index.js +366 -0
  35. package/dist/server/drizzle.d.ts +233 -0
  36. package/dist/server/drizzle.js +9 -0
  37. package/dist/server/index.d.ts +1988 -0
  38. package/dist/server/index.js +3959 -0
  39. package/dist/shared/esm-3tkwvysa.js +54 -0
  40. package/dist/shared/esm-b7xs9cde.js +4 -0
  41. package/dist/shared/esm-rw7jjtrv.js +58 -0
  42. package/dist/shared/esm-wkwx6bd9.js +25 -0
  43. package/dist/shared/esm-ytrd3hbq.js +1007 -0
  44. package/dist/shared/esm-z1xse19c.js +369 -0
  45. package/dist/svelte/index.d.ts +645 -0
  46. package/dist/svelte/index.js +262 -0
  47. package/dist/transport/bun-ws.d.ts +215 -0
  48. package/dist/transport/bun-ws.js +140 -0
  49. package/dist/transport/polling.d.ts +210 -0
  50. package/dist/transport/polling.js +181 -0
  51. package/dist/transport/sse.d.ts +205 -0
  52. package/dist/transport/sse.js +208 -0
  53. package/dist/transport/ws.d.ts +236 -0
  54. package/dist/transport/ws.js +226 -0
  55. package/dist/vanilla/index.d.ts +657 -0
  56. package/dist/vanilla/index.js +278 -0
  57. package/package.json +253 -0
@@ -0,0 +1,366 @@
1
+ import {
2
+ SyncClient,
3
+ pushSafely
4
+ } from "../shared/esm-ytrd3hbq.js";
5
+ import"../shared/esm-rw7jjtrv.js";
6
+ import"../shared/esm-3tkwvysa.js";
7
+ import"../shared/esm-b7xs9cde.js";
8
+
9
+ // src/react/context.tsx
10
+ import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
11
+ import { jsxDEV } from "react/jsx-dev-runtime";
12
+ var SyncContext = createContext(null);
13
+ function createBrowserWsTransport(url) {
14
+ let ws = null;
15
+ let handler = null;
16
+ let intentionalClose = false;
17
+ const sendQueue = [];
18
+ function ensureConnected() {
19
+ if (ws && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {
20
+ return ws;
21
+ }
22
+ const socket = new WebSocket(url);
23
+ socket.onopen = () => {
24
+ for (const msg of sendQueue) {
25
+ socket.send(JSON.stringify(msg));
26
+ }
27
+ sendQueue.length = 0;
28
+ };
29
+ socket.onmessage = (event) => {
30
+ try {
31
+ handler?.(JSON.parse(event.data));
32
+ } catch (err) {
33
+ console.warn("[reflectdb] react WS: malformed message ignored:", err instanceof Error ? err.message : String(err));
34
+ }
35
+ };
36
+ socket.onclose = () => {
37
+ ws = null;
38
+ if (!intentionalClose) {
39
+ handler?.({ type: "disconnect", reason: "transport_closed" });
40
+ }
41
+ };
42
+ ws = socket;
43
+ return socket;
44
+ }
45
+ return {
46
+ async send(message) {
47
+ const socket = ensureConnected();
48
+ if (socket.readyState === WebSocket.OPEN) {
49
+ socket.send(JSON.stringify(message));
50
+ } else {
51
+ sendQueue.push(message);
52
+ }
53
+ },
54
+ subscribe(h) {
55
+ handler = h;
56
+ },
57
+ async close() {
58
+ intentionalClose = true;
59
+ if (ws) {
60
+ ws.close();
61
+ ws = null;
62
+ }
63
+ }
64
+ };
65
+ }
66
+ function SyncProvider({
67
+ url,
68
+ token,
69
+ tables,
70
+ clientId,
71
+ storage,
72
+ onReauth,
73
+ onError,
74
+ children
75
+ }) {
76
+ const [ctx, setCtx] = useState(null);
77
+ const tablesKey = useMemo(() => JSON.stringify([...tables ?? []].sort()), [tables]);
78
+ const managedTables = useMemo(() => new Set(tables ?? []), [tablesKey]);
79
+ const onReauthRef = useRef(onReauth);
80
+ onReauthRef.current = onReauth;
81
+ const onErrorRef = useRef(onError);
82
+ onErrorRef.current = onError;
83
+ const tokenRef = useRef(token);
84
+ useEffect(() => {
85
+ tokenRef.current = token;
86
+ }, [token]);
87
+ useEffect(() => {
88
+ let cancelled = false;
89
+ const id = clientId ?? `browser-${crypto.randomUUID().slice(0, 8)}`;
90
+ const transport = createBrowserWsTransport(url);
91
+ const client = new SyncClient({
92
+ clientId: id,
93
+ transport,
94
+ token: tokenRef.current,
95
+ storage,
96
+ onReauth: () => onReauthRef.current?.() ?? Promise.reject(new Error("No onReauth handler")),
97
+ onError: (err) => onErrorRef.current?.(err)
98
+ });
99
+ (async () => {
100
+ if (storage) {
101
+ await client.init();
102
+ if (cancelled)
103
+ return;
104
+ setCtx({ client, managedTables });
105
+ }
106
+ await client.connect();
107
+ if (cancelled)
108
+ return;
109
+ for (const table of managedTables) {
110
+ await client.sync(table);
111
+ }
112
+ await client.resume();
113
+ if (cancelled)
114
+ return;
115
+ if (!storage) {
116
+ setCtx({ client, managedTables });
117
+ }
118
+ })();
119
+ return () => {
120
+ cancelled = true;
121
+ setCtx(null);
122
+ client.close();
123
+ };
124
+ }, [url, clientId, storage, managedTables]);
125
+ if (!ctx)
126
+ return null;
127
+ return /* @__PURE__ */ jsxDEV(SyncContext.Provider, {
128
+ value: ctx,
129
+ children
130
+ }, undefined, false, undefined, this);
131
+ }
132
+ function useSyncClient() {
133
+ const ctx = useContext(SyncContext);
134
+ if (!ctx) {
135
+ throw new Error("useSyncClient must be used within a <SyncProvider>");
136
+ }
137
+ return ctx.client;
138
+ }
139
+ // src/react/hooks.ts
140
+ import {
141
+ useCallback,
142
+ useContext as useContext2,
143
+ useEffect as useEffect2,
144
+ useMemo as useMemo2,
145
+ useRef as useRef2,
146
+ useState as useState2,
147
+ useSyncExternalStore
148
+ } from "react";
149
+ function useSync(table, options) {
150
+ const ctx = useContext2(SyncContext);
151
+ if (!ctx) {
152
+ throw new Error("useSync must be used within a <SyncProvider>");
153
+ }
154
+ const { client, managedTables } = ctx;
155
+ useEffect2(() => {
156
+ if (managedTables.has(table))
157
+ return;
158
+ acquireSync(client, table, options?.params, options?.window ? { window: options.window } : undefined);
159
+ client.scheduleBootstrap();
160
+ return () => {
161
+ releaseSync(client, table);
162
+ };
163
+ }, [client, table, managedTables, stableKey(options?.params), options?.window]);
164
+ const subscribe = useCallback((listener) => client.subscribeTable(table, listener), [client, table]);
165
+ const version = useSyncExternalStore(subscribe, () => client.getTableVersion(table), () => client.getTableVersion(table));
166
+ const rows = useMemo2(() => client.getRows(table, { includeDeleted: options?.includeDeleted }), [client, table, version, options?.includeDeleted]);
167
+ const insert = useCallback((rowId, payload) => {
168
+ client.insert(table, rowId, payload);
169
+ pushSafely(client);
170
+ }, [client, table]);
171
+ const update = useCallback((rowId, payload) => {
172
+ client.update(table, rowId, payload);
173
+ pushSafely(client);
174
+ }, [client, table]);
175
+ const remove = useCallback((rowId) => {
176
+ client.delete(table, rowId);
177
+ pushSafely(client);
178
+ }, [client, table]);
179
+ return { rows, insert, update, remove };
180
+ }
181
+ function useSyncStatus() {
182
+ const client = useSyncClient();
183
+ const subscribe = useCallback((listener) => client.subscribe(listener), [client]);
184
+ return useSyncExternalStore(subscribe, () => client.getState(), () => client.getState());
185
+ }
186
+ function useRow(table, rowId) {
187
+ const client = useSyncClient();
188
+ const subscribe = useCallback((listener) => client.subscribeTable(table, listener), [client, table]);
189
+ const version = useSyncExternalStore(subscribe, () => client.getTableVersion(table), () => client.getTableVersion(table));
190
+ return useMemo2(() => client.getRow(table, rowId), [client, table, rowId, version]);
191
+ }
192
+ function usePendingCount() {
193
+ const client = useSyncClient();
194
+ const subscribe = useCallback((listener) => client.subscribe(listener), [client]);
195
+ const version = useSyncExternalStore(subscribe, () => client.getVersion(), () => client.getVersion());
196
+ return useMemo2(() => client.getPendingCount(), [client, version]);
197
+ }
198
+ function useEphemeral(config) {
199
+ const client = useSyncClient();
200
+ const [events, setEvents] = useState2({});
201
+ const timersRef = useRef2(new Map);
202
+ useEffect2(() => {
203
+ const unsub = client.subscribeEphemeral(config.key, (event) => {
204
+ setEvents((prev) => ({ ...prev, [event.userId]: event.data }));
205
+ if (config.ttlMs) {
206
+ const existing = timersRef.current.get(event.userId);
207
+ if (existing) {
208
+ clearTimeout(existing);
209
+ }
210
+ const timer = setTimeout(() => {
211
+ setEvents((prev) => {
212
+ const next = { ...prev };
213
+ delete next[event.userId];
214
+ return next;
215
+ });
216
+ timersRef.current.delete(event.userId);
217
+ }, config.ttlMs);
218
+ timersRef.current.set(event.userId, timer);
219
+ }
220
+ });
221
+ return () => {
222
+ unsub();
223
+ for (const timer of timersRef.current.values()) {
224
+ clearTimeout(timer);
225
+ }
226
+ timersRef.current.clear();
227
+ };
228
+ }, [client, config.key, config.ttlMs]);
229
+ const broadcast = useCallback((data) => client.sendEphemeral({
230
+ key: config.key,
231
+ userId: config.userId,
232
+ data,
233
+ ttlMs: config.ttlMs
234
+ }), [client, config.key, config.userId, config.ttlMs]);
235
+ return { events, broadcast };
236
+ }
237
+ function useTotalCount(table) {
238
+ const client = useSyncClient();
239
+ const subscribe = useCallback((listener) => client.subscribeTable(table, listener), [client, table]);
240
+ const version = useSyncExternalStore(subscribe, () => client.getTableVersion(table), () => client.getTableVersion(table));
241
+ return useMemo2(() => client.getTotalCount(table), [client, table, version]);
242
+ }
243
+ function useLoadMore(table) {
244
+ const client = useSyncClient();
245
+ return useCallback((count) => {
246
+ client.loadMore(table, count);
247
+ }, [client, table]);
248
+ }
249
+ function stableKey(params) {
250
+ if (!params)
251
+ return "";
252
+ return JSON.stringify(params, Object.keys(params).sort());
253
+ }
254
+ var useSyncRefCounts = new WeakMap;
255
+ var useSyncParamKeys = new WeakMap;
256
+ function acquireSync(client, table, params, options) {
257
+ let counts = useSyncRefCounts.get(client);
258
+ if (!counts) {
259
+ counts = new Map;
260
+ useSyncRefCounts.set(client, counts);
261
+ }
262
+ let keys = useSyncParamKeys.get(client);
263
+ if (!keys) {
264
+ keys = new Map;
265
+ useSyncParamKeys.set(client, keys);
266
+ }
267
+ const key = stableKey(params);
268
+ const prev = keys.get(table);
269
+ const cnt = counts.get(table) ?? 0;
270
+ if (cnt === 0 || prev !== key) {
271
+ if (prev !== undefined && prev !== key)
272
+ client.unsync(table);
273
+ client.sync(table, params, options);
274
+ keys.set(table, key);
275
+ }
276
+ counts.set(table, cnt + 1);
277
+ }
278
+ function releaseSync(client, table) {
279
+ const counts = useSyncRefCounts.get(client);
280
+ const keys = useSyncParamKeys.get(client);
281
+ if (!counts || !keys)
282
+ return;
283
+ const cnt = (counts.get(table) ?? 1) - 1;
284
+ if (cnt <= 0) {
285
+ counts.delete(table);
286
+ keys.delete(table);
287
+ client.unsync(table);
288
+ } else {
289
+ counts.set(table, cnt);
290
+ }
291
+ }
292
+ // src/react/typed.ts
293
+ import { useMemo as useMemo3 } from "react";
294
+ function derivePresenceKey(name, params) {
295
+ if (!params)
296
+ return `presence:${name}`;
297
+ const sortedKeys = Object.keys(params).sort();
298
+ const stable = {};
299
+ for (const k of sortedKeys)
300
+ stable[k] = params[k];
301
+ return `presence:${name}:${JSON.stringify(stable)}`;
302
+ }
303
+ function createSyncReact(queries) {
304
+ function useSync2(...args) {
305
+ const [table, options] = args;
306
+ const result = useSync(table, options);
307
+ const def = queries?.[table];
308
+ if (def?.__view) {
309
+ return { rows: result.rows, loading: false };
310
+ }
311
+ return result;
312
+ }
313
+ function useRow2(table, rowId) {
314
+ return useRow(table, rowId);
315
+ }
316
+ function useTotalCount2(table) {
317
+ return useTotalCount(table);
318
+ }
319
+ function useLoadMore2(table) {
320
+ return useLoadMore(table);
321
+ }
322
+ function usePresence(...args) {
323
+ const name = args[0];
324
+ const params = args[1];
325
+ const def = queries?.[name] ?? {};
326
+ const ttlMs = def.ttlMs;
327
+ const client = useSyncClient();
328
+ const userId = client.config?.clientId ?? "anonymous";
329
+ const key = useMemo3(() => derivePresenceKey(name, params), [name, params]);
330
+ const { events, broadcast } = useEphemeral({
331
+ key,
332
+ userId,
333
+ ttlMs
334
+ });
335
+ const peers = useMemo3(() => Object.entries(events).map(([uid, state]) => ({
336
+ userId: uid,
337
+ state
338
+ })), [events]);
339
+ return { peers, set: broadcast };
340
+ }
341
+ return {
342
+ SyncProvider,
343
+ useSync: useSync2,
344
+ useSyncStatus,
345
+ useRow: useRow2,
346
+ usePendingCount,
347
+ useEphemeral,
348
+ usePresence,
349
+ useTotalCount: useTotalCount2,
350
+ useLoadMore: useLoadMore2
351
+ };
352
+ }
353
+ export {
354
+ useTotalCount,
355
+ useSyncStatus,
356
+ useSyncClient,
357
+ useSync,
358
+ useRow,
359
+ usePendingCount,
360
+ useLoadMore,
361
+ useEphemeral,
362
+ derivePresenceKey,
363
+ createSyncReact,
364
+ SyncProvider,
365
+ SyncContext
366
+ };
@@ -0,0 +1,233 @@
1
+ type OpType = "insert" | "update" | "delete";
2
+ interface AuthContext {
3
+ userId: string;
4
+ [key: string]: unknown;
5
+ }
6
+ interface DrizzleTableLike {
7
+ $inferSelect: Record<string, unknown>;
8
+ $inferInsert: Record<string, unknown>;
9
+ }
10
+ /**
11
+ * Adapter contract for `server.tx({ atomic: true })` — pluggable so non-drizzle
12
+ * data layers (kysely, prisma, raw SQL) can wrap their own BEGIN/COMMIT/ROLLBACK
13
+ * without reflectdb hard-coding a drizzle dependency.
14
+ *
15
+ * Pass an adapter via:
16
+ * - `server.tx({ atomic: <adapter> }, fn)` — per-call override.
17
+ * - `ServerConfig.txAtomic` — server-wide default for `atomic: true`.
18
+ *
19
+ * If neither is supplied and `atomic: true` is requested, reflectdb falls back to
20
+ * the bundled drizzle adapter via dynamic import (no top-level dep). When
21
+ * drizzle isn't installed the fallback throws a clear error directing users to
22
+ * supply a `txAtomic` adapter.
23
+ */
24
+ interface TxAtomicAdapter {
25
+ /** Begin a transaction on the given db handle. */
26
+ begin(db: unknown): Promise<void>;
27
+ /** Commit the in-flight transaction. */
28
+ commit(db: unknown): Promise<void>;
29
+ /** Roll back the in-flight transaction. */
30
+ rollback(db: unknown): Promise<void>;
31
+ }
32
+ interface ResolvedOp {
33
+ type: OpType;
34
+ table: string;
35
+ rowId: string;
36
+ payload: Record<string, unknown> | null;
37
+ hlc: string;
38
+ }
39
+ interface MutationContext<TAuth extends AuthContext = AuthContext> {
40
+ auth: TAuth;
41
+ params: Record<string, unknown>;
42
+ }
43
+ type AuthorizeAction = {
44
+ type: "read";
45
+ table: string;
46
+ params: Record<string, unknown>;
47
+ } | {
48
+ type: "write";
49
+ table: string;
50
+ op: ResolvedOp;
51
+ };
52
+ type Ctx<
53
+ TAuth extends AuthContext,
54
+ TParams extends Record<string, unknown>
55
+ > = {
56
+ auth: TAuth;
57
+ params: TParams;
58
+ };
59
+ /**
60
+ * Function-form scope returns an adapter-interpreted SQL fragment (e.g. a
61
+ * drizzle `SQL`, a kysely `Expression<boolean>`, or a raw string). The generic
62
+ * factory treats it as opaque (`unknown`) — ORM-specific helpers like
63
+ * `drizzleTable` narrow the return type at their call site.
64
+ */
65
+ type ScopeFn<
66
+ TAuth extends AuthContext,
67
+ TParams extends Record<string, unknown>
68
+ > = (ctx: Ctx<TAuth, TParams>) => unknown;
69
+ type ScopeOpt<
70
+ TRowKey extends string,
71
+ TAuth extends AuthContext,
72
+ TParams extends Record<string, unknown>
73
+ > = TRowKey | {
74
+ auth: keyof TAuth & string;
75
+ column: TRowKey;
76
+ } | ScopeFn<TAuth, TParams>;
77
+ type StampSource<
78
+ TAuth extends AuthContext,
79
+ TParams extends Record<string, unknown>
80
+ > = "auth.userId" | "auth.name" | string | ((ctx: Ctx<TAuth, TParams>) => unknown);
81
+ type ServerSetSource<
82
+ TAuth extends AuthContext,
83
+ TParams extends Record<string, unknown>
84
+ > = unknown | ((ctx: Ctx<TAuth, TParams>) => unknown);
85
+ type FieldPolicy<
86
+ TAuth extends AuthContext,
87
+ TParams extends Record<string, unknown>
88
+ > = "server-only" | "insert-only" | {
89
+ write: (input: {
90
+ ctx: Ctx<TAuth, TParams>;
91
+ existing?: Record<string, unknown> | null;
92
+ payload: Record<string, unknown>;
93
+ }) => boolean | Promise<boolean>;
94
+ };
95
+ interface DefineTableOpts<
96
+ TRow extends Record<string, unknown> = Record<string, unknown>,
97
+ TAuth extends AuthContext = AuthContext,
98
+ TParams extends Record<string, unknown> = Record<string, unknown>
99
+ > {
100
+ scope?: ScopeOpt<keyof TRow & string, TAuth, TParams>;
101
+ rowId?: string;
102
+ ownerStamp?: Partial<Record<keyof TRow & string, StampSource<TAuth, TParams>>>;
103
+ serverSet?: Partial<Record<keyof TRow & string, ServerSetSource<TAuth, TParams>>>;
104
+ policy?: {
105
+ read?: (input: {
106
+ ctx: Ctx<TAuth, TParams>;
107
+ existing?: Record<string, unknown> | null;
108
+ }) => boolean | Promise<boolean>;
109
+ insert?: (input: {
110
+ ctx: Ctx<TAuth, TParams>;
111
+ payload: Record<string, unknown>;
112
+ }) => boolean | Promise<boolean>;
113
+ update?: (input: {
114
+ ctx: Ctx<TAuth, TParams>;
115
+ existing: Record<string, unknown>;
116
+ payload: Record<string, unknown>;
117
+ }) => boolean | Promise<boolean>;
118
+ delete?: (input: {
119
+ ctx: Ctx<TAuth, TParams>;
120
+ existing: Record<string, unknown>;
121
+ }) => boolean | Promise<boolean>;
122
+ fields?: Partial<Record<keyof TRow & string, FieldPolicy<TAuth, TParams>>>;
123
+ };
124
+ hooks?: {
125
+ beforeInsert?: (input: {
126
+ payload: Record<string, unknown>;
127
+ ctx: Ctx<TAuth, TParams>;
128
+ db: any;
129
+ }) => Promise<void> | void;
130
+ afterInsert?: (input: {
131
+ row: Record<string, unknown>;
132
+ ctx: Ctx<TAuth, TParams>;
133
+ db: any;
134
+ }) => Promise<void> | void;
135
+ beforeUpdate?: (input: {
136
+ payload: Record<string, unknown>;
137
+ existing: Record<string, unknown>;
138
+ ctx: Ctx<TAuth, TParams>;
139
+ db: any;
140
+ }) => Promise<void> | void;
141
+ afterUpdate?: (input: {
142
+ row: Record<string, unknown>;
143
+ previous: Record<string, unknown>;
144
+ ctx: Ctx<TAuth, TParams>;
145
+ db: any;
146
+ }) => Promise<void> | void;
147
+ beforeDelete?: (input: {
148
+ existing: Record<string, unknown>;
149
+ ctx: Ctx<TAuth, TParams>;
150
+ db: any;
151
+ }) => Promise<void> | void;
152
+ afterDelete?: (input: {
153
+ rowId: string;
154
+ ctx: Ctx<TAuth, TParams>;
155
+ db: any;
156
+ }) => Promise<void> | void;
157
+ };
158
+ query?: (ctx: Ctx<TAuth, TParams>, db: any) => unknown;
159
+ insert?: (input: {
160
+ payload: Record<string, unknown>;
161
+ existing?: Record<string, unknown> | null;
162
+ ctx: Ctx<TAuth, TParams>;
163
+ db: any;
164
+ }) => Promise<void> | void;
165
+ update?: (input: {
166
+ payload: Record<string, unknown>;
167
+ existing: Record<string, unknown>;
168
+ ctx: Ctx<TAuth, TParams>;
169
+ db: any;
170
+ }) => Promise<void> | void;
171
+ delete?: (input: {
172
+ rowId: string;
173
+ existing: Record<string, unknown> | null;
174
+ ctx: Ctx<TAuth, TParams>;
175
+ db: any;
176
+ }) => Promise<void> | void;
177
+ pk?: keyof TRow & string;
178
+ }
179
+ import { SQL as SQL_1rji } from "drizzle-orm";
180
+ type SQL = SQL_1rji;
181
+ type Ctx2<
182
+ TAuth extends AuthContext,
183
+ TParams extends Record<string, unknown>
184
+ > = {
185
+ auth: TAuth;
186
+ params: TParams;
187
+ };
188
+ type DrizzleScopeFn<
189
+ TAuth extends AuthContext,
190
+ TParams extends Record<string, unknown>
191
+ > = (ctx: Ctx2<TAuth, TParams>) => SQL;
192
+ type DrizzleScopeOpt<
193
+ TRowKey extends string,
194
+ TAuth extends AuthContext,
195
+ TParams extends Record<string, unknown>
196
+ > = TRowKey | {
197
+ auth: keyof TAuth & string;
198
+ column: TRowKey;
199
+ } | DrizzleScopeFn<TAuth, TParams>;
200
+ /**
201
+ * Drizzle-flavored options. Identical surface to `DefineTableOpts` except the
202
+ * row type is derived from `$inferSelect` and function-form scope returns
203
+ * drizzle's `SQL` (vs. the generic factory's `unknown`). Kept for backward
204
+ * compat with the existing public `drizzleTable<TTable, ...>(opts)` call shape.
205
+ */
206
+ interface DrizzleTableOpts<
207
+ TTable extends DrizzleTableLike,
208
+ TAuth extends AuthContext = AuthContext,
209
+ TParams extends Record<string, unknown> = Record<string, unknown>
210
+ > extends Omit<DefineTableOpts<TTable["$inferSelect"], TAuth, TParams>, "scope"> {
211
+ scope?: DrizzleScopeOpt<keyof TTable["$inferSelect"] & string, TAuth, TParams>;
212
+ }
213
+ declare function drizzleTable<
214
+ TTable extends DrizzleTableLike,
215
+ TAuth extends AuthContext = AuthContext,
216
+ TParams extends Record<string, unknown> = Record<string, unknown>
217
+ >(table: TTable, opts?: DrizzleTableOpts<TTable, TAuth, TParams>): {
218
+ query: (ctx: Ctx2<TAuth, TParams>, db: any) => unknown;
219
+ mutate: (op: ResolvedOp, ctx: MutationContext<TAuth>, db: any) => Promise<void>;
220
+ authorize: (action: AuthorizeAction, ctx: MutationContext<TAuth>, db: any) => Promise<void>;
221
+ };
222
+ /**
223
+ * `TxAtomicAdapter` for drizzle handles. Uses raw SQL via `db.run(sql\`BEGIN\`)`
224
+ * — drizzle's async `db.transaction(...)` is unsafe on the bun-sqlite dialect
225
+ * (sync transaction commits before the async body finishes), so we route
226
+ * through `db.run(...)` which is portable across sqlite and postgres adapters.
227
+ *
228
+ * Exported so users can pass it explicitly to `tx({ atomic: drizzleTxAtomic })`
229
+ * or to `ServerConfig.txAtomic` — and so the server's lazy-loaded fallback
230
+ * has a single source of truth.
231
+ */
232
+ declare const drizzleTxAtomic: TxAtomicAdapter;
233
+ export { drizzleTxAtomic, drizzleTable, DrizzleTableOpts };
@@ -0,0 +1,9 @@
1
+ import {
2
+ drizzleTable,
3
+ drizzleTxAtomic
4
+ } from "../shared/esm-z1xse19c.js";
5
+ import"../shared/esm-b7xs9cde.js";
6
+ export {
7
+ drizzleTxAtomic,
8
+ drizzleTable
9
+ };