@ai0x0/utils 0.1.3 → 0.2.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.
@@ -1,16 +1,12 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
- import { NextRequest } from "next/server";
3
2
  import { ZodSchema } from "zod";
4
3
  import { BaseTable } from "../types.js";
5
4
  import { type DeleteOperationOptions as CoreDeleteOperationOptions } from "./operation-core.js";
6
- export type DeleteOperationOptions<TTable extends BaseTable, B extends ZodSchema> = CoreDeleteOperationOptions<TTable, B>;
7
- export declare const createDeleteOperation: ({ db, getSession, }: {
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type DeleteOperationOptions<TTable extends BaseTable, B extends ZodSchema, TSession = DefaultSession> = CoreDeleteOperationOptions<TTable, B, TSession>;
7
+ export declare const createDeleteOperation: <TSession = DefaultSession>({ db, getSession, }: {
8
8
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
9
- getSession(req: NextRequest): Promise<{
10
- userId?: string;
11
- } | undefined>;
9
+ getSession: SessionGetter<TSession>;
12
10
  }) => <B extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> = import("zod").ZodObject<{
13
11
  id: import("zod").ZodString;
14
- }, import("zod/v4/core").$strip>>({ table, schemas, access, openApiOperation, handler, catch: catchHandler, }: CoreDeleteOperationOptions<BaseTable, B, {
15
- userId?: string | undefined;
16
- }>) => import("next-rest-framework").RouteOperationDefinition<"DELETE">;
12
+ }, import("zod/v4/core").$strip>>({ table, schemas, access, openApiOperation, handler, catch: catchHandler, }: CoreDeleteOperationOptions<BaseTable, B, TSession>) => import("next-rest-framework").RouteOperationDefinition<"DELETE">;
@@ -1,14 +1,10 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
- import { NextRequest } from "next/server";
3
2
  import { ZodSchema } from "zod";
4
3
  import { BaseTable, GetListRelations } from "../types.js";
5
4
  import { type GetListOperationOptions as CoreGetListOperationOptions } from "./operation-core.js";
6
- export type GetListOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable> = CoreGetListOperationOptions<T, Q, TTable, GetListRelations>;
7
- export declare const createGetListOperation: ({ db, getSession, }: {
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type GetListOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable, TSession = DefaultSession> = CoreGetListOperationOptions<T, Q, TTable, GetListRelations, TSession>;
7
+ export declare const createGetListOperation: <TSession = DefaultSession>({ db, getSession, }: {
8
8
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
9
- getSession(req: NextRequest): Promise<{
10
- userId?: string;
11
- } | undefined>;
12
- }) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, jsonArrayFields, setParams, handler, catch: catchHandler, }: CoreGetListOperationOptions<T, Q, BaseTable, GetListRelations, {
13
- userId?: string | undefined;
14
- }>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
9
+ getSession: SessionGetter<TSession>;
10
+ }) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, jsonArrayFields, setParams, handler, catch: catchHandler, }: CoreGetListOperationOptions<T, Q, BaseTable, GetListRelations, TSession>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
@@ -1,14 +1,10 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
- import { NextRequest } from "next/server";
3
2
  import { ZodSchema } from "zod";
4
3
  import { BaseTable, GetListRelations } from "../types.js";
5
4
  import { type GetOperationOptions as CoreGetOperationOptions } from "./operation-core.js";
6
- export type GetOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable> = CoreGetOperationOptions<T, Q, TTable, GetListRelations>;
7
- export declare const createGetOperation: ({ getSession, db, }: {
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type GetOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable, TSession = DefaultSession> = CoreGetOperationOptions<T, Q, TTable, GetListRelations, TSession>;
7
+ export declare const createGetOperation: <TSession = DefaultSession>({ getSession, db, }: {
8
8
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
9
- getSession(req: NextRequest): Promise<{
10
- userId?: string;
11
- } | undefined>;
12
- }) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, setParams, jsonArrayFields, handler, catch: catchHandler, }: CoreGetOperationOptions<T, Q, BaseTable, GetListRelations, {
13
- userId?: string | undefined;
14
- }>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
9
+ getSession: SessionGetter<TSession>;
10
+ }) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, setParams, jsonArrayFields, handler, catch: catchHandler, }: CoreGetOperationOptions<T, Q, BaseTable, GetListRelations, TSession>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
@@ -4,3 +4,5 @@ export * from "./delete-operation.js";
4
4
  export * from "./post-operation.js";
