@gencow/core 0.1.17 → 0.1.19
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/dist/crud.js +0 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/reactive.d.ts +35 -22
- package/dist/reactive.js +66 -46
- package/dist/scoped-db.d.ts +34 -0
- package/dist/scoped-db.js +364 -0
- package/dist/storage.js +8 -7
- package/dist/table.d.ts +67 -0
- package/dist/table.js +98 -0
- package/dist/v.js +5 -1
- package/package.json +1 -1
- package/src/__tests__/crud-codegen-integration.test.ts +0 -1
- package/src/__tests__/load.test.ts +13 -31
- package/src/__tests__/reactive.test.ts +44 -62
- package/src/__tests__/storage.test.ts +113 -0
- package/src/__tests__/validator.test.ts +35 -0
- package/src/crud.ts +0 -3
- package/src/index.ts +1 -1
- package/src/reactive.ts +89 -50
- package/src/storage.ts +8 -7
- package/src/v.ts +5 -1
package/dist/crud.js
CHANGED
|
@@ -280,7 +280,6 @@ export function crud(table, options) {
|
|
|
280
280
|
// ── create ────────────────────────────────────
|
|
281
281
|
const createDef = !enabledMethods.has('create') ? undefined : mutation(`${prefix}.create`, {
|
|
282
282
|
public: isPublic,
|
|
283
|
-
invalidates: [],
|
|
284
283
|
handler: async (ctx, args) => {
|
|
285
284
|
const user = isPublic ? null : ctx.auth.requireAuth();
|
|
286
285
|
let insertData = { ...args };
|
|
@@ -304,7 +303,6 @@ export function crud(table, options) {
|
|
|
304
303
|
// ── update ────────────────────────────────────
|
|
305
304
|
const updateDef = !enabledMethods.has('update') ? undefined : mutation(`${prefix}.update`, {
|
|
306
305
|
public: isPublic,
|
|
307
|
-
invalidates: [],
|
|
308
306
|
handler: async (ctx, args) => {
|
|
309
307
|
if (!isPublic)
|
|
310
308
|
ctx.auth.requireAuth();
|
|
@@ -338,7 +336,6 @@ export function crud(table, options) {
|
|
|
338
336
|
// ── remove ────────────────────────────────────
|
|
339
337
|
const removeDef = !enabledMethods.has('remove') ? undefined : mutation(`${prefix}.remove`, {
|
|
340
338
|
public: isPublic,
|
|
341
|
-
invalidates: [],
|
|
342
339
|
handler: async (ctx, args) => {
|
|
343
340
|
if (!isPublic)
|
|
344
341
|
ctx.auth.requireAuth();
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* All with Convex-compatible DX patterns.
|
|
6
6
|
*/
|
|
7
7
|
export type { GencowCtx, AuthCtx, UserIdentity, QueryDef, MutationDef, RealtimeCtx, HttpActionDef, HttpActionRequest, HttpActionResponse, HttpActionHandler, AIContext, AIMessage, AIResult } from "./reactive";
|
|
8
|
-
export { query, mutation, httpAction,
|
|
8
|
+
export { query, mutation, httpAction, buildRealtimeCtx, subscribe, unsubscribe, registerClient, deregisterClient, handleWsMessage, getQueryHandler, getQueryDef, getRegisteredQueries, getRegisteredMutations, getRegisteredHttpActions } from "./reactive";
|
|
9
9
|
export type { Storage } from "./storage";
|
|
10
10
|
export { createScheduler, getSchedulerInfo } from "./scheduler";
|
|
11
11
|
export type { Scheduler, ScheduleOptions, FailedJob } from "./scheduler";
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides: query, mutation, storage, scheduler, auth
|
|
5
5
|
* All with Convex-compatible DX patterns.
|
|
6
6
|
*/
|
|
7
|
-
export { query, mutation, httpAction,
|
|
7
|
+
export { query, mutation, httpAction, buildRealtimeCtx, subscribe, unsubscribe, registerClient, deregisterClient, handleWsMessage, getQueryHandler, getQueryDef, getRegisteredQueries, getRegisteredMutations, getRegisteredHttpActions } from "./reactive";
|
|
8
8
|
export { createScheduler, getSchedulerInfo } from "./scheduler";
|
|
9
9
|
export { v, parseArgs, GencowValidationError } from "./v";
|
|
10
10
|
export { withRetry } from "./retry";
|
package/dist/reactive.d.ts
CHANGED
|
@@ -19,6 +19,9 @@ export interface AuthCtx {
|
|
|
19
19
|
*/
|
|
20
20
|
export interface RealtimeCtx {
|
|
21
21
|
/**
|
|
22
|
+
* 특정 queryKey를 구독 중인 클라이언트에 데이터를 즉시 push합니다.
|
|
23
|
+
* 초고빈도 mutation (채팅 등)에서 query re-run 비용을 회피할 때 사용.
|
|
24
|
+
*
|
|
22
25
|
* @param queryKey - 업데이트할 쿼리 키 (예: "tasks.list")
|
|
23
26
|
* @param data - push할 데이터 (해당 query 결과와 동일한 타입)
|
|
24
27
|
*
|
|
@@ -27,6 +30,23 @@ export interface RealtimeCtx {
|
|
|
27
30
|
* ctx.realtime.emit("tasks.list", freshList);
|
|
28
31
|
*/
|
|
29
32
|
emit(queryKey: string, data: unknown): void;
|
|
33
|
+
/**
|
|
34
|
+
* 수동 mutation에서 리얼타임 업데이트의 기본 권장 방식.
|
|
35
|
+
* mutation handler 완료 후 서버가 해당 query를 re-run하여 결과를 push합니다.
|
|
36
|
+
* 복잡한 JOIN query도 queryKey만 지정하면 됩니다.
|
|
37
|
+
*
|
|
38
|
+
* @param queryKey - re-run할 쿼리 키 (예: "dashboard.revenue")
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* mutation("orders.place", {
|
|
42
|
+
* handler: async (ctx, args) => {
|
|
43
|
+
* await ctx.db.insert(orders).values(args);
|
|
44
|
+
* ctx.realtime.refresh("orders.list");
|
|
45
|
+
* ctx.realtime.refresh("dashboard.revenue"); // JOIN query도 OK
|
|
46
|
+
* }
|
|
47
|
+
* });
|
|
48
|
+
*/
|
|
49
|
+
refresh(queryKey: string): void;
|
|
30
50
|
}
|
|
31
51
|
/**
|
|
32
52
|
* 사용자 함수(query/mutation)에 주입되는 컨텍스트.
|
|
@@ -128,7 +148,6 @@ export interface QueryDef<TSchema = any, TReturn = any> {
|
|
|
128
148
|
_return?: TReturn;
|
|
129
149
|
}
|
|
130
150
|
export interface MutationDef<TSchema = any, TReturn = any> {
|
|
131
|
-
invalidates: string[];
|
|
132
151
|
handler: MutationHandler<InferArgs<TSchema>, TReturn>;
|
|
133
152
|
argsSchema?: TSchema;
|
|
134
153
|
/** true = 인증 없이 접근 가능, false(기본) = auth 필수 (Secure by Default) */
|
|
@@ -170,7 +189,6 @@ export declare function query<TSchema = any, TReturn = any>(key: string, handler
|
|
|
170
189
|
* ```typescript
|
|
171
190
|
* // ✅ 권장: query와 동일한 (name, def) 패턴
|
|
172
191
|
* mutation("tasks.create", {
|
|
173
|
-
* invalidates: [],
|
|
174
192
|
* args: { title: v.string() },
|
|
175
193
|
* handler: async (ctx, args) => { ... },
|
|
176
194
|
* });
|
|
@@ -178,19 +196,21 @@ export declare function query<TSchema = any, TReturn = any>(key: string, handler
|
|
|
178
196
|
* // ✅ 객체 스타일 (하위 호환)
|
|
179
197
|
* mutation({
|
|
180
198
|
* name: "tasks.create",
|
|
181
|
-
* invalidates: [],
|
|
182
199
|
* handler: async (ctx) => { ... },
|
|
183
200
|
* });
|
|
184
201
|
*
|
|
185
202
|
* // ⚠️ Legacy 배열 스타일 (비권장)
|
|
186
203
|
* mutation(["tasks.list"], handler, "tasks.create");
|
|
187
204
|
* ```
|
|
205
|
+
*
|
|
206
|
+
* @note invalidates 필드는 deprecated — 전달해도 무시됩니다.
|
|
207
|
+
* 리얼타임 UI 갱신에는 ctx.realtime.emit() 또는 ctx.realtime.refresh()를 사용하세요.
|
|
188
208
|
*/
|
|
189
209
|
export declare function mutation<TSchema = any, TReturn = any>(nameOrInvalidatesOrDef: string | string[] | {
|
|
190
210
|
name?: string;
|
|
191
211
|
args?: TSchema;
|
|
192
212
|
public?: boolean;
|
|
193
|
-
invalidates
|
|
213
|
+
invalidates?: string[];
|
|
194
214
|
handler: MutationHandler<InferArgs<TSchema>, TReturn>;
|
|
195
215
|
}, handlerOrDef?: MutationHandler<InferArgs<TSchema>, TReturn> | {
|
|
196
216
|
invalidates?: string[];
|
|
@@ -235,14 +255,10 @@ export declare function unsubscribe(ws: WSContext): void;
|
|
|
235
255
|
/** Register a raw WS connection without any query subscription (e.g. admin dashboard) */
|
|
236
256
|
export declare function registerClient(ws: WSContext): void;
|
|
237
257
|
export declare function deregisterClient(ws: WSContext): void;
|
|
238
|
-
/**
|
|
239
|
-
* After a mutation, re-run invalidated queries and push results
|
|
240
|
-
* to all subscribers — Convex의 자동 reactive 업데이트 재현
|
|
241
|
-
*/
|
|
242
258
|
/**
|
|
243
259
|
* mutation 실행 시점에 생성되는 RealtimeCtx.
|
|
244
|
-
* emit()
|
|
245
|
-
*
|
|
260
|
+
* emit(): 데이터를 직접 push (초고빈도 mutation용).
|
|
261
|
+
* refresh(): queryKey를 pending 큐에 추가, mutation 완료 후 서버가 query re-run하여 push.
|
|
246
262
|
*
|
|
247
263
|
* 💡 Batching: 같은 queryKey에 대한 emit이 50ms 내에 여러 번 호출되면
|
|
248
264
|
* 마지막 데이터만 push하여 불필요한 전송을 방지합니다.
|
|
@@ -252,6 +268,8 @@ export declare function deregisterClient(ws: WSContext): void;
|
|
|
252
268
|
* @param options.httpCallback BaaS 모드: Platform WS Gateway에 HTTP로 emit 전달.
|
|
253
269
|
* 설정되면 WS 직접 push 대신 이 콜백을 호출.
|
|
254
270
|
* 로컬 dev에서는 미설정 → 기존 WS 직접 push 유지.
|
|
271
|
+
* @param options.queryMap query 레지스트리 — refresh()에서 query handler를 찾아 re-run.
|
|
272
|
+
* @param options.buildCtxForRefresh refresh 시 query handler에 전달할 ctx 생성 함수.
|
|
255
273
|
*/
|
|
256
274
|
export declare function buildRealtimeCtx(options?: {
|
|
257
275
|
httpCallback?: (event: {
|
|
@@ -259,18 +277,13 @@ export declare function buildRealtimeCtx(options?: {
|
|
|
259
277
|
queryKey: string;
|
|
260
278
|
data: unknown;
|
|
261
279
|
}) => void;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
*
|
|
270
|
-
* @deprecated ctx.realtime.emit() 사용 권장
|
|
271
|
-
* @param httpInvalidateCallback BaaS 모드: Platform WS Gateway에 HTTP로 invalidation 전달.
|
|
272
|
-
*/
|
|
273
|
-
export declare function invalidateQueries(queryKeys: string[], ctx: GencowCtx, httpInvalidateCallback?: (queryKeys: string[]) => void): Promise<void>;
|
|
280
|
+
queryMap?: Map<string, QueryDef<any, any>>;
|
|
281
|
+
buildCtxForRefresh?: () => GencowCtx;
|
|
282
|
+
}): RealtimeCtx & {
|
|
283
|
+
_hasEmitted: boolean;
|
|
284
|
+
_pendingRefresh: string[];
|
|
285
|
+
_flushRefresh: () => Promise<void>;
|
|
286
|
+
};
|
|
274
287
|
export declare function handleWsMessage(ws: WSContext, raw: string | ArrayBuffer): void;
|
|
275
288
|
export declare function getQueryHandler(key: string): QueryHandler | undefined;
|
|
276
289
|
export declare function getQueryDef(key: string): QueryDef | undefined;
|
package/dist/reactive.js
CHANGED
|
@@ -45,7 +45,6 @@ let mutationCounter = 0;
|
|
|
45
45
|
* ```typescript
|
|
46
46
|
* // ✅ 권장: query와 동일한 (name, def) 패턴
|
|
47
47
|
* mutation("tasks.create", {
|
|
48
|
-
* invalidates: [],
|
|
49
48
|
* args: { title: v.string() },
|
|
50
49
|
* handler: async (ctx, args) => { ... },
|
|
51
50
|
* });
|
|
@@ -53,38 +52,36 @@ let mutationCounter = 0;
|
|
|
53
52
|
* // ✅ 객체 스타일 (하위 호환)
|
|
54
53
|
* mutation({
|
|
55
54
|
* name: "tasks.create",
|
|
56
|
-
* invalidates: [],
|
|
57
55
|
* handler: async (ctx) => { ... },
|
|
58
56
|
* });
|
|
59
57
|
*
|
|
60
58
|
* // ⚠️ Legacy 배열 스타일 (비권장)
|
|
61
59
|
* mutation(["tasks.list"], handler, "tasks.create");
|
|
62
60
|
* ```
|
|
61
|
+
*
|
|
62
|
+
* @note invalidates 필드는 deprecated — 전달해도 무시됩니다.
|
|
63
|
+
* 리얼타임 UI 갱신에는 ctx.realtime.emit() 또는 ctx.realtime.refresh()를 사용하세요.
|
|
63
64
|
*/
|
|
64
65
|
export function mutation(nameOrInvalidatesOrDef, handlerOrDef, name) {
|
|
65
|
-
let invalidates;
|
|
66
66
|
let argsSchema;
|
|
67
67
|
let actualHandler;
|
|
68
68
|
let mutName;
|
|
69
69
|
let isPublic = false;
|
|
70
70
|
if (typeof nameOrInvalidatesOrDef === "string") {
|
|
71
|
-
// New primary style: mutation("name", {
|
|
71
|
+
// New primary style: mutation("name", { args?, public?, handler })
|
|
72
72
|
mutName = nameOrInvalidatesOrDef;
|
|
73
73
|
const def = handlerOrDef;
|
|
74
|
-
invalidates = def.invalidates || [];
|
|
75
74
|
actualHandler = def.handler;
|
|
76
75
|
argsSchema = def.args;
|
|
77
76
|
isPublic = def.public === true;
|
|
78
77
|
}
|
|
79
78
|
else if (Array.isArray(nameOrInvalidatesOrDef)) {
|
|
80
|
-
// Legacy style: mutation([...], handler, "name")
|
|
81
|
-
invalidates = nameOrInvalidatesOrDef;
|
|
79
|
+
// Legacy style: mutation([...], handler, "name") — invalidates ignored
|
|
82
80
|
actualHandler = handlerOrDef;
|
|
83
81
|
mutName = name || `mutation_${++mutationCounter}`;
|
|
84
82
|
}
|
|
85
83
|
else {
|
|
86
|
-
// Object style: mutation({ name?,
|
|
87
|
-
invalidates = nameOrInvalidatesOrDef.invalidates;
|
|
84
|
+
// Object style: mutation({ name?, args?, public?, handler })
|
|
88
85
|
actualHandler = nameOrInvalidatesOrDef.handler;
|
|
89
86
|
argsSchema = nameOrInvalidatesOrDef.args;
|
|
90
87
|
isPublic = nameOrInvalidatesOrDef.public === true;
|
|
@@ -97,7 +94,6 @@ export function mutation(nameOrInvalidatesOrDef, handlerOrDef, name) {
|
|
|
97
94
|
}
|
|
98
95
|
const def = {
|
|
99
96
|
name: mutName,
|
|
100
|
-
invalidates,
|
|
101
97
|
handler: actualHandler,
|
|
102
98
|
argsSchema,
|
|
103
99
|
isPublic,
|
|
@@ -168,14 +164,10 @@ export function deregisterClient(ws) {
|
|
|
168
164
|
clients.delete(ws);
|
|
169
165
|
}
|
|
170
166
|
}
|
|
171
|
-
/**
|
|
172
|
-
* After a mutation, re-run invalidated queries and push results
|
|
173
|
-
* to all subscribers — Convex의 자동 reactive 업데이트 재현
|
|
174
|
-
*/
|
|
175
167
|
/**
|
|
176
168
|
* mutation 실행 시점에 생성되는 RealtimeCtx.
|
|
177
|
-
* emit()
|
|
178
|
-
*
|
|
169
|
+
* emit(): 데이터를 직접 push (초고빈도 mutation용).
|
|
170
|
+
* refresh(): queryKey를 pending 큐에 추가, mutation 완료 후 서버가 query re-run하여 push.
|
|
179
171
|
*
|
|
180
172
|
* 💡 Batching: 같은 queryKey에 대한 emit이 50ms 내에 여러 번 호출되면
|
|
181
173
|
* 마지막 데이터만 push하여 불필요한 전송을 방지합니다.
|
|
@@ -185,11 +177,16 @@ export function deregisterClient(ws) {
|
|
|
185
177
|
* @param options.httpCallback BaaS 모드: Platform WS Gateway에 HTTP로 emit 전달.
|
|
186
178
|
* 설정되면 WS 직접 push 대신 이 콜백을 호출.
|
|
187
179
|
* 로컬 dev에서는 미설정 → 기존 WS 직접 push 유지.
|
|
180
|
+
* @param options.queryMap query 레지스트리 — refresh()에서 query handler를 찾아 re-run.
|
|
181
|
+
* @param options.buildCtxForRefresh refresh 시 query handler에 전달할 ctx 생성 함수.
|
|
188
182
|
*/
|
|
189
183
|
export function buildRealtimeCtx(options) {
|
|
190
184
|
const pendingEmits = new Map();
|
|
185
|
+
const _pendingRefresh = [];
|
|
186
|
+
let _hasEmitted = false;
|
|
191
187
|
return {
|
|
192
188
|
emit(queryKey, data) {
|
|
189
|
+
_hasEmitted = true;
|
|
193
190
|
// 기존 pending timer가 있으면 취소 (debounce)
|
|
194
191
|
const existing = pendingEmits.get(queryKey);
|
|
195
192
|
if (existing)
|
|
@@ -220,38 +217,61 @@ export function buildRealtimeCtx(options) {
|
|
|
220
217
|
}
|
|
221
218
|
}, 50); // 50ms batch window
|
|
222
219
|
pendingEmits.set(queryKey, { data, timer });
|
|
223
|
-
}
|
|
220
|
+
},
|
|
221
|
+
refresh(queryKey) {
|
|
222
|
+
_hasEmitted = true; // 경고 억제
|
|
223
|
+
if (!_pendingRefresh.includes(queryKey)) {
|
|
224
|
+
_pendingRefresh.push(queryKey);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
get _hasEmitted() { return _hasEmitted; },
|
|
228
|
+
get _pendingRefresh() { return [..._pendingRefresh]; },
|
|
229
|
+
async _flushRefresh() {
|
|
230
|
+
if (_pendingRefresh.length === 0)
|
|
231
|
+
return;
|
|
232
|
+
// queryMap이 없으면 refresh 동작 불가 (로그 경고)
|
|
233
|
+
const qMap = options?.queryMap ?? queryRegistry;
|
|
234
|
+
for (const key of _pendingRefresh) {
|
|
235
|
+
const queryDef = qMap.get(key);
|
|
236
|
+
if (!queryDef) {
|
|
237
|
+
console.warn(`[gencow] refresh("${key}"): query not found in registry. Skipping.`);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
// refresh용 ctx 생성 (mutation ctx와 동일한 DB/auth 스코프)
|
|
242
|
+
const refreshCtx = options?.buildCtxForRefresh?.() ?? {};
|
|
243
|
+
const result = await queryDef.handler(refreshCtx, {});
|
|
244
|
+
// emit과 동일한 경로로 push
|
|
245
|
+
if (options?.httpCallback) {
|
|
246
|
+
options.httpCallback({ type: "emit", queryKey: key, data: result });
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const clients = subscribers.get(key);
|
|
250
|
+
if (clients && clients.size > 0) {
|
|
251
|
+
const message = JSON.stringify({
|
|
252
|
+
type: "query:updated",
|
|
253
|
+
query: key,
|
|
254
|
+
data: result,
|
|
255
|
+
});
|
|
256
|
+
for (const ws of clients) {
|
|
257
|
+
try {
|
|
258
|
+
ws.send(message);
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
clients.delete(ws);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
console.warn(`[gencow] refresh("${key}") failed:`, e instanceof Error ? e.message : e);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
_pendingRefresh.length = 0;
|
|
272
|
+
},
|
|
224
273
|
};
|
|
225
274
|
}
|
|
226
|
-
/**
|
|
227
|
-
* mutation이 끝난 후 호출되는 legacy fallback.
|
|
228
|
-
* `ctx.realtime.emit()`을 사용하는 새 mutation에서는 빈 배열([])을 전달하면 됩니다.
|
|
229
|
-
*
|
|
230
|
-
* invalidate 신호만 broadcast하여 클라이언트가 re-fetch 여부를 결정하게 합니다.
|
|
231
|
-
* (서버에서 쿼리를 재실행하지 않으므로 DB 부하 없음)
|
|
232
|
-
*
|
|
233
|
-
* @deprecated ctx.realtime.emit() 사용 권장
|
|
234
|
-
* @param httpInvalidateCallback BaaS 모드: Platform WS Gateway에 HTTP로 invalidation 전달.
|
|
235
|
-
*/
|
|
236
|
-
export async function invalidateQueries(queryKeys, ctx, httpInvalidateCallback) {
|
|
237
|
-
if (queryKeys.length === 0)
|
|
238
|
-
return; // emit() 방식에서는 no-op
|
|
239
|
-
// BaaS 모드: Platform WS Gateway에 HTTP callback
|
|
240
|
-
if (httpInvalidateCallback) {
|
|
241
|
-
httpInvalidateCallback(queryKeys);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
// 로컬 dev: WS 직접 broadcast (기존 동작)
|
|
245
|
-
const invalidateMsg = JSON.stringify({ type: "invalidate", queries: queryKeys });
|
|
246
|
-
for (const ws of connectedClients) {
|
|
247
|
-
try {
|
|
248
|
-
ws.send(invalidateMsg);
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
connectedClients.delete(ws);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
275
|
// ─── WebSocket message handler ──────────────────────────
|
|
256
276
|
export function handleWsMessage(ws, raw) {
|
|
257
277
|
try {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* packages/core/src/scoped-db.ts
|
|
3
|
+
*
|
|
4
|
+
* Creates a scoped (Proxy-wrapped) Drizzle DB instance that auto-injects
|
|
5
|
+
* schema-level access control filters from gencowTable() metadata.
|
|
6
|
+
*
|
|
7
|
+
* Key behaviors:
|
|
8
|
+
* - .select().from(gencowTable) → auto-inject filter into WHERE
|
|
9
|
+
* - .insert(table) / .update(table) / .delete(table) → inject filter for writes
|
|
10
|
+
* - .leftJoin(table) / .innerJoin(table) → detect and inject filter
|
|
11
|
+
* - .execute() → blocked (throws Error)
|
|
12
|
+
* - .query.tableName.findMany() → inject filter into relational queries
|
|
13
|
+
*
|
|
14
|
+
* Run tests: bun test packages/core/src/__tests__/scoped-db.test.ts
|
|
15
|
+
*/
|
|
16
|
+
import type { GencowCtx } from "./reactive";
|
|
17
|
+
/**
|
|
18
|
+
* Wrap a Drizzle DB instance with access control Proxy.
|
|
19
|
+
*
|
|
20
|
+
* @param db - Raw Drizzle DB instance
|
|
21
|
+
* @param ctx - GencowCtx (provides auth for filter evaluation)
|
|
22
|
+
* @returns Proxy-wrapped DB with auto-filter injection
|
|
23
|
+
*/
|
|
24
|
+
export declare function createScopedDb(db: any, ctx: GencowCtx): any;
|
|
25
|
+
/**
|
|
26
|
+
* Apply field-level access control to query results.
|
|
27
|
+
* Nullifies fields that the current user is not authorized to read.
|
|
28
|
+
*
|
|
29
|
+
* @param result - Query result (array or single object)
|
|
30
|
+
* @param table - The gencowTable used in the query
|
|
31
|
+
* @param ctx - GencowCtx for auth checks
|
|
32
|
+
* @returns Filtered result with unauthorized fields set to null
|
|
33
|
+
*/
|
|
34
|
+
export declare function applyFieldAccess(result: any, table: any, ctx: GencowCtx): any;
|