matrix-nodejs-homeserver 0.0.0-stage → 0.3.1
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/LICENSE +75 -0
- package/README.md +479 -2
- package/dist/adapters/auth.d.ts +50 -0
- package/dist/adapters/auth.js +18 -0
- package/dist/adapters/cluster.d.ts +55 -0
- package/dist/adapters/cluster.js +249 -0
- package/dist/adapters/media.d.ts +50 -0
- package/dist/adapters/media.js +63 -0
- package/dist/adapters/s3.d.ts +33 -0
- package/dist/adapters/s3.js +95 -0
- package/dist/api/account.js +594 -0
- package/dist/api/admin.js +267 -0
- package/dist/api/appservice.js +45 -0
- package/dist/api/backup.js +280 -0
- package/dist/api/federation.js +826 -0
- package/dist/api/filters.js +88 -0
- package/dist/api/index.js +47 -0
- package/dist/api/legacy.js +183 -0
- package/dist/api/media.js +271 -0
- package/dist/api/misc.js +95 -0
- package/dist/api/preview.js +201 -0
- package/dist/api/profile.js +176 -0
- package/dist/api/push.js +202 -0
- package/dist/api/relationships.js +270 -0
- package/dist/api/rooms.js +1487 -0
- package/dist/api/rtc.d.ts +23 -0
- package/dist/api/rtc.js +66 -0
- package/dist/api/search.js +161 -0
- package/dist/api/slidingsync.js +474 -0
- package/dist/api/spaces.js +177 -0
- package/dist/api/sync.js +609 -0
- package/dist/api/threadsubs.js +140 -0
- package/dist/api/threepid.js +155 -0
- package/dist/api/uia.js +127 -0
- package/dist/api/user.js +329 -0
- package/dist/core/admin.d.ts +84 -0
- package/dist/core/admin.js +308 -0
- package/dist/core/adminroom.d.ts +49 -0
- package/dist/core/adminroom.js +483 -0
- package/dist/core/appservices.d.ts +74 -0
- package/dist/core/appservices.js +369 -0
- package/dist/core/audit.d.ts +50 -0
- package/dist/core/audit.js +86 -0
- package/dist/core/auth.d.ts +48 -0
- package/dist/core/auth.js +237 -0
- package/dist/core/authrules.d.ts +4 -0
- package/dist/core/authrules.js +348 -0
- package/dist/core/cluster.d.ts +66 -0
- package/dist/core/cluster.js +191 -0
- package/dist/core/crypto.d.ts +10 -0
- package/dist/core/crypto.js +100 -0
- package/dist/core/delayed.d.ts +39 -0
- package/dist/core/delayed.js +160 -0
- package/dist/core/errors.d.ts +9 -0
- package/dist/core/errors.js +45 -0
- package/dist/core/events.d.ts +32 -0
- package/dist/core/events.js +186 -0
- package/dist/core/glob.js +66 -0
- package/dist/core/identity.d.ts +24 -0
- package/dist/core/identity.js +96 -0
- package/dist/core/ids.js +36 -0
- package/dist/core/invitefilter.js +36 -0
- package/dist/core/json.js +16 -0
- package/dist/core/keys.d.ts +36 -0
- package/dist/core/keys.js +450 -0
- package/dist/core/m.d.ts +209 -0
- package/dist/core/m.js +182 -0
- package/dist/core/notifier.d.ts +17 -0
- package/dist/core/notifier.js +54 -0
- package/dist/core/oauth.d.ts +38 -0
- package/dist/core/oauth.js +108 -0
- package/dist/core/partialstate.d.ts +32 -0
- package/dist/core/partialstate.js +198 -0
- package/dist/core/phone.js +268 -0
- package/dist/core/policy.d.ts +29 -0
- package/dist/core/policy.js +97 -0
- package/dist/core/profile.js +39 -0
- package/dist/core/push.d.ts +46 -0
- package/dist/core/push.js +307 -0
- package/dist/core/pushrules.js +229 -0
- package/dist/core/ratelimit.d.ts +23 -0
- package/dist/core/ratelimit.js +77 -0
- package/dist/core/rooms.d.ts +215 -0
- package/dist/core/rooms.js +1731 -0
- package/dist/core/settings.d.ts +11 -0
- package/dist/core/settings.js +29 -0
- package/dist/core/sso.d.ts +46 -0
- package/dist/core/sso.js +165 -0
- package/dist/core/stateres.d.ts +4 -0
- package/dist/core/stateres.js +209 -0
- package/dist/core/threepid.d.ts +80 -0
- package/dist/core/threepid.js +275 -0
- package/dist/core/typing.d.ts +16 -0
- package/dist/core/typing.js +60 -0
- package/dist/federation/client.d.ts +51 -0
- package/dist/federation/client.js +279 -0
- package/dist/federation/index.d.ts +153 -0
- package/dist/federation/index.js +1059 -0
- package/dist/federation/keyring.d.ts +26 -0
- package/dist/federation/keyring.js +122 -0
- package/dist/federation/sender.d.ts +26 -0
- package/dist/federation/sender.js +164 -0
- package/dist/homeserver.d.ts +224 -0
- package/dist/homeserver.js +302 -0
- package/dist/http/adapters.d.ts +40 -0
- package/dist/http/adapters.js +130 -0
- package/dist/http/handler.d.ts +8 -0
- package/dist/http/handler.js +181 -0
- package/dist/http/input.js +24 -0
- package/dist/http/procedure.js +25 -0
- package/dist/http/router.d.ts +43 -0
- package/dist/http/router.js +56 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +15 -0
- package/dist/storage/memory.d.ts +5 -0
- package/dist/storage/memory.js +122 -0
- package/dist/storage/schema.d.ts +4 -0
- package/dist/storage/schema.js +664 -0
- package/dist/storage/sql.d.ts +72 -0
- package/dist/storage/sql.js +274 -0
- package/dist/storage/types.d.ts +55 -0
- package/dist/vendor/zod/LICENSE +21 -0
- package/dist/vendor/zod/v4/classic/errors.js +54 -0
- package/dist/vendor/zod/v4/classic/parse.js +17 -0
- package/dist/vendor/zod/v4/classic/schemas.js +865 -0
- package/dist/vendor/zod/v4/core/api.js +512 -0
- package/dist/vendor/zod/v4/core/checks.js +300 -0
- package/dist/vendor/zod/v4/core/core.js +109 -0
- package/dist/vendor/zod/v4/core/doc.js +35 -0
- package/dist/vendor/zod/v4/core/errors.js +118 -0
- package/dist/vendor/zod/v4/core/json-schema-processors.js +428 -0
- package/dist/vendor/zod/v4/core/memoizer.js +255 -0
- package/dist/vendor/zod/v4/core/parse.js +206 -0
- package/dist/vendor/zod/v4/core/regexes.js +73 -0
- package/dist/vendor/zod/v4/core/registries.js +48 -0
- package/dist/vendor/zod/v4/core/schemas.js +1378 -0
- package/dist/vendor/zod/v4/core/to-json-schema.js +448 -0
- package/dist/vendor/zod/v4/core/util.js +537 -0
- package/dist/vendor/zod/v4/core/versions.js +8 -0
- package/dist/vendor/zod/v4/locales/en.js +110 -0
- package/package.json +65 -4
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
//#region src/storage/sql.ts
|
|
3
|
+
const OPS = {
|
|
4
|
+
eq: "=",
|
|
5
|
+
ne: "<>",
|
|
6
|
+
lt: "<",
|
|
7
|
+
lte: "<=",
|
|
8
|
+
gt: ">",
|
|
9
|
+
gte: ">="
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Generic SQL adapter. Works with any driver that can run parameterised
|
|
13
|
+
* queries; see `sqliteAdapter` and `postgresAdapter` for ready-made wrappers.
|
|
14
|
+
*/
|
|
15
|
+
function sqlAdapter({ dialect, executor: rootExecutor, tablePrefix = "", transaction }) {
|
|
16
|
+
let schema = {};
|
|
17
|
+
const q = (ident) => dialect === "mysql" ? `\`${ident}\`` : `"${ident}"`;
|
|
18
|
+
const t = (model) => q(tablePrefix + model);
|
|
19
|
+
const columnType = (type, isKey) => {
|
|
20
|
+
switch (type) {
|
|
21
|
+
case "string": return dialect === "mysql" ? isKey ? "VARCHAR(255)" : "TEXT" : "TEXT";
|
|
22
|
+
case "number": return dialect === "sqlite" ? "INTEGER" : "BIGINT";
|
|
23
|
+
case "boolean": return dialect === "postgres" ? "BOOLEAN" : "INTEGER";
|
|
24
|
+
case "json": return dialect === "mysql" ? "LONGTEXT" : "TEXT";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const columnsOf = async (executor, table) => {
|
|
28
|
+
const rows = dialect === "sqlite" ? await executor.query(`PRAGMA table_info(${q(table)})`, []) : dialect === "postgres" ? await executor.query("SELECT column_name AS name FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = $1", [table]) : await executor.query("SELECT COLUMN_NAME AS name FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ?", [table]);
|
|
29
|
+
return new Set(rows.map((r) => String(r.name)));
|
|
30
|
+
};
|
|
31
|
+
const fieldType = (model, field) => schema[model]?.fields[field]?.type ?? "string";
|
|
32
|
+
const toDb = (model, field, value) => {
|
|
33
|
+
if (value === void 0 || value === null) return null;
|
|
34
|
+
switch (fieldType(model, field)) {
|
|
35
|
+
case "json": return JSON.stringify(value);
|
|
36
|
+
case "boolean": return dialect === "postgres" ? !!value : value ? 1 : 0;
|
|
37
|
+
default: return value;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const fromDb = (model, row) => {
|
|
41
|
+
const out = {};
|
|
42
|
+
for (const [field, value] of Object.entries(row)) {
|
|
43
|
+
if (value === null || value === void 0) {
|
|
44
|
+
out[field] = null;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
switch (fieldType(model, field)) {
|
|
48
|
+
case "json":
|
|
49
|
+
out[field] = typeof value === "string" ? JSON.parse(value) : value;
|
|
50
|
+
break;
|
|
51
|
+
case "boolean":
|
|
52
|
+
out[field] = value === true || value === 1 || value === "1" || value === 1n;
|
|
53
|
+
break;
|
|
54
|
+
case "number":
|
|
55
|
+
out[field] = Number(value);
|
|
56
|
+
break;
|
|
57
|
+
default: out[field] = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
};
|
|
62
|
+
const placeholder = (i) => dialect === "postgres" ? `$${i}` : "?";
|
|
63
|
+
const buildWhere = (model, where = [], params) => {
|
|
64
|
+
if (where.length === 0) return "";
|
|
65
|
+
return ` WHERE ${where.map((w) => {
|
|
66
|
+
const op = w.operator ?? "eq";
|
|
67
|
+
if (op === "in") {
|
|
68
|
+
const values = w.value;
|
|
69
|
+
if (values.length === 0) return "1=0";
|
|
70
|
+
const ph = values.map((v) => {
|
|
71
|
+
params.push(toDb(model, w.field, v));
|
|
72
|
+
return placeholder(params.length);
|
|
73
|
+
});
|
|
74
|
+
return `${q(w.field)} IN (${ph.join(", ")})`;
|
|
75
|
+
}
|
|
76
|
+
if (w.value === null || w.value === void 0) {
|
|
77
|
+
if (op === "eq") return `${q(w.field)} IS NULL`;
|
|
78
|
+
if (op === "ne") return `${q(w.field)} IS NOT NULL`;
|
|
79
|
+
}
|
|
80
|
+
params.push(toDb(model, w.field, w.value));
|
|
81
|
+
return `${q(w.field)} ${OPS[op]} ${placeholder(params.length)}`;
|
|
82
|
+
}).join(" AND ")}`;
|
|
83
|
+
};
|
|
84
|
+
const build = (executor, inTx) => {
|
|
85
|
+
const insert = async (model, data, mode) => {
|
|
86
|
+
const fields = Object.keys(data).filter((k) => data[k] !== void 0);
|
|
87
|
+
const params = fields.map((f) => toDb(model, f, data[f]));
|
|
88
|
+
const cols = fields.map(q).join(", ");
|
|
89
|
+
const ph = fields.map((_, i) => placeholder(i + 1)).join(", ");
|
|
90
|
+
let sql = `INSERT INTO ${t(model)} (${cols}) VALUES (${ph})`;
|
|
91
|
+
if (mode === "upsert") {
|
|
92
|
+
const pk = schema[model]?.primaryKey ?? [];
|
|
93
|
+
const rest = fields.filter((f) => !pk.includes(f));
|
|
94
|
+
if (dialect === "mysql") sql += rest.length ? ` ON DUPLICATE KEY UPDATE ${rest.map((f) => `${q(f)} = VALUES(${q(f)})`).join(", ")}` : ` ON DUPLICATE KEY UPDATE ${q(pk[0])} = ${q(pk[0])}`;
|
|
95
|
+
else sql += ` ON CONFLICT (${pk.map(q).join(", ")}) DO ${rest.length ? `UPDATE SET ${rest.map((f) => `${q(f)} = excluded.${q(f)}`).join(", ")}` : "NOTHING"}`;
|
|
96
|
+
}
|
|
97
|
+
await executor.execute(sql, params);
|
|
98
|
+
};
|
|
99
|
+
const adapter = {
|
|
100
|
+
async migrate(s) {
|
|
101
|
+
schema = s;
|
|
102
|
+
for (const [model, def] of Object.entries(s)) {
|
|
103
|
+
const cols = Object.entries(def.fields).map(([name, f]) => {
|
|
104
|
+
const isKey = def.primaryKey.includes(name) || (def.indexes ?? []).some((i) => i.includes(name));
|
|
105
|
+
return `${q(name)} ${columnType(f.type, isKey)}${f.required === false ? "" : " NOT NULL"}`;
|
|
106
|
+
});
|
|
107
|
+
cols.push(`PRIMARY KEY (${def.primaryKey.map(q).join(", ")})`);
|
|
108
|
+
const ine = "IF NOT EXISTS";
|
|
109
|
+
await executor.execute(`CREATE TABLE ${ine} ${t(model)} (${cols.join(", ")})`, []);
|
|
110
|
+
const existing = await columnsOf(executor, tablePrefix + model);
|
|
111
|
+
for (const [name, f] of Object.entries(def.fields)) {
|
|
112
|
+
if (existing.has(name)) continue;
|
|
113
|
+
await executor.execute(`ALTER TABLE ${t(model)} ADD COLUMN ${q(name)} ${columnType(f.type, false)}`, []);
|
|
114
|
+
}
|
|
115
|
+
for (const index of def.indexes ?? []) {
|
|
116
|
+
const name = q(`idx_${tablePrefix}${model}_${index.join("_")}`);
|
|
117
|
+
if (dialect === "mysql") try {
|
|
118
|
+
await executor.execute(`CREATE INDEX ${name} ON ${t(model)} (${index.map(q).join(", ")})`, []);
|
|
119
|
+
} catch {}
|
|
120
|
+
else await executor.execute(`CREATE INDEX ${ine} ${name} ON ${t(model)} (${index.map(q).join(", ")})`, []);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
create: (model, data) => insert(model, data, "insert"),
|
|
125
|
+
upsert: (model, data) => insert(model, data, "upsert"),
|
|
126
|
+
async findOne(model, where) {
|
|
127
|
+
const params = [];
|
|
128
|
+
const rows = await executor.query(`SELECT * FROM ${t(model)}${buildWhere(model, where, params)} LIMIT 1`, params);
|
|
129
|
+
return rows[0] ? fromDb(model, rows[0]) : null;
|
|
130
|
+
},
|
|
131
|
+
async findMany(model, opts = {}) {
|
|
132
|
+
const params = [];
|
|
133
|
+
let sql = `SELECT * FROM ${t(model)}${buildWhere(model, opts.where, params)}`;
|
|
134
|
+
if (opts.sortBy) sql += ` ORDER BY ${q(opts.sortBy.field)} ${opts.sortBy.direction === "asc" ? "ASC" : "DESC"}`;
|
|
135
|
+
if (opts.limit !== void 0) sql += ` LIMIT ${Math.max(0, Math.floor(opts.limit))}`;
|
|
136
|
+
return (await executor.query(sql, params)).map((r) => fromDb(model, r));
|
|
137
|
+
},
|
|
138
|
+
async update(model, where, data) {
|
|
139
|
+
const fields = Object.keys(data).filter((k) => data[k] !== void 0);
|
|
140
|
+
if (fields.length === 0) return 0;
|
|
141
|
+
const params = fields.map((f) => toDb(model, f, data[f]));
|
|
142
|
+
const set = fields.map((f, i) => `${q(f)} = ${placeholder(i + 1)}`).join(", ");
|
|
143
|
+
return executor.execute(`UPDATE ${t(model)} SET ${set}${buildWhere(model, where, params)}`, params);
|
|
144
|
+
},
|
|
145
|
+
async delete(model, where) {
|
|
146
|
+
const params = [];
|
|
147
|
+
return executor.execute(`DELETE FROM ${t(model)}${buildWhere(model, where, params)}`, params);
|
|
148
|
+
},
|
|
149
|
+
async max(model, field) {
|
|
150
|
+
const m = (await executor.query(`SELECT MAX(${q(field)}) AS m FROM ${t(model)}`, []))[0]?.m;
|
|
151
|
+
return m === null || m === void 0 ? null : Number(m);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
if (transaction) adapter.transaction = (fn) => inTx ? fn(adapter) : transaction((ex) => fn(build(ex, true)));
|
|
155
|
+
return adapter;
|
|
156
|
+
};
|
|
157
|
+
return build(rootExecutor, false);
|
|
158
|
+
}
|
|
159
|
+
/** SQLite adapter for `node:sqlite` (`new DatabaseSync(path)`) or `better-sqlite3`. */
|
|
160
|
+
function sqliteAdapter(db, options = {}) {
|
|
161
|
+
try {
|
|
162
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
163
|
+
} catch {}
|
|
164
|
+
const cache = /* @__PURE__ */ new Map();
|
|
165
|
+
const stmt = (sql) => {
|
|
166
|
+
let s = cache.get(sql);
|
|
167
|
+
if (!s) cache.set(sql, s = db.prepare(sql));
|
|
168
|
+
return s;
|
|
169
|
+
};
|
|
170
|
+
const inTransaction = new AsyncLocalStorage();
|
|
171
|
+
let gate = Promise.resolve();
|
|
172
|
+
const waitGate = async () => {
|
|
173
|
+
if (!inTransaction.getStore()) await gate;
|
|
174
|
+
};
|
|
175
|
+
const executor = {
|
|
176
|
+
async query(sql, params) {
|
|
177
|
+
await waitGate();
|
|
178
|
+
return stmt(sql).all(...params);
|
|
179
|
+
},
|
|
180
|
+
async execute(sql, params) {
|
|
181
|
+
await waitGate();
|
|
182
|
+
return Number(stmt(sql).run(...params).changes);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
return sqlAdapter({
|
|
186
|
+
dialect: "sqlite",
|
|
187
|
+
tablePrefix: options.tablePrefix,
|
|
188
|
+
executor,
|
|
189
|
+
async transaction(fn) {
|
|
190
|
+
if (inTransaction.getStore()) return fn(executor);
|
|
191
|
+
const previous = gate;
|
|
192
|
+
let release;
|
|
193
|
+
gate = new Promise((r) => release = r);
|
|
194
|
+
await previous;
|
|
195
|
+
try {
|
|
196
|
+
return await inTransaction.run(true, async () => {
|
|
197
|
+
db.exec("BEGIN IMMEDIATE");
|
|
198
|
+
try {
|
|
199
|
+
const result = await fn(executor);
|
|
200
|
+
db.exec("COMMIT");
|
|
201
|
+
return result;
|
|
202
|
+
} catch (err) {
|
|
203
|
+
db.exec("ROLLBACK");
|
|
204
|
+
throw err;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
} finally {
|
|
208
|
+
release();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const pgExecutor = (conn) => ({
|
|
214
|
+
async query(sql, params) {
|
|
215
|
+
return (await conn.query(sql, params)).rows;
|
|
216
|
+
},
|
|
217
|
+
async execute(sql, params) {
|
|
218
|
+
return (await conn.query(sql, params)).rowCount ?? 0;
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
/** PostgreSQL adapter for `pg` (node-postgres). Pass a `Pool` to get transactions. */
|
|
222
|
+
function postgresAdapter(pool, options = {}) {
|
|
223
|
+
return sqlAdapter({
|
|
224
|
+
dialect: "postgres",
|
|
225
|
+
tablePrefix: options.tablePrefix,
|
|
226
|
+
executor: pgExecutor(pool),
|
|
227
|
+
transaction: pool.connect ? async (fn) => {
|
|
228
|
+
const client = await pool.connect();
|
|
229
|
+
try {
|
|
230
|
+
await client.query("BEGIN");
|
|
231
|
+
const result = await fn(pgExecutor(client));
|
|
232
|
+
await client.query("COMMIT");
|
|
233
|
+
return result;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
await client.query("ROLLBACK").catch(() => {});
|
|
236
|
+
throw err;
|
|
237
|
+
} finally {
|
|
238
|
+
client.release();
|
|
239
|
+
}
|
|
240
|
+
} : void 0
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
const mysqlExecutor = (conn) => ({
|
|
244
|
+
async query(sql, params) {
|
|
245
|
+
return (await conn.query(sql, params))[0];
|
|
246
|
+
},
|
|
247
|
+
async execute(sql, params) {
|
|
248
|
+
return (await conn.query(sql, params))[0]?.affectedRows ?? 0;
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
/** MySQL / MariaDB adapter for `mysql2/promise` pools. */
|
|
252
|
+
function mysqlAdapter(pool, options = {}) {
|
|
253
|
+
return sqlAdapter({
|
|
254
|
+
dialect: "mysql",
|
|
255
|
+
tablePrefix: options.tablePrefix,
|
|
256
|
+
executor: mysqlExecutor(pool),
|
|
257
|
+
transaction: pool.getConnection ? async (fn) => {
|
|
258
|
+
const conn = await pool.getConnection();
|
|
259
|
+
try {
|
|
260
|
+
await conn.query("START TRANSACTION");
|
|
261
|
+
const result = await fn(mysqlExecutor(conn));
|
|
262
|
+
await conn.query("COMMIT");
|
|
263
|
+
return result;
|
|
264
|
+
} catch (err) {
|
|
265
|
+
await conn.query("ROLLBACK").catch(() => {});
|
|
266
|
+
throw err;
|
|
267
|
+
} finally {
|
|
268
|
+
conn.release();
|
|
269
|
+
}
|
|
270
|
+
} : void 0
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
export { mysqlAdapter, postgresAdapter, sqlAdapter, sqliteAdapter };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/storage/types.d.ts
|
|
2
|
+
export type FieldType = "string" | "number" | "boolean" | "json";
|
|
3
|
+
export interface FieldDef {
|
|
4
|
+
type: FieldType;
|
|
5
|
+
/** Defaults to true. Nullable fields may be omitted on create. */
|
|
6
|
+
required?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface ModelDef {
|
|
9
|
+
fields: Record<string, FieldDef>;
|
|
10
|
+
/** Primary key, possibly composite. */
|
|
11
|
+
primaryKey: string[];
|
|
12
|
+
/** Secondary indexes to create (SQL adapters). */
|
|
13
|
+
indexes?: string[][];
|
|
14
|
+
}
|
|
15
|
+
export type Schema = Record<string, ModelDef>;
|
|
16
|
+
export type Operator = "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in";
|
|
17
|
+
export interface WhereClause {
|
|
18
|
+
field: string;
|
|
19
|
+
value: unknown;
|
|
20
|
+
/** Defaults to "eq". */
|
|
21
|
+
operator?: Operator;
|
|
22
|
+
}
|
|
23
|
+
export type Row = Record<string, any>;
|
|
24
|
+
export interface FindManyOptions {
|
|
25
|
+
where?: WhereClause[];
|
|
26
|
+
sortBy?: {
|
|
27
|
+
field: string;
|
|
28
|
+
direction: "asc" | "desc";
|
|
29
|
+
};
|
|
30
|
+
limit?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A storage backend. Implement this interface to plug in any database
|
|
34
|
+
* (the "raw adapter"), or use one of the bundled adapters.
|
|
35
|
+
* All `where` clauses are combined with AND.
|
|
36
|
+
*/
|
|
37
|
+
export interface DatabaseAdapter {
|
|
38
|
+
/** Create tables / collections for the given schema. Must be idempotent. */
|
|
39
|
+
migrate?(schema: Schema): Promise<void>;
|
|
40
|
+
create(model: string, data: Row): Promise<void>;
|
|
41
|
+
/** Insert or replace by primary key. */
|
|
42
|
+
upsert(model: string, data: Row): Promise<void>;
|
|
43
|
+
findOne(model: string, where: WhereClause[]): Promise<Row | null>;
|
|
44
|
+
findMany(model: string, options?: FindManyOptions): Promise<Row[]>;
|
|
45
|
+
update(model: string, where: WhereClause[], data: Row): Promise<number>;
|
|
46
|
+
delete(model: string, where: WhereClause[]): Promise<number>;
|
|
47
|
+
/** Maximum value of a numeric field, or null if there are no rows. */
|
|
48
|
+
max(model: string, field: string): Promise<number | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Run `fn` atomically: either all of its writes are applied or none are.
|
|
51
|
+
* Optional; without it, writes are applied one by one.
|
|
52
|
+
*/
|
|
53
|
+
transaction?<T>(fn: (tx: DatabaseAdapter) => Promise<T>): Promise<T>;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Colin McDonnell
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsonStringifyReplacer } from "../core/util.js";
|
|
2
|
+
import { $constructor } from "../core/core.js";
|
|
3
|
+
import { $ZodError, flattenError, formatError } from "../core/errors.js";
|
|
4
|
+
//#region node_modules/zod/v4/classic/errors.js
|
|
5
|
+
const _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);
|
|
6
|
+
function _lazyMethod(proto, key, make) {
|
|
7
|
+
Object.defineProperty(proto, key, {
|
|
8
|
+
configurable: true,
|
|
9
|
+
enumerable: false,
|
|
10
|
+
get() {
|
|
11
|
+
const value = make(this);
|
|
12
|
+
Object.defineProperty(this, key, {
|
|
13
|
+
value,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true
|
|
16
|
+
});
|
|
17
|
+
return value;
|
|
18
|
+
},
|
|
19
|
+
set(value) {
|
|
20
|
+
Object.defineProperty(this, key, {
|
|
21
|
+
value,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const initializer = (inst, issues) => {
|
|
29
|
+
$ZodError.init(inst, issues);
|
|
30
|
+
inst.name = "ZodError";
|
|
31
|
+
const proto = Object.getPrototypeOf(inst);
|
|
32
|
+
if (_installedErrorProtos.has(proto)) return;
|
|
33
|
+
_installedErrorProtos.add(proto);
|
|
34
|
+
_lazyMethod(proto, "format", (self) => (mapper) => formatError(self, mapper));
|
|
35
|
+
_lazyMethod(proto, "flatten", (self) => (mapper) => flattenError(self, mapper));
|
|
36
|
+
_lazyMethod(proto, "addIssue", (self) => (issue) => {
|
|
37
|
+
self.issues.push(issue);
|
|
38
|
+
self.message = JSON.stringify(self.issues, jsonStringifyReplacer, 2);
|
|
39
|
+
});
|
|
40
|
+
_lazyMethod(proto, "addIssues", (self) => (issues) => {
|
|
41
|
+
self.issues.push(...issues);
|
|
42
|
+
self.message = JSON.stringify(self.issues, jsonStringifyReplacer, 2);
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(proto, "isEmpty", {
|
|
45
|
+
configurable: true,
|
|
46
|
+
enumerable: false,
|
|
47
|
+
get() {
|
|
48
|
+
return this.issues.length === 0;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const ZodRealError = /*@__PURE__*/ $constructor("ZodError", initializer, void 0, { Parent: Error });
|
|
53
|
+
//#endregion
|
|
54
|
+
export { ZodRealError };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { _decode, _decodeAsync, _encode, _encodeAsync, _parse, _parseAsync, _safeDecode, _safeDecodeAsync, _safeEncode, _safeEncodeAsync, _safeParse, _safeParseAsync } from "../core/parse.js";
|
|
2
|
+
import { ZodRealError } from "./errors.js";
|
|
3
|
+
//#region node_modules/zod/v4/classic/parse.js
|
|
4
|
+
const parse = /* @__PURE__ */ _parse(ZodRealError);
|
|
5
|
+
const parseAsync = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
6
|
+
const safeParse = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
7
|
+
const safeParseAsync = /* @__PURE__ */ _safeParseAsync(ZodRealError);
|
|
8
|
+
const encode = /* @__PURE__ */ _encode(ZodRealError);
|
|
9
|
+
const decode = /* @__PURE__ */ _decode(ZodRealError);
|
|
10
|
+
const encodeAsync = /* @__PURE__ */ _encodeAsync(ZodRealError);
|
|
11
|
+
const decodeAsync = /* @__PURE__ */ _decodeAsync(ZodRealError);
|
|
12
|
+
const safeEncode = /* @__PURE__ */ _safeEncode(ZodRealError);
|
|
13
|
+
const safeDecode = /* @__PURE__ */ _safeDecode(ZodRealError);
|
|
14
|
+
const safeEncodeAsync = /* @__PURE__ */ _safeEncodeAsync(ZodRealError);
|
|
15
|
+
const safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);
|
|
16
|
+
//#endregion
|
|
17
|
+
export { decode, decodeAsync, encode, encodeAsync, parse, parseAsync, safeDecode, safeDecodeAsync, safeEncode, safeEncodeAsync, safeParse, safeParseAsync };
|