5
5
  export * from "./put-operation.js";
6
6
  export * from "./open-api-operation.js";
7
+ export type { AccessDenial, AccessOptions, DefaultSession, HttpMethod, SessionGetter, } from "./operation-common.js";
8
+ export type { ScopeCondition, ScopeValue } from "../scope.js";
@@ -3,4 +3,7 @@ export * from "./get-list-operation.js";
3
3
  export * from "./delete-operation.js";
4
4
  export * from "./post-operation.js";
5
5
  export * from "./put-operation.js";
6
- export * from "./open-api-operation.js";
6
+ export * from "./open-api-operation.js";
7
+ // 调用方要自己拼 access 预设(把 scope / can / stamp 收在一处复用)就得有这几个类型 ——
8
+ // 不导出的话只能在业务里重写一遍 HttpMethod 之类,两边一旦漂移就是静默的错配。
9
+ export {};
@@ -44,6 +44,18 @@ export interface AccessOptions<TSession = DefaultSession> {
44
44
  session: TSession;
45
45
  method: HttpMethod;
46
46
  }) => boolean | AccessDenial | Promise<boolean | AccessDenial>;
47
+ /**
48
+ * 新建(POST)时额外要盖在行上的列。归属列由 `scope` 盖,这里管别的服务端字段 ——
49
+ * 最典型的就是「谁建的」:归属列一旦不是 `creatorId`(比如换成 `ownerId`),作者就没人写了,
50
+ * 而 `setBody` 只能返回请求体 schema 里有的字段,`creatorId` 不在里面。
51
+ *
52
+ * 与归属戳同一条不变量:**最后展开,客户端覆盖不了**。所以这里能安全地盖任何服务端字段,
53
+ * 不必担心请求体里塞一个同名键把它顶掉。
54
+ *
55
+ * 库不替业务猜要盖什么(哪张表有 creatorId、要不要记作者,都是业务的事),所以给的是钩子
56
+ * 而不是默认行为。
57
+ */
58
+ stamp?: (session: TSession) => Record<string, unknown>;
47
59
  }
