@ai0x0/utils 0.2.0 → 0.4.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/es/backend/route-operation/write-operation-core.js +2 -2
- package/es/backend/schemas/index.d.ts +21 -1
- package/es/backend/schemas/index.js +98 -18
- package/es/backend/sqlite/schemas/index.d.ts +11 -0
- package/es/backend/sqlite/schemas/index.js +98 -18
- package/lib/backend/route-operation/write-operation-core.js +2 -2
- package/lib/backend/schemas/index.d.ts +21 -1
- package/lib/backend/schemas/index.js +72 -18
- package/lib/backend/sqlite/schemas/index.d.ts +11 -0
- package/lib/backend/sqlite/schemas/index.js +73 -18
- package/package.json +1 -1
|
@@ -48,7 +48,7 @@ export function createPostOperationFactory(_ref) {
|
|
|
48
48
|
contentType: contentType
|
|
49
49
|
}).outputs([{
|
|
50
50
|
body: schemas.response || z.object({
|
|
51
|
-
id: z.string()
|
|
51
|
+
id: z.string().describe("Id of the row created.")
|
|
52
52
|
}),
|
|
53
53
|
contentType: "application/json",
|
|
54
54
|
status: 200
|
|
@@ -316,7 +316,7 @@ export function createPutOperationFactory(_ref5) {
|
|
|
316
316
|
// ==============================================================================
|
|
317
317
|
|
|
318
318
|
var defaultDeleteBodySchema = z.object({
|
|
319
|
-
id: z.string()
|
|
319
|
+
id: z.string().describe("Id of the row to delete.")
|
|
320
320
|
});
|
|
321
321
|
export function createDeleteOperationFactory(_ref9) {
|
|
322
322
|
var createAction = _ref9.createAction,
|
|
@@ -113,13 +113,33 @@ export declare const listBodySchema: <T extends z.ZodType<unknown, unknown, z.co
|
|
|
113
113
|
total: z.ZodNumber;
|
|
114
114
|
data: z.ZodArray<T>;
|
|
115
115
|
}, z.core.$strip>;
|
|
116
|
+
/**
|
|
117
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
118
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
119
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
120
|
+
*/
|
|
121
|
+
export declare const describeFields: <T extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: T, notes: Record<string, string>) => T;
|
|
116
122
|
/**
|
|
117
123
|
* 基于 drizzle + drizzle-zod 快速生成带基础字段(id / 创建时间等)的
|
|
118
124
|
* 表定义以及 select / insert / update / query / list zod schema。
|
|
119
125
|
*/
|
|
120
|
-
export declare const createTableSchema: <TTableName extends string, TColumnsMap extends Record<string, PgColumnBuilderBase<import("drizzle-orm").ColumnBuilderBaseConfig<import("drizzle-orm").ColumnDataType, string>, object>>, TServerColumns extends Record<string, PgColumnBuilderBase<import("drizzle-orm").ColumnBuilderBaseConfig<import("drizzle-orm").ColumnDataType, string>, object>> = {}>({ name, columns, serverColumns, extraConfig, }: {
|
|
126
|
+
export declare const createTableSchema: <TTableName extends string, TColumnsMap extends Record<string, PgColumnBuilderBase<import("drizzle-orm").ColumnBuilderBaseConfig<import("drizzle-orm").ColumnDataType, string>, object>>, TServerColumns extends Record<string, PgColumnBuilderBase<import("drizzle-orm").ColumnBuilderBaseConfig<import("drizzle-orm").ColumnDataType, string>, object>> = {}>({ name, columns, serverColumns, extraConfig, describe, }: {
|
|
121
127
|
/** 客户端能写的业务列。insert / update schema 只从这里推。 */
|
|
122
128
|
columns: TColumnsMap;
|
|
129
|
+
/**
|
|
130
|
+
* 每一列的说明,键是列名。**同时挂到 insert / update / select 三份 schema 上** —— 同一列在
|
|
131
|
+
* 请求体里和响应里是同一个东西,说明没有理由写两遍。
|
|
132
|
+
*
|
|
133
|
+
* 为什么需要这个参数:drizzle 列没有「描述」这个概念,而 `.describe()` 只能挂在 zod 上。不给
|
|
134
|
+
* 这条路,业务侧就只能在每张表后面自己把三份 schema 各 `.extend()` 一遍 —— 那是三份副本,
|
|
135
|
+
* 而副本会漂。
|
|
136
|
+
*
|
|
137
|
+
* 键有类型约束(列名的联合),所以列改名了、拼错了都是编译错误,不是静默失效。
|
|
138
|
+
*
|
|
139
|
+
* 说明会出现在 OpenAPI spec 里,再流进按 spec 生成的 client 与文档 —— 所以它是写给调用方
|
|
140
|
+
* 看的:这一列是什么、合法值从哪来、不传会怎样。
|
|
141
|
+
*/
|
|
142
|
+
describe?: Partial<Record<Extract<keyof TColumnsMap, string> | Extract<keyof TServerColumns, string>, string>> | undefined;
|
|
123
143
|
name: TTableName;
|
|
124
144
|
/**
|
|
125
145
|
* 由服务端盖、**绝不接受客户端传**的业务列:承载归属的 `ownerId`(配合
|
|
@@ -4,6 +4,12 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
4
4
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
5
5
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
6
6
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
8
|
+
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."); }
|
|
9
|
+
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); }
|
|
10
|
+
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; }
|
|
11
|
+
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; } }
|
|
12
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
13
|
import { z } from "zod";
|
|
8
14
|
import { pgTable, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
9
15
|
import { createInsertSchema, createSelectSchema, createUpdateSchema } from "drizzle-zod";
|
|
@@ -72,19 +78,21 @@ export var BASIC_UPDATE_OMIT = {
|
|
|
72
78
|
// =============================================================================
|
|
73
79
|
// 列表查询字段
|
|
74
80
|
// =============================================================================
|
|
81
|
+
// 说明写成 `.describe()` 而不是行末注释:注释只有读源码的人看得到,而这七个字段会出现在每一个
|
|
82
|
+
// list 端点的 OpenAPI spec 里,再从那里流进生成的 client 与 CLI 文档。写成注释的代价是下游只能
|
|
83
|
+
// 自己再手写一份 —— 那份必然会漂,而且漂了没有任何东西会报错。
|
|
84
|
+
//
|
|
85
|
+
// 类型是 string 而不是 number:query 参数在线上只有字符串,服务端收下之后才转数字。默认值也
|
|
86
|
+
// 因此是 "1" / "10"。
|
|
75
87
|
export var queryListSchema = function queryListSchema(schema) {
|
|
76
88
|
return z.object({
|
|
77
|
-
current: z.string().optional().default("1"),
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// 筛选结束日期
|
|
85
|
-
orderBy: z.string().optional(),
|
|
86
|
-
creatorId: z.string().optional(),
|
|
87
|
-
orderDir: z.enum(["asc", "desc"]).optional()
|
|
89
|
+
current: z.string().optional().default("1").describe("Page number, starting at 1."),
|
|
90
|
+
pageSize: z.string().optional().default("10").describe("Rows per page. Omitting it yields only 10 — pass a larger value to get the whole set."),
|
|
91
|
+
createdAtFrom: z.string().optional().describe("Keep only rows created at or after this instant (ISO 8601, e.g. 2026-08-01T00:00:00Z)."),
|
|
92
|
+
createdAtTo: z.string().optional().describe("Keep only rows created at or before this instant."),
|
|
93
|
+
orderBy: z.string().optional().describe("Column to sort by — a column name of this resource (createdAt / updatedAt and the like)."),
|
|
94
|
+
creatorId: z.string().optional().describe("Keep only rows created by this user. Useful in shared spaces to filter down to one member."),
|
|
95
|
+
orderDir: z.enum(["asc", "desc"]).optional().describe("Sort ascending or descending.")
|
|
88
96
|
}).merge(schema);
|
|
89
97
|
};
|
|
90
98
|
|
|
@@ -98,6 +106,74 @@ export var listBodySchema = function listBodySchema(schema) {
|
|
|
98
106
|
});
|
|
99
107
|
};
|
|
100
108
|
|
|
109
|
+
// =============================================================================
|
|
110
|
+
// 给基础字段挂说明
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// basicFields 是 drizzle 列,而 drizzle 列没有「描述」这个概念 —— 所以说明只能挂在
|
|
113
|
+
// drizzle-zod 出来的 schema 上。值得做是因为这六个字段出现在**每一张表**的 select schema 里:
|
|
114
|
+
// 一处写完,所有 list / get 端点的返回字段表都有了(在 do-tv 上是 173 行)。
|
|
115
|
+
//
|
|
116
|
+
// 一律只挂元数据,**不换类型**:`.describe()` / `.meta()` 返回的是带元数据的克隆,校验行为一模
|
|
117
|
+
// 一样。selectSchema 是有人拿去 safeParse 的(本包自己的测试就在用 Date 校验它),换类型等于改
|
|
118
|
+
// 这个包的契约。
|
|
119
|
+
//
|
|
120
|
+
// 时间戳那三列额外用 `.meta()` 补上 JSON Schema 表示,因为不补就是错的:drizzle-zod 给的是
|
|
121
|
+
// `z.date()`,而 `Date` 在 JSON Schema 里表达不出来,于是这三个字段在 spec 里是**空对象** ——
|
|
122
|
+
// 没有 `type`,照 spec 生成的 client / CLI 只能得到 `unknown`。而线上根本不可能是 Date:响应一经
|
|
123
|
+
// JSON 序列化就是 ISO 8601 字符串。
|
|
124
|
+
//
|
|
125
|
+
// `.meta({ type, format })` 恰好只改「怎么描述」不改「怎么校验」:spec 里成为
|
|
126
|
+
// `{"type":"string","format":"date-time"}`,而 `safeParse(new Date())` 照样通过。
|
|
127
|
+
// 不用 `z.iso.datetime()` 换掉它,是因为那既改了校验(不再收 Date),又会往 JSON Schema 里塞一条
|
|
128
|
+
// 380 字节的正则 —— 乘上每张表几十 KB,而 `format` 已经把「按日期时间解析」说清楚了。
|
|
129
|
+
var BASIC_FIELD_NOTES = {
|
|
130
|
+
creatorId: "User id of the creator. Stamped by the server; clients cannot send it.",
|
|
131
|
+
editorId: "User id of whoever changed this row last. Stamped by the server; clients cannot send it.",
|
|
132
|
+
id: "Primary key of this row (uuid)."
|
|
133
|
+
};
|
|
134
|
+
var BASIC_TIMESTAMP_NOTES = {
|
|
135
|
+
accessedAt: "When this row was last accessed.",
|
|
136
|
+
createdAt: "When this row was created.",
|
|
137
|
+
updatedAt: "When this row was last modified."
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
142
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
143
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
144
|
+
*/
|
|
145
|
+
export var describeFields = function describeFields(schema, notes) {
|
|
146
|
+
var overlay = {};
|
|
147
|
+
for (var _i = 0, _Object$entries = Object.entries(notes); _i < _Object$entries.length; _i++) {
|
|
148
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
149
|
+
field = _Object$entries$_i[0],
|
|
150
|
+
note = _Object$entries$_i[1];
|
|
151
|
+
var existing = schema.shape[field];
|
|
152
|
+
if (existing) {
|
|
153
|
+
overlay[field] = existing.describe(note);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return Object.keys(overlay).length ? schema.extend(overlay) : schema;
|
|
157
|
+
};
|
|
158
|
+
var describeBasicFields = function describeBasicFields(schema) {
|
|
159
|
+
var withNotes = describeFields(schema, BASIC_FIELD_NOTES);
|
|
160
|
+
var overlay = {};
|
|
161
|
+
for (var _i2 = 0, _Object$entries2 = Object.entries(BASIC_TIMESTAMP_NOTES); _i2 < _Object$entries2.length; _i2++) {
|
|
162
|
+
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
|
|
163
|
+
field = _Object$entries2$_i[0],
|
|
164
|
+
note = _Object$entries2$_i[1];
|
|
165
|
+
var existing = withNotes.shape[field];
|
|
166
|
+
if (existing) {
|
|
167
|
+
overlay[field] = existing.meta({
|
|
168
|
+
description: note,
|
|
169
|
+
format: "date-time",
|
|
170
|
+
type: "string"
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return Object.keys(overlay).length ? withNotes.extend(overlay) : withNotes;
|
|
175
|
+
};
|
|
176
|
+
|
|
101
177
|
// =============================================================================
|
|
102
178
|
// 建表 —— 表 + 5 份 zod schema
|
|
103
179
|
// =============================================================================
|
|
@@ -109,19 +185,23 @@ export var createTableSchema = function createTableSchema(_ref) {
|
|
|
109
185
|
var name = _ref.name,
|
|
110
186
|
columns = _ref.columns,
|
|
111
187
|
serverColumns = _ref.serverColumns,
|
|
112
|
-
extraConfig = _ref.extraConfig
|
|
188
|
+
extraConfig = _ref.extraConfig,
|
|
189
|
+
describe = _ref.describe;
|
|
113
190
|
var mergedColumns = _objectSpread(_objectSpread(_objectSpread({}, basicFields), serverColumns), columns);
|
|
114
191
|
var table = pgTable(name, mergedColumns, extraConfig);
|
|
115
|
-
|
|
192
|
+
|
|
193
|
+
// 同一份说明喂给三份 schema:含哪些列各不相同,describeFields 会跳过不存在的。
|
|
194
|
+
var notes = describe !== null && describe !== void 0 ? describe : {};
|
|
195
|
+
var selectSchema = describeFields(describeBasicFields(createSelectSchema(table)), notes);
|
|
116
196
|
// 只从 columns 推 —— basicFields 与 serverColumns 都由服务端写,不该出现在请求体里。
|
|
117
|
-
var insertSchema = createInsertSchema(pgTable(name, columns));
|
|
118
|
-
var updateSchema = createUpdateSchema(pgTable(name, _objectSpread({
|
|
197
|
+
var insertSchema = describeFields(createInsertSchema(pgTable(name, columns)), notes);
|
|
198
|
+
var updateSchema = describeFields(createUpdateSchema(pgTable(name, _objectSpread({
|
|
119
199
|
id: uuid("id")
|
|
120
200
|
}, columns))).extend({
|
|
121
|
-
id: z.string()
|
|
122
|
-
});
|
|
201
|
+
id: z.string().describe("Id of the row to update.")
|
|
202
|
+
}), notes);
|
|
123
203
|
var querySchema = z.object({
|
|
124
|
-
id: z.string()
|
|
204
|
+
id: z.string().describe("Id of the row to fetch.")
|
|
125
205
|
});
|
|
126
206
|
var queryListWithSchema = queryListSchema(z.object({}).catchall(z.unknown()));
|
|
127
207
|
return {
|
|
@@ -113,6 +113,12 @@ export declare const listBodySchema: <T extends z.ZodType<unknown, unknown, z.co
|
|
|
113
113
|
total: z.ZodNumber;
|
|
114
114
|
data: z.ZodArray<T>;
|
|
115
115
|
}, z.core.$strip>;
|
|
116
|
+
/**
|
|
117
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
118
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
119
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
120
|
+
*/
|
|
121
|
+
export declare const describeFields: <T extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: T, notes: Record<string, string>) => T;
|
|
116
122
|
/**
|
|
117
123
|
* 基于 drizzle + drizzle-zod 快速生成带基础字段(id / 创建时间等)的
|
|
118
124
|
* 表定义以及 select / insert / update / query / list zod schema。
|
|
@@ -121,6 +127,11 @@ export declare const createTableSchema: <TTableName extends string, TColumnsMap
|
|
|
121
127
|
name: TTableName;
|
|
122
128
|
/** 客户端能写的业务列。insert / update schema 只从这里推。 */
|
|
123
129
|
columns: TColumnsMap;
|
|
130
|
+
/**
|
|
131
|
+
* 每一列的说明,键是列名。同时挂到 insert / update / select 三份 schema 上。语义与 pg 版
|
|
132
|
+
* 一致,见那边的说明。
|
|
133
|
+
*/
|
|
134
|
+
describe?: Partial<Record<string, string>>;
|
|
124
135
|
/** 由服务端盖、绝不接受客户端传的业务列(归属、租户 id 这类)。语义与 pg 版一致,见那边的说明。 */
|
|
125
136
|
serverColumns?: Record<string, SQLiteColumnBuilderBase>;
|
|
126
137
|
extraConfig?: (_self: BuildExtraConfigColumns<TTableName, typeof basicFields & TColumnsMap, "sqlite">) => SQLiteTableExtraConfigValue[];
|
|
@@ -4,6 +4,12 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
4
4
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
5
5
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
6
6
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
8
|
+
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."); }
|
|
9
|
+
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); }
|
|
10
|
+
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; }
|
|
11
|
+
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; } }
|
|
12
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
13
|
import { z } from "zod";
|
|
8
14
|
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
9
15
|
import { createInsertSchema, createSelectSchema, createUpdateSchema } from "drizzle-zod";
|
|
@@ -78,19 +84,21 @@ export var BASIC_UPDATE_OMIT = {
|
|
|
78
84
|
// =============================================================================
|
|
79
85
|
// 列表查询字段
|
|
80
86
|
// =============================================================================
|
|
87
|
+
// 说明写成 `.describe()` 而不是行末注释:注释只有读源码的人看得到,而这七个字段会出现在每一个
|
|
88
|
+
// list 端点的 OpenAPI spec 里,再从那里流进生成的 client 与 CLI 文档。写成注释的代价是下游只能
|
|
89
|
+
// 自己再手写一份 —— 那份必然会漂,而且漂了没有任何东西会报错。
|
|
90
|
+
//
|
|
91
|
+
// 类型是 string 而不是 number:query 参数在线上只有字符串,服务端收下之后才转数字。默认值也
|
|
92
|
+
// 因此是 "1" / "10"。
|
|
81
93
|
export var queryListSchema = function queryListSchema(schema) {
|
|
82
94
|
return z.object({
|
|
83
|
-
current: z.string().optional().default("1"),
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// 筛选结束日期
|
|
91
|
-
orderBy: z.string().optional(),
|
|
92
|
-
creatorId: z.string().optional(),
|
|
93
|
-
orderDir: z.enum(["asc", "desc"]).optional()
|
|
95
|
+
current: z.string().optional().default("1").describe("Page number, starting at 1."),
|
|
96
|
+
pageSize: z.string().optional().default("10").describe("Rows per page. Omitting it yields only 10 — pass a larger value to get the whole set."),
|
|
97
|
+
createdAtFrom: z.string().optional().describe("Keep only rows created at or after this instant (ISO 8601, e.g. 2026-08-01T00:00:00Z)."),
|
|
98
|
+
createdAtTo: z.string().optional().describe("Keep only rows created at or before this instant."),
|
|
99
|
+
orderBy: z.string().optional().describe("Column to sort by — a column name of this resource (createdAt / updatedAt and the like)."),
|
|
100
|
+
creatorId: z.string().optional().describe("Keep only rows created by this user. Useful in shared spaces to filter down to one member."),
|
|
101
|
+
orderDir: z.enum(["asc", "desc"]).optional().describe("Sort ascending or descending.")
|
|
94
102
|
}).merge(schema);
|
|
95
103
|
};
|
|
96
104
|
|
|
@@ -104,6 +112,74 @@ export var listBodySchema = function listBodySchema(schema) {
|
|
|
104
112
|
});
|
|
105
113
|
};
|
|
106
114
|
|
|
115
|
+
// =============================================================================
|
|
116
|
+
// 给基础字段挂说明
|
|
117
|
+
// =============================================================================
|
|
118
|
+
// basicFields 是 drizzle 列,而 drizzle 列没有「描述」这个概念 —— 所以说明只能挂在
|
|
119
|
+
// drizzle-zod 出来的 schema 上。值得做是因为这六个字段出现在**每一张表**的 select schema 里:
|
|
120
|
+
// 一处写完,所有 list / get 端点的返回字段都有说明了。
|
|
121
|
+
//
|
|
122
|
+
// 一律只挂元数据,**不换类型**:`.describe()` / `.meta()` 返回的是带元数据的克隆,校验行为一模
|
|
123
|
+
// 一样。selectSchema 是有人拿去 safeParse 的(本包自己的测试就在用 Date 校验它),换类型等于改
|
|
124
|
+
// 这个包的契约。
|
|
125
|
+
//
|
|
126
|
+
// 时间戳那三列额外用 `.meta()` 补上 JSON Schema 表示,因为不补就是错的:drizzle-zod 给的是
|
|
127
|
+
// `z.date()`,而 `Date` 在 JSON Schema 里表达不出来,于是这三个字段在 spec 里是**空对象** ——
|
|
128
|
+
// 没有 `type`,照 spec 生成的 client / CLI 只能得到 `unknown`。而线上根本不可能是 Date:响应一经
|
|
129
|
+
// JSON 序列化就是 ISO 8601 字符串。
|
|
130
|
+
//
|
|
131
|
+
// `.meta({ type, format })` 恰好只改「怎么描述」不改「怎么校验」:spec 里成为
|
|
132
|
+
// `{"type":"string","format":"date-time"}`,而 `safeParse(new Date())` 照样通过。
|
|
133
|
+
// 不用 `z.iso.datetime()` 换掉它,是因为那既改了校验(不再收 Date),又会往 JSON Schema 里塞一条
|
|
134
|
+
// 380 字节的正则 —— 乘上每张表几十 KB,而 `format` 已经把「按日期时间解析」说清楚了。
|
|
135
|
+
var BASIC_FIELD_NOTES = {
|
|
136
|
+
creatorId: "User id of the creator. Stamped by the server; clients cannot send it.",
|
|
137
|
+
editorId: "User id of whoever changed this row last. Stamped by the server; clients cannot send it.",
|
|
138
|
+
id: "Primary key of this row (uuid)."
|
|
139
|
+
};
|
|
140
|
+
var BASIC_TIMESTAMP_NOTES = {
|
|
141
|
+
accessedAt: "When this row was last accessed.",
|
|
142
|
+
createdAt: "When this row was created.",
|
|
143
|
+
updatedAt: "When this row was last modified."
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
148
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
149
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
150
|
+
*/
|
|
151
|
+
export var describeFields = function describeFields(schema, notes) {
|
|
152
|
+
var overlay = {};
|
|
153
|
+
for (var _i = 0, _Object$entries = Object.entries(notes); _i < _Object$entries.length; _i++) {
|
|
154
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
155
|
+
field = _Object$entries$_i[0],
|
|
156
|
+
note = _Object$entries$_i[1];
|
|
157
|
+
var existing = schema.shape[field];
|
|
158
|
+
if (existing) {
|
|
159
|
+
overlay[field] = existing.describe(note);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return Object.keys(overlay).length ? schema.extend(overlay) : schema;
|
|
163
|
+
};
|
|
164
|
+
var describeBasicFields = function describeBasicFields(schema) {
|
|
165
|
+
var withNotes = describeFields(schema, BASIC_FIELD_NOTES);
|
|
166
|
+
var overlay = {};
|
|
167
|
+
for (var _i2 = 0, _Object$entries2 = Object.entries(BASIC_TIMESTAMP_NOTES); _i2 < _Object$entries2.length; _i2++) {
|
|
168
|
+
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
|
|
169
|
+
field = _Object$entries2$_i[0],
|
|
170
|
+
note = _Object$entries2$_i[1];
|
|
171
|
+
var existing = withNotes.shape[field];
|
|
172
|
+
if (existing) {
|
|
173
|
+
overlay[field] = existing.meta({
|
|
174
|
+
description: note,
|
|
175
|
+
format: "date-time",
|
|
176
|
+
type: "string"
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return Object.keys(overlay).length ? withNotes.extend(overlay) : withNotes;
|
|
181
|
+
};
|
|
182
|
+
|
|
107
183
|
// =============================================================================
|
|
108
184
|
// 建表 —— 表 + 5 份 zod schema
|
|
109
185
|
// =============================================================================
|
|
@@ -115,19 +191,23 @@ export var createTableSchema = function createTableSchema(_ref) {
|
|
|
115
191
|
var name = _ref.name,
|
|
116
192
|
columns = _ref.columns,
|
|
117
193
|
serverColumns = _ref.serverColumns,
|
|
118
|
-
extraConfig = _ref.extraConfig
|
|
194
|
+
extraConfig = _ref.extraConfig,
|
|
195
|
+
describe = _ref.describe;
|
|
119
196
|
var mergedColumns = _objectSpread(_objectSpread(_objectSpread({}, basicFields), serverColumns), columns);
|
|
120
197
|
var table = sqliteTable(name, mergedColumns, extraConfig);
|
|
121
|
-
|
|
198
|
+
|
|
199
|
+
// 同一份说明喂给三份 schema:含哪些列各不相同,describeFields 会跳过不存在的。
|
|
200
|
+
var notes = describe !== null && describe !== void 0 ? describe : {};
|
|
201
|
+
var selectSchema = describeFields(describeBasicFields(createSelectSchema(table)), notes);
|
|
122
202
|
// 只从 columns 推 —— basicFields 与 serverColumns 都由服务端写,不该出现在请求体里。
|
|
123
|
-
var insertSchema = createInsertSchema(sqliteTable(name, columns));
|
|
124
|
-
var updateSchema = createUpdateSchema(sqliteTable(name, _objectSpread({
|
|
203
|
+
var insertSchema = describeFields(createInsertSchema(sqliteTable(name, columns)), notes);
|
|
204
|
+
var updateSchema = describeFields(createUpdateSchema(sqliteTable(name, _objectSpread({
|
|
125
205
|
id: text("id")
|
|
126
206
|
}, columns))).extend({
|
|
127
|
-
id: z.string()
|
|
128
|
-
});
|
|
207
|
+
id: z.string().describe("Id of the row to update.")
|
|
208
|
+
}), notes);
|
|
129
209
|
var querySchema = z.object({
|
|
130
|
-
id: z.string()
|
|
210
|
+
id: z.string().describe("Id of the row to fetch.")
|
|
131
211
|
});
|
|
132
212
|
var queryListWithSchema = queryListSchema(z.object({}).catchall(z.unknown()));
|
|
133
213
|
return {
|
|
@@ -57,7 +57,7 @@ function createPostOperationFactory({
|
|
|
57
57
|
contentType
|
|
58
58
|
}).outputs([
|
|
59
59
|
{
|
|
60
|
-
body: schemas.response || import_zod.z.object({ id: import_zod.z.string() }),
|
|
60
|
+
body: schemas.response || import_zod.z.object({ id: import_zod.z.string().describe("Id of the row created.") }),
|
|
61
61
|
contentType: "application/json",
|
|
62
62
|
status: 200
|
|
63
63
|
}
|
|
@@ -156,7 +156,7 @@ function createPutOperationFactory({
|
|
|
156
156
|
});
|
|
157
157
|
}
|
|
158
158
|
var defaultDeleteBodySchema = import_zod.z.object({
|
|
159
|
-
id: import_zod.z.string()
|
|
159
|
+
id: import_zod.z.string().describe("Id of the row to delete.")
|
|
160
160
|
});
|
|
161
161
|
function createDeleteOperationFactory({
|
|
162
162
|
createAction,
|
|
@@ -113,13 +113,33 @@ export declare const listBodySchema: <T extends z.ZodType<unknown, unknown, z.co
|
|
|
113
113
|
total: z.ZodNumber;
|
|
114
114
|
data: z.ZodArray<T>;
|
|
115
115
|
}, z.core.$strip>;
|
|
116
|
+
/**
|
|
117
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
118
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
119
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
120
|
+
*/
|
|
121
|
+
export declare const describeFields: <T extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: T, notes: Record<string, string>) => T;
|
|
116
122
|
/**
|
|
117
123
|
* 基于 drizzle + drizzle-zod 快速生成带基础字段(id / 创建时间等)的
|
|
118
124
|
* 表定义以及 select / insert / update / query / list zod schema。
|
|
119
125
|
*/
|
|
120
|
-
export declare const createTableSchema: <TTableName extends string, TColumnsMap extends Record<string, PgColumnBuilderBase<import("drizzle-orm").ColumnBuilderBaseConfig<import("drizzle-orm").ColumnDataType, string>, object>>, TServerColumns extends Record<string, PgColumnBuilderBase<import("drizzle-orm").ColumnBuilderBaseConfig<import("drizzle-orm").ColumnDataType, string>, object>> = {}>({ name, columns, serverColumns, extraConfig, }: {
|
|
126
|
+
export declare const createTableSchema: <TTableName extends string, TColumnsMap extends Record<string, PgColumnBuilderBase<import("drizzle-orm").ColumnBuilderBaseConfig<import("drizzle-orm").ColumnDataType, string>, object>>, TServerColumns extends Record<string, PgColumnBuilderBase<import("drizzle-orm").ColumnBuilderBaseConfig<import("drizzle-orm").ColumnDataType, string>, object>> = {}>({ name, columns, serverColumns, extraConfig, describe, }: {
|
|
121
127
|
/** 客户端能写的业务列。insert / update schema 只从这里推。 */
|
|
122
128
|
columns: TColumnsMap;
|
|
129
|
+
/**
|
|
130
|
+
* 每一列的说明,键是列名。**同时挂到 insert / update / select 三份 schema 上** —— 同一列在
|
|
131
|
+
* 请求体里和响应里是同一个东西,说明没有理由写两遍。
|
|
132
|
+
*
|
|
133
|
+
* 为什么需要这个参数:drizzle 列没有「描述」这个概念,而 `.describe()` 只能挂在 zod 上。不给
|
|
134
|
+
* 这条路,业务侧就只能在每张表后面自己把三份 schema 各 `.extend()` 一遍 —— 那是三份副本,
|
|
135
|
+
* 而副本会漂。
|
|
136
|
+
*
|
|
137
|
+
* 键有类型约束(列名的联合),所以列改名了、拼错了都是编译错误,不是静默失效。
|
|
138
|
+
*
|
|
139
|
+
* 说明会出现在 OpenAPI spec 里,再流进按 spec 生成的 client 与文档 —— 所以它是写给调用方
|
|
140
|
+
* 看的:这一列是什么、合法值从哪来、不传会怎样。
|
|
141
|
+
*/
|
|
142
|
+
describe?: Partial<Record<Extract<keyof TColumnsMap, string> | Extract<keyof TServerColumns, string>, string>> | undefined;
|
|
123
143
|
name: TTableName;
|
|
124
144
|
/**
|
|
125
145
|
* 由服务端盖、**绝不接受客户端传**的业务列:承载归属的 `ownerId`(配合
|
|
@@ -23,6 +23,7 @@ __export(schemas_exports, {
|
|
|
23
23
|
BASIC_UPDATE_OMIT: () => BASIC_UPDATE_OMIT,
|
|
24
24
|
basicFields: () => basicFields,
|
|
25
25
|
createTableSchema: () => createTableSchema,
|
|
26
|
+
describeFields: () => describeFields,
|
|
26
27
|
listBodySchema: () => listBodySchema,
|
|
27
28
|
queryListSchema: () => queryListSchema
|
|
28
29
|
});
|
|
@@ -58,27 +59,67 @@ var BASIC_UPDATE_OMIT = {
|
|
|
58
59
|
updatedAt: true
|
|
59
60
|
};
|
|
60
61
|
var queryListSchema = (schema) => import_zod.z.object({
|
|
61
|
-
current: import_zod.z.string().optional().default("1"),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
createdAtFrom: import_zod.z.string().optional()
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
orderBy: import_zod.z.string().optional()
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
current: import_zod.z.string().optional().default("1").describe("Page number, starting at 1."),
|
|
63
|
+
pageSize: import_zod.z.string().optional().default("10").describe(
|
|
64
|
+
"Rows per page. Omitting it yields only 10 — pass a larger value to get the whole set."
|
|
65
|
+
),
|
|
66
|
+
createdAtFrom: import_zod.z.string().optional().describe(
|
|
67
|
+
"Keep only rows created at or after this instant (ISO 8601, e.g. 2026-08-01T00:00:00Z)."
|
|
68
|
+
),
|
|
69
|
+
createdAtTo: import_zod.z.string().optional().describe("Keep only rows created at or before this instant."),
|
|
70
|
+
orderBy: import_zod.z.string().optional().describe(
|
|
71
|
+
"Column to sort by — a column name of this resource (createdAt / updatedAt and the like)."
|
|
72
|
+
),
|
|
73
|
+
creatorId: import_zod.z.string().optional().describe(
|
|
74
|
+
"Keep only rows created by this user. Useful in shared spaces to filter down to one member."
|
|
75
|
+
),
|
|
76
|
+
orderDir: import_zod.z.enum(["asc", "desc"]).optional().describe("Sort ascending or descending.")
|
|
72
77
|
}).merge(schema);
|
|
73
78
|
var listBodySchema = (schema) => import_zod.z.object({
|
|
74
79
|
total: import_zod.z.number(),
|
|
75
80
|
data: import_zod.z.array(schema)
|
|
76
81
|
});
|
|
82
|
+
var BASIC_FIELD_NOTES = {
|
|
83
|
+
creatorId: "User id of the creator. Stamped by the server; clients cannot send it.",
|
|
84
|
+
editorId: "User id of whoever changed this row last. Stamped by the server; clients cannot send it.",
|
|
85
|
+
id: "Primary key of this row (uuid)."
|
|
86
|
+
};
|
|
87
|
+
var BASIC_TIMESTAMP_NOTES = {
|
|
88
|
+
accessedAt: "When this row was last accessed.",
|
|
89
|
+
createdAt: "When this row was created.",
|
|
90
|
+
updatedAt: "When this row was last modified."
|
|
91
|
+
};
|
|
92
|
+
var describeFields = (schema, notes) => {
|
|
93
|
+
const overlay = {};
|
|
94
|
+
for (const [field, note] of Object.entries(notes)) {
|
|
95
|
+
const existing = schema.shape[field];
|
|
96
|
+
if (existing) {
|
|
97
|
+
overlay[field] = existing.describe(note);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return Object.keys(overlay).length ? schema.extend(overlay) : schema;
|
|
101
|
+
};
|
|
102
|
+
var describeBasicFields = (schema) => {
|
|
103
|
+
const withNotes = describeFields(schema, BASIC_FIELD_NOTES);
|
|
104
|
+
const overlay = {};
|
|
105
|
+
for (const [field, note] of Object.entries(BASIC_TIMESTAMP_NOTES)) {
|
|
106
|
+
const existing = withNotes.shape[field];
|
|
107
|
+
if (existing) {
|
|
108
|
+
overlay[field] = existing.meta({
|
|
109
|
+
description: note,
|
|
110
|
+
format: "date-time",
|
|
111
|
+
type: "string"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return Object.keys(overlay).length ? withNotes.extend(overlay) : withNotes;
|
|
116
|
+
};
|
|
77
117
|
var createTableSchema = ({
|
|
78
118
|
name,
|
|
79
119
|
columns,
|
|
80
120
|
serverColumns,
|
|
81
|
-
extraConfig
|
|
121
|
+
extraConfig,
|
|
122
|
+
describe
|
|
82
123
|
}) => {
|
|
83
124
|
const mergedColumns = {
|
|
84
125
|
...basicFields,
|
|
@@ -86,12 +127,24 @@ var createTableSchema = ({
|
|
|
86
127
|
...columns
|
|
87
128
|
};
|
|
88
129
|
const table = (0, import_pg_core.pgTable)(name, mergedColumns, extraConfig);
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
const
|
|
130
|
+
const notes = describe ?? {};
|
|
131
|
+
const selectSchema = describeFields(
|
|
132
|
+
describeBasicFields((0, import_drizzle_zod.createSelectSchema)(table)),
|
|
133
|
+
notes
|
|
134
|
+
);
|
|
135
|
+
const insertSchema = describeFields(
|
|
136
|
+
(0, import_drizzle_zod.createInsertSchema)((0, import_pg_core.pgTable)(name, columns)),
|
|
137
|
+
notes
|
|
138
|
+
);
|
|
139
|
+
const updateSchema = describeFields(
|
|
140
|
+
(0, import_drizzle_zod.createUpdateSchema)((0, import_pg_core.pgTable)(name, { id: (0, import_pg_core.uuid)("id"), ...columns })).extend({
|
|
141
|
+
id: import_zod.z.string().describe("Id of the row to update.")
|
|
142
|
+
}),
|
|
143
|
+
notes
|
|
144
|
+
);
|
|
145
|
+
const querySchema = import_zod.z.object({
|
|
146
|
+
id: import_zod.z.string().describe("Id of the row to fetch.")
|
|
147
|
+
});
|
|
95
148
|
const queryListWithSchema = queryListSchema(
|
|
96
149
|
import_zod.z.object({}).catchall(import_zod.z.unknown())
|
|
97
150
|
);
|
|
@@ -111,6 +164,7 @@ var createTableSchema = ({
|
|
|
111
164
|
BASIC_UPDATE_OMIT,
|
|
112
165
|
basicFields,
|
|
113
166
|
createTableSchema,
|
|
167
|
+
describeFields,
|
|
114
168
|
listBodySchema,
|
|
115
169
|
queryListSchema
|
|
116
170
|
});
|
|
@@ -113,6 +113,12 @@ export declare const listBodySchema: <T extends z.ZodType<unknown, unknown, z.co
|
|
|
113
113
|
total: z.ZodNumber;
|
|
114
114
|
data: z.ZodArray<T>;
|
|
115
115
|
}, z.core.$strip>;
|
|
116
|
+
/**
|
|
117
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
118
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
119
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
120
|
+
*/
|
|
121
|
+
export declare const describeFields: <T extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: T, notes: Record<string, string>) => T;
|
|
116
122
|
/**
|
|
117
123
|
* 基于 drizzle + drizzle-zod 快速生成带基础字段(id / 创建时间等)的
|
|
118
124
|
* 表定义以及 select / insert / update / query / list zod schema。
|
|
@@ -121,6 +127,11 @@ export declare const createTableSchema: <TTableName extends string, TColumnsMap
|
|
|
121
127
|
name: TTableName;
|
|
122
128
|
/** 客户端能写的业务列。insert / update schema 只从这里推。 */
|
|
123
129
|
columns: TColumnsMap;
|
|
130
|
+
/**
|
|
131
|
+
* 每一列的说明,键是列名。同时挂到 insert / update / select 三份 schema 上。语义与 pg 版
|
|
132
|
+
* 一致,见那边的说明。
|
|
133
|
+
*/
|
|
134
|
+
describe?: Partial<Record<string, string>>;
|
|
124
135
|
/** 由服务端盖、绝不接受客户端传的业务列(归属、租户 id 这类)。语义与 pg 版一致,见那边的说明。 */
|
|
125
136
|
serverColumns?: Record<string, SQLiteColumnBuilderBase>;
|
|
126
137
|
extraConfig?: (_self: BuildExtraConfigColumns<TTableName, typeof basicFields & TColumnsMap, "sqlite">) => SQLiteTableExtraConfigValue[];
|
|
@@ -23,6 +23,7 @@ __export(schemas_exports, {
|
|
|
23
23
|
BASIC_UPDATE_OMIT: () => BASIC_UPDATE_OMIT,
|
|
24
24
|
basicFields: () => basicFields,
|
|
25
25
|
createTableSchema: () => createTableSchema,
|
|
26
|
+
describeFields: () => describeFields,
|
|
26
27
|
listBodySchema: () => listBodySchema,
|
|
27
28
|
queryListSchema: () => queryListSchema
|
|
28
29
|
});
|
|
@@ -60,23 +61,62 @@ var BASIC_UPDATE_OMIT = {
|
|
|
60
61
|
updatedAt: true
|
|
61
62
|
};
|
|
62
63
|
var queryListSchema = (schema) => import_zod.z.object({
|
|
63
|
-
current: import_zod.z.string().optional().default("1"),
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
createdAtFrom: import_zod.z.string().optional()
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
orderBy: import_zod.z.string().optional()
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
current: import_zod.z.string().optional().default("1").describe("Page number, starting at 1."),
|
|
65
|
+
pageSize: import_zod.z.string().optional().default("10").describe(
|
|
66
|
+
"Rows per page. Omitting it yields only 10 — pass a larger value to get the whole set."
|
|
67
|
+
),
|
|
68
|
+
createdAtFrom: import_zod.z.string().optional().describe(
|
|
69
|
+
"Keep only rows created at or after this instant (ISO 8601, e.g. 2026-08-01T00:00:00Z)."
|
|
70
|
+
),
|
|
71
|
+
createdAtTo: import_zod.z.string().optional().describe("Keep only rows created at or before this instant."),
|
|
72
|
+
orderBy: import_zod.z.string().optional().describe(
|
|
73
|
+
"Column to sort by — a column name of this resource (createdAt / updatedAt and the like)."
|
|
74
|
+
),
|
|
75
|
+
creatorId: import_zod.z.string().optional().describe(
|
|
76
|
+
"Keep only rows created by this user. Useful in shared spaces to filter down to one member."
|
|
77
|
+
),
|
|
78
|
+
orderDir: import_zod.z.enum(["asc", "desc"]).optional().describe("Sort ascending or descending.")
|
|
74
79
|
}).merge(schema);
|
|
75
80
|
var listBodySchema = (schema) => import_zod.z.object({
|
|
76
81
|
total: import_zod.z.number(),
|
|
77
82
|
data: import_zod.z.array(schema)
|
|
78
83
|
});
|
|
79
|
-
var
|
|
84
|
+
var BASIC_FIELD_NOTES = {
|
|
85
|
+
creatorId: "User id of the creator. Stamped by the server; clients cannot send it.",
|
|
86
|
+
editorId: "User id of whoever changed this row last. Stamped by the server; clients cannot send it.",
|
|
87
|
+
id: "Primary key of this row (uuid)."
|
|
88
|
+
};
|
|
89
|
+
var BASIC_TIMESTAMP_NOTES = {
|
|
90
|
+
accessedAt: "When this row was last accessed.",
|
|
91
|
+
createdAt: "When this row was created.",
|
|
92
|
+
updatedAt: "When this row was last modified."
|
|
93
|
+
};
|
|
94
|
+
var describeFields = (schema, notes) => {
|
|
95
|
+
const overlay = {};
|
|
96
|
+
for (const [field, note] of Object.entries(notes)) {
|
|
97
|
+
const existing = schema.shape[field];
|
|
98
|
+
if (existing) {
|
|
99
|
+
overlay[field] = existing.describe(note);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return Object.keys(overlay).length ? schema.extend(overlay) : schema;
|
|
103
|
+
};
|
|
104
|
+
var describeBasicFields = (schema) => {
|
|
105
|
+
const withNotes = describeFields(schema, BASIC_FIELD_NOTES);
|
|
106
|
+
const overlay = {};
|
|
107
|
+
for (const [field, note] of Object.entries(BASIC_TIMESTAMP_NOTES)) {
|
|
108
|
+
const existing = withNotes.shape[field];
|
|
109
|
+
if (existing) {
|
|
110
|
+
overlay[field] = existing.meta({
|
|
111
|
+
description: note,
|
|
112
|
+
format: "date-time",
|
|
113
|
+
type: "string"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return Object.keys(overlay).length ? withNotes.extend(overlay) : withNotes;
|
|
118
|
+
};
|
|
119
|
+
var createTableSchema = ({ name, columns, serverColumns, extraConfig, describe }) => {
|
|
80
120
|
const mergedColumns = {
|
|
81
121
|
...basicFields,
|
|
82
122
|
...serverColumns,
|
|
@@ -87,12 +127,26 @@ var createTableSchema = ({ name, columns, serverColumns, extraConfig }) => {
|
|
|
87
127
|
mergedColumns,
|
|
88
128
|
extraConfig
|
|
89
129
|
);
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
const
|
|
130
|
+
const notes = describe ?? {};
|
|
131
|
+
const selectSchema = describeFields(
|
|
132
|
+
describeBasicFields((0, import_drizzle_zod.createSelectSchema)(table)),
|
|
133
|
+
notes
|
|
134
|
+
);
|
|
135
|
+
const insertSchema = describeFields(
|
|
136
|
+
(0, import_drizzle_zod.createInsertSchema)((0, import_sqlite_core.sqliteTable)(name, columns)),
|
|
137
|
+
notes
|
|
138
|
+
);
|
|
139
|
+
const updateSchema = describeFields(
|
|
140
|
+
(0, import_drizzle_zod.createUpdateSchema)(
|
|
141
|
+
(0, import_sqlite_core.sqliteTable)(name, { id: (0, import_sqlite_core.text)("id"), ...columns })
|
|
142
|
+
).extend({
|
|
143
|
+
id: import_zod.z.string().describe("Id of the row to update.")
|
|
144
|
+
}),
|
|
145
|
+
notes
|
|
146
|
+
);
|
|
147
|
+
const querySchema = import_zod.z.object({
|
|
148
|
+
id: import_zod.z.string().describe("Id of the row to fetch.")
|
|
149
|
+
});
|
|
96
150
|
const queryListWithSchema = queryListSchema(
|
|
97
151
|
import_zod.z.object({}).catchall(import_zod.z.unknown())
|
|
98
152
|
);
|
|
@@ -112,6 +166,7 @@ var createTableSchema = ({ name, columns, serverColumns, extraConfig }) => {
|
|
|
112
166
|
BASIC_UPDATE_OMIT,
|
|
113
167
|
basicFields,
|
|
114
168
|
createTableSchema,
|
|
169
|
+
describeFields,
|
|
115
170
|
listBodySchema,
|
|
116
171
|
queryListSchema
|
|
117
172
|
});
|