@ai0x0/utils 0.6.0 → 0.7.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.
- package/.agents/skills/ai0x0-utils/backend/SKILL.md +8 -5
- package/SKILL.md +8 -5
- package/es/backend/actions/create-delete-action.d.ts +2 -3
- package/es/backend/actions/create-delete-action.js +8 -44
- package/es/backend/actions/create-get-action.js +11 -7
- package/es/backend/actions/create-get-list-action.js +11 -6
- package/es/backend/actions/create-put-action.d.ts +1 -1
- package/es/backend/actions/create-put-action.js +15 -39
- package/es/backend/actions/get-list-data.d.ts +1 -1
- package/es/backend/actions/get-list-data.js +12 -5
- package/es/backend/actions/get-list-query-core.d.ts +67 -0
- package/es/backend/actions/get-list-query-core.js +306 -0
- package/es/backend/actions/get-list-query.d.ts +7 -18
- package/es/backend/actions/get-list-query.js +23 -220
- package/es/backend/actions/index.d.ts +1 -0
- package/es/backend/actions/index.js +2 -1
- package/es/backend/actions/paging.d.ts +11 -0
- package/es/backend/actions/paging.js +32 -0
- package/es/backend/actions/write-action-core.d.ts +18 -0
- package/es/backend/actions/write-action-core.js +119 -0
- package/es/backend/route-operation/write-operation-core.js +12 -5
- package/es/backend/schemas/common.d.ts +112 -0
- package/es/backend/schemas/common.js +158 -0
- package/es/backend/schemas/index.d.ts +735 -364
- package/es/backend/schemas/index.js +8 -139
- package/es/backend/sqlite/actions/create-delete-action.d.ts +1 -2
- package/es/backend/sqlite/actions/create-delete-action.js +7 -43
- package/es/backend/sqlite/actions/create-get-action.js +9 -6
- package/es/backend/sqlite/actions/create-get-list-action.js +7 -5
- package/es/backend/sqlite/actions/create-put-action.d.ts +1 -1
- package/es/backend/sqlite/actions/create-put-action.js +13 -36
- package/es/backend/sqlite/actions/get-list-data.d.ts +1 -1
- package/es/backend/sqlite/actions/get-list-data.js +11 -5
- package/es/backend/sqlite/actions/get-list-query.d.ts +6 -9
- package/es/backend/sqlite/actions/get-list-query.js +24 -217
- package/es/backend/sqlite/errors.d.ts +1 -4
- package/es/backend/sqlite/errors.js +6 -31
- package/es/backend/sqlite/schemas/index.d.ts +1 -111
- package/es/backend/sqlite/schemas/index.js +8 -139
- package/lib/backend/actions/create-delete-action.d.ts +2 -3
- package/lib/backend/actions/create-delete-action.js +2 -14
- package/lib/backend/actions/create-get-action.js +3 -1
- package/lib/backend/actions/create-get-list-action.js +3 -1
- package/lib/backend/actions/create-put-action.d.ts +1 -1
- package/lib/backend/actions/create-put-action.js +3 -16
- package/lib/backend/actions/get-list-data.d.ts +1 -1
- package/lib/backend/actions/get-list-data.js +8 -3
- package/lib/backend/actions/get-list-query-core.d.ts +67 -0
- package/lib/backend/actions/get-list-query-core.js +206 -0
- package/lib/backend/actions/get-list-query.d.ts +7 -18
- package/lib/backend/actions/get-list-query.js +16 -141
- package/lib/backend/actions/index.d.ts +1 -0
- package/lib/backend/actions/index.js +7 -2
- package/lib/backend/actions/paging.d.ts +11 -0
- package/lib/backend/actions/paging.js +50 -0
- package/lib/backend/actions/write-action-core.d.ts +18 -0
- package/lib/backend/actions/write-action-core.js +70 -0
- package/lib/backend/schemas/common.d.ts +112 -0
- package/lib/backend/schemas/common.js +112 -0
- package/lib/backend/schemas/index.d.ts +735 -364
- package/lib/backend/schemas/index.js +12 -83
- package/lib/backend/sqlite/actions/create-delete-action.d.ts +1 -2
- package/lib/backend/sqlite/actions/create-delete-action.js +2 -14
- package/lib/backend/sqlite/actions/create-get-action.js +3 -1
- package/lib/backend/sqlite/actions/create-get-list-action.js +3 -1
- package/lib/backend/sqlite/actions/create-put-action.d.ts +1 -1
- package/lib/backend/sqlite/actions/create-put-action.js +3 -16
- package/lib/backend/sqlite/actions/get-list-data.d.ts +1 -1
- package/lib/backend/sqlite/actions/get-list-data.js +8 -3
- package/lib/backend/sqlite/actions/get-list-query.d.ts +6 -9
- package/lib/backend/sqlite/actions/get-list-query.js +16 -141
- package/lib/backend/sqlite/errors.d.ts +1 -4
- package/lib/backend/sqlite/errors.js +2 -8
- package/lib/backend/sqlite/schemas/index.d.ts +1 -111
- package/lib/backend/sqlite/schemas/index.js +12 -83
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// ==============================================================================
|
|
2
|
+
// 分页参数归一化(pg / sqlite 共用)
|
|
3
|
+
// ==============================================================================
|
|
4
|
+
// query 参数在线上只有字符串,而这两个值最后是直接进 `.limit()` / `.offset()` 的。不归一化的
|
|
5
|
+
// 后果不是「筛不准」,是三种更难看的症状:
|
|
6
|
+
//
|
|
7
|
+
// · `?pageSize=abc` → `+"abc"` 是 NaN,而 drizzle 对 falsy 的 limit 是**整条 LIMIT 不加** ——
|
|
8
|
+
// 一个笔误就是全量返回。这是这个文件存在的首要理由。
|
|
9
|
+
// · `?current=0` → OFFSET 变成负数,数据库直接报错,一个 500。
|
|
10
|
+
// · `?pageSize=99999999` → 真的去拉这么多行。
|
|
11
|
+
//
|
|
12
|
+
// 上界是**截断**而不是报错:列表的 describe 一直在教调用方「要整批就把 pageSize 调大」,
|
|
13
|
+
// 那些调用方传的是 5000 这种量级的真实值,为了一个上界让它们 400 不值当。截断之后 total 照常
|
|
14
|
+
// 是真实总数,调用方分得清自己有没有拿全。
|
|
15
|
+
|
|
16
|
+
/** pageSize 的硬上界。取这个量级是因为「一次把一整块数据取回来」是列表的正常用法。 */
|
|
17
|
+
export var MAX_PAGE_SIZE = 10000;
|
|
18
|
+
export var DEFAULT_PAGE_SIZE = 10;
|
|
19
|
+
function toPositiveInt(value, fallback) {
|
|
20
|
+
var parsed = Math.floor(Number(value));
|
|
21
|
+
return Number.isFinite(parsed) && parsed >= 1 ? parsed : fallback;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 返回一定能安全喂给 limit / offset 的一对整数。 */
|
|
25
|
+
export function normalizePaging(_ref) {
|
|
26
|
+
var current = _ref.current,
|
|
27
|
+
pageSize = _ref.pageSize;
|
|
28
|
+
return {
|
|
29
|
+
current: toPositiveInt(current, 1),
|
|
30
|
+
pageSize: Math.min(toPositiveInt(pageSize, DEFAULT_PAGE_SIZE), MAX_PAGE_SIZE)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ScopeArg } from "../scope.js";
|
|
2
|
+
/** 核心用得到表的哪一部分:只有 id。dialect 各自的 Table 类型不进来。 */
|
|
3
|
+
export interface WriteActionTable {
|
|
4
|
+
id: unknown;
|
|
5
|
+
}
|
|
6
|
+
interface WriteActionDeps<TTable extends WriteActionTable> {
|
|
7
|
+
db: any;
|
|
8
|
+
table: TTable;
|
|
9
|
+
}
|
|
10
|
+
export declare function createPutActionCore<TTable extends WriteActionTable>({ db, table, }: WriteActionDeps<TTable>): (body: Record<string, unknown>, { scope }: {
|
|
11
|
+
scope: ScopeArg;
|
|
12
|
+
}) => Promise<unknown>;
|
|
13
|
+
export declare function createDeleteActionCore<TTable extends WriteActionTable>({ db, table, }: WriteActionDeps<TTable>): ({ id }: {
|
|
14
|
+
id: string;
|
|
15
|
+
}, { scope }: {
|
|
16
|
+
scope: ScopeArg;
|
|
17
|
+
}) => Promise<any>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
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
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
4
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
5
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
6
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
7
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
8
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
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
|
+
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
|
+
// ==============================================================================
|
|
12
|
+
// 单行写操作的公共实现(pg / sqlite 共用)
|
|
13
|
+
// ==============================================================================
|
|
14
|
+
// 改一行、删一行,两套方言的函数体逐字相同 —— 差的只有 db 的类型(NodePgDatabase vs any)
|
|
15
|
+
// 和一句给 scopeCondition 的断言。而这两个函数里恰恰藏着三条**安全性质**,抄两份意味着
|
|
16
|
+
// 三条都要在两棵树上各维护一遍:
|
|
17
|
+
//
|
|
18
|
+
// 1. **归属写进 where,不先查后改**。先查一次确认归属、再无条件写,中间那一瞬归属可能已经
|
|
19
|
+
// 变了(转移给团队、被踢出成员);一条语句里带上归属,改不到就是改不到。
|
|
20
|
+
// 2. **作用域必填**(不隔离要显式传 NO_SCOPE)。这里原先是可选的 creatorId + `if (有值)
|
|
21
|
+
// 才校验`,于是未登录传下来的空值直接变成「不校验、按 id 删任意行」。
|
|
22
|
+
// 3. **不区分「不存在」与「不归你」**。分得清就等于给了一个探测接口:拿 id 挨个试就能问出
|
|
23
|
+
// 某条记录存不存在。
|
|
24
|
+
//
|
|
25
|
+
// 这三条现在只有一份。两个方言各自的薄封装只负责把签名收紧回本方言的类型。
|
|
26
|
+
|
|
27
|
+
import { and, eq } from "drizzle-orm";
|
|
28
|
+
import { NO_SCOPE, scopeCondition } from "../scope.js";
|
|
29
|
+
import { HttpError } from "../errors.js";
|
|
30
|
+
import { transformBody } from "./transform-body.js";
|
|
31
|
+
|
|
32
|
+
/** 核心用得到表的哪一部分:只有 id。dialect 各自的 Table 类型不进来。 */
|
|
33
|
+
|
|
34
|
+
// id 那一列在两边都是 Column,但类型不同;比较条件交给 drizzle 自己去解。
|
|
35
|
+
function byId(table, id, scope) {
|
|
36
|
+
var guard = scopeCondition(table, scope);
|
|
37
|
+
var same = eq(table.id, id);
|
|
38
|
+
return guard ? and(same, guard) : same;
|
|
39
|
+
}
|
|
40
|
+
export function createPutActionCore(_ref) {
|
|
41
|
+
var db = _ref.db,
|
|
42
|
+
table = _ref.table;
|
|
43
|
+
return /*#__PURE__*/function () {
|
|
44
|
+
var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(body, _ref2) {
|
|
45
|
+
var scope, id, _yield$db$update$set$, _yield$db$update$set$2, data;
|
|
46
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
47
|
+
while (1) switch (_context.prev = _context.next) {
|
|
48
|
+
case 0:
|
|
49
|
+
scope = _ref2.scope;
|
|
50
|
+
id = body.id;
|
|
51
|
+
if (id) {
|
|
52
|
+
_context.next = 4;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
throw new HttpError(400, "缺少 id");
|
|
56
|
+
case 4:
|
|
57
|
+
_context.next = 6;
|
|
58
|
+
return db.update(table).set(transformBody(body)).where(byId(table, id, scope)).returning();
|
|
59
|
+
case 6:
|
|
60
|
+
_yield$db$update$set$ = _context.sent;
|
|
61
|
+
_yield$db$update$set$2 = _slicedToArray(_yield$db$update$set$, 1);
|
|
62
|
+
data = _yield$db$update$set$2[0];
|
|
63
|
+
if (!(!data && scope !== NO_SCOPE)) {
|
|
64
|
+
_context.next = 11;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
throw new HttpError(404, "未找到编辑对象,或没有权限");
|
|
68
|
+
case 11:
|
|
69
|
+
return _context.abrupt("return", data);
|
|
70
|
+
case 12:
|
|
71
|
+
case "end":
|
|
72
|
+
return _context.stop();
|
|
73
|
+
}
|
|
74
|
+
}, _callee);
|
|
75
|
+
}));
|
|
76
|
+
return function (_x, _x2) {
|
|
77
|
+
return _ref3.apply(this, arguments);
|
|
78
|
+
};
|
|
79
|
+
}();
|
|
80
|
+
}
|
|
81
|
+
export function createDeleteActionCore(_ref4) {
|
|
82
|
+
var db = _ref4.db,
|
|
83
|
+
table = _ref4.table;
|
|
84
|
+
return /*#__PURE__*/function () {
|
|
85
|
+
var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(_ref5, _ref6) {
|
|
86
|
+
var id, scope, rows;
|
|
87
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
88
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
89
|
+
case 0:
|
|
90
|
+
id = _ref5.id;
|
|
91
|
+
scope = _ref6.scope;
|
|
92
|
+
if (id) {
|
|
93
|
+
_context2.next = 4;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
throw new HttpError(400, "缺少 id");
|
|
97
|
+
case 4:
|
|
98
|
+
_context2.next = 6;
|
|
99
|
+
return db.delete(table).where(byId(table, id, scope)).returning();
|
|
100
|
+
case 6:
|
|
101
|
+
rows = _context2.sent;
|
|
102
|
+
if (!(rows.length === 0 && scope !== NO_SCOPE)) {
|
|
103
|
+
_context2.next = 9;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
throw new HttpError(404, "未找到删除对象,或没有权限");
|
|
107
|
+
case 9:
|
|
108
|
+
return _context2.abrupt("return", rows);
|
|
109
|
+
case 10:
|
|
110
|
+
case "end":
|
|
111
|
+
return _context2.stop();
|
|
112
|
+
}
|
|
113
|
+
}, _callee2);
|
|
114
|
+
}));
|
|
115
|
+
return function (_x3, _x4) {
|
|
116
|
+
return _ref7.apply(this, arguments);
|
|
117
|
+
};
|
|
118
|
+
}();
|
|
119
|
+
}
|
|
@@ -98,11 +98,18 @@ export function createPostOperationFactory(_ref) {
|
|
|
98
98
|
// creatorId,作者就没人写了,而 setBody 只能返回请求体 schema 里有的字段。
|
|
99
99
|
// 归属戳排在它后面 —— 两者撞同一个键时以归属为准,那是更强的那条不变量。
|
|
100
100
|
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
101
|
+
// **归属戳必须最后展开**,这是一条安全不变量,别为了「让调用方能覆盖」而调顺序。
|
|
102
|
+
//
|
|
103
|
+
// 请求体到这里已经被 `.input({ body })` 过了一遍 zod:next-rest-framework 把 handler
|
|
104
|
+
// 收到的那个 clone 的 `.json()` 换成了校验后的结果,所以 schema 里没有的键(归属列、
|
|
105
|
+
// basicFields、serverColumns)在这一步之前就没了。**但别把那当成这里的边界**:
|
|
106
|
+
// · 它是被钉住的那个 fork 的行为,本仓库拦不住它变(见
|
|
107
|
+
// __tests__/write-body-strip,那组用例就是钉它的);
|
|
108
|
+
// · 自定义 parseBody 会绕开它;`.catchall()` / `.passthrough()` 的 body schema
|
|
109
|
+
// 也会让未知键活着过来;
|
|
110
|
+
// · createPostAction 是 `db.insert(table).values(body)`,中间没有第二道 schema
|
|
111
|
+
// 过滤,而 drizzle 会老老实实写下每一个**是真列**的键。
|
|
112
|
+
// 所以归属这一条不靠 schema,靠顺序:戳排在 body 后面,客户端塞什么都盖不掉。
|
|
106
113
|
// 见 __tests__/route-operation-scope-stamp。
|
|
107
114
|
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 : {};
|
|
108
115
|
params = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, body), extraBody), stamped), scopeStamp(scope));
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* 业务侧 insert schema 需要从 drizzle-zod 生成的 schema 中排除的基础字段。
|
|
4
|
+
* 这些字段由后端在 createPostAction 中自动写入。
|
|
5
|
+
*
|
|
6
|
+
* 使用方式(在业务 schema 中):
|
|
7
|
+
* const insertFooSchema = createInsertSchema(foos)
|
|
8
|
+
* .omit(BASIC_INSERT_OMIT)
|
|
9
|
+
* .extend({ ... });
|
|
10
|
+
*
|
|
11
|
+
* 说明:上游导出为 const 而不是 helper function,是为了避免经过泛型
|
|
12
|
+
* wrapper 后 TS 把 drizzle-zod 推导出的深层条件 Shape 归约成 Omit,
|
|
13
|
+
* 从而在下游 `.extend(...)` 时丢失原有字段类型。
|
|
14
|
+
*/
|
|
15
|
+
export declare const BASIC_INSERT_OMIT: {
|
|
16
|
+
readonly id: true;
|
|
17
|
+
readonly creatorId: true;
|
|
18
|
+
readonly editorId: true;
|
|
19
|
+
readonly accessedAt: true;
|
|
20
|
+
readonly createdAt: true;
|
|
21
|
+
readonly updatedAt: true;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 业务侧 update schema 需要从 drizzle-zod 生成的 schema 中排除的基础字段。
|
|
25
|
+
* update 保留 id(随后用 `.required({ id: true })` 变成必填),其余由
|
|
26
|
+
* 后端 createPutAction 自动维护。
|
|
27
|
+
*/
|
|
28
|
+
export declare const BASIC_UPDATE_OMIT: {
|
|
29
|
+
readonly creatorId: true;
|
|
30
|
+
readonly editorId: true;
|
|
31
|
+
readonly accessedAt: true;
|
|
32
|
+
readonly createdAt: true;
|
|
33
|
+
readonly updatedAt: true;
|
|
34
|
+
};
|
|
35
|
+
export declare const queryListSchema: <Incoming extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: Incoming) => z.ZodObject<(("current" | "pageSize" | "orderBy" | "orderDir" | "creatorId" | "createdAtFrom" | "createdAtTo") & keyof Incoming["shape"] extends never ? {
|
|
36
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
37
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
38
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
39
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
40
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
41
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
42
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
43
|
+
asc: "asc";
|
|
44
|
+
desc: "desc";
|
|
45
|
+
}>>;
|
|
46
|
+
} & Incoming["shape"] : ({
|
|
47
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
48
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
49
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
50
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
51
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
52
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
53
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
54
|
+
asc: "asc";
|
|
55
|
+
desc: "desc";
|
|
56
|
+
}>>;
|
|
57
|
+
} extends infer T_1 extends z.core.util.SomeObject ? { [K in keyof T_1 as K extends keyof Incoming["shape"] ? never : K]: {
|
|
58
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
59
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
60
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
61
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
62
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
63
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
64
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
65
|
+
asc: "asc";
|
|
66
|
+
desc: "desc";
|
|
67
|
+
}>>;
|
|
68
|
+
}[K]; } : never) & (Incoming["shape"] extends infer T_2 extends z.core.util.SomeObject ? { [K_1 in keyof T_2]: Incoming["shape"][K_1]; } : never)) extends infer T ? { [k in keyof T]: (("current" | "pageSize" | "orderBy" | "orderDir" | "creatorId" | "createdAtFrom" | "createdAtTo") & keyof Incoming["shape"] extends never ? {
|
|
69
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
70
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
71
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
72
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
73
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
74
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
75
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
76
|
+
asc: "asc";
|
|
77
|
+
desc: "desc";
|
|
78
|
+
}>>;
|
|
79
|
+
} & Incoming["shape"] : ({
|
|
80
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
81
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
82
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
83
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
84
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
85
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
86
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
87
|
+
asc: "asc";
|
|
88
|
+
desc: "desc";
|
|
89
|
+
}>>;
|
|
90
|
+
} extends infer T_1 extends z.core.util.SomeObject ? { [K in keyof T_1 as K extends keyof Incoming["shape"] ? never : K]: {
|
|
91
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
92
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
93
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
94
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
95
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
96
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
97
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
98
|
+
asc: "asc";
|
|
99
|
+
desc: "desc";
|
|
100
|
+
}>>;
|
|
101
|
+
}[K]; } : never) & (Incoming["shape"] extends infer T_2 extends z.core.util.SomeObject ? { [K_1 in keyof T_2]: Incoming["shape"][K_1]; } : never))[k]; } : never, Incoming["_zod"]["config"]>;
|
|
102
|
+
export declare const listBodySchema: <T extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>(schema: T) => z.ZodObject<{
|
|
103
|
+
total: z.ZodNumber;
|
|
104
|
+
data: z.ZodArray<T>;
|
|
105
|
+
}, z.core.$strip>;
|
|
106
|
+
/**
|
|
107
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
108
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
109
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
110
|
+
*/
|
|
111
|
+
export declare const describeFields: <T extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: T, notes: Record<string, string>) => T;
|
|
112
|
+
export declare const describeBasicFields: <T extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: T) => T;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
4
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
5
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
// ==============================================================================
|
|
8
|
+
// 与方言无关的那几份 schema 助手(pg / sqlite 共用)
|
|
9
|
+
// ==============================================================================
|
|
10
|
+
// 下面这些统统只碰 zod,与用哪种数据库没有半点关系 —— 从前 pg 与 sqlite 的 schemas/index
|
|
11
|
+
// 各抄了一份,170 行逐字相同,只有一句注释的措辞不一样。
|
|
12
|
+
//
|
|
13
|
+
// 抄两份的代价在 queryListSchema 上最明显:那七个字段的 `.describe()` 会流进每一个 list 端点
|
|
14
|
+
// 的 OpenAPI spec,再从那里进生成的 client 与 CLI 文档。改一处措辞而忘了另一棵树,两边的接口
|
|
15
|
+
// 文档就开始各说各话,而没有任何东西会报错。
|
|
16
|
+
//
|
|
17
|
+
// 留在各自 index 里的是真·方言相关的两样:basicFields(那几列怎么建)与 createTableSchema
|
|
18
|
+
// (怎么拼表),它们绕不开各自的列构造器。
|
|
19
|
+
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 业务侧 insert schema 需要从 drizzle-zod 生成的 schema 中排除的基础字段。
|
|
24
|
+
* 这些字段由后端在 createPostAction 中自动写入。
|
|
25
|
+
*
|
|
26
|
+
* 使用方式(在业务 schema 中):
|
|
27
|
+
* const insertFooSchema = createInsertSchema(foos)
|
|
28
|
+
* .omit(BASIC_INSERT_OMIT)
|
|
29
|
+
* .extend({ ... });
|
|
30
|
+
*
|
|
31
|
+
* 说明:上游导出为 const 而不是 helper function,是为了避免经过泛型
|
|
32
|
+
* wrapper 后 TS 把 drizzle-zod 推导出的深层条件 Shape 归约成 Omit,
|
|
33
|
+
* 从而在下游 `.extend(...)` 时丢失原有字段类型。
|
|
34
|
+
*/
|
|
35
|
+
export var BASIC_INSERT_OMIT = {
|
|
36
|
+
id: true,
|
|
37
|
+
creatorId: true,
|
|
38
|
+
editorId: true,
|
|
39
|
+
accessedAt: true,
|
|
40
|
+
createdAt: true,
|
|
41
|
+
updatedAt: true
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 业务侧 update schema 需要从 drizzle-zod 生成的 schema 中排除的基础字段。
|
|
46
|
+
* update 保留 id(随后用 `.required({ id: true })` 变成必填),其余由
|
|
47
|
+
* 后端 createPutAction 自动维护。
|
|
48
|
+
*/
|
|
49
|
+
export var BASIC_UPDATE_OMIT = {
|
|
50
|
+
creatorId: true,
|
|
51
|
+
editorId: true,
|
|
52
|
+
accessedAt: true,
|
|
53
|
+
createdAt: true,
|
|
54
|
+
updatedAt: true
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// =============================================================================
|
|
58
|
+
// 列表查询字段
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// 说明写成 `.describe()` 而不是行末注释:注释只有读源码的人看得到,而这七个字段会出现在每一个
|
|
61
|
+
// list 端点的 OpenAPI spec 里,再从那里流进生成的 client 与 CLI 文档。写成注释的代价是下游只能
|
|
62
|
+
// 自己再手写一份 —— 那份必然会漂,而且漂了没有任何东西会报错。
|
|
63
|
+
//
|
|
64
|
+
// 类型是 string 而不是 number:query 参数在线上只有字符串,服务端收下之后才转数字。默认值也
|
|
65
|
+
// 因此是 "1" / "10"。
|
|
66
|
+
export var queryListSchema = function queryListSchema(schema) {
|
|
67
|
+
return z.object({
|
|
68
|
+
current: z.string().optional().default("1").describe("Page number, starting at 1."),
|
|
69
|
+
pageSize: z.string().optional().default("10").describe("Rows per page. Omitting it yields only 10 — pass a larger value to get the whole set. Capped at 10000; compare `total` against what you received to tell whether more remain."),
|
|
70
|
+
createdAtFrom: z.string().optional().describe("Keep only rows created at or after this instant (ISO 8601, e.g. 2026-08-01T00:00:00Z)."),
|
|
71
|
+
createdAtTo: z.string().optional().describe("Keep only rows created at or before this instant."),
|
|
72
|
+
orderBy: z.string().optional().describe("Column to sort by — a column name of this resource (createdAt / updatedAt and the like)."),
|
|
73
|
+
creatorId: z.string().optional().describe("Keep only rows created by this user. Useful in shared spaces to filter down to one member."),
|
|
74
|
+
orderDir: z.enum(["asc", "desc"]).optional().describe("Sort ascending or descending.")
|
|
75
|
+
}).merge(schema);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// =============================================================================
|
|
79
|
+
// 列表返回字段
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// 这两个字段出现在**每一个** list 端点的响应里,所以说明写在这儿一次就够 —— 下游那边它们是
|
|
82
|
+
// 逐个 list 命令重复一遍的(在一个 101 端点的项目上是 24 行空白)。
|
|
83
|
+
export var listBodySchema = function listBodySchema(schema) {
|
|
84
|
+
return z.object({
|
|
85
|
+
total: z.number().describe("Total number of rows matching the filters — not the length of `data`. Use it to decide whether another page exists."),
|
|
86
|
+
data: z.array(schema).describe("This page's rows.")
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// =============================================================================
|
|
91
|
+
// 给基础字段挂说明
|
|
92
|
+
// =============================================================================
|
|
93
|
+
// basicFields 是 drizzle 列,而 drizzle 列没有「描述」这个概念 —— 所以说明只能挂在
|
|
94
|
+
// drizzle-zod 出来的 schema 上。值得做是因为这六个字段出现在**每一张表**的 select schema 里:
|
|
95
|
+
// 一处写完,所有 list / get 端点的返回字段表都有了(在 do-tv 上是 173 行)。
|
|
96
|
+
//
|
|
97
|
+
// 一律只挂元数据,**不换类型**:`.describe()` / `.meta()` 返回的是带元数据的克隆,校验行为一模
|
|
98
|
+
// 一样。selectSchema 是有人拿去 safeParse 的(本包自己的测试就在用 Date 校验它),换类型等于改
|
|
99
|
+
// 这个包的契约。
|
|
100
|
+
//
|
|
101
|
+
// 时间戳那三列额外用 `.meta()` 补上 JSON Schema 表示,因为不补就是错的:drizzle-zod 给的是
|
|
102
|
+
// `z.date()`,而 `Date` 在 JSON Schema 里表达不出来,于是这三个字段在 spec 里是**空对象** ——
|
|
103
|
+
// 没有 `type`,照 spec 生成的 client / CLI 只能得到 `unknown`。而线上根本不可能是 Date:响应一经
|
|
104
|
+
// JSON 序列化就是 ISO 8601 字符串。
|
|
105
|
+
//
|
|
106
|
+
// `.meta({ type, format })` 恰好只改「怎么描述」不改「怎么校验」:spec 里成为
|
|
107
|
+
// `{"type":"string","format":"date-time"}`,而 `safeParse(new Date())` 照样通过。
|
|
108
|
+
// 不用 `z.iso.datetime()` 换掉它,是因为那既改了校验(不再收 Date),又会往 JSON Schema 里塞一条
|
|
109
|
+
// 380 字节的正则 —— 乘上每张表几十 KB,而 `format` 已经把「按日期时间解析」说清楚了。
|
|
110
|
+
var BASIC_FIELD_NOTES = {
|
|
111
|
+
creatorId: "User id of the creator. Stamped by the server; clients cannot send it.",
|
|
112
|
+
editorId: "User id of whoever changed this row last. Stamped by the server; clients cannot send it.",
|
|
113
|
+
id: "Primary key of this row (uuid)."
|
|
114
|
+
};
|
|
115
|
+
var BASIC_TIMESTAMP_NOTES = {
|
|
116
|
+
accessedAt: "When this row was last accessed.",
|
|
117
|
+
createdAt: "When this row was created.",
|
|
118
|
+
updatedAt: "When this row was last modified."
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
123
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
124
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
125
|
+
*/
|
|
126
|
+
export var describeFields = function describeFields(schema, notes) {
|
|
127
|
+
var overlay = {};
|
|
128
|
+
for (var _i = 0, _Object$entries = Object.entries(notes); _i < _Object$entries.length; _i++) {
|
|
129
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
130
|
+
field = _Object$entries$_i[0],
|
|
131
|
+
note = _Object$entries$_i[1];
|
|
132
|
+
var existing = schema.shape[field];
|
|
133
|
+
if (existing) {
|
|
134
|
+
overlay[field] = existing.describe(note);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return Object.keys(overlay).length ? schema.extend(overlay) : schema;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// 只有两棵树的 createTableSchema 用得到它,所以是具名导出而不是公开 API 的一部分。
|
|
141
|
+
export var describeBasicFields = function describeBasicFields(schema) {
|
|
142
|
+
var withNotes = describeFields(schema, BASIC_FIELD_NOTES);
|
|
143
|
+
var overlay = {};
|
|
144
|
+
for (var _i2 = 0, _Object$entries2 = Object.entries(BASIC_TIMESTAMP_NOTES); _i2 < _Object$entries2.length; _i2++) {
|
|
145
|
+
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
|
|
146
|
+
field = _Object$entries2$_i[0],
|
|
147
|
+
note = _Object$entries2$_i[1];
|
|
148
|
+
var existing = withNotes.shape[field];
|
|
149
|
+
if (existing) {
|
|
150
|
+
overlay[field] = existing.meta({
|
|
151
|
+
description: note,
|
|
152
|
+
format: "date-time",
|
|
153
|
+
type: "string"
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return Object.keys(overlay).length ? withNotes.extend(overlay) : withNotes;
|
|
158
|
+
};
|