48
60
  /**
49
61
  * 把会话读取包成「本次请求最多取一次」的取值器。
@@ -69,7 +69,8 @@ function _resolveAccess() {
69
69
  case 0:
70
70
  access = _ref.access, loadSession = _ref.loadSession, method = _ref.method;
71
71
  wantsScope = Boolean(access === null || access === void 0 ? void 0 : access.scope) || ((_access$byCreator = access === null || access === void 0 ? void 0 : access.byCreator) !== null && _access$byCreator !== void 0 ? _access$byCreator : true); // 不需要就不取:关掉隔离的路由(多半是公开端点)不该为此白付一次会话读取。
72
- if (!(wantsScope || access !== null && access !== void 0 && access.can)) {
72
+ // stamp 也要会话 —— 它是拿会话算「盖哪些列」的。
73
+ if (!(wantsScope || access !== null && access !== void 0 && access.can || access !== null && access !== void 0 && access.stamp)) {
73
74
  _context.next = 8;
74
75
  break;
75
76
  }
@@ -1,14 +1,10 @@
1
1
  import { PgTable } from "drizzle-orm/pg-core";
2
2
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
- import { NextRequest } from "next/server";
4
3
  import { ZodSchema } from "zod";
5
4
  import { type PostOperationOptions as CorePostOperationOptions } from "./operation-core.js";
6
- export type PostOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable extends PgTable> = CorePostOperationOptions<IB, OB, TTable>;
7
- export declare const createPostOperation: ({ getSession, db, }: {
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type PostOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable extends PgTable, TSession = DefaultSession> = CorePostOperationOptions<IB, OB, TTable, TSession>;
7
+ export declare const createPostOperation: <TSession = DefaultSession>({ getSession, db, }: {
8
8
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
9
- getSession(req: NextRequest): Promise<{
10
- userId?: string;
11
- } | undefined>;
12
- }) => <IB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, OB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, contentType, openApiOperation, parseBody, setBody, table, handler, catch: catchHandler, }: CorePostOperationOptions<IB, OB, PgTable<import("drizzle-orm/pg-core").TableConfig>, {
13
- userId?: string | undefined;
14
- }>) => import("next-rest-framework").RouteOperationDefinition<"POST">;
9
+ getSession: SessionGetter<TSession>;
10
+ }) => <IB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, OB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, contentType, openApiOperation, parseBody, setBody, table, handler, catch: catchHandler, }: CorePostOperationOptions<IB, OB, PgTable<import("drizzle-orm/pg-core").TableConfig>, TSession>) => import("next-rest-framework").RouteOperationDefinition<"POST">;
@@ -1,14 +1,10 @@
1
1
  import { PgTable } from "drizzle-orm/pg-core";
2
2
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
- import { NextRequest } from "next/server";
4
3
  import { ZodSchema } from "zod";
5
4
  import { type PutOperationOptions as CorePutOperationOptions } from "./operation-core.js";
6
- export type PutOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable extends PgTable> = CorePutOperationOptions<IB, OB, TTable>;
7
- export declare const createPutOperation: ({ getSession, db, }: {
8
- getSession(req: NextRequest): Promise<{
9
- userId?: string;
10
- } | undefined>;
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type PutOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable extends PgTable, TSession = DefaultSession> = CorePutOperationOptions<IB, OB, TTable, TSession>;
7
+ export declare const createPutOperation: <TSession = DefaultSession>({ getSession, db, }: {
8
+ getSession: SessionGetter<TSession>;
11
9
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
12
- }) => <IB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, OB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, openApiOperation, table, setBody, handler, catch: catchHandler, }: CorePutOperationOptions<IB, OB, PgTable<import("drizzle-orm/pg-core").TableConfig>, {
13
- userId?: string | undefined;
14
- }>) => import("next-rest-framework").RouteOperationDefinition<"PUT">;
10
+ }) => <IB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, OB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, openApiOperation, table, setBody, handler, catch: catchHandler, }: CorePutOperationOptions<IB, OB, PgTable<import("drizzle-orm/pg-core").TableConfig>, TSession>) => import("next-rest-framework").RouteOperationDefinition<"PUT">;
@@ -54,7 +54,7 @@ export function createPostOperationFactory(_ref) {
54
54
  status: 200
55
55
  }]).handler( /*#__PURE__*/function () {
56
56
  var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(req) {
57
- var _yield$resolveAccess, scope, body, extraBody, params, raw, data;
57
+ var _access$stamp, _access$stamp2, _yield$resolveAccess, scope, session, body, extraBody, stamped, params, raw, data;
58
58
  return _regeneratorRuntime().wrap(function _callee$(_context) {
59
59
  while (1) switch (_context.prev = _context.next) {
60
60
  case 0:
@@ -68,27 +68,31 @@ export function createPostOperationFactory(_ref) {
68
68
  case 3:
69
69
  _yield$resolveAccess = _context.sent;
70
70
  scope = _yield$resolveAccess.scope;
71
- _context.next = 7;
71
+ session = _yield$resolveAccess.session;
72
+ _context.next = 8;
72
73
  return readPostBody(req, {
73
74
  contentType: contentType,
74
75
  parseBody: parseBody
75
76
  });
76
- case 7:
77
+ case 8:
77
78
  body = _context.sent;
78
- _context.next = 10;
79
+ _context.next = 11;
79
80
  return setBody === null || setBody === void 0 ? void 0 : setBody(req);
80
- case 10:
81
+ case 11:
81
82
  _context.t0 = _context.sent;
82
83
  if (_context.t0) {
83
- _context.next = 13;
84
+ _context.next = 14;
84
85
  break;
85
86
  }
86
87
  _context.t0 = {};
87
- case 13:
88
+ case 14:
88
89
  extraBody = _context.t0;
89
90
  // 新建的行归属这次请求的作用域。默认情形下作用域就是 creatorId = 当前用户,
90
91
  // 换了归属列(比如 ownerId)时它自动跟着换。
91
- // 想再记一个「谁建的」用 setBody 补 —— 那是调用方的语义,库不猜。
92
+ //
93
+ // 别的服务端字段(最典型的是「谁建的」)由 access.stamp 盖:归属列一旦不是
94
+ // creatorId,作者就没人写了,而 setBody 只能返回请求体 schema 里有的字段。
95
+ // 归属戳排在它后面 —— 两者撞同一个键时以归属为准,那是更强的那条不变量。
92
96
  //
93
97
  // **归属戳必须最后展开**,这是一条安全不变量,别为了「让调用方能覆盖」而调顺序:
94
98
  // `.input({ body })` 只校验、**不替换**请求体(next-rest-framework 交给 handler 的
@@ -96,55 +100,56 @@ export function createPostOperationFactory(_ref) {
96
100
  // schema 过滤。所以只要 body 排在戳后面,客户端在 JSON 里塞一个 `ownerId` /
97
101
  // `creatorId` 就能把行写到别人名下 —— 调用方把该列从 schema 里 omit 掉也拦不住。
98
102
  // 见 __tests__/route-operation-scope-stamp。
99
- params = _objectSpread(_objectSpread(_objectSpread({}, body), extraBody), scopeStamp(scope));
103
+ stamped = session === undefined ? {} : (_access$stamp = access === null || access === void 0 || (_access$stamp2 = access.stamp) === null || _access$stamp2 === void 0 ? void 0 : _access$stamp2.call(access, session)) !== null && _access$stamp !== void 0 ? _access$stamp : {};
104
+ params = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, body), extraBody), stamped), scopeStamp(scope));
100
105
  if (!(table && db)) {
101
- _context.next = 21;
106
+ _context.next = 23;
102
107
  break;
103
108
  }
104
- _context.next = 18;
109
+ _context.next = 20;
105
110
  return createAction({
106
111
  bodySchema: schemas.body,
107
112
  db: db,
108
113
  table: table
109
114
  })(params);
110
- case 18:
115
+ case 20:
111
116
  _context.t1 = _context.sent[0];
112
- _context.next = 22;
117
+ _context.next = 24;
113
118
  break;
114
- case 21:
119
+ case 23:
115
120
  _context.t1 = params;
116
- case 22:
121
+ case 24:
117
122
  raw = _context.t1;
118
123
  if (!handler) {
119
- _context.next = 29;
124
+ _context.next = 31;
120
125
  break;
121
126
  }
122
- _context.next = 26;
127
+ _context.next = 28;
123
128
  return handler({
124
129
  data: raw,
125
130
  params: params,
126
131
  req: req
127
132
  });
128
- case 26:
133
+ case 28:
129
134
  _context.t2 = _context.sent;
130
- _context.next = 30;
135
+ _context.next = 32;
131
136
  break;
132
- case 29:
137
+ case 31:
133
138
  _context.t2 = raw;
134
- case 30:
139
+ case 32:
135
140
  data = _context.t2;
136
141
  return _context.abrupt("return", TypedNextResponse.json(data, {
137
142
  status: 200
138
143
  }));
139
- case 34:
140
- _context.prev = 34;
144
+ case 36:
145
+ _context.prev = 36;
141
146
  _context.t3 = _context["catch"](0);
142
147
  return _context.abrupt("return", handleOperationError(_context.t3, catchHandler));
143
- case 37:
148
+ case 39:
144
149
  case "end":
145
150
  return _context.stop();
146
151
  }
147
- }, _callee, null, [[0, 34]]);
152
+ }, _callee, null, [[0, 36]]);
148
153
  }));
