@ai0x0/utils 0.0.50 → 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 (86) hide show
  1. package/.agents/skills/ai0x0-utils/backend/SKILL.md +35 -11
  2. package/README.md +3 -3
  3. package/SKILL.md +35 -11
  4. package/es/backend/actions/create-delete-action.d.ts +4 -2
  5. package/es/backend/actions/create-delete-action.js +22 -28
  6. package/es/backend/actions/create-get-action.d.ts +4 -1
  7. package/es/backend/actions/create-get-action.js +10 -10
  8. package/es/backend/actions/create-get-list-action.d.ts +4 -1
  9. package/es/backend/actions/create-get-list-action.js +9 -9
  10. package/es/backend/actions/create-put-action.d.ts +3 -2
  11. package/es/backend/actions/create-put-action.js +23 -38
  12. package/es/backend/actions/get-list-query.d.ts +8 -1
  13. package/es/backend/actions/get-list-query.js +19 -8
  14. package/es/backend/index.d.ts +1 -0
  15. package/es/backend/index.js +2 -1
  16. package/es/backend/route-operation/delete-operation.d.ts +3 -1
  17. package/es/backend/route-operation/get-list-operation.d.ts +3 -1
  18. package/es/backend/route-operation/get-operation.d.ts +3 -1
  19. package/es/backend/route-operation/operation-common.d.ts +80 -8
  20. package/es/backend/route-operation/operation-common.js +216 -67
  21. package/es/backend/route-operation/post-operation.d.ts +3 -1
  22. package/es/backend/route-operation/put-operation.d.ts +3 -1
  23. package/es/backend/route-operation/read-operation-core.d.ts +17 -14
  24. package/es/backend/route-operation/read-operation-core.js +42 -34
  25. package/es/backend/route-operation/write-operation-core.d.ts +25 -22
  26. package/es/backend/route-operation/write-operation-core.js +195 -165
  27. package/es/backend/schemas/index.d.ts +240 -240
  28. package/es/backend/scope.d.ts +27 -0
  29. package/es/backend/scope.js +60 -0
  30. package/es/backend/sqlite/actions/create-delete-action.d.ts +4 -2
  31. package/es/backend/sqlite/actions/create-delete-action.js +21 -28
  32. package/es/backend/sqlite/actions/create-get-action.d.ts +4 -1
  33. package/es/backend/sqlite/actions/create-get-action.js +9 -7
  34. package/es/backend/sqlite/actions/create-get-list-action.d.ts +4 -1
  35. package/es/backend/sqlite/actions/create-get-list-action.js +8 -6
  36. package/es/backend/sqlite/actions/create-put-action.d.ts +3 -2
  37. package/es/backend/sqlite/actions/create-put-action.js +20 -38
  38. package/es/backend/sqlite/actions/get-list-query.d.ts +4 -1
  39. package/es/backend/sqlite/actions/get-list-query.js +8 -3
  40. package/es/backend/sqlite/route-operation/delete-operation.d.ts +3 -1
  41. package/es/backend/sqlite/route-operation/get-list-operation.d.ts +3 -1
  42. package/es/backend/sqlite/route-operation/get-operation.d.ts +3 -1
  43. package/es/backend/sqlite/route-operation/post-operation.d.ts +3 -1
  44. package/es/backend/sqlite/route-operation/put-operation.d.ts +3 -1
  45. package/lib/backend/actions/create-delete-action.d.ts +4 -2
  46. package/lib/backend/actions/create-delete-action.js +10 -16
  47. package/lib/backend/actions/create-get-action.d.ts +4 -1
  48. package/lib/backend/actions/create-get-action.js +2 -1
  49. package/lib/backend/actions/create-get-list-action.d.ts +4 -1
  50. package/lib/backend/actions/create-get-list-action.js +2 -1
  51. package/lib/backend/actions/create-put-action.d.ts +3 -2
  52. package/lib/backend/actions/create-put-action.js +10 -21
  53. package/lib/backend/actions/get-list-query.d.ts +8 -1
  54. package/lib/backend/actions/get-list-query.js +11 -4
  55. package/lib/backend/index.d.ts +1 -0
  56. package/lib/backend/index.js +3 -1
  57. package/lib/backend/route-operation/delete-operation.d.ts +3 -1
  58. package/lib/backend/route-operation/get-list-operation.d.ts +3 -1
  59. package/lib/backend/route-operation/get-operation.d.ts +3 -1
  60. package/lib/backend/route-operation/operation-common.d.ts +80 -8
  61. package/lib/backend/route-operation/operation-common.js +57 -7
  62. package/lib/backend/route-operation/post-operation.d.ts +3 -1
  63. package/lib/backend/route-operation/put-operation.d.ts +3 -1
  64. package/lib/backend/route-operation/read-operation-core.d.ts +17 -14
  65. package/lib/backend/route-operation/read-operation-core.js +12 -6
  66. package/lib/backend/route-operation/write-operation-core.d.ts +25 -22
  67. package/lib/backend/route-operation/write-operation-core.js +52 -16
  68. package/lib/backend/schemas/index.d.ts +240 -240
  69. package/lib/backend/scope.d.ts +27 -0
  70. package/lib/backend/scope.js +57 -0
  71. package/lib/backend/sqlite/actions/create-delete-action.d.ts +4 -2
  72. package/lib/backend/sqlite/actions/create-delete-action.js +10 -16
  73. package/lib/backend/sqlite/actions/create-get-action.d.ts +4 -1
  74. package/lib/backend/sqlite/actions/create-get-action.js +2 -1
  75. package/lib/backend/sqlite/actions/create-get-list-action.d.ts +4 -1
  76. package/lib/backend/sqlite/actions/create-get-list-action.js +2 -1
  77. package/lib/backend/sqlite/actions/create-put-action.d.ts +3 -2
  78. package/lib/backend/sqlite/actions/create-put-action.js +10 -21
  79. package/lib/backend/sqlite/actions/get-list-query.d.ts +4 -1
  80. package/lib/backend/sqlite/actions/get-list-query.js +5 -1
  81. package/lib/backend/sqlite/route-operation/delete-operation.d.ts +3 -1
  82. package/lib/backend/sqlite/route-operation/get-list-operation.d.ts +3 -1
  83. package/lib/backend/sqlite/route-operation/get-operation.d.ts +3 -1
  84. package/lib/backend/sqlite/route-operation/post-operation.d.ts +3 -1
  85. package/lib/backend/sqlite/route-operation/put-operation.d.ts +3 -1
  86. package/package.json +38 -1
