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,25 @@
|
|
|
1
|
+
import { inputError } from "./input.js";
|
|
2
|
+
//#region src/http/procedure.ts
|
|
3
|
+
function procedure(def) {
|
|
4
|
+
return async (ctx) => {
|
|
5
|
+
const session = def.auth === "user" ? await ctx.auth() : def.auth === "optional" ? await ctx.optionalAuth() : void 0;
|
|
6
|
+
const extra = def.guard ? await def.guard(ctx) : {};
|
|
7
|
+
if (def.rateLimit) await ctx.rateLimit(def.rateLimit);
|
|
8
|
+
let input;
|
|
9
|
+
if (def.input) {
|
|
10
|
+
const parsed = def.input.safeParse(await ctx.json());
|
|
11
|
+
if (!parsed.success) throw inputError(parsed.error);
|
|
12
|
+
input = parsed.data;
|
|
13
|
+
}
|
|
14
|
+
const result = await def.handler(Object.assign(ctx, extra, {
|
|
15
|
+
session,
|
|
16
|
+
input
|
|
17
|
+
}));
|
|
18
|
+
if (!def.output || result instanceof Response) return result;
|
|
19
|
+
const checked = def.output.safeParse(result);
|
|
20
|
+
if (!checked.success) throw new Error(`Response does not match the output schema: ${checked.error.message}`);
|
|
21
|
+
return checked.data;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { procedure };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Session } from "../core/auth.js";
|
|
2
|
+
import { RateLimitAction } from "../core/ratelimit.js";
|
|
3
|
+
import { Homeserver } from "../homeserver.js";
|
|
4
|
+
//#region src/http/router.d.ts
|
|
5
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
6
|
+
export interface RequestContext {
|
|
7
|
+
hs: Homeserver;
|
|
8
|
+
request: Request;
|
|
9
|
+
url: URL;
|
|
10
|
+
method: string;
|
|
11
|
+
params: Record<string, string>;
|
|
12
|
+
query: URLSearchParams;
|
|
13
|
+
/** Remote address, if known. */
|
|
14
|
+
ip: string | null;
|
|
15
|
+
/** Parsed JSON body (empty object when no body was sent). */
|
|
16
|
+
json<T = any>(): Promise<T>;
|
|
17
|
+
bytes(): Promise<Uint8Array>;
|
|
18
|
+
/** Authenticate the request; throws M_MISSING_TOKEN / M_UNKNOWN_TOKEN. */
|
|
19
|
+
auth(): Promise<Session>;
|
|
20
|
+
/** Authenticate if a token was supplied. */
|
|
21
|
+
optionalAuth(): Promise<Session | null>;
|
|
22
|
+
accessToken(): string | null;
|
|
23
|
+
/** Apply the rate limit for `action` to the requesting user (or IP when unauthenticated). */
|
|
24
|
+
rateLimit(action: RateLimitAction): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export type HandlerResult = Response | object | void;
|
|
27
|
+
/** What the router calls; endpoints build one with `procedure()` (see http/procedure.ts). */
|
|
28
|
+
export type Handler = (ctx: RequestContext) => Promise<HandlerResult> | HandlerResult;
|
|
29
|
+
export declare class Router {
|
|
30
|
+
private routes;
|
|
31
|
+
add(method: HttpMethod, path: string, handler: Handler): void;
|
|
32
|
+
get: (path: string, h: Handler) => void;
|
|
33
|
+
post: (path: string, h: Handler) => void;
|
|
34
|
+
put: (path: string, h: Handler) => void;
|
|
35
|
+
delete: (path: string, h: Handler) => void;
|
|
36
|
+
/** Register a client-server API route under both `r0` and `v3` (or the given versions). */
|
|
37
|
+
client(method: HttpMethod, path: string, handler: Handler, versions?: string[]): void;
|
|
38
|
+
match(method: string, pathname: string): {
|
|
39
|
+
handler: Handler;
|
|
40
|
+
params: Record<string, string>;
|
|
41
|
+
} | "method" | null;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/http/router.ts
|
|
2
|
+
var Router = class {
|
|
3
|
+
routes = [];
|
|
4
|
+
add(method, path, handler) {
|
|
5
|
+
this.routes.push({
|
|
6
|
+
method,
|
|
7
|
+
segments: path.split("/").filter(Boolean),
|
|
8
|
+
handler
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
get = (path, h) => this.add("GET", path, h);
|
|
12
|
+
post = (path, h) => this.add("POST", path, h);
|
|
13
|
+
put = (path, h) => this.add("PUT", path, h);
|
|
14
|
+
delete = (path, h) => this.add("DELETE", path, h);
|
|
15
|
+
/** Register a client-server API route under both `r0` and `v3` (or the given versions). */
|
|
16
|
+
client(method, path, handler, versions = ["r0", "v3"]) {
|
|
17
|
+
for (const v of versions) this.add(method, `/_matrix/client/${v}${path}`, handler);
|
|
18
|
+
}
|
|
19
|
+
match(method, pathname) {
|
|
20
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
21
|
+
let methodMismatch = false;
|
|
22
|
+
for (const route of this.routes) {
|
|
23
|
+
const params = matchSegments(route.segments, parts);
|
|
24
|
+
if (!params) continue;
|
|
25
|
+
if (route.method !== method) {
|
|
26
|
+
methodMismatch = true;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
handler: route.handler,
|
|
31
|
+
params
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return methodMismatch ? "method" : null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
function matchSegments(segments, parts) {
|
|
38
|
+
const params = {};
|
|
39
|
+
for (let i = 0; i < segments.length; i++) {
|
|
40
|
+
const seg = segments[i];
|
|
41
|
+
if (seg.startsWith("*")) {
|
|
42
|
+
params[seg.slice(1)] = parts.slice(i).map(decodeURIComponent).join("/");
|
|
43
|
+
return params;
|
|
44
|
+
}
|
|
45
|
+
if (i >= parts.length) return null;
|
|
46
|
+
if (seg.startsWith(":")) try {
|
|
47
|
+
params[seg.slice(1)] = decodeURIComponent(parts[i]);
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
else if (seg !== parts[i]) return null;
|
|
52
|
+
}
|
|
53
|
+
return segments.length === parts.length ? params : null;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { Router };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ThreepidEmail, ThreepidOptions, ThreepidPurpose, ThreepidSms } from "./core/threepid.js";
|
|
2
|
+
import { M, UNSTABLE } from "./core/m.js";
|
|
3
|
+
import { AppServiceRegistration, loadAppServiceRegistrations, parseAppServiceRegistration } from "./core/appservices.js";
|
|
4
|
+
import { ClusterAdapter, localCluster } from "./core/cluster.js";
|
|
5
|
+
import { DatabaseAdapter, FieldDef, FindManyOptions, ModelDef, Operator, Row, Schema, WhereClause } from "./storage/types.js";
|
|
6
|
+
import { AdminService, ServerNoticesOptions } from "./core/admin.js";
|
|
7
|
+
import { OAuthDelegationOptions } from "./core/oauth.js";
|
|
8
|
+
import { SsoOptions, SsoProvider } from "./core/sso.js";
|
|
9
|
+
import { MatrixError } from "./core/errors.js";
|
|
10
|
+
import { FederationOptions } from "./federation/index.js";
|
|
11
|
+
import { DelayedEventsOptions } from "./core/delayed.js";
|
|
12
|
+
import { RateLimitAction, RateLimitRule, RateLimiter, memoryRateLimiter } from "./core/ratelimit.js";
|
|
13
|
+
import { RtcOptions, TurnOptions } from "./api/rtc.js";
|
|
14
|
+
import { AuditAction, AuditEntry, AuditOptions } from "./core/audit.js";
|
|
15
|
+
import { AdminRoomOptions } from "./core/adminroom.js";
|
|
16
|
+
import { PasswordCheckContext, UserAuthAdapter } from "./adapters/auth.js";
|
|
17
|
+
import { MediaAdapter, MediaMetadata, ThumbnailRequest, Thumbnailer, filesystemMedia, memoryMedia, sharpThumbnailer } from "./adapters/media.js";
|
|
18
|
+
import { Homeserver, HomeserverOptions, Logger, createHomeserver } from "./homeserver.js";
|
|
19
|
+
import { NodeHandler, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, isMatrixPath } from "./http/adapters.js";
|
|
20
|
+
import { handleRequest } from "./http/handler.js";
|
|
21
|
+
import { memoryAdapter } from "./storage/memory.js";
|
|
22
|
+
import { MysqlPoolLike, PgPoolLike, SqlAdapterOptions, SqlDialect, SqlExecutor, SqliteDatabaseLike, mysqlAdapter, postgresAdapter, sqlAdapter, sqliteAdapter } from "./storage/sql.js";
|
|
23
|
+
import { schema } from "./storage/schema.js";
|
|
24
|
+
import { S3MediaOptions, s3Media } from "./adapters/s3.js";
|
|
25
|
+
import { PgClusterPoolLike, RedisLike, RedisSubscriberLike, postgresCluster, redisCluster } from "./adapters/cluster.js";
|
|
26
|
+
export { type AdminRoomOptions, type AdminService, type AppServiceRegistration, type AuditAction, type AuditEntry, type AuditOptions, type ClusterAdapter, type DatabaseAdapter, type DelayedEventsOptions, type FederationOptions, type FieldDef, type FindManyOptions, Homeserver, type HomeserverOptions, type Logger, M, MatrixError, type MediaAdapter, type MediaMetadata, type ModelDef, type MysqlPoolLike, type NodeHandler, type OAuthDelegationOptions, type Operator, type PasswordCheckContext, type PgClusterPoolLike, type PgPoolLike, type RateLimitAction, type RateLimitRule, type RateLimiter, type RedisLike, type RedisSubscriberLike, type Row, type RtcOptions, type S3MediaOptions, type Schema, type ServerNoticesOptions, type SqlAdapterOptions, type SqlDialect, type SqlExecutor, type SqliteDatabaseLike, type SsoOptions, type SsoProvider, type ThreepidEmail, type ThreepidOptions, type ThreepidPurpose, type ThreepidSms, type ThumbnailRequest, type Thumbnailer, type TurnOptions, UNSTABLE, type UserAuthAdapter, type WhereClause, createHomeserver, filesystemMedia, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, handleRequest, isMatrixPath, loadAppServiceRegistrations, localCluster, memoryAdapter, memoryMedia, memoryRateLimiter, mysqlAdapter, parseAppServiceRegistration, postgresAdapter, postgresCluster, redisCluster, s3Media, schema, sharpThumbnailer, sqlAdapter, sqliteAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { M, UNSTABLE } from "./core/m.js";
|
|
2
|
+
import { MatrixError } from "./core/errors.js";
|
|
3
|
+
import { localCluster } from "./core/cluster.js";
|
|
4
|
+
import { loadAppServiceRegistrations, parseAppServiceRegistration } from "./core/appservices.js";
|
|
5
|
+
import { memoryRateLimiter } from "./core/ratelimit.js";
|
|
6
|
+
import { filesystemMedia, memoryMedia, sharpThumbnailer } from "./adapters/media.js";
|
|
7
|
+
import { memoryAdapter } from "./storage/memory.js";
|
|
8
|
+
import { schema } from "./storage/schema.js";
|
|
9
|
+
import { handleRequest } from "./http/handler.js";
|
|
10
|
+
import { Homeserver, createHomeserver } from "./homeserver.js";
|
|
11
|
+
import { getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, isMatrixPath } from "./http/adapters.js";
|
|
12
|
+
import { mysqlAdapter, postgresAdapter, sqlAdapter, sqliteAdapter } from "./storage/sql.js";
|
|
13
|
+
import { s3Media } from "./adapters/s3.js";
|
|
14
|
+
import { postgresCluster, redisCluster } from "./adapters/cluster.js";
|
|
15
|
+
export { Homeserver, M, MatrixError, UNSTABLE, createHomeserver, filesystemMedia, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, handleRequest, isMatrixPath, loadAppServiceRegistrations, localCluster, memoryAdapter, memoryMedia, memoryRateLimiter, mysqlAdapter, parseAppServiceRegistration, postgresAdapter, postgresCluster, redisCluster, s3Media, schema, sharpThumbnailer, sqlAdapter, sqliteAdapter };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
//#region src/storage/memory.ts
|
|
2
|
+
function matches(row, where = []) {
|
|
3
|
+
for (const w of where) {
|
|
4
|
+
const v = row[w.field] ?? null;
|
|
5
|
+
const target = w.value ?? null;
|
|
6
|
+
switch (w.operator ?? "eq") {
|
|
7
|
+
case "eq":
|
|
8
|
+
if (v !== target) return false;
|
|
9
|
+
break;
|
|
10
|
+
case "ne":
|
|
11
|
+
if (v === target) return false;
|
|
12
|
+
break;
|
|
13
|
+
case "lt":
|
|
14
|
+
if (!(v < target)) return false;
|
|
15
|
+
break;
|
|
16
|
+
case "lte":
|
|
17
|
+
if (!(v <= target)) return false;
|
|
18
|
+
break;
|
|
19
|
+
case "gt":
|
|
20
|
+
if (!(v > target)) return false;
|
|
21
|
+
break;
|
|
22
|
+
case "gte":
|
|
23
|
+
if (!(v >= target)) return false;
|
|
24
|
+
break;
|
|
25
|
+
case "in": if (!target.includes(v)) return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
/** In-memory adapter. Data is lost on restart; useful for tests and development. */
|
|
31
|
+
function memoryAdapter() {
|
|
32
|
+
const tables = /* @__PURE__ */ new Map();
|
|
33
|
+
const pks = /* @__PURE__ */ new Map();
|
|
34
|
+
const table = (model) => {
|
|
35
|
+
let t = tables.get(model);
|
|
36
|
+
if (!t) tables.set(model, t = /* @__PURE__ */ new Map());
|
|
37
|
+
return t;
|
|
38
|
+
};
|
|
39
|
+
const keyOf = (model, row) => JSON.stringify((pks.get(model) ?? Object.keys(row)).map((k) => row[k]));
|
|
40
|
+
const build = (journal) => {
|
|
41
|
+
const set = (model, key, row) => {
|
|
42
|
+
const t = table(model);
|
|
43
|
+
journal?.push({
|
|
44
|
+
model,
|
|
45
|
+
key,
|
|
46
|
+
row: t.get(key)
|
|
47
|
+
});
|
|
48
|
+
if (row === void 0) t.delete(key);
|
|
49
|
+
else t.set(key, row);
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
async migrate(schema) {
|
|
53
|
+
for (const [name, def] of Object.entries(schema)) pks.set(name, def.primaryKey);
|
|
54
|
+
},
|
|
55
|
+
async create(model, data) {
|
|
56
|
+
const key = keyOf(model, data);
|
|
57
|
+
if (table(model).has(key)) throw new Error(`Duplicate primary key in ${model}`);
|
|
58
|
+
set(model, key, structuredClone(data));
|
|
59
|
+
},
|
|
60
|
+
async upsert(model, data) {
|
|
61
|
+
set(model, keyOf(model, data), structuredClone(data));
|
|
62
|
+
},
|
|
63
|
+
async findOne(model, where) {
|
|
64
|
+
for (const row of table(model).values()) if (matches(row, where)) return structuredClone(row);
|
|
65
|
+
return null;
|
|
66
|
+
},
|
|
67
|
+
async findMany(model, opts = {}) {
|
|
68
|
+
let rows = [...table(model).values()].filter((r) => matches(r, opts.where));
|
|
69
|
+
if (opts.sortBy) {
|
|
70
|
+
const { field, direction } = opts.sortBy;
|
|
71
|
+
const dir = direction === "asc" ? 1 : -1;
|
|
72
|
+
rows.sort((a, b) => a[field] < b[field] ? -dir : a[field] > b[field] ? dir : 0);
|
|
73
|
+
}
|
|
74
|
+
if (opts.limit !== void 0) rows = rows.slice(0, opts.limit);
|
|
75
|
+
return rows.map((r) => structuredClone(r));
|
|
76
|
+
},
|
|
77
|
+
async update(model, where, data) {
|
|
78
|
+
let count = 0;
|
|
79
|
+
for (const [key, row] of [...table(model).entries()]) {
|
|
80
|
+
if (!matches(row, where)) continue;
|
|
81
|
+
const updated = {
|
|
82
|
+
...row,
|
|
83
|
+
...structuredClone(data)
|
|
84
|
+
};
|
|
85
|
+
set(model, key, void 0);
|
|
86
|
+
set(model, keyOf(model, updated), updated);
|
|
87
|
+
count++;
|
|
88
|
+
}
|
|
89
|
+
return count;
|
|
90
|
+
},
|
|
91
|
+
async delete(model, where) {
|
|
92
|
+
let count = 0;
|
|
93
|
+
for (const [key, row] of [...table(model).entries()]) if (matches(row, where)) {
|
|
94
|
+
set(model, key, void 0);
|
|
95
|
+
count++;
|
|
96
|
+
}
|
|
97
|
+
return count;
|
|
98
|
+
},
|
|
99
|
+
async max(model, field) {
|
|
100
|
+
let max = null;
|
|
101
|
+
for (const row of table(model).values()) {
|
|
102
|
+
const v = row[field];
|
|
103
|
+
if (typeof v === "number" && (max === null || v > max)) max = v;
|
|
104
|
+
}
|
|
105
|
+
return max;
|
|
106
|
+
},
|
|
107
|
+
async transaction(fn) {
|
|
108
|
+
const log = [];
|
|
109
|
+
try {
|
|
110
|
+
return await fn(build(log));
|
|
111
|
+
} catch (err) {
|
|
112
|
+
for (const entry of log.reverse()) if (entry.row === void 0) table(entry.model).delete(entry.key);
|
|
113
|
+
else table(entry.model).set(entry.key, entry.row);
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
return build(null);
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { matches, memoryAdapter };
|