149
154
  return function (_x) {
150
155
  return _ref4.apply(this, arguments);
@@ -1,16 +1,12 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
- import { NextRequest } from "next/server";
3
2
  import { ZodSchema } from "zod";
4
3
  import { BaseTable } from "../types.js";
5
4
  import { type DeleteOperationOptions as CoreDeleteOperationOptions } from "./operation-core.js";
6
- export type DeleteOperationOptions<TTable extends BaseTable, B extends ZodSchema> = CoreDeleteOperationOptions<TTable, B>;
7
- export declare const createDeleteOperation: ({ db, getSession, }: {
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type DeleteOperationOptions<TTable extends BaseTable, B extends ZodSchema, TSession = DefaultSession> = CoreDeleteOperationOptions<TTable, B, TSession>;
7
+ export declare const createDeleteOperation: <TSession = DefaultSession>({ db, getSession, }: {
8
8
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
9
- getSession(req: NextRequest): Promise<{
10
- userId?: string;
11
- } | undefined>;
9
+ getSession: SessionGetter<TSession>;
12
10
  }) => <B extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> = import("zod").ZodObject<{
13
11
  id: import("zod").ZodString;
14
- }, import("zod/v4/core").$strip>>({ table, schemas, access, openApiOperation, handler, catch: catchHandler, }: CoreDeleteOperationOptions<BaseTable, B, {
15
- userId?: string | undefined;
16
- }>) => import("next-rest-framework").RouteOperationDefinition<"DELETE">;
12
+ }, import("zod/v4/core").$strip>>({ table, schemas, access, openApiOperation, handler, catch: catchHandler, }: CoreDeleteOperationOptions<BaseTable, B, TSession>) => import("next-rest-framework").RouteOperationDefinition<"DELETE">;
@@ -1,14 +1,10 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
- import { NextRequest } from "next/server";
3
2
  import { ZodSchema } from "zod";
4
3
  import { BaseTable, GetListRelations } from "../types.js";
5
4
  import { type GetListOperationOptions as CoreGetListOperationOptions } from "./operation-core.js";
6
- export type GetListOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable> = CoreGetListOperationOptions<T, Q, TTable, GetListRelations>;
7
- export declare const createGetListOperation: ({ db, getSession, }: {
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type GetListOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable, TSession = DefaultSession> = CoreGetListOperationOptions<T, Q, TTable, GetListRelations, TSession>;
7
+ export declare const createGetListOperation: <TSession = DefaultSession>({ db, getSession, }: {
8
8
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
9
- getSession(req: NextRequest): Promise<{
10
- userId?: string;
11
- } | undefined>;
12
- }) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, jsonArrayFields, setParams, handler, catch: catchHandler, }: CoreGetListOperationOptions<T, Q, BaseTable, GetListRelations, {
13
- userId?: string | undefined;
14
- }>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
9
+ getSession: SessionGetter<TSession>;
10
+ }) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, jsonArrayFields, setParams, handler, catch: catchHandler, }: CoreGetListOperationOptions<T, Q, BaseTable, GetListRelations, TSession>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
@@ -1,14 +1,10 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
- import { NextRequest } from "next/server";
3
2
  import { ZodSchema } from "zod";
4
3
  import { BaseTable, GetListRelations } from "../types.js";
5
4
  import { type GetOperationOptions as CoreGetOperationOptions } from "./operation-core.js";
6
- export type GetOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable> = CoreGetOperationOptions<T, Q, TTable, GetListRelations>;
7
- export declare const createGetOperation: ({ getSession, db, }: {
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type GetOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable, TSession = DefaultSession> = CoreGetOperationOptions<T, Q, TTable, GetListRelations, TSession>;
7
+ export declare const createGetOperation: <TSession = DefaultSession>({ getSession, db, }: {
8
8
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
9
- getSession(req: NextRequest): Promise<{
10
- userId?: string;
11
- } | undefined>;
12
- }) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, setParams, jsonArrayFields, handler, catch: catchHandler, }: CoreGetOperationOptions<T, Q, BaseTable, GetListRelations, {
13
- userId?: string | undefined;
14
- }>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
9
+ getSession: SessionGetter<TSession>;
10
+ }) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, setParams, jsonArrayFields, handler, catch: catchHandler, }: CoreGetOperationOptions<T, Q, BaseTable, GetListRelations, TSession>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
@@ -4,3 +4,5 @@ export * from "./delete-operation.js";
4
4
  export * from "./post-operation.js";
5
5
  export * from "./put-operation.js";
6
6
  export * from "./open-api-operation.js";
7
+ export type { AccessDenial, AccessOptions, DefaultSession, HttpMethod, SessionGetter, } from "./operation-common.js";
8
+ export type { ScopeCondition, ScopeValue } from "../scope.js";
@@ -44,6 +44,18 @@ export interface AccessOptions<TSession = DefaultSession> {
44
44
  session: TSession;
45
45
  method: HttpMethod;
46
46
  }) => boolean | AccessDenial | Promise<boolean | AccessDenial>;
47
+ /**
48
+ * 新建(POST)时额外要盖在行上的列。归属列由 `scope` 盖,这里管别的服务端字段 ——
49
+ * 最典型的就是「谁建的」:归属列一旦不是 `creatorId`(比如换成 `ownerId`),作者就没人写了,
50
+ * 而 `setBody` 只能返回请求体 schema 里有的字段,`creatorId` 不在里面。
51
+ *
52
+ * 与归属戳同一条不变量:**最后展开,客户端覆盖不了**。所以这里能安全地盖任何服务端字段,
53
+ * 不必担心请求体里塞一个同名键把它顶掉。
54
+ *
55
+ * 库不替业务猜要盖什么(哪张表有 creatorId、要不要记作者,都是业务的事),所以给的是钩子
56
+ * 而不是默认行为。
57
+ */
58
+ stamp?: (session: TSession) => Record<string, unknown>;
47
59
  }
