@ifc-lite/collab-server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +47 -0
- package/dist/audit-log.d.ts +77 -0
- package/dist/audit-log.d.ts.map +1 -0
- package/dist/audit-log.js +105 -0
- package/dist/audit-log.js.map +1 -0
- package/dist/auth.d.ts +24 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +15 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +34 -0
- package/dist/bin.js.map +1 -0
- package/dist/blob-route.d.ts +58 -0
- package/dist/blob-route.d.ts.map +1 -0
- package/dist/blob-route.js +132 -0
- package/dist/blob-route.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics.d.ts +56 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +161 -0
- package/dist/metrics.js.map +1 -0
- package/dist/path-locks.d.ts +39 -0
- package/dist/path-locks.d.ts.map +1 -0
- package/dist/path-locks.js +176 -0
- package/dist/path-locks.js.map +1 -0
- package/dist/persistence-redis.d.ts +52 -0
- package/dist/persistence-redis.d.ts.map +1 -0
- package/dist/persistence-redis.js +50 -0
- package/dist/persistence-redis.js.map +1 -0
- package/dist/persistence-s3.d.ts +81 -0
- package/dist/persistence-s3.d.ts.map +1 -0
- package/dist/persistence-s3.js +183 -0
- package/dist/persistence-s3.js.map +1 -0
- package/dist/persistence.d.ts +41 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +108 -0
- package/dist/persistence.js.map +1 -0
- package/dist/rate-limit.d.ts +29 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +36 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/replay-protect.d.ts +51 -0
- package/dist/replay-protect.d.ts.map +1 -0
- package/dist/replay-protect.js +134 -0
- package/dist/replay-protect.js.map +1 -0
- package/dist/retention.d.ts +32 -0
- package/dist/retention.d.ts.map +1 -0
- package/dist/retention.js +112 -0
- package/dist/retention.js.map +1 -0
- package/dist/room-manager.d.ts +138 -0
- package/dist/room-manager.d.ts.map +1 -0
- package/dist/room-manager.js +380 -0
- package/dist/room-manager.js.map +1 -0
- package/dist/secure-bundle.d.ts +15 -0
- package/dist/secure-bundle.d.ts.map +1 -0
- package/dist/secure-bundle.js +49 -0
- package/dist/secure-bundle.js.map +1 -0
- package/dist/secure-server.d.ts +49 -0
- package/dist/secure-server.d.ts.map +1 -0
- package/dist/secure-server.js +86 -0
- package/dist/secure-server.js.map +1 -0
- package/dist/server.d.ts +59 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +196 -0
- package/dist/server.js.map +1 -0
- package/dist/snapshot-worker.d.ts +32 -0
- package/dist/snapshot-worker.d.ts.map +1 -0
- package/dist/snapshot-worker.js +87 -0
- package/dist/snapshot-worker.js.map +1 -0
- package/package.json +66 -0
package/dist/bin.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
5
|
+
/** CLI entry point: `ifc-lite-collab-server`. */
|
|
6
|
+
import { FilePersistence, startCollabServer } from './server.js';
|
|
7
|
+
const port = Number(process.env.COLLAB_PORT ?? 1234);
|
|
8
|
+
const host = process.env.COLLAB_HOST ?? '0.0.0.0';
|
|
9
|
+
const dataDir = process.env.COLLAB_DATA_DIR ?? './.collab-data';
|
|
10
|
+
const maxRooms = Number(process.env.COLLAB_MAX_ROOMS ?? 1024);
|
|
11
|
+
async function main() {
|
|
12
|
+
const handle = await startCollabServer({
|
|
13
|
+
port,
|
|
14
|
+
host,
|
|
15
|
+
persistence: new FilePersistence({ dataDir }),
|
|
16
|
+
maxRooms,
|
|
17
|
+
});
|
|
18
|
+
// eslint-disable-next-line no-console
|
|
19
|
+
console.log(`[collab-server] listening at ${handle.url} (data: ${dataDir})`);
|
|
20
|
+
const shutdown = async () => {
|
|
21
|
+
// eslint-disable-next-line no-console
|
|
22
|
+
console.log('[collab-server] shutting down…');
|
|
23
|
+
await handle.stop();
|
|
24
|
+
process.exit(0);
|
|
25
|
+
};
|
|
26
|
+
process.once('SIGINT', shutdown);
|
|
27
|
+
process.once('SIGTERM', shutdown);
|
|
28
|
+
}
|
|
29
|
+
main().catch((err) => {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.error('[collab-server] fatal:', err);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;+DAE+D;AAE/D,iDAAiD;AAEjD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;AACrD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,SAAS,CAAC;AAClD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,gBAAgB,CAAC;AAChE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC,CAAC;AAE9D,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;QACrC,IAAI;QACJ,IAAI;QACJ,WAAW,EAAE,IAAI,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;QAC7C,QAAQ;KACT,CAAC,CAAC;IACH,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,gCAAgC,MAAM,CAAC,GAAG,WAAW,OAAO,GAAG,CAAC,CAAC;IAE7E,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC9C,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP blob route (spec §11.1 + §11.3).
|
|
3
|
+
*
|
|
4
|
+
* Mounts `/blobs/<hash>` (PUT/GET/HEAD/DELETE) and `/blobs` (GET list)
|
|
5
|
+
* on the supplied http server. Storage is pluggable so deployments can
|
|
6
|
+
* back this with S3, GCS, local disk, or in-memory for tests.
|
|
7
|
+
*
|
|
8
|
+
* Authentication is intentionally NOT included here — the existing
|
|
9
|
+
* `AuthenticateFn` only sees websocket upgrades. Production deployments
|
|
10
|
+
* should layer their own JWT validation in front of this route (e.g. a
|
|
11
|
+
* reverse proxy). For dev/tests we accept anonymous traffic.
|
|
12
|
+
*/
|
|
13
|
+
import * as http from 'node:http';
|
|
14
|
+
export interface ServerBlobMeta {
|
|
15
|
+
hash: string;
|
|
16
|
+
byteLength: number;
|
|
17
|
+
contentType?: string;
|
|
18
|
+
uploadedAt: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ServerBlobStorage {
|
|
21
|
+
put(hash: string, bytes: Uint8Array, contentType?: string): Promise<ServerBlobMeta>;
|
|
22
|
+
get(hash: string): Promise<{
|
|
23
|
+
bytes: Uint8Array;
|
|
24
|
+
meta: ServerBlobMeta;
|
|
25
|
+
} | null>;
|
|
26
|
+
has(hash: string): Promise<boolean>;
|
|
27
|
+
delete(hash: string): Promise<boolean>;
|
|
28
|
+
list(): Promise<string[]>;
|
|
29
|
+
}
|
|
30
|
+
/** In-memory storage. Default for tests + dev. */
|
|
31
|
+
export declare class InMemoryBlobStorage implements ServerBlobStorage {
|
|
32
|
+
private readonly blobs;
|
|
33
|
+
put(hash: string, bytes: Uint8Array, contentType?: string): Promise<ServerBlobMeta>;
|
|
34
|
+
get(hash: string): Promise<{
|
|
35
|
+
bytes: Uint8Array<ArrayBuffer>;
|
|
36
|
+
meta: ServerBlobMeta;
|
|
37
|
+
} | null>;
|
|
38
|
+
has(hash: string): Promise<boolean>;
|
|
39
|
+
delete(hash: string): Promise<boolean>;
|
|
40
|
+
list(): Promise<string[]>;
|
|
41
|
+
}
|
|
42
|
+
export interface BlobRouteOptions {
|
|
43
|
+
storage: ServerBlobStorage;
|
|
44
|
+
/** Reject PUTs over this size (default 100 MB). */
|
|
45
|
+
maxBytes?: number;
|
|
46
|
+
/**
|
|
47
|
+
* If set, server recomputes a sha256 of the body and 409s when it
|
|
48
|
+
* doesn't match the URL hash. Defaults to `false` because clients use
|
|
49
|
+
* `fnv128` which is not sha256-compatible.
|
|
50
|
+
*/
|
|
51
|
+
verifySha256?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Route handler for `/blobs/...` requests. Returns `true` if the
|
|
55
|
+
* request was handled (the caller should not write its own response).
|
|
56
|
+
*/
|
|
57
|
+
export declare function handleBlobRequest(req: http.IncomingMessage, res: http.ServerResponse, opts: BlobRouteOptions): Promise<boolean> | boolean;
|
|
58
|
+
//# sourceMappingURL=blob-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-route.d.ts","sourceRoot":"","sources":["../src/blob-route.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACpF,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC/E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC3B;AAED,kDAAkD;AAClD,qBAAa,mBAAoB,YAAW,iBAAiB;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkE;IAElF,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAUnF,GAAG,CAAC,IAAI,EAAE,MAAM;;;;IAIhB,GAAG,CAAC,IAAI,EAAE,MAAM;IAGhB,MAAM,CAAC,IAAI,EAAE,MAAM;IAGnB,IAAI;CAGX;AAKD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAsC5B"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import * as crypto from 'node:crypto';
|
|
5
|
+
/** In-memory storage. Default for tests + dev. */
|
|
6
|
+
export class InMemoryBlobStorage {
|
|
7
|
+
blobs = new Map();
|
|
8
|
+
async put(hash, bytes, contentType) {
|
|
9
|
+
const meta = {
|
|
10
|
+
hash,
|
|
11
|
+
byteLength: bytes.byteLength,
|
|
12
|
+
contentType,
|
|
13
|
+
uploadedAt: new Date().toISOString(),
|
|
14
|
+
};
|
|
15
|
+
this.blobs.set(hash, { bytes: new Uint8Array(bytes), meta });
|
|
16
|
+
return meta;
|
|
17
|
+
}
|
|
18
|
+
async get(hash) {
|
|
19
|
+
const v = this.blobs.get(hash);
|
|
20
|
+
return v ? { bytes: new Uint8Array(v.bytes), meta: v.meta } : null;
|
|
21
|
+
}
|
|
22
|
+
async has(hash) {
|
|
23
|
+
return this.blobs.has(hash);
|
|
24
|
+
}
|
|
25
|
+
async delete(hash) {
|
|
26
|
+
return this.blobs.delete(hash);
|
|
27
|
+
}
|
|
28
|
+
async list() {
|
|
29
|
+
return Array.from(this.blobs.keys());
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Match exactly 32 lowercase hex chars (the client's `fnv128` output). */
|
|
33
|
+
const HASH_REGEX = /^[a-f0-9]{32}$/;
|
|
34
|
+
/**
|
|
35
|
+
* Route handler for `/blobs/...` requests. Returns `true` if the
|
|
36
|
+
* request was handled (the caller should not write its own response).
|
|
37
|
+
*/
|
|
38
|
+
export function handleBlobRequest(req, res, opts) {
|
|
39
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
40
|
+
if (!url.pathname.startsWith('/blobs'))
|
|
41
|
+
return false;
|
|
42
|
+
if (url.pathname === '/blobs' || url.pathname === '/blobs/') {
|
|
43
|
+
if (req.method === 'GET') {
|
|
44
|
+
return opts.storage.list().then((hashes) => {
|
|
45
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
46
|
+
res.end(JSON.stringify({ hashes }));
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
res.writeHead(405);
|
|
51
|
+
res.end();
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
const hash = url.pathname.slice('/blobs/'.length);
|
|
55
|
+
if (!HASH_REGEX.test(hash)) {
|
|
56
|
+
res.writeHead(400, { 'content-type': 'application/json' });
|
|
57
|
+
res.end(JSON.stringify({ error: 'invalid-hash' }));
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
switch (req.method) {
|
|
61
|
+
case 'PUT':
|
|
62
|
+
return handlePut(req, res, hash, opts);
|
|
63
|
+
case 'GET':
|
|
64
|
+
return handleGet(res, hash, opts);
|
|
65
|
+
case 'HEAD':
|
|
66
|
+
return handleHead(res, hash, opts);
|
|
67
|
+
case 'DELETE':
|
|
68
|
+
return handleDelete(res, hash, opts);
|
|
69
|
+
default:
|
|
70
|
+
res.writeHead(405);
|
|
71
|
+
res.end();
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function handlePut(req, res, hash, opts) {
|
|
76
|
+
const max = opts.maxBytes ?? 100 * 1024 * 1024;
|
|
77
|
+
const chunks = [];
|
|
78
|
+
let total = 0;
|
|
79
|
+
for await (const chunk of req) {
|
|
80
|
+
const buf = chunk;
|
|
81
|
+
total += buf.byteLength;
|
|
82
|
+
if (total > max) {
|
|
83
|
+
res.writeHead(413, { 'content-type': 'application/json' });
|
|
84
|
+
res.end(JSON.stringify({ error: 'payload-too-large', max }));
|
|
85
|
+
req.destroy();
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
chunks.push(buf);
|
|
89
|
+
}
|
|
90
|
+
const body = Buffer.concat(chunks);
|
|
91
|
+
if (opts.verifySha256) {
|
|
92
|
+
const sha = crypto.createHash('sha256').update(body).digest('hex');
|
|
93
|
+
if (sha !== hash) {
|
|
94
|
+
res.writeHead(409, { 'content-type': 'application/json' });
|
|
95
|
+
res.end(JSON.stringify({ error: 'hash-mismatch', expected: hash, computed: sha }));
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const contentType = req.headers['content-type'] ?? undefined;
|
|
100
|
+
const meta = await opts.storage.put(hash, body, contentType);
|
|
101
|
+
res.writeHead(201, { 'content-type': 'application/json' });
|
|
102
|
+
res.end(JSON.stringify(meta));
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
async function handleGet(res, hash, opts) {
|
|
106
|
+
const v = await opts.storage.get(hash);
|
|
107
|
+
if (!v) {
|
|
108
|
+
res.writeHead(404);
|
|
109
|
+
res.end();
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
res.writeHead(200, {
|
|
113
|
+
'content-type': v.meta.contentType ?? 'application/octet-stream',
|
|
114
|
+
'content-length': String(v.meta.byteLength),
|
|
115
|
+
'x-blob-hash': v.meta.hash,
|
|
116
|
+
});
|
|
117
|
+
res.end(Buffer.from(v.bytes));
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
async function handleHead(res, hash, opts) {
|
|
121
|
+
const exists = await opts.storage.has(hash);
|
|
122
|
+
res.writeHead(exists ? 200 : 404);
|
|
123
|
+
res.end();
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
async function handleDelete(res, hash, opts) {
|
|
127
|
+
const ok = await opts.storage.delete(hash);
|
|
128
|
+
res.writeHead(ok ? 204 : 404);
|
|
129
|
+
res.end();
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=blob-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-route.js","sourceRoot":"","sources":["../src/blob-route.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { startCollabServer, FilePersistence, MemoryPersistence, type StartCollabServerOptions, type CollabServerHandle, } from './server.js';
|
|
2
|
+
export { type Persistence, type FilePersistenceOptions, } from './persistence.js';
|
|
3
|
+
export { S3Persistence, type S3PersistenceOptions, type S3LikeClient, type S3Commands, } from './persistence-s3.js';
|
|
4
|
+
export { RedisPersistence, type RedisPersistenceOptions, type RedisLikeClient, } from './persistence-redis.js';
|
|
5
|
+
export { type AuthenticateFn, type Principal, type Role, allowAnonymousEditor, denyAll, canWrite, } from './auth.js';
|
|
6
|
+
export { Room, RoomManager, type RoomOptions, type RoomManagerOptions, type PeerConnection, } from './room-manager.js';
|
|
7
|
+
export { MemoryAuditSink, JsonlFileAuditSink, noopAuditSink, shortHash, type AuditEntry, type AuditOpType, type AuditSink, type JsonlFileAuditSinkOptions, } from './audit-log.js';
|
|
8
|
+
export { DEFAULT_RETENTION, applyRetention, planRetention, type RetentionDecision, type RetentionPolicy, } from './retention.js';
|
|
9
|
+
export { SnapshotWorker, type SnapshotWorkerOptions, type SnapshotResult, } from './snapshot-worker.js';
|
|
10
|
+
export { MetricsRegistry, defaultMetrics, type LabelValues, } from './metrics.js';
|
|
11
|
+
export { computeHmac, createReplayProtector, decodeSignedFrame, encodeSignedFrame, verifyWithReplayProtector, type ReplayDecision, type ReplayProtector, type ReplayProtectorOptions, type UpdateEnvelope, } from './replay-protect.js';
|
|
12
|
+
export { type VerifyDecision, type VerifyMessageFn, } from './room-manager.js';
|
|
13
|
+
export { applySecurityHeaders, createSecureHttpServer, secureHttpHandler, type SecureHttpServerOptions, } from './secure-server.js';
|
|
14
|
+
export { startSecureCollabServer, type StartSecureCollabServerOptions, } from './secure-bundle.js';
|
|
15
|
+
export { createPathLockRegistry, harvestUpdatePaths, verifyAgainstPathLocks, type PathLock, type PathLockRegistry, } from './path-locks.js';
|
|
16
|
+
export { createRateLimiter, type RateLimitOptions, type RateLimiter, } from './rate-limit.js';
|
|
17
|
+
export { InMemoryBlobStorage, handleBlobRequest, type ServerBlobStorage, type ServerBlobMeta, type BlobRouteOptions, } from './blob-route.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,sBAAsB,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,IAAI,EACT,oBAAoB,EACpB,OAAO,EACP,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,SAAS,EACT,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,yBAAyB,GAC/B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,uBAAuB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,uBAAuB,EACvB,KAAK,8BAA8B,GACpC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
export { startCollabServer, FilePersistence, MemoryPersistence, } from './server.js';
|
|
5
|
+
export { S3Persistence, } from './persistence-s3.js';
|
|
6
|
+
export { RedisPersistence, } from './persistence-redis.js';
|
|
7
|
+
export { allowAnonymousEditor, denyAll, canWrite, } from './auth.js';
|
|
8
|
+
export { Room, RoomManager, } from './room-manager.js';
|
|
9
|
+
export { MemoryAuditSink, JsonlFileAuditSink, noopAuditSink, shortHash, } from './audit-log.js';
|
|
10
|
+
export { DEFAULT_RETENTION, applyRetention, planRetention, } from './retention.js';
|
|
11
|
+
export { SnapshotWorker, } from './snapshot-worker.js';
|
|
12
|
+
export { MetricsRegistry, defaultMetrics, } from './metrics.js';
|
|
13
|
+
export { computeHmac, createReplayProtector, decodeSignedFrame, encodeSignedFrame, verifyWithReplayProtector, } from './replay-protect.js';
|
|
14
|
+
export { applySecurityHeaders, createSecureHttpServer, secureHttpHandler, } from './secure-server.js';
|
|
15
|
+
export { startSecureCollabServer, } from './secure-bundle.js';
|
|
16
|
+
export { createPathLockRegistry, harvestUpdatePaths, verifyAgainstPathLocks, } from './path-locks.js';
|
|
17
|
+
export { createRateLimiter, } from './rate-limit.js';
|
|
18
|
+
export { InMemoryBlobStorage, handleBlobRequest, } from './blob-route.js';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAGlB,MAAM,aAAa,CAAC;AAKrB,OAAO,EACL,aAAa,GAId,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAIL,oBAAoB,EACpB,OAAO,EACP,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,IAAI,EACJ,WAAW,GAIZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,SAAS,GAKV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,aAAa,GAGd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,GAGf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,cAAc,GAEf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,GAK1B,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,GAElB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,uBAAuB,GAExB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,GAGvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,GAGlB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,GAIlB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight metrics registry (spec §15 §14.4).
|
|
3
|
+
*
|
|
4
|
+
* Prometheus-text-format compatible — apps can scrape `/metrics` and
|
|
5
|
+
* point Grafana / Datadog / etc. at it. Metric types are intentionally
|
|
6
|
+
* minimal:
|
|
7
|
+
* - counters (monotonic)
|
|
8
|
+
* - gauges (set/inc/dec to any value)
|
|
9
|
+
* - histograms via simple sum + count (we don't ship buckets — those
|
|
10
|
+
* come in v0.5+)
|
|
11
|
+
*
|
|
12
|
+
* Built dependency-free so we can swap for prom-client later without an
|
|
13
|
+
* API churn.
|
|
14
|
+
*/
|
|
15
|
+
export type LabelValues = Record<string, string>;
|
|
16
|
+
export declare class MetricsRegistry {
|
|
17
|
+
private readonly metrics;
|
|
18
|
+
private ensure;
|
|
19
|
+
/** Register or fetch a counter. Counters are monotonic (only `inc`). */
|
|
20
|
+
counter(name: string, help: string): {
|
|
21
|
+
inc: (n?: number, labels?: LabelValues) => void;
|
|
22
|
+
get: (labels?: LabelValues) => number;
|
|
23
|
+
};
|
|
24
|
+
/** Register or fetch a gauge. Gauges can move in any direction. */
|
|
25
|
+
gauge(name: string, help: string): {
|
|
26
|
+
set: (value: number, labels?: LabelValues) => void;
|
|
27
|
+
inc: (n?: number, labels?: LabelValues) => void;
|
|
28
|
+
dec: (n?: number, labels?: LabelValues) => void;
|
|
29
|
+
get: (labels?: LabelValues) => number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Tiny histogram: tracks `sum` and `count`. Apps can compute average
|
|
33
|
+
* client-side. Real bucketed histograms are also available via
|
|
34
|
+
* `bucketedHistogram(name, buckets, help)`.
|
|
35
|
+
*/
|
|
36
|
+
histogram(name: string, help: string): {
|
|
37
|
+
observe: (value: number, labels?: LabelValues) => void;
|
|
38
|
+
mean: (labels?: LabelValues) => number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Prometheus-style bucketed histogram. `buckets` are upper bounds
|
|
42
|
+
* (e.g. `[5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000]`). Each
|
|
43
|
+
* observation increments every bucket whose upper bound it doesn't
|
|
44
|
+
* exceed, plus `sum` and `count`. Render output uses the
|
|
45
|
+
* conventional `<name>_bucket{le="<bound>"}` labels.
|
|
46
|
+
*/
|
|
47
|
+
bucketedHistogram(name: string, buckets: readonly number[], help: string): {
|
|
48
|
+
observe: (value: number, labels?: LabelValues) => void;
|
|
49
|
+
buckets: number[];
|
|
50
|
+
};
|
|
51
|
+
/** Render the full registry as Prometheus text format. */
|
|
52
|
+
render(): string;
|
|
53
|
+
}
|
|
54
|
+
/** Default registry shared by the server. Apps can inject their own. */
|
|
55
|
+
export declare const defaultMetrics: MetricsRegistry;
|
|
56
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAoBjD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAE1D,OAAO,CAAC,MAAM;IAuBd,wEAAwE;IACxE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;mCAGT,WAAW;uBAYlB,WAAW;;IAI7B,mEAAmE;IACnE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;qBAGf,MAAM,WAAU,WAAW;mCAGnB,WAAW;mCAIX,WAAW;uBAIlB,WAAW;;IAI7B;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;yBAMf,MAAM,WAAU,WAAW;wBAK7B,WAAW;;IAS9B;;;;;;OAMG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM;yBAsBnD,MAAM,WAAU,WAAW;;;IAkBhD,0DAA0D;IAC1D,MAAM,IAAI,MAAM;CAsBjB;AAED,wEAAwE;AACxE,eAAO,MAAM,cAAc,iBAAwB,CAAC"}
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
function labelKey(labels) {
|
|
5
|
+
const keys = Object.keys(labels).sort();
|
|
6
|
+
if (keys.length === 0)
|
|
7
|
+
return '';
|
|
8
|
+
return keys.map((k) => `${k}="${escapeLabel(labels[k])}"`).join(',');
|
|
9
|
+
}
|
|
10
|
+
function escapeLabel(value) {
|
|
11
|
+
return value.replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/"/g, '\\"');
|
|
12
|
+
}
|
|
13
|
+
export class MetricsRegistry {
|
|
14
|
+
metrics = new Map();
|
|
15
|
+
ensure(name, type, help) {
|
|
16
|
+
let m = this.metrics.get(name);
|
|
17
|
+
if (!m) {
|
|
18
|
+
m = { type, help, values: new Map() };
|
|
19
|
+
this.metrics.set(name, m);
|
|
20
|
+
return m;
|
|
21
|
+
}
|
|
22
|
+
// Silently reusing a name with a different type/help corrupts the
|
|
23
|
+
// metric family (mismatched HELP/TYPE lines on render, ambiguous
|
|
24
|
+
// semantics for consumers). Surface re-registration loudly.
|
|
25
|
+
if (m.type !== type) {
|
|
26
|
+
throw new Error(`@ifc-lite/collab-server: metric "${name}" already registered as ${m.type}, cannot redefine as ${type}`);
|
|
27
|
+
}
|
|
28
|
+
if (m.help !== help) {
|
|
29
|
+
throw new Error(`@ifc-lite/collab-server: metric "${name}" already registered with different help text`);
|
|
30
|
+
}
|
|
31
|
+
return m;
|
|
32
|
+
}
|
|
33
|
+
/** Register or fetch a counter. Counters are monotonic (only `inc`). */
|
|
34
|
+
counter(name, help) {
|
|
35
|
+
const m = this.ensure(name, 'counter', help);
|
|
36
|
+
return {
|
|
37
|
+
inc: (n = 1, labels = {}) => {
|
|
38
|
+
// Counters are monotonic by definition. Negative/NaN/Infinity
|
|
39
|
+
// increments silently corrupt the series and downstream
|
|
40
|
+
// rate(...) calculations.
|
|
41
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
42
|
+
throw new Error(`@ifc-lite/collab-server: counter "${name}" increment must be a finite non-negative number (got ${n})`);
|
|
43
|
+
}
|
|
44
|
+
const key = labelKey(labels);
|
|
45
|
+
m.values.set(key, (m.values.get(key) ?? 0) + n);
|
|
46
|
+
},
|
|
47
|
+
get: (labels = {}) => m.values.get(labelKey(labels)) ?? 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Register or fetch a gauge. Gauges can move in any direction. */
|
|
51
|
+
gauge(name, help) {
|
|
52
|
+
const m = this.ensure(name, 'gauge', help);
|
|
53
|
+
return {
|
|
54
|
+
set: (value, labels = {}) => {
|
|
55
|
+
m.values.set(labelKey(labels), value);
|
|
56
|
+
},
|
|
57
|
+
inc: (n = 1, labels = {}) => {
|
|
58
|
+
const key = labelKey(labels);
|
|
59
|
+
m.values.set(key, (m.values.get(key) ?? 0) + n);
|
|
60
|
+
},
|
|
61
|
+
dec: (n = 1, labels = {}) => {
|
|
62
|
+
const key = labelKey(labels);
|
|
63
|
+
m.values.set(key, (m.values.get(key) ?? 0) - n);
|
|
64
|
+
},
|
|
65
|
+
get: (labels = {}) => m.values.get(labelKey(labels)) ?? 0,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Tiny histogram: tracks `sum` and `count`. Apps can compute average
|
|
70
|
+
* client-side. Real bucketed histograms are also available via
|
|
71
|
+
* `bucketedHistogram(name, buckets, help)`.
|
|
72
|
+
*/
|
|
73
|
+
histogram(name, help) {
|
|
74
|
+
const sumName = `${name}_sum`;
|
|
75
|
+
const countName = `${name}_count`;
|
|
76
|
+
const sum = this.ensure(sumName, 'histogram-sum', help);
|
|
77
|
+
const count = this.ensure(countName, 'histogram-count', help);
|
|
78
|
+
return {
|
|
79
|
+
observe: (value, labels = {}) => {
|
|
80
|
+
const key = labelKey(labels);
|
|
81
|
+
sum.values.set(key, (sum.values.get(key) ?? 0) + value);
|
|
82
|
+
count.values.set(key, (count.values.get(key) ?? 0) + 1);
|
|
83
|
+
},
|
|
84
|
+
mean: (labels = {}) => {
|
|
85
|
+
const key = labelKey(labels);
|
|
86
|
+
const c = count.values.get(key) ?? 0;
|
|
87
|
+
if (c === 0)
|
|
88
|
+
return 0;
|
|
89
|
+
return (sum.values.get(key) ?? 0) / c;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Prometheus-style bucketed histogram. `buckets` are upper bounds
|
|
95
|
+
* (e.g. `[5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000]`). Each
|
|
96
|
+
* observation increments every bucket whose upper bound it doesn't
|
|
97
|
+
* exceed, plus `sum` and `count`. Render output uses the
|
|
98
|
+
* conventional `<name>_bucket{le="<bound>"}` labels.
|
|
99
|
+
*/
|
|
100
|
+
bucketedHistogram(name, buckets, help) {
|
|
101
|
+
if (buckets.length === 0) {
|
|
102
|
+
throw new Error(`@ifc-lite/collab-server: bucketedHistogram requires at least one bucket`);
|
|
103
|
+
}
|
|
104
|
+
// Dedupe before sorting — duplicate bounds increment the same `le`
|
|
105
|
+
// bucket series twice per observation, throwing off bucket counts
|
|
106
|
+
// and Prometheus's `histogram_quantile` math.
|
|
107
|
+
const sortedBuckets = [...new Set(buckets)].sort((a, b) => a - b);
|
|
108
|
+
if (sortedBuckets.some((b) => !Number.isFinite(b))) {
|
|
109
|
+
throw new Error(`@ifc-lite/collab-server: bucketedHistogram "${name}" buckets must be finite numbers`);
|
|
110
|
+
}
|
|
111
|
+
const sumName = `${name}_sum`;
|
|
112
|
+
const countName = `${name}_count`;
|
|
113
|
+
const bucketName = `${name}_bucket`;
|
|
114
|
+
const sum = this.ensure(sumName, 'histogram-sum', help);
|
|
115
|
+
const count = this.ensure(countName, 'histogram-count', help);
|
|
116
|
+
const bucketEntry = this.ensure(bucketName, 'histogram-bucket', help);
|
|
117
|
+
bucketEntry.buckets = sortedBuckets;
|
|
118
|
+
return {
|
|
119
|
+
observe: (value, labels = {}) => {
|
|
120
|
+
const key = labelKey(labels);
|
|
121
|
+
sum.values.set(key, (sum.values.get(key) ?? 0) + value);
|
|
122
|
+
count.values.set(key, (count.values.get(key) ?? 0) + 1);
|
|
123
|
+
for (const b of sortedBuckets) {
|
|
124
|
+
if (value <= b) {
|
|
125
|
+
const bucketKey = labelKey({ ...labels, le: String(b) });
|
|
126
|
+
bucketEntry.values.set(bucketKey, (bucketEntry.values.get(bucketKey) ?? 0) + 1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// +Inf bucket — every observation counts.
|
|
130
|
+
const infKey = labelKey({ ...labels, le: '+Inf' });
|
|
131
|
+
bucketEntry.values.set(infKey, (bucketEntry.values.get(infKey) ?? 0) + 1);
|
|
132
|
+
},
|
|
133
|
+
buckets: sortedBuckets,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** Render the full registry as Prometheus text format. */
|
|
137
|
+
render() {
|
|
138
|
+
const lines = [];
|
|
139
|
+
for (const [name, m] of this.metrics) {
|
|
140
|
+
lines.push(`# HELP ${name} ${m.help}`);
|
|
141
|
+
const renderedType = m.type === 'counter'
|
|
142
|
+
? 'counter'
|
|
143
|
+
: m.type === 'histogram-bucket'
|
|
144
|
+
? 'histogram'
|
|
145
|
+
: 'gauge';
|
|
146
|
+
lines.push(`# TYPE ${name} ${renderedType}`);
|
|
147
|
+
if (m.values.size === 0) {
|
|
148
|
+
lines.push(`${name} 0`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
for (const [key, value] of m.values) {
|
|
152
|
+
const renderedKey = key.length > 0 ? `{${key}}` : '';
|
|
153
|
+
lines.push(`${name}${renderedKey} ${value}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return lines.join('\n') + '\n';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Default registry shared by the server. Apps can inject their own. */
|
|
160
|
+
export const defaultMetrics = new MetricsRegistry();
|
|
161
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Principal, Role } from './auth.js';
|
|
2
|
+
import type { VerifyMessageFn } from './room-manager.js';
|
|
3
|
+
export interface PathLock {
|
|
4
|
+
/** Prefix that triggers this lock. Either `entities/<path-prefix>` or `geometry/<id-prefix>` etc. */
|
|
5
|
+
prefix: string;
|
|
6
|
+
/** Optional human-readable label for audit logs. */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Allow these userIds to bypass the lock. */
|
|
9
|
+
exemptUserIds?: ReadonlySet<string>;
|
|
10
|
+
/** Allow these roles to bypass the lock (e.g. the admin that placed it). */
|
|
11
|
+
exemptRoles?: ReadonlySet<Role>;
|
|
12
|
+
}
|
|
13
|
+
export interface PathLockRegistry {
|
|
14
|
+
/** Snapshot of currently-active locks. */
|
|
15
|
+
list(): readonly PathLock[];
|
|
16
|
+
/** Add a lock. Returns the lock for the caller to keep a reference to. */
|
|
17
|
+
add(lock: PathLock): PathLock;
|
|
18
|
+
/** Remove a previously-added lock. */
|
|
19
|
+
remove(lock: PathLock): boolean;
|
|
20
|
+
/** Drop every lock. */
|
|
21
|
+
clear(): void;
|
|
22
|
+
/** Returns the matching lock if `path` is locked AND `principal` is not exempt. */
|
|
23
|
+
matches(path: string, principal: Principal): PathLock | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function createPathLockRegistry(): PathLockRegistry;
|
|
26
|
+
/**
|
|
27
|
+
* Apply `update` to a throwaway Y.Doc and harvest the touched paths
|
|
28
|
+
* as a list of `<top>/<key>` strings (`entities/wall`, `geometry/g1`,
|
|
29
|
+
* etc.). Each top-level shared type the runtime knows about is
|
|
30
|
+
* inspected.
|
|
31
|
+
*/
|
|
32
|
+
export declare function harvestUpdatePaths(update: Uint8Array): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Build a `VerifyMessageFn` that rejects sync-update messages whose
|
|
35
|
+
* harvested paths intersect a locked prefix (and whose principal
|
|
36
|
+
* isn't exempt). Pass-through for non-update frames.
|
|
37
|
+
*/
|
|
38
|
+
export declare function verifyAgainstPathLocks(registry: PathLockRegistry): VerifyMessageFn;
|
|
39
|
+
//# sourceMappingURL=path-locks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-locks.d.ts","sourceRoot":"","sources":["../src/path-locks.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAkB,eAAe,EAAkB,MAAM,mBAAmB,CAAC;AAIzF,MAAM,WAAW,QAAQ;IACvB,qGAAqG;IACrG,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,0CAA0C;IAC1C,IAAI,IAAI,SAAS,QAAQ,EAAE,CAAC;IAC5B,0EAA0E;IAC1E,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC9B,sCAAsC;IACtC,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAChC,uBAAuB;IACvB,KAAK,IAAI,IAAI,CAAC;IACd,mFAAmF;IACnF,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC;CAC9D;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CA4BzD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAwC/D;AASD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,eAAe,CAgDlF"}
|