@@ -48,7 +48,7 @@ export {
48
48
  createTableSchema,
49
49
  queryListSchema,
50
50
  listBodySchema,
51
- } from "@ai0x0/utils/lib/backend/schemas";
51
+ } from "@ai0x0/utils/backend/schemas";
52
52
  ```
53
53
 
54
54
  ```ts
@@ -59,7 +59,7 @@ export {
59
59
  createGetListAction,
60
60
  createPostAction,
61
61
  createPutAction,
62
- } from "@ai0x0/utils/lib/backend/actions";
62
+ } from "@ai0x0/utils/backend";
63
63
  ```
64
64
 
65
65
  ```ts
@@ -70,7 +70,7 @@ import {
70
70
  createPostOperation,
71
71
  createDeleteOperation,
72
72
  createGetListOperation,
73
- } from "@ai0x0/utils/lib/backend";
73
+ } from "@ai0x0/utils/backend";
74
74
  import { db } from "@backend/db";
75
75
  import getSession from "@backend/utils/get-session";
76
76
 
@@ -330,8 +330,13 @@ export const { GET } = route({
330
330
  ## Built-in Conventions
331
331
 
332
332
  - **Base fields** auto-added by `createTableSchema`: `id` (uuid, pk, default random) / `creatorId` / `editorId` / `accessedAt` / `createdAt` / `updatedAt`.
333
- - **Ownership check**: with `access: { byCreator: true }` (default on PUT/DELETE/GET), PUT/DELETE pre-fetch the row filtered by `creatorId = session.userId` and throw `"未找到…对象,或没有权限"` if missing. Set `access: { byCreator: false }` to skip (e.g. admin endpoints).
334
- - **Auto injection**: POST writes `creatorId = session.userId`; PUT writes `editorId = session.userId`; GET/GET_LIST append `creatorId = session.userId` to the filter when `access.byCreator` is on.
333
+ - **Row-level scope** (`access`, on by default for every verb): the request may only see / touch rows whose scope column matches. Three knobs:
334
+ - `byCreator?: boolean` — the old spelling, equivalent to `scope: { column: "creatorId", value: (s) => s.userId }`. Default `true`.
335
+ - `scope?: { column?, value }` — pick the column (default `creatorId`) and derive the value from the session. Return an array for `IN (...)`. Use this when a row can belong to something other than one user (a team, a workspace).
336
+ - `can?: ({ session, method }) => boolean | { status?, message? }` — the _action_ gate, as opposed to the _row_ filter. Read-only roles belong here, in one place, not spread across every route.
337
+ - **Fail closed**: if the session is missing → 401; if `scope.value` returns empty → 403. A scope that cannot be resolved is **never** downgraded to "no filter". PUT/DELETE put the scope straight into the `UPDATE`/`DELETE` `where` (one statement, no read-then-write window) and return 404 when it affects zero rows — "does not exist" and "not yours" are deliberately indistinguishable, otherwise the endpoint becomes an existence oracle.
338
+ - **Auto injection**: POST stamps the scope column with the scope value (by default that is `creatorId = session.userId`); PUT writes `editorId = session.userId`; GET/GET_LIST apply the scope as a mandatory `where`.
339
+ - **Session shape** is yours: `getSession` is generic, so resolve "who + which space + what role" once and let `scope.value` / `can` read the same object instead of each querying again.
335
340
  - **Date coercion**: any body field ending in `At` is passed through `dayjs(value).toDate()` before write — accept ISO strings from clients.
336
341
  - **List filtering** (all querystring values are strings):
337
342
  - `key=a,b,c` → `IN (...)` when key ends with `Id`; otherwise `OR ILIKE %a% OR ILIKE %b%`
@@ -352,6 +357,26 @@ export const { GET } = route({
352
357
  - Swallowing errors: an unhandled throw in a handler propagates to next-rest-framework as a 500. Provide `catch` for any operation exposed to untrusted clients.
353
358
  - For non-CRUD behavior (middleware chains, streaming, custom `TypedNextResponse` unions, RPC) stop using these factories and switch to the `create-next-rest-framework-api` skill.
354
359
 
360
+ ## Using the actions directly
361
+
362
+ `createGetAction` / `createGetListAction` / `createPutAction` / `createDeleteAction` and `getListQuery` all take the scope as a **required** argument — pass `NO_SCOPE` to opt out explicitly:
363
+
364
+ ```ts
365
+ import { NO_SCOPE } from "@ai0x0/utils/backend";
366
+
367
+ await createGetListAction({ bodySchema, db, table })(params, {
368
+ scope: NO_SCOPE,
369
+ });
370
+ await createDeleteAction({ db, table })(
371
+ { id },
372
+ {
373
+ scope: { column: "ownerId", value: currentTeamId },
374
+ },
375
+ );
376
+ ```
377
+
378
+ It is required rather than optional on purpose: a forgotten scope is a compile error, not a silent full-table query.
379
+
355
380
  ## Escape Hatches
356
381
 
357
382
  - Extra server-side filters — `setParams(req) => Promise<Record<string, unknown>>` on GET/GET_LIST; returned keys are merged into the filter.
@@ -362,19 +387,18 @@ export const { GET } = route({
362
387
  ## Imports
363
388
 
364
389
  ```
365
- @ai0x0/utils/lib/backend // actions + route-operation + schemas (CJS, used by Next.js server bundle)
366
- @ai0x0/utils/lib/backend/actions
367
- @ai0x0/utils/lib/backend/route-operation
368
- @ai0x0/utils/lib/backend/schemas
390
+ @ai0x0/utils/backend // actions + route-operation + schemas + scope
391
+ @ai0x0/utils/backend/schemas // createTableSchema / queryListSchema
392
+ @ai0x0/utils/backend/sqlite // same surface, SQLite flavour
369
393
  ```
370
394
 
371
- ESM consumers can substitute `/es/backend/...` instead of `/lib/backend/...` when Next.js resolves the `exports` map that way; prefer `/lib/backend` in Next.js projects because the server bundle is CommonJS.
395
+ The `exports` map picks CJS or ESM per consumer; you do not choose. **Do not write `@ai0x0/utils/es/backend/...` or `/lib/backend/...`** — those name build artifacts and hard-code an internal detail into every import. The `./es/*` and `./lib/*` wildcards stay only so existing callers keep working.
372
396
 
373
397
  ## Done Criteria
374
398
 
375
399
  - Route files contain zero hand-written `{ db, getSession }` wiring; all operations come from `@backend/utils/route-operation`.
376
400
  - Each domain has a `table.ts` (columns + `createTableSchema`) and an `index.ts` (five zod schemas + re-exports).
377
401
  - Every list endpoint accepts `current / pageSize / orderBy / orderDir / *AtFrom / *AtTo` and — where applicable — `jsonArrayFields` is declared.
378
- - `access.byCreator` is explicitly set when the endpoint is admin-facing or otherwise crosses ownership boundaries.
402
+ - `access` is explicitly set when the endpoint is admin-facing or otherwise crosses ownership boundaries — `byCreator: false` for genuinely public data, `scope` when ownership is not "one user", `can` for read-only roles.
379
403
  - Non-CRUD endpoints are implemented via raw `routeOperation` + action helpers, not by forcing a CRUD factory.
380
404
  - `next-rest-framework validate` / `generate` (from the partner skill) still pass — the factory values are plain `RouteOperationDefinition` and participate in OpenAPI generation.
package/README.md CHANGED
@@ -19,17 +19,17 @@ See [SKILL.md](./SKILL.md) for detailed workflow, setup, and escape hatches.
19
19
 
20
20
  ## SQLite Backend (`src/backend/sqlite/`)
21
21
 
22
- Projects that use a local SQLite database can import the same CRUD factory surface from `@ai0x0/utils/es/backend/sqlite`.
22
+ Projects that use a local SQLite database can import the same CRUD factory surface from `@ai0x0/utils/backend/sqlite`.
23
23
 
24
24
  ```ts
25
25
  import {
26
26
  createTableSchema,
27
27
  queryListSchema,
28
- } from "@ai0x0/utils/es/backend/sqlite/schemas/index.js";
28
+ } from "@ai0x0/utils/backend/sqlite/schemas";
29
29
  import {
30
30
  createGetOperation,
31
31
  createPostOperation,
32
- } from "@ai0x0/utils/es/backend/sqlite/index.js";
32
+ } from "@ai0x0/utils/backend/sqlite";
33
33
  ```
34
34
 
35
35
  The SQLite helpers mirror the PostgreSQL helpers, but use `drizzle-orm/sqlite-core`, SQLite `LIKE`, and `json_each(...)` for JSON-array filtering.
package/SKILL.md CHANGED
@@ -27,7 +27,7 @@ export {
27
27
  createTableSchema,
28
28
  queryListSchema,
29
29
  listBodySchema,
30
- } from "@ai0x0/utils/lib/backend/schemas";
30
+ } from "@ai0x0/utils/backend/schemas";
31
31
  ```
32
32
 
33
33
  ```ts
@@ -38,7 +38,7 @@ export {
38
38
  createGetListAction,
39
39
  createPostAction,
40
40
  createPutAction,
41
- } from "@ai0x0/utils/lib/backend/actions";
41
+ } from "@ai0x0/utils/backend";
42
42
  ```
43
43
 
44
44
  ```ts
@@ -49,7 +49,7 @@ import {
49
49
  createPostOperation,
50
50
  createDeleteOperation,
51
51
  createGetListOperation,
52
- } from "@ai0x0/utils/lib/backend";
52
+ } from "@ai0x0/utils/backend";
53
53
  import { db } from "@backend/db";
54
54
  import getSession from "@backend/utils/get-session";
55
55
 
@@ -309,8 +309,13 @@ export const { GET } = route({
309
309
  ## Built-in Conventions
310
310
 
311
311
  - **Base fields** auto-added by `createTableSchema`: `id` (uuid, pk, default random) / `creatorId` / `editorId` / `accessedAt` / `createdAt` / `updatedAt`.
312
- - **Ownership check**: with `access: { byCreator: true }` (default on PUT/DELETE/GET), PUT/DELETE pre-fetch the row filtered by `creatorId = session.userId` and throw `"未找到…对象,或没有权限"` if missing. Set `access: { byCreator: false }` to skip (e.g. admin endpoints).
313
- - **Auto injection**: POST writes `creatorId = session.userId`; PUT writes `editorId = session.userId`; GET/GET_LIST append `creatorId = session.userId` to the filter when `access.byCreator` is on.
312
+ - **Row-level scope** (`access`, on by default for every verb): the request may only see / touch rows whose scope column matches. Three knobs:
313
+ - `byCreator?: boolean` — the old spelling, equivalent to `scope: { column: "creatorId", value: (s) => s.userId }`. Default `true`.
314
+ - `scope?: { column?, value }` — pick the column (default `creatorId`) and derive the value from the session. Return an array for `IN (...)`. Use this when a row can belong to something other than one user (a team, a workspace).
315
+ - `can?: ({ session, method }) => boolean | { status?, message? }` — the _action_ gate, as opposed to the _row_ filter. Read-only roles belong here, in one place, not spread across every route.
316
+ - **Fail closed**: if the session is missing → 401; if `scope.value` returns empty → 403. A scope that cannot be resolved is **never** downgraded to "no filter". PUT/DELETE put the scope straight into the `UPDATE`/`DELETE` `where` (one statement, no read-then-write window) and return 404 when it affects zero rows — "does not exist" and "not yours" are deliberately indistinguishable, otherwise the endpoint becomes an existence oracle.
317
+ - **Auto injection**: POST stamps the scope column with the scope value (by default that is `creatorId = session.userId`); PUT writes `editorId = session.userId`; GET/GET_LIST apply the scope as a mandatory `where`.
318
+ - **Session shape** is yours: `getSession` is generic, so resolve "who + which space + what role" once and let `scope.value` / `can` read the same object instead of each querying again.
314
319
  - **Date coercion**: any body field ending in `At` is passed through `dayjs(value).toDate()` before write — accept ISO strings from clients.
315
320
  - **List filtering** (all querystring values are strings):
316
321
  - `key=a,b,c` → `IN (...)` when key ends with `Id`; otherwise `OR ILIKE %a% OR ILIKE %b%`
@@ -331,6 +336,26 @@ export const { GET } = route({
331
336
  - Swallowing errors: an unhandled throw in a handler propagates to next-rest-framework as a 500. Provide `catch` for any operation exposed to untrusted clients.
332
337
  - For non-CRUD behavior (middleware chains, streaming, custom `TypedNextResponse` unions, RPC) stop using these factories and switch to the `create-next-rest-framework-api` skill.
333
338
 
339
+ ## Using the actions directly
340
+
341
+ `createGetAction` / `createGetListAction` / `createPutAction` / `createDeleteAction` and `getListQuery` all take the scope as a **required** argument — pass `NO_SCOPE` to opt out explicitly:
342
+
343
+ ```ts
344
+ import { NO_SCOPE } from "@ai0x0/utils/backend";
345
+
346
+ await createGetListAction({ bodySchema, db, table })(params, {
347
+ scope: NO_SCOPE,
348
+ });
349
+ await createDeleteAction({ db, table })(
350
+ { id },
351
+ {
352
+ scope: { column: "ownerId", value: currentTeamId },
353
+ },
354
+ );
355
+ ```
356
+
357
+ It is required rather than optional on purpose: a forgotten scope is a compile error, not a silent full-table query.
358
+
334
359
  ## Escape Hatches
335
360
 
336
361
  - Extra server-side filters — `setParams(req) => Promise<Record<string, unknown>>` on GET/GET_LIST; returned keys are merged into the filter.
@@ -341,19 +366,18 @@ export const { GET } = route({
341
366
  ## Imports
342
367
 
343
368
  ```
344
- @ai0x0/utils/lib/backend // actions + route-operation + schemas (CJS, used by Next.js server bundle)
345
- @ai0x0/utils/lib/backend/actions
346
- @ai0x0/utils/lib/backend/route-operation
347
- @ai0x0/utils/lib/backend/schemas
369
+ @ai0x0/utils/backend // actions + route-operation + schemas + scope
370
+ @ai0x0/utils/backend/schemas // createTableSchema / queryListSchema
371
+ @ai0x0/utils/backend/sqlite // same surface, SQLite flavour
348
372
  ```
349
373
 
350
- ESM consumers can substitute `/es/backend/...` instead of `/lib/backend/...` when Next.js resolves the `exports` map that way; prefer `/lib/backend` in Next.js projects because the server bundle is CommonJS.
374
+ The `exports` map picks CJS or ESM per consumer; you do not choose. **Do not write `@ai0x0/utils/es/backend/...` or `/lib/backend/...`** — those name build artifacts and hard-code an internal detail into every import. The `./es/*` and `./lib/*` wildcards stay only so existing callers keep working.
351
375
 
352
376
  ## Done Criteria
353
377
 
354
378
  - Route files contain zero hand-written `{ db, getSession }` wiring; all operations come from `@backend/utils/route-operation`.
355
379
  - Each domain has a `table.ts` (columns + `createTableSchema`) and an `index.ts` (five zod schemas + re-exports).
356
380
  - Every list endpoint accepts `current / pageSize / orderBy / orderDir / *AtFrom / *AtTo` and — where applicable — `jsonArrayFields` is declared.
357
- - `access.byCreator` is explicitly set when the endpoint is admin-facing or otherwise crosses ownership boundaries.
381
+ - `access` is explicitly set when the endpoint is admin-facing or otherwise crosses ownership boundaries — `byCreator: false` for genuinely public data, `scope` when ownership is not "one user", `can` for read-only roles.
358
382
  - Non-CRUD endpoints are implemented via raw `routeOperation` + action helpers, not by forcing a CRUD factory.
359
383
  - `next-rest-framework validate` / `generate` (from the partner skill) still pass — the factory values are plain `RouteOperationDefinition` and participate in OpenAPI generation.
@@ -1,9 +1,11 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
2
  import { BaseTable } from "../types.js";
3
+ import { ScopeArg } from "../scope.js";
3
4
  export declare function createDeleteAction<TTable extends BaseTable>({ table, db, }: {
4
5
  db: NodePgDatabase<Record<string, unknown>>;
5
6
  table: TTable;
6
- }): ({ id, creatorId }: {
7
+ }): ({ id }: {
7
8
  id: string;
8
- creatorId?: string | undefined;
9
+ }, { scope }: {
10
+ scope: ScopeArg;
9
11
  }) => Promise<TTable["$inferSelect"] extends infer T ? T extends TTable["$inferSelect"] ? T extends undefined ? QueryResult<import("drizzle-orm").Assume<this["row"], QueryResultRow>> : T[] : never : never>;
@@ -2,53 +2,47 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
2
2
  function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
3
3
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
4
4
  function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
5
- import { eq } from "drizzle-orm";
6
- import { createGetAction } from "./../index.js";
7
- import { z } from "zod";
5
+ import { and, eq } from "drizzle-orm";
6
+ import { NO_SCOPE, scopeCondition } from "../scope.js";
8
7
  import { HttpError } from "../errors.js";
9
8
  export function createDeleteAction(_ref) {
10
9
  var table = _ref.table,
11
10
  db = _ref.db;
12
11
  return /*#__PURE__*/function () {
13
- var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(_ref2) {
14
- var id, creatorId, data;
12
+ var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(_ref2, _ref3) {
13
+ var id, scope, guard, rows;
15
14
  return _regeneratorRuntime().wrap(function _callee$(_context) {
16
15
  while (1) switch (_context.prev = _context.next) {
17
16
  case 0:
18
- id = _ref2.id, creatorId = _ref2.creatorId;
19
- if (!creatorId) {
20
- _context.next = 7;
17
+ id = _ref2.id;
18
+ scope = _ref3.scope;
19
+ if (id) {
20
+ _context.next = 4;
21
21
  break;
22
22
  }
23
- _context.next = 4;
24
- return createGetAction({
25
- db: db,
26
- table: table,
27
- bodySchema: z.object({
28
- creatorId: z.string(),
29
- id: z.string()
30
- })
31
- })({
32
- creatorId: creatorId,
33
- id: id
34
- });
23
+ throw new HttpError(400, "缺少 id");
35
24
  case 4:
36
- data = _context.sent;
37
- if (data) {
38
- _context.next = 7;
25
+ // 归属写进 DELETE 的 where,不先查后删(理由同 create-put-action)。
26
+ guard = scopeCondition(table, scope);
27
+ _context.next = 7;
28
+ return db.delete(table).where(guard ? and(eq(table.id, id), guard) : eq(table.id, id)).returning();
29
+ case 7:
30
+ rows = _context.sent;
31
+ if (!(rows.length === 0 && scope !== NO_SCOPE)) {
32
+ _context.next = 10;
39
33
  break;
40
34
  }
41
35
  throw new HttpError(404, "未找到删除对象,或没有权限");
42
- case 7:
43
- return _context.abrupt("return", db.delete(table).where(eq(table.id, id)).returning());
44
- case 8:
36
+ case 10:
37
+ return _context.abrupt("return", rows);
38
+ case 11:
45
39
  case "end":
46
40
  return _context.stop();
47
41
  }
48
42
  }, _callee);
49
43
  }));
50
- return function (_x) {
51
- return _ref3.apply(this, arguments);
44
+ return function (_x, _x2) {
45
+ return _ref4.apply(this, arguments);
52
46
  };
53
47
  }();
54
48
  }
@@ -1,10 +1,13 @@
1
1
  import { z, ZodSchema } from "zod";
2
2
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
3
  import { BaseTable, GetListRelations } from "../types.js";
4
+ import { ScopeArg } from "../scope.js";
4
5
  export declare function createGetAction<T extends ZodSchema, TTable extends BaseTable>({ bodySchema, db, jsonArrayFields, relations, table, }: {
5
6
  bodySchema: T;
6
7
  db: NodePgDatabase<Record<string, unknown>>;
7
8
  jsonArrayFields?: string[];
8
9
  relations?: GetListRelations;
9
10
  table: TTable;
10
- }): (params: Partial<z.infer<T>> & Record<string, unknown>) => Promise<z.core.output<T> | undefined>;
11
+ }): (params: Partial<z.infer<T>> & Record<string, unknown>, { scope }: {
12
+ scope: ScopeArg;
13
+ }) => Promise<z.core.output<T> | undefined>;
@@ -17,16 +17,15 @@ export function createGetAction(_ref) {
17
17
  relations = _ref.relations,
18
18
  table = _ref.table;
19
19
  return /*#__PURE__*/function () {
20
- var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(params) {
21
- var fields, _i, _Object$keys, key, field, _getListQuery, query, countQuery, _yield$getListData, _yield$getListData$da, result;
20
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(params, _ref2) {
21
+ var scope, fields, _i, _Object$keys, key, field, _getListQuery, query, countQuery, _yield$getListData, _yield$getListData$da, result;
22
22
  return _regeneratorRuntime().wrap(function _callee$(_context) {
23
23
  while (1) switch (_context.prev = _context.next) {
24
24
  case 0:
25
- fields = {}; // eslint-disable-next-line @typescript-eslint/ban-ts-comment
26
- // @ts-ignore
25
+ scope = _ref2.scope;
26
+ fields = {}; // @ts-ignore
27
27
  for (_i = 0, _Object$keys = Object.keys(bodySchema.shape); _i < _Object$keys.length; _i++) {
28
28
  key = _Object$keys[_i];
29
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
30
29
  // @ts-ignore
31
30
  field = table[key];
32
31
  if (field) {
@@ -39,9 +38,10 @@ export function createGetAction(_ref) {
39
38
  jsonArrayFields: jsonArrayFields,
40
39
  params: params,
41
40
  relations: relations,
41
+ scope: scope,
42
42
  table: table
43
43
  }), query = _getListQuery.query, countQuery = _getListQuery.countQuery;
44
- _context.next = 5;
44
+ _context.next = 6;
45
45
  return getListData({
46
46
  bodySchema: bodySchema,
47
47
  countQuery: countQuery,
@@ -49,19 +49,19 @@ export function createGetAction(_ref) {
49
49
  pageSize: 1,
50
50
  query: query
51
51
  })();
52
- case 5:
52
+ case 6:
53
53
  _yield$getListData = _context.sent;
54
54
  _yield$getListData$da = _slicedToArray(_yield$getListData.data, 1);
55
55
  result = _yield$getListData$da[0];
56
56
  return _context.abrupt("return", result);
57
- case 9:
57
+ case 10:
58
58
  case "end":
59
59
  return _context.stop();
60
60
  }
61
61
  }, _callee);
62
62
  }));
63
- return function (_x) {
64
- return _ref2.apply(this, arguments);
63
+ return function (_x, _x2) {
64
+ return _ref3.apply(this, arguments);
65
65
  };
66
66
  }();
67
67
  }
@@ -1,13 +1,16 @@
1
1
  import { z, ZodSchema } from "zod";
2
2
  import { BaseTable, GetListRelations } from "../types.js";
3
3
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
4
+ import { ScopeArg } from "../scope.js";
4
5
  export declare function createGetListAction<T extends ZodSchema, TTable extends BaseTable>({ bodySchema, db, jsonArrayFields, relations, table, }: {
5
6
  bodySchema: T;
6
7
  db: NodePgDatabase<Record<string, unknown>>;
7
8
  jsonArrayFields?: string[];
8
9
  relations?: GetListRelations;
9
10
  table: TTable;
10
- }): (params: Partial<z.infer<T>> & Record<string, unknown>) => Promise<{
11
+ }): (params: Partial<z.infer<T>> & Record<string, unknown>, { scope }: {
12
+ scope: ScopeArg;
13
+ }) => Promise<{
11
14
  data: any;
12
15
  total: number;
13
16
  }>;
@@ -14,17 +14,16 @@ export function createGetListAction(_ref) {
14
14
  relations = _ref.relations,
15
15
  table = _ref.table;
16
16
  return /*#__PURE__*/function () {
17
- var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(params) {
18
- var _ref3, current, pageSize, other, fields, _i, _Object$keys, key, field, _getListQuery, query, countQuery;
17
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(params, _ref2) {
18
+ var scope, _ref4, current, pageSize, other, fields, _i, _Object$keys, key, field, _getListQuery, query, countQuery;
19
19
  return _regeneratorRuntime().wrap(function _callee$(_context) {
20
20
  while (1) switch (_context.prev = _context.next) {
21
21
  case 0:
22
- _ref3 = params, current = _ref3.current, pageSize = _ref3.pageSize, other = _objectWithoutProperties(_ref3, _excluded);
23
- fields = {}; // eslint-disable-next-line @typescript-eslint/ban-ts-comment
24
- // @ts-ignore
22
+ scope = _ref2.scope;
23
+ _ref4 = params, current = _ref4.current, pageSize = _ref4.pageSize, other = _objectWithoutProperties(_ref4, _excluded);
24
+ fields = {}; // @ts-ignore
25
25
  for (_i = 0, _Object$keys = Object.keys(bodySchema.shape); _i < _Object$keys.length; _i++) {
26
26
  key = _Object$keys[_i];
27
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
28
27
  // @ts-ignore
29
28
  field = table[key];
30
29
  if (field) {
@@ -37,6 +36,7 @@ export function createGetListAction(_ref) {
37
36
  jsonArrayFields: jsonArrayFields,
38
37
  params: other,
39
38
  relations: relations,
39
+ scope: scope,
40
40
  table: table
41
41
  }), query = _getListQuery.query, countQuery = _getListQuery.countQuery;
42
42
  return _context.abrupt("return", getListData({
@@ -46,14 +46,14 @@ export function createGetListAction(_ref) {
46
46
  pageSize: pageSize,
47
47
  query: query
48
48
  })());
49
- case 5:
49
+ case 6:
50
50
  case "end":
51
51
  return _context.stop();
52
52
  }
53
53
  }, _callee);
54
54
  }));
55
- return function (_x) {
56
- return _ref2.apply(this, arguments);
55
+ return function (_x, _x2) {
56
+ return _ref3.apply(this, arguments);
57
57
  };
58
58
  }();
59
59
  }
@@ -1,10 +1,11 @@
1
1
  import { z, ZodSchema } from "zod";
2
2
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
3
  import { BaseTable } from "../types.js";
4
+ import { ScopeArg } from "../scope.js";
4
5
  export declare function createPutAction<T extends ZodSchema, TTable extends BaseTable>({ db, table, }: {
5
6
  bodySchema: T;
6
7
  db: NodePgDatabase<Record<string, unknown>>;
7
8
  table: TTable;
8
- }): (body: Partial<z.infer<T>> & Record<string, unknown>, { byCreator, }?: {
9
- byCreator?: boolean | undefined;
9
+ }): (body: Partial<z.infer<T>> & Record<string, unknown>, { scope }: {
10
+ scope: ScopeArg;
10
11
  }) => Promise<z.infer<T>>;
@@ -8,58 +8,43 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
8
8
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
9
9
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
10
10
  function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
11
- import { z } from "zod";
12
11
  import { transformBody } from "./transform-body.js";
13
- import { eq } from "drizzle-orm";
14
- import { createGetAction } from "./../index.js";
12
+ import { and, eq } from "drizzle-orm";
13
+ import { NO_SCOPE, scopeCondition } from "../scope.js";
15
14
  import { HttpError } from "../errors.js";
16
15
  export function createPutAction(_ref) {
17
16
  var db = _ref.db,
18
17
  table = _ref.table;
19
18
  return /*#__PURE__*/function () {
20
- var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(body) {
21
- var _ref3,
22
- _ref3$byCreator,
23
- byCreator,
24
- _data,
25
- _yield$db$update$set$,
26
- _yield$db$update$set$2,
27
- data,
28
- _args = arguments;
19
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(body, _ref2) {
20
+ var scope, id, guard, _yield$db$update$set$, _yield$db$update$set$2, data;
29
21
  return _regeneratorRuntime().wrap(function _callee$(_context) {
30
22
  while (1) switch (_context.prev = _context.next) {
31
23
  case 0:
32
- _ref3 = _args.length > 1 && _args[1] !== undefined ? _args[1] : {}, _ref3$byCreator = _ref3.byCreator, byCreator = _ref3$byCreator === void 0 ? true : _ref3$byCreator;
33
- if (!byCreator) {
34
- _context.next = 7;
24
+ scope = _ref2.scope;
25
+ id = body.id;
26
+ if (id) {
27
+ _context.next = 4;
35
28
  break;
36
29
  }
37
- _context.next = 4;
38
- return createGetAction({
39
- db: db,
40
- table: table,
41
- bodySchema: z.object({
42
- creatorId: z.string(),
43
- id: z.string()
44
- })
45
- })({
46
- creatorId: body.editorId,
47
- id: body.id
48
- });
30
+ throw new HttpError(400, "缺少 id");
49
31
  case 4:
50
- _data = _context.sent;
51
- if (_data) {
52
- _context.next = 7;
53
- break;
54
- }
55
- throw new HttpError(404, "未找到编辑对象,或没有权限");
32
+ // 归属条件直接写进 UPDATE 的 where,而不是「先查一次确认归属、再无条件更新」。
33
+ // 先查后改有两个问题:多一次往返,以及那一查一改之间归属可能已经变了(转移给团队、
34
+ // 踢出成员)。一条语句里带上归属,改不到就是改不到。
35
+ guard = scopeCondition(table, scope);
36
+ _context.next = 7;
37
+ return db.update(table).set(transformBody(body)).where(guard ? and(eq(table.id, id), guard) : eq(table.id, id)).returning();
56
38
  case 7:
57
- _context.next = 9;
58
- return db.update(table).set(transformBody(body)).where(eq(table.id, body.id)).returning();
59
- case 9:
60
39
  _yield$db$update$set$ = _context.sent;
61
40
  _yield$db$update$set$2 = _slicedToArray(_yield$db$update$set$, 1);
62
41
  data = _yield$db$update$set$2[0];
42
+ if (!(!data && scope !== NO_SCOPE)) {
43
+ _context.next = 12;
44
+ break;
45
+ }
46
+ throw new HttpError(404, "未找到编辑对象,或没有权限");
47
+ case 12:
63
48
  return _context.abrupt("return", data);
64
49
  case 13:
65
50
  case "end":
@@ -67,8 +52,8 @@ export function createPutAction(_ref) {
67
52
  }
68
53
  }, _callee);
69
54
  }));
70
- return function (_x) {
71
- return _ref2.apply(this, arguments);
55
+ return function (_x, _x2) {
56
+ return _ref3.apply(this, arguments);
72
57
  };
73
58
  }();
74
59
  }
@@ -2,10 +2,17 @@ import { BaseTable, GetListRelations } from "../types.js";
2
2
  import { SelectedFields } from "drizzle-orm/pg-core/query-builders/select.types";
3
3
  import { SQL } from "drizzle-orm";
4
4
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
5
- export declare function getListQuery<TTable extends BaseTable, TSelection extends SelectedFields>({ db, fields, jsonArrayFields, params, relations, table, }: {
5
+ import { ScopeArg } from "../scope.js";
6
+ export declare function getListQuery<TTable extends BaseTable, TSelection extends SelectedFields>({ db, fields, jsonArrayFields, params, relations, scope, table, }: {
6
7
  db: NodePgDatabase<Record<string, unknown>>;
7
8
  fields: TSelection;
8
9
  jsonArrayFields?: string[];
10
+ /**
11
+ * 行级作用域。**必填**,不想隔离就显式传 NO_SCOPE —— 写成可选的话,漏传就退化成查全表,
12
+ * 而那是静默的越权(见 scope.ts)。它不走下面 params 那条筛选通道,因为那条的规矩是
13
+ * 「空值跳过」,一个取不到值的作用域会让整条 where 消失。
14
+ */
15
+ scope: ScopeArg;
9
16
  params: {
10
17
  [field: string]: unknown;
11
18
  orderBy?: keyof BaseTable;