48
60
  /**
49
61
  * 把会话读取包成「本次请求最多取一次」的取值器。
@@ -42,7 +42,7 @@ async function resolveAccess({
42
42
  }) {
43
43
  var _a;
44
44
  const wantsScope = Boolean(access == null ? void 0 : access.scope) || ((access == null ? void 0 : access.byCreator) ?? true);
45
- const session = wantsScope || (access == null ? void 0 : access.can) ? await loadSession() : void 0;
45
+ const session = wantsScope || (access == null ? void 0 : access.can) || (access == null ? void 0 : access.stamp) ? await loadSession() : void 0;
46
46
  if (access == null ? void 0 : access.can) {
47
47
  if (!session) {
48
48
  throw new import_errors.HttpError(401, "未登录");
@@ -1,14 +1,10 @@
1
1
  import { PgTable } from "drizzle-orm/pg-core";
2
2
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
- import { NextRequest } from "next/server";
4
3
  import { ZodSchema } from "zod";
5
4
  import { type PostOperationOptions as CorePostOperationOptions } from "./operation-core.js";
6
- export type PostOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable extends PgTable> = CorePostOperationOptions<IB, OB, TTable>;
7
- export declare const createPostOperation: ({ getSession, db, }: {
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type PostOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable extends PgTable, TSession = DefaultSession> = CorePostOperationOptions<IB, OB, TTable, TSession>;
7
+ export declare const createPostOperation: <TSession = DefaultSession>({ getSession, db, }: {
8
8
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
9
- getSession(req: NextRequest): Promise<{
10
- userId?: string;
11
- } | undefined>;
12
- }) => <IB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, OB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, contentType, openApiOperation, parseBody, setBody, table, handler, catch: catchHandler, }: CorePostOperationOptions<IB, OB, PgTable<import("drizzle-orm/pg-core").TableConfig>, {
13
- userId?: string | undefined;
14
- }>) => import("next-rest-framework").RouteOperationDefinition<"POST">;
9
+ getSession: SessionGetter<TSession>;
10
+ }) => <IB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, OB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, contentType, openApiOperation, parseBody, setBody, table, handler, catch: catchHandler, }: CorePostOperationOptions<IB, OB, PgTable<import("drizzle-orm/pg-core").TableConfig>, TSession>) => import("next-rest-framework").RouteOperationDefinition<"POST">;
@@ -1,14 +1,10 @@
1
1
  import { PgTable } from "drizzle-orm/pg-core";
2
2
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
- import { NextRequest } from "next/server";
4
3
  import { ZodSchema } from "zod";
5
4
  import { type PutOperationOptions as CorePutOperationOptions } from "./operation-core.js";
6
- export type PutOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable extends PgTable> = CorePutOperationOptions<IB, OB, TTable>;
7
- export declare const createPutOperation: ({ getSession, db, }: {
8
- getSession(req: NextRequest): Promise<{
9
- userId?: string;
10
- } | undefined>;
5
+ import type { DefaultSession, SessionGetter } from "./operation-common.js";
6
+ export type PutOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable extends PgTable, TSession = DefaultSession> = CorePutOperationOptions<IB, OB, TTable, TSession>;
7
+ export declare const createPutOperation: <TSession = DefaultSession>({ getSession, db, }: {
8
+ getSession: SessionGetter<TSession>;
11
9
  db?: NodePgDatabase<Record<string, unknown>> | undefined;
12
- }) => <IB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, OB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, openApiOperation, table, setBody, handler, catch: catchHandler, }: CorePutOperationOptions<IB, OB, PgTable<import("drizzle-orm/pg-core").TableConfig>, {
13
- userId?: string | undefined;
14
- }>) => import("next-rest-framework").RouteOperationDefinition<"PUT">;
10
+ }) => <IB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, OB extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, openApiOperation, table, setBody, handler, catch: catchHandler, }: CorePutOperationOptions<IB, OB, PgTable<import("drizzle-orm/pg-core").TableConfig>, TSession>) => import("next-rest-framework").RouteOperationDefinition<"PUT">;
@@ -62,15 +62,22 @@ function createPostOperationFactory({
62
62
  status: 200
63
63
  }
64
64
  ]).handler(async (req) => {
65
+ var _a;
65
66
  try {
66
- const { scope } = await (0, import_operation_common.resolveAccess)({
67
+ const { scope, session } = await (0, import_operation_common.resolveAccess)({
67
68
  access,
68
69
  loadSession: (0, import_operation_common.sessionLoader)(getSession, req),
69
70
  method: "POST"
70
71
  });
71
72
  const body = await (0, import_operation_common.readPostBody)(req, { contentType, parseBody });
72
73
  const extraBody = await (setBody == null ? void 0 : setBody(req)) || {};
73
- const params = { ...body, ...extraBody, ...scopeStamp(scope) };
74
+ const stamped = session === void 0 ? {} : ((_a = access == null ? void 0 : access.stamp) == null ? void 0 : _a.call(access, session)) ?? {};
75
+ const params = {
76
+ ...body,
77
+ ...extraBody,
78
+ ...stamped,
79
+ ...scopeStamp(scope)
80
+ };
74
81
  const raw = table && db ? (await createAction({
75
82
  bodySchema: schemas.body,
76
83
  db,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai0x0/utils",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "AI0x0 utils",
5
5
  "keywords": [
6
6
  "ai0x0"