@tensorix/metahub 0.3.2 → 0.4.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/README.md +23 -10
- package/README.zh-CN.md +23 -10
- package/dist/cli/edge-worker-script.d.ts +4 -0
- package/dist/cli/edge-worker-script.d.ts.map +1 -0
- package/dist/cli.js +16716 -8375
- package/dist/cli.js.map +110 -60
- package/dist/core/access-policy.d.ts +82 -0
- package/dist/core/access-policy.d.ts.map +1 -0
- package/dist/core/blobs-core.d.ts +19 -0
- package/dist/core/blobs-core.d.ts.map +1 -1
- package/dist/core/blobs.d.ts.map +1 -1
- package/dist/core/compact.d.ts.map +1 -1
- package/dist/core/config.d.ts +13 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/crdt.d.ts +29 -2
- package/dist/core/crdt.d.ts.map +1 -1
- package/dist/core/data-map.d.ts +105 -0
- package/dist/core/data-map.d.ts.map +1 -0
- package/dist/core/db.d.ts +1 -1
- package/dist/core/db.d.ts.map +1 -1
- package/dist/core/documents.d.ts.map +1 -1
- package/dist/core/errors.d.ts +2 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/grants-core.d.ts +132 -0
- package/dist/core/grants-core.d.ts.map +1 -0
- package/dist/core/guest-intent.d.ts +33 -0
- package/dist/core/guest-intent.d.ts.map +1 -0
- package/dist/core/hash.d.ts +4 -0
- package/dist/core/hash.d.ts.map +1 -0
- package/dist/core/history.d.ts.map +1 -1
- package/dist/core/hlc.d.ts +5 -0
- package/dist/core/hlc.d.ts.map +1 -1
- package/dist/core/ids.d.ts +1 -1
- package/dist/core/ids.d.ts.map +1 -1
- package/dist/core/inject-runtime.d.ts +8 -0
- package/dist/core/inject-runtime.d.ts.map +1 -0
- package/dist/core/intent-retention.d.ts +23 -0
- package/dist/core/intent-retention.d.ts.map +1 -0
- package/dist/core/md/inline.d.ts +6 -2
- package/dist/core/md/inline.d.ts.map +1 -1
- package/dist/core/node.d.ts +13 -0
- package/dist/core/node.d.ts.map +1 -1
- package/dist/core/properties.d.ts +8 -0
- package/dist/core/properties.d.ts.map +1 -1
- package/dist/core/records.d.ts +31 -0
- package/dist/core/records.d.ts.map +1 -1
- package/dist/core/schema-init.d.ts +19 -2
- package/dist/core/schema-init.d.ts.map +1 -1
- package/dist/core/schema.d.ts +1 -1
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/shares.d.ts +15 -0
- package/dist/core/shares.d.ts.map +1 -1
- package/dist/core/site-channel-lifecycle.d.ts +10 -0
- package/dist/core/site-channel-lifecycle.d.ts.map +1 -0
- package/dist/core/site-channel-store.d.ts +120 -0
- package/dist/core/site-channel-store.d.ts.map +1 -0
- package/dist/core/site-channels.d.ts +65 -0
- package/dist/core/site-channels.d.ts.map +1 -0
- package/dist/core/sites-core.d.ts +100 -7
- package/dist/core/sites-core.d.ts.map +1 -1
- package/dist/core/sites.d.ts +65 -2
- package/dist/core/sites.d.ts.map +1 -1
- package/dist/core/sync/anti-abuse.d.ts +26 -0
- package/dist/core/sync/anti-abuse.d.ts.map +1 -0
- package/dist/core/sync/auth.d.ts +30 -2
- package/dist/core/sync/auth.d.ts.map +1 -1
- package/dist/core/sync/blob-routes.d.ts.map +1 -1
- package/dist/core/sync/cf-api.d.ts +54 -0
- package/dist/core/sync/cf-api.d.ts.map +1 -0
- package/dist/core/sync/cf-oauth.d.ts +91 -0
- package/dist/core/sync/cf-oauth.d.ts.map +1 -0
- package/dist/core/sync/client.d.ts +12 -0
- package/dist/core/sync/client.d.ts.map +1 -1
- package/dist/core/sync/data-map-db.d.ts +8 -0
- package/dist/core/sync/data-map-db.d.ts.map +1 -0
- package/dist/core/sync/devices.d.ts +65 -0
- package/dist/core/sync/devices.d.ts.map +1 -0
- package/dist/core/sync/drop-host.d.ts +40 -0
- package/dist/core/sync/drop-host.d.ts.map +1 -0
- package/dist/core/sync/drop-keys.d.ts +64 -0
- package/dist/core/sync/drop-keys.d.ts.map +1 -0
- package/dist/core/sync/drop-protocol.d.ts +94 -0
- package/dist/core/sync/drop-protocol.d.ts.map +1 -0
- package/dist/core/sync/drop-pull.d.ts +67 -0
- package/dist/core/sync/drop-pull.d.ts.map +1 -0
- package/dist/core/sync/drop-wire.d.ts +39 -0
- package/dist/core/sync/drop-wire.d.ts.map +1 -0
- package/dist/core/sync/e2ee.d.ts +3 -0
- package/dist/core/sync/e2ee.d.ts.map +1 -1
- package/dist/core/sync/edge-config.d.ts +77 -0
- package/dist/core/sync/edge-config.d.ts.map +1 -0
- package/dist/core/sync/edge-connect.d.ts +13 -0
- package/dist/core/sync/edge-connect.d.ts.map +1 -0
- package/dist/core/sync/edge-service.d.ts +100 -0
- package/dist/core/sync/edge-service.d.ts.map +1 -0
- package/dist/core/sync/grants-routes.d.ts +46 -0
- package/dist/core/sync/grants-routes.d.ts.map +1 -0
- package/dist/core/sync/guest-session.d.ts +29 -0
- package/dist/core/sync/guest-session.d.ts.map +1 -0
- package/dist/core/sync/html-escape.d.ts +2 -0
- package/dist/core/sync/html-escape.d.ts.map +1 -0
- package/dist/core/sync/http-status.d.ts +14 -0
- package/dist/core/sync/http-status.d.ts.map +1 -0
- package/dist/core/sync/http-util.d.ts +6 -0
- package/dist/core/sync/http-util.d.ts.map +1 -0
- package/dist/core/sync/pairing.d.ts +9 -9
- package/dist/core/sync/pairing.d.ts.map +1 -1
- package/dist/core/sync/partition.d.ts +77 -0
- package/dist/core/sync/partition.d.ts.map +1 -0
- package/dist/core/sync/peers-routes.d.ts.map +1 -1
- package/dist/core/sync/peers.d.ts +77 -0
- package/dist/core/sync/peers.d.ts.map +1 -1
- package/dist/core/sync/protocol.d.ts +2 -0
- package/dist/core/sync/protocol.d.ts.map +1 -1
- package/dist/core/sync/rate-limit.d.ts +20 -0
- package/dist/core/sync/rate-limit.d.ts.map +1 -0
- package/dist/core/sync/recovery.d.ts +9 -0
- package/dist/core/sync/recovery.d.ts.map +1 -0
- package/dist/core/sync/room-client.d.ts +50 -0
- package/dist/core/sync/room-client.d.ts.map +1 -0
- package/dist/core/sync/room-peer.d.ts +62 -0
- package/dist/core/sync/room-peer.d.ts.map +1 -0
- package/dist/core/sync/room-protocol.d.ts +130 -0
- package/dist/core/sync/room-protocol.d.ts.map +1 -0
- package/dist/core/sync/room-serve.d.ts +38 -0
- package/dist/core/sync/room-serve.d.ts.map +1 -0
- package/dist/core/sync/room-url.d.ts +4 -0
- package/dist/core/sync/room-url.d.ts.map +1 -0
- package/dist/core/sync/routes.d.ts +4 -1
- package/dist/core/sync/routes.d.ts.map +1 -1
- package/dist/core/sync/seal.d.ts +22 -0
- package/dist/core/sync/seal.d.ts.map +1 -0
- package/dist/core/sync/server.d.ts +7 -0
- package/dist/core/sync/server.d.ts.map +1 -1
- package/dist/core/sync/share-actions.d.ts +55 -6
- package/dist/core/sync/share-actions.d.ts.map +1 -1
- package/dist/core/sync/share-export.d.ts +14 -0
- package/dist/core/sync/share-export.d.ts.map +1 -1
- package/dist/core/sync/share-render.d.ts +9 -1
- package/dist/core/sync/share-render.d.ts.map +1 -1
- package/dist/core/sync/share-serve.d.ts +5 -1
- package/dist/core/sync/share-serve.d.ts.map +1 -1
- package/dist/core/sync/site-channel-reconcile.d.ts +26 -0
- package/dist/core/sync/site-channel-reconcile.d.ts.map +1 -0
- package/dist/core/sync/site-publish-recovery.d.ts +32 -0
- package/dist/core/sync/site-publish-recovery.d.ts.map +1 -0
- package/dist/core/sync/site-reachability.d.ts +13 -0
- package/dist/core/sync/site-reachability.d.ts.map +1 -0
- package/dist/core/sync/sites-routes.d.ts.map +1 -1
- package/dist/core/sync/sites-serve.d.ts +24 -1
- package/dist/core/sync/sites-serve.d.ts.map +1 -1
- package/dist/core/sync/storage-s3-bun.d.ts +1 -24
- package/dist/core/sync/storage-s3-bun.d.ts.map +1 -1
- package/dist/core/sync/storage-s3-sign.d.ts +43 -0
- package/dist/core/sync/storage-s3-sign.d.ts.map +1 -0
- package/dist/core/sync/storage.d.ts +59 -0
- package/dist/core/sync/storage.d.ts.map +1 -1
- package/dist/db-worker.js +206 -76
- package/dist/edge-worker.js +4038 -0
- package/dist/index.js +13715 -7064
- package/dist/index.js.map +88 -42
- package/dist/metahub-sdk.js +10 -1
- package/dist/mh-runtime.js +1 -1
- package/dist/room/do-driver.d.ts +30 -0
- package/dist/room/do-driver.d.ts.map +1 -0
- package/dist/sw.js +10 -1
- package/dist/webui/server/edge-routes.d.ts +3 -0
- package/dist/webui/server/edge-routes.d.ts.map +1 -0
- package/dist/webui/server/json-handler.d.ts +4 -0
- package/dist/webui/server/json-handler.d.ts.map +1 -0
- package/dist/webui/server/routes.d.ts.map +1 -1
- package/dist/webui/server/share-routes.d.ts.map +1 -1
- package/dist/webui/server/site-hosting-routes.d.ts +7 -0
- package/dist/webui/server/site-hosting-routes.d.ts.map +1 -0
- package/dist/webui.js +208 -147
- package/dist/workers/edge-worker.d.ts +66 -0
- package/dist/workers/edge-worker.d.ts.map +1 -0
- package/dist/workers/room.d.ts +34 -0
- package/dist/workers/room.d.ts.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { DbDriver } from "../driver.ts";
|
|
2
|
+
import { type GrantSet, type GrantPrincipal } from "../grants-core.ts";
|
|
3
|
+
import type { AccessPolicy } from "../access-policy.ts";
|
|
4
|
+
import { type VerifyTurnstile } from "./anti-abuse.ts";
|
|
5
|
+
export interface GrantedApiDeps {
|
|
6
|
+
db: DbDriver;
|
|
7
|
+
set: GrantSet;
|
|
8
|
+
principal: GrantPrincipal;
|
|
9
|
+
/** Host-injected rate limit: return false to answer 429. Called once per
|
|
10
|
+
* request with its class ("read" for GETs, "write" for mutations). */
|
|
11
|
+
allow: (cls: "read" | "write") => boolean;
|
|
12
|
+
/** Optional async gate run before a write (POST/PATCH) is accepted, after the
|
|
13
|
+
* rate limit and before the body is read — the anti-abuse (Turnstile /
|
|
14
|
+
* password) check on the public site mount. Throws MhError to reject. */
|
|
15
|
+
beforeWrite?: () => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build GrantedApiDeps from a resolved AccessPolicy — the one place a mount
|
|
19
|
+
* turns "who may do what" into the serving deps, so the grants + write-gate
|
|
20
|
+
* wiring is identical across the site / share / room adapters (the drift that
|
|
21
|
+
* once let a --password grant be enforced on one transport and skipped on
|
|
22
|
+
* another). `beforeWrite` is synthesized from policy.writeGate via the shared
|
|
23
|
+
* assertAntiAbuse.
|
|
24
|
+
*
|
|
25
|
+
* The gate is per-write ONLY for the public audience: a public site has no
|
|
26
|
+
* session, so the SDK sends the password/Turnstile proof (x-drop-pass /
|
|
27
|
+
* x-turnstile-token) on every write and this gate verifies it — the SAME gate
|
|
28
|
+
* the write-inbox enforces. Share/room audiences spend their password once at
|
|
29
|
+
* unlock and carry a session cookie thereafter, so their writeGate is an
|
|
30
|
+
* unlock-time gate, not a per-write one → no beforeWrite here.
|
|
31
|
+
*/
|
|
32
|
+
export declare function grantedDepsFromPolicy(policy: AccessPolicy, host: {
|
|
33
|
+
db: DbDriver;
|
|
34
|
+
principal: GrantPrincipal;
|
|
35
|
+
allow: (cls: "read" | "write") => boolean;
|
|
36
|
+
req: Request;
|
|
37
|
+
ip?: string | null;
|
|
38
|
+
verifyTurnstile?: VerifyTurnstile;
|
|
39
|
+
}): GrantedApiDeps;
|
|
40
|
+
/**
|
|
41
|
+
* Serve one granted-API request. `sub` is the path under the api/ mount
|
|
42
|
+
* (e.g. "records", "record", "properties"); query params ride on req.url.
|
|
43
|
+
* Never throws — every outcome is a Response.
|
|
44
|
+
*/
|
|
45
|
+
export declare function serveGrantedApi(req: Request, sub: string, deps: GrantedApiDeps): Promise<Response>;
|
|
46
|
+
//# sourceMappingURL=grants-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants-routes.d.ts","sourceRoot":"","sources":["../../../src/core/sync/grants-routes.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EAIpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,QAAQ,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,SAAS,EAAE,cAAc,CAAC;IAC1B;2EACuE;IACvE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,KAAK,OAAO,CAAC;IAC1C;;8EAE0E;IAC1E,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE;IACJ,EAAE,EAAE,QAAQ,CAAC;IACb,SAAS,EAAE,cAAc,CAAC;IAC1B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,KAAK,OAAO,CAAC;IAC1C,GAAG,EAAE,OAAO,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,GACA,cAAc,CAiBhB;AA6ED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,QAAQ,CAAC,CA6DnB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface GuestSessionScope {
|
|
2
|
+
/** HMAC secret (host-local, e.g. meta.share_cookie_secret / room cookie secret). */
|
|
3
|
+
secret: string;
|
|
4
|
+
/** Cookie name, e.g. `mh_share_<slug>` / `mh_room_<slug>`. */
|
|
5
|
+
cookieName: string;
|
|
6
|
+
/** MAC scope discriminator — the slug. */
|
|
7
|
+
scopeKey: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function hmacHex(secret: string, msg: string): Promise<string>;
|
|
10
|
+
export declare function timingSafeStr(a: string, b: string): boolean;
|
|
11
|
+
export declare function readCookie(header: string | null, name: string): string | null;
|
|
12
|
+
/** Verified session payload from the cookie header, or null when absent/
|
|
13
|
+
* expired/forged. `sub` is "" for read-only sessions (no author identity). */
|
|
14
|
+
export declare function readGuestSession(scope: GuestSessionScope, cookieHeader: string | null, now?: number): Promise<{
|
|
15
|
+
exp: number;
|
|
16
|
+
sub: string;
|
|
17
|
+
} | null>;
|
|
18
|
+
/** Mint a fresh session cookie for `sub`, valid for `ttlMs`, scoped to `path`. */
|
|
19
|
+
export declare function mintGuestSession(scope: GuestSessionScope, opts: {
|
|
20
|
+
sub: string;
|
|
21
|
+
ttlMs: number;
|
|
22
|
+
path: string;
|
|
23
|
+
secure: boolean;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
sub: string;
|
|
26
|
+
exp: number;
|
|
27
|
+
cookie: string;
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=guest-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guest-session.d.ts","sourceRoot":"","sources":["../../../src/core/sync/guest-session.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,iBAAiB;IAChC,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAQD,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW1E;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAK3D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO7E;AAED;+EAC+E;AAC/E,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,iBAAiB,EACxB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,GAAG,SAAa,GACf,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAS9C;AAED,kFAAkF;AAClF,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAClE,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAQvD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-escape.d.ts","sourceRoot":"","sources":["../../../src/core/sync/html-escape.ts"],"names":[],"mappings":"AAKA,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAM5C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type MhErrorCode } from "../errors.ts";
|
|
2
|
+
/** HTTP status for an MhError code (400 fallback for unknown/absent codes). */
|
|
3
|
+
export declare function httpStatusForCode(code: MhErrorCode | undefined): number;
|
|
4
|
+
/** The `{error, code?}` body + status for a thrown error — shared by the guest
|
|
5
|
+
* mounts. Callers pick their own Response constructor (Response.json vs a DO
|
|
6
|
+
* json() helper), so this returns the parts, not a Response. */
|
|
7
|
+
export declare function errorBody(e: unknown): {
|
|
8
|
+
body: {
|
|
9
|
+
error: string;
|
|
10
|
+
code?: MhErrorCode;
|
|
11
|
+
};
|
|
12
|
+
status: number;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=http-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-status.d.ts","sourceRoot":"","sources":["../../../src/core/sync/http-status.ts"],"names":[],"mappings":"AAKA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAY3D,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,CAEvE;AAED;;iEAEiE;AACjE,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,WAAW,CAAA;KAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAIrG"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** decodeURIComponent that returns null on a malformed %-escape instead of
|
|
2
|
+
* throwing URIError. An uncaught throw on these anonymous surfaces becomes an
|
|
3
|
+
* unhandled 500 — Bun.serve has no error handler, and workerd returns 1101 —
|
|
4
|
+
* plus a stack in the logs, all reachable by any client sending `%E0%A4`. */
|
|
5
|
+
export declare function safeDecode(s: string): string | null;
|
|
6
|
+
//# sourceMappingURL=http-util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-util.d.ts","sourceRoot":"","sources":["../../../src/core/sync/http-util.ts"],"names":[],"mappings":"AAIA;;;8EAG8E;AAC9E,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMnD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DbDriver } from "../driver.ts";
|
|
2
2
|
import { type PairRequest, type PairResponse } from "./protocol.ts";
|
|
3
3
|
/** Default one-time pairing-code lifetime (override with METAHUB_PAIR_TTL). */
|
|
4
4
|
export declare const DEFAULT_PAIR_TTL_MS: number;
|
|
@@ -7,17 +7,17 @@ export interface PairingCode {
|
|
|
7
7
|
exp: number;
|
|
8
8
|
}
|
|
9
9
|
/** Mint a one-time pairing code, stored until redeemed or expired. */
|
|
10
|
-
export declare function generatePairingCode(db:
|
|
10
|
+
export declare function generatePairingCode(db: DbDriver, ttlMs?: number): PairingCode;
|
|
11
11
|
/**
|
|
12
12
|
* Validate and consume a pairing code. The check-and-consume is a single atomic
|
|
13
13
|
* UPDATE (guarded on used=0 AND not-expired) so two concurrent redemptions of
|
|
14
14
|
* the same code can't both succeed. Returns false if unknown/used/expired.
|
|
15
15
|
*/
|
|
16
|
-
export declare function redeemPairingCode(db:
|
|
16
|
+
export declare function redeemPairingCode(db: DbDriver, code: string): boolean;
|
|
17
17
|
/** Mint a durable credential we issue to a peer and will accept on /sync. */
|
|
18
|
-
export declare function mintGrant(db:
|
|
18
|
+
export declare function mintGrant(db: DbDriver, peerUrl: string | null, nodeId: string | null): string;
|
|
19
19
|
/** Whether a presented token matches a credential we issued during pairing. */
|
|
20
|
-
export declare function isAcceptedGrant(db:
|
|
20
|
+
export declare function isAcceptedGrant(db: DbDriver, token: string): boolean;
|
|
21
21
|
export interface GrantRow {
|
|
22
22
|
token: string;
|
|
23
23
|
peer_url: string | null;
|
|
@@ -25,18 +25,18 @@ export interface GrantRow {
|
|
|
25
25
|
created_at: number | null;
|
|
26
26
|
}
|
|
27
27
|
/** All credentials we have issued and still accept on /sync (inbound access). */
|
|
28
|
-
export declare function listGrants(db:
|
|
28
|
+
export declare function listGrants(db: DbDriver): GrantRow[];
|
|
29
29
|
/**
|
|
30
30
|
* Revoke issued credentials by exact token or a unique prefix (handy for the
|
|
31
31
|
* grants minted by one-directional pairing, which removePeer can't reach since
|
|
32
32
|
* they have a null peer_url). Returns the number of grants revoked.
|
|
33
33
|
*/
|
|
34
|
-
export declare function revokeGrant(db:
|
|
34
|
+
export declare function revokeGrant(db: DbDriver, tokenOrPrefix: string): number;
|
|
35
35
|
/**
|
|
36
36
|
* Inbound side (device A): redeem the caller's one-time code, mint a grant for
|
|
37
37
|
* the caller, register the caller back as a peer if it gave a reachable URL.
|
|
38
38
|
*/
|
|
39
|
-
export declare function handlePairRequest(db:
|
|
39
|
+
export declare function handlePairRequest(db: DbDriver, node: string, body: PairRequest): PairResponse;
|
|
40
40
|
export interface PairOutcome {
|
|
41
41
|
/** The paired peer's node id. */
|
|
42
42
|
node_id: string;
|
|
@@ -46,5 +46,5 @@ export interface PairOutcome {
|
|
|
46
46
|
* Outbound side (device B): mint a grant for the remote, post the handshake to
|
|
47
47
|
* its /api/pair, then register it as a peer with the grant it returned.
|
|
48
48
|
*/
|
|
49
|
-
export declare function performPairing(db:
|
|
49
|
+
export declare function performPairing(db: DbDriver, node: string, remoteUrl: string, code: string, selfUrl?: string): Promise<PairOutcome>;
|
|
50
50
|
//# sourceMappingURL=pairing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../../src/core/sync/pairing.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../../src/core/sync/pairing.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAI7C,OAAO,EAAa,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAG/E,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,QAA2D,CAAC;AAE5F,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,sEAAsE;AACtE,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,GAAE,MAA4B,GAAG,WAAW,CAUlG;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAMrE;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAM7F;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,EAAE,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAInD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAIvE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,YAAY,CAW7F;AAED,MAAM,WAAW,WAAW;IAC1B,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CAoBtB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { DbDriver } from "../driver.ts";
|
|
2
|
+
import { type Change, type ChangeBatch } from "../crdt.ts";
|
|
3
|
+
/** The scope of one share's partition. `siteId` is null for pure-data shares. */
|
|
4
|
+
export interface PartitionScope {
|
|
5
|
+
grantedDbIds: string[];
|
|
6
|
+
siteId: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** Identifies one row of one dataset — the unit of membership and eviction. */
|
|
9
|
+
export interface RowKey {
|
|
10
|
+
dataset: string;
|
|
11
|
+
row_id: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const PARTITION_DATASETS: readonly ["databases", "properties", "records", "sites", "site_files"];
|
|
14
|
+
/** The current member set M of a scope, from the state tables. */
|
|
15
|
+
export declare function computePartitionMembers(db: DbDriver, scope: PartitionScope): RowKey[];
|
|
16
|
+
/** Membership check for a single row under the CURRENT state — used to
|
|
17
|
+
* re-judge a room's need_baseline keys (member → send baseline, gone or
|
|
18
|
+
* out-of-scope → evict). A row that no longer exists is not a member. */
|
|
19
|
+
export declare function isPartitionMember(db: DbDriver, scope: PartitionScope, key: RowKey): boolean;
|
|
20
|
+
export interface PartitionDiff {
|
|
21
|
+
/** In M but not yet in the shadow — need a full-row baseline. */
|
|
22
|
+
entered: RowKey[];
|
|
23
|
+
/** In the shadow but no longer in M — need an evict. */
|
|
24
|
+
left: RowKey[];
|
|
25
|
+
}
|
|
26
|
+
/** entered/left between the current member set and the peer's shadow.
|
|
27
|
+
* READ-ONLY: the caller applies the diff (applyPartitionDiff) only after the
|
|
28
|
+
* payload actually reached the room, so a lost response is re-prepared
|
|
29
|
+
* identically on retry. */
|
|
30
|
+
export declare function partitionDiff(db: DbDriver, peerKey: string, scope: PartitionScope): PartitionDiff;
|
|
31
|
+
/** Commit a delivered diff into the shadow: shipped entered rows join, left
|
|
32
|
+
* rows leave. Idempotent (INSERT OR IGNORE / blind DELETE). */
|
|
33
|
+
export declare function applyPartitionDiff(db: DbDriver, peerKey: string, diff: PartitionDiff): void;
|
|
34
|
+
/** Replace the peer's whole shadow (digest-mismatch full reconcile). */
|
|
35
|
+
export declare function resetPartitionShadow(db: DbDriver, peerKey: string, members: RowKey[]): void;
|
|
36
|
+
export interface PartitionChangesOpts {
|
|
37
|
+
/** Max rows to return — pagination, same semantics as changesAfterSeq. */
|
|
38
|
+
limit?: number;
|
|
39
|
+
/** Skip changes authored under this guest base node id (exact or
|
|
40
|
+
* `<base>-<sub>`): the room authored those itself, echoing them back is
|
|
41
|
+
* pure noise (the room's oplog UNIQUE would dedup, but a room that already
|
|
42
|
+
* GC'd them would re-insert). The cursor still advances over skipped rows. */
|
|
43
|
+
excludeGuestBase?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* changesAfterSeq filtered to the peer's shadow: oplog changes with seq > `seq`
|
|
47
|
+
* whose (dataset,row_id) is in room_rows for `peerKey`, in insertion order.
|
|
48
|
+
* Cursor semantics mirror crdt.ts changesAfterSeq exactly: with `limit` the
|
|
49
|
+
* cursor stops at the last RETURNED row so the next pull resumes there; when
|
|
50
|
+
* the scan is exhausted it jumps to the table's high-water seq so filtered-out
|
|
51
|
+
* tails are skipped once, not rescanned every round.
|
|
52
|
+
*/
|
|
53
|
+
export declare function partitionChangesAfterSeq(db: DbDriver, seq: number, peerKey: string, opts?: PartitionChangesOpts): ChangeBatch;
|
|
54
|
+
/** Current winners (max-HLC change per register) of ONE row — the baseline
|
|
55
|
+
* shipped when a row enters the partition. Mirrors storage.ts's
|
|
56
|
+
* winnersSnapshot NOT EXISTS shape, scoped to a single (dataset,row_id). */
|
|
57
|
+
export declare function rowBaseline(db: DbDriver, dataset: string, rowId: string): Change[];
|
|
58
|
+
/** Winners of every register whose row is currently in the partition —
|
|
59
|
+
* the owner side of the digest, and the payload of a full reconcile. */
|
|
60
|
+
export declare function partitionWinners(db: DbDriver, scope: PartitionScope): Change[];
|
|
61
|
+
/** Winners of every MATERIALIZED register in the whole oplog — the room side
|
|
62
|
+
* of the partition digest. Oplog-only protocol state (intent receipts) is
|
|
63
|
+
* replicated and retained, but must not make anti-entropy compare a room's
|
|
64
|
+
* protocol metadata against the owner's materialized partition projection. */
|
|
65
|
+
export declare function allWinners(db: DbDriver): Change[];
|
|
66
|
+
/**
|
|
67
|
+
* Order-insensitive anti-entropy digest over a winner set: the winners'
|
|
68
|
+
* (dataset, row_id, col, hlc) tuples are canonically sorted, then FNV-1a-64
|
|
69
|
+
* hashed as one stream. The hlc pins the winning change exactly (the oplog's
|
|
70
|
+
* (dataset,row_id,col,hlc) UNIQUE means equal tuples imply equal values), so
|
|
71
|
+
* two ends holding the same winners produce the same digest — BOTH ends must
|
|
72
|
+
* use this same function.
|
|
73
|
+
*/
|
|
74
|
+
export declare function winnersDigest(rows: Pick<Change, "dataset" | "row_id" | "col" | "hlc">[]): string;
|
|
75
|
+
/** The owner-side digest of a scope (winners restricted to current members). */
|
|
76
|
+
export declare function partitionDigest(db: DbDriver, scope: PartitionScope): string;
|
|
77
|
+
//# sourceMappingURL=partition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"partition.d.ts","sourceRoot":"","sources":["../../../src/core/sync/partition.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAIL,KAAK,MAAM,EACX,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAGpB,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,+EAA+E;AAC/E,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,wEAMrB,CAAC;AAkCX,kEAAkE;AAClE,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE,CAKrF;AAED;;0EAE0E;AAC1E,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CA6B3F;AAID,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wDAAwD;IACxD,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;4BAG4B;AAC5B,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,aAAa,CAiBjG;AAED;gEACgE;AAChE,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAO3F;AAED,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAG3F;AAID,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;mFAG+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,oBAAyB,GAC9B,WAAW,CAyBb;AAID;;6EAE6E;AAC7E,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAUlF;AAED;yEACyE;AACzE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE,CAa9E;AAED;;;8EAG8E;AAC9E,wBAAgB,UAAU,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,EAAE,CAcjD;AAID;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC,EAAE,GAAG,MAAM,CAGhG;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAE3E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peers-routes.d.ts","sourceRoot":"","sources":["../../../src/core/sync/peers-routes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,KAAK,EAAiB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"peers-routes.d.ts","sourceRoot":"","sources":["../../../src/core/sync/peers-routes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,KAAK,EAAiB,MAAM,aAAa,CAAC;AAoRvE,eAAO,MAAM,WAAW,EAAE,KAAK,EA+N9B,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DbDriver } from "../driver.ts";
|
|
2
2
|
import { type S3Config, type StorageSyncOpts } from "./storage.ts";
|
|
3
|
+
import { type HealthResponse } from "./protocol.ts";
|
|
3
4
|
export interface PeerRow {
|
|
4
5
|
url: string;
|
|
5
6
|
pull_cursor: number;
|
|
@@ -37,6 +38,28 @@ export interface AddStoragePeerInput {
|
|
|
37
38
|
}
|
|
38
39
|
/** Upsert an 's3' (bucket store-and-forward) peer, preserving cursors on conflict. */
|
|
39
40
|
export declare function addStoragePeer(db: DbDriver, input: AddStoragePeerInput): void;
|
|
41
|
+
/** Connection settings of a kind='room' peer (a Durable Object room serving
|
|
42
|
+
* one share). Node-local like every peers.config. `base` is the worker origin,
|
|
43
|
+
* `slug` the share/room id, `ownerSecret` the Bearer credential for the
|
|
44
|
+
* owner-side sync endpoint — independent of the master token by design. */
|
|
45
|
+
export interface RoomPeerConfig {
|
|
46
|
+
base: string;
|
|
47
|
+
slug: string;
|
|
48
|
+
ownerSecret: string;
|
|
49
|
+
/** The share's base guest node id (pull filter + sub-id derivation). Persisted
|
|
50
|
+
* here because a read-only share row carries no guest_node_id of its own. */
|
|
51
|
+
guestBase?: string;
|
|
52
|
+
lifecycle?: "provisioning" | "active" | "cleanup_pending";
|
|
53
|
+
cleanupError?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface AddRoomPeerInput {
|
|
56
|
+
/** Synthetic peer key, by convention room://<slug>. */
|
|
57
|
+
url: string;
|
|
58
|
+
config: RoomPeerConfig;
|
|
59
|
+
label?: string | null;
|
|
60
|
+
}
|
|
61
|
+
/** Upsert a 'room' peer, preserving replication cursors on conflict. */
|
|
62
|
+
export declare function addRoomPeer(db: DbDriver, input: AddRoomPeerInput): void;
|
|
40
63
|
export { storageUrl, storageEndpointHost } from "./storage-url.ts";
|
|
41
64
|
export interface StoragePeerSpec {
|
|
42
65
|
endpoint: string;
|
|
@@ -52,6 +75,10 @@ export interface StoragePeerSpec {
|
|
|
52
75
|
encrypt?: boolean;
|
|
53
76
|
/** Required when encrypt — provisions/adopts the bucket's wrapped master key. */
|
|
54
77
|
passphrase?: string;
|
|
78
|
+
/** Alternative to the passphrase: an MH1- recovery code carrying the master
|
|
79
|
+
* key directly ("忘记口令" join). Verified against the bucket's ciphertext and
|
|
80
|
+
* used as-is — this path NEVER writes keys/main.json. */
|
|
81
|
+
recoveryCode?: string;
|
|
55
82
|
/** Mark this node the bucket's publisher (writes whole-hub snapshots). */
|
|
56
83
|
publish?: boolean;
|
|
57
84
|
priority?: number;
|
|
@@ -69,6 +96,47 @@ export declare function addAndSyncStoragePeer(db: DbDriver, spec: StoragePeerSpe
|
|
|
69
96
|
config: S3Config;
|
|
70
97
|
sync: PeerSyncOutcome;
|
|
71
98
|
}>;
|
|
99
|
+
/** Replace an s3 peer's config JSON in place. Cursors, status and label are
|
|
100
|
+
* untouched — a credential/passphrase rotation moves no bucket objects, so
|
|
101
|
+
* replication state stays valid. */
|
|
102
|
+
export declare function updateStoragePeerConfig(db: DbDriver, url: string, config: S3Config): void;
|
|
103
|
+
export interface RotateStoragePeerInput {
|
|
104
|
+
/** New S3 credentials (both or neither) — minted by the user at the provider;
|
|
105
|
+
* the old keys stay valid until the user disables them AFTER the rotate. */
|
|
106
|
+
accessKeyId?: string;
|
|
107
|
+
secretAccessKey?: string;
|
|
108
|
+
/** Rewrap keys/main.json under this passphrase (K itself never changes). */
|
|
109
|
+
newPassphrase?: string;
|
|
110
|
+
/** Optional K sources when this device's config lost its cached master key:
|
|
111
|
+
* the printed recovery code, or the current passphrase (unwraps the bucket
|
|
112
|
+
* envelope). The cached key, when present, needs neither. */
|
|
113
|
+
oldPassphrase?: string;
|
|
114
|
+
recoveryCode?: string;
|
|
115
|
+
}
|
|
116
|
+
export interface RotateOutcome {
|
|
117
|
+
url: string;
|
|
118
|
+
rotatedCredentials: boolean;
|
|
119
|
+
rotatedPassphrase: boolean;
|
|
120
|
+
/** Whether K was cryptographically checked against bucket ciphertext:
|
|
121
|
+
* "skipped" = K came from this device's own trusted config. */
|
|
122
|
+
keyVerified: "verified" | "no_ciphertext" | "skipped";
|
|
123
|
+
/** Post-rotate sync (effect evidence the new credentials work end-to-end).
|
|
124
|
+
* Not rolled back on failure — the new creds are the intended end state. */
|
|
125
|
+
sync: PeerSyncOutcome;
|
|
126
|
+
/** Fresh enroll token (new credentials) for re-attaching other devices. */
|
|
127
|
+
enroll: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Rotate an S3 storage peer's credentials and/or passphrase — the "lost
|
|
131
|
+
* device" remedy. Ordered so every failure point either mutates nothing or is
|
|
132
|
+
* safely re-runnable (idempotent: same K, CAS'd envelope, upsert config):
|
|
133
|
+
* validate new creds (zero side effects) → resolve K (cached / recovery code /
|
|
134
|
+
* old passphrase) → verify K against ciphertext when it came from outside →
|
|
135
|
+
* CAS-rewrap the envelope → persist local config → one sync as effect
|
|
136
|
+
* evidence. A crash between rewrap and persist is healed by re-running the
|
|
137
|
+
* same command.
|
|
138
|
+
*/
|
|
139
|
+
export declare function rotateStoragePeer(db: DbDriver, url: string, input: RotateStoragePeerInput): Promise<RotateOutcome>;
|
|
72
140
|
/**
|
|
73
141
|
* Remove a peer AND revoke the credential we issued to it during pairing, so
|
|
74
142
|
* disconnecting is mutual: we stop syncing out to it (peers row) and it can no
|
|
@@ -86,13 +154,22 @@ export interface PeerSyncOutcome {
|
|
|
86
154
|
pulled?: number;
|
|
87
155
|
pendingPush?: boolean;
|
|
88
156
|
error?: string;
|
|
157
|
+
/** Non-fatal follow-up problems (e.g. channel maintenance) after a
|
|
158
|
+
* SUCCESSFUL data sync. Never flips `ok` and never sets a network error. */
|
|
159
|
+
warnings?: string[];
|
|
89
160
|
}
|
|
90
161
|
/** Sync once with a single peer, recording status. Errors are captured, not
|
|
91
162
|
* thrown. Dispatches on transport: 's3' peers go through the bucket
|
|
92
163
|
* store-and-forward client, everything else POSTs /sync. */
|
|
93
164
|
export declare function syncPeer(db: DbDriver, url: string, opts?: {
|
|
94
165
|
storage?: StorageSyncOpts;
|
|
166
|
+
timeoutMs?: number;
|
|
95
167
|
}): Promise<PeerSyncOutcome>;
|
|
168
|
+
/** Mixed-version workspace warning, from a peer's /health handshake. A peer
|
|
169
|
+
* without the site_channels capability treats v2 channel rows as malformed
|
|
170
|
+
* (validRow fail-closed), so publish/share changes silently do nothing there
|
|
171
|
+
* — that deserves a loud line, not a shrug. Pure for tests. */
|
|
172
|
+
export declare function versionWarning(local: string, health: HealthResponse | null): string | null;
|
|
96
173
|
/** Sync once with every enabled peer. Used by the auto-sync timer. */
|
|
97
174
|
export declare function syncAllPeers(db: DbDriver): Promise<PeerSyncOutcome[]>;
|
|
98
175
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../../src/core/sync/peers.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAI7C,OAAO,
|
|
1
|
+
{"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../../src/core/sync/peers.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,6 +40,8 @@ export declare const SyncResponseSchema: z.ZodObject<{
|
|
|
40
40
|
export declare const HealthResponseSchema: z.ZodObject<{
|
|
41
41
|
ok: z.ZodBoolean;
|
|
42
42
|
node: z.ZodString;
|
|
43
|
+
capabilities: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
44
|
+
version: z.ZodOptional<z.ZodString>;
|
|
43
45
|
}, z.core.$strip>;
|
|
44
46
|
export declare const PairRequestSchema: z.ZodObject<{
|
|
45
47
|
code: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/core/sync/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AAMvD,eAAO,MAAM,YAAY;;;;;;;;iBAUvB,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;iBAa5B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;iBAI7B,CAAC;AAEH,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/core/sync/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AAMvD,eAAO,MAAM,YAAY;;;;;;;;iBAUvB,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;iBAa5B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;iBAI7B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;iBAW/B,CAAC;AAKH,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;iBAG7B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,GAAG;IAAE,OAAO,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC;AACxF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,GAAG;IAAE,OAAO,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC;AAC1F,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,SAAS,UAAU,CAAC;AACjC,eAAO,MAAM,WAAW,YAAY,CAAC;AAGrC,eAAO,MAAM,UAAU,gBAAgB,CAAC;AAGxC,eAAO,MAAM,SAAS,cAAc,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class FixedWindowLimiter {
|
|
2
|
+
private readonly maxEntries;
|
|
3
|
+
private hits;
|
|
4
|
+
constructor(maxEntries?: number);
|
|
5
|
+
/**
|
|
6
|
+
* Record one hit for (bucket, key) and report whether it is within `limit`
|
|
7
|
+
* for the current window. `now` is injectable for tests.
|
|
8
|
+
*/
|
|
9
|
+
allow(bucket: string, key: string, limit: number, windowMs?: number, now?: number): boolean;
|
|
10
|
+
/** Drop expired entries; if still at capacity, drop oldest-inserted. */
|
|
11
|
+
private evict;
|
|
12
|
+
/** Live counter count (tests / diagnostics). */
|
|
13
|
+
get size(): number;
|
|
14
|
+
}
|
|
15
|
+
/** The process-wide limiter the serve paths share. */
|
|
16
|
+
export declare const rateLimiter: FixedWindowLimiter;
|
|
17
|
+
export declare const PUBLIC_READ_LIMIT = 120;
|
|
18
|
+
export declare const PUBLIC_WRITE_LIMIT = 20;
|
|
19
|
+
export declare const SHARE_LIMIT = 240;
|
|
20
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/core/sync/rate-limit.ts"],"names":[],"mappings":"AAWA,qBAAa,kBAAkB;IAGjB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAFvC,OAAO,CAAC,IAAI,CAAyD;gBAExC,UAAU,SAAS;IAEhD;;;OAGG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAS,EAAE,GAAG,SAAa,GAAG,OAAO;IAe/F,wEAAwE;IACxE,OAAO,CAAC,KAAK;IAWb,gDAAgD;IAChD,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED,sDAAsD;AACtD,eAAO,MAAM,WAAW,oBAA2B,CAAC;AAIpD,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,WAAW,MAAM,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const RECOVERY_PREFIX = "MH1";
|
|
2
|
+
/** Encode the raw 32-byte master key as a grouped, prefixed recovery code. */
|
|
3
|
+
export declare function encodeRecoveryCode(rawKey: Uint8Array): Promise<string>;
|
|
4
|
+
/** Decode a (possibly sloppily pasted) recovery code back to the raw master
|
|
5
|
+
* key. Tolerates case, spaces, dashes and the o→0 / i,l→1 confusions; throws
|
|
6
|
+
* MhError("invalid_input") on bad characters, wrong length, or a failed
|
|
7
|
+
* checksum (a typo the confusion-folding couldn't absorb). */
|
|
8
|
+
export declare function decodeRecoveryCode(code: string): Promise<Uint8Array>;
|
|
9
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../src/core/sync/recovery.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,eAAe,QAAQ,CAAC;AA8CrC,8EAA8E;AAC9E,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAU5E;AAED;;;+DAG+D;AAC/D,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAgB1E"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DbDriver } from "../driver.ts";
|
|
2
|
+
import { type PartitionScope } from "./partition.ts";
|
|
3
|
+
import { type OwnerSyncRequest, type OwnerSyncResponse, type ShareState } from "./room-protocol.ts";
|
|
4
|
+
/** The wire. C2 wraps HTTP + Bearer ownerSecret; tests call handleOwnerSync. */
|
|
5
|
+
export type RoomTransport = (req: OwnerSyncRequest) => Promise<OwnerSyncResponse>;
|
|
6
|
+
export interface RoomClientConfig {
|
|
7
|
+
/** peers.url of the kind='room' row (cursor storage), e.g. room://<slug>. */
|
|
8
|
+
peerKey: string;
|
|
9
|
+
/** The share's authorization closure — computed by the caller from the
|
|
10
|
+
* share row (grants + target site) each round, so re-grants apply next round. */
|
|
11
|
+
scope: PartitionScope;
|
|
12
|
+
/** The share's base guest node id — pulled ops must be authored under it,
|
|
13
|
+
* and owner pushes exclude it (no echo). */
|
|
14
|
+
guestBase: string;
|
|
15
|
+
}
|
|
16
|
+
export interface RoomSyncOpts {
|
|
17
|
+
/** Change budget per request (increment + baselines). Default 500. */
|
|
18
|
+
limit?: number;
|
|
19
|
+
/** Attach a digest this round (anti-entropy). The caller schedules these —
|
|
20
|
+
* every ROOM_DIGEST_INTERVAL rounds and after any grants change. */
|
|
21
|
+
digest?: boolean;
|
|
22
|
+
mhVersion?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface RoomSyncResult {
|
|
25
|
+
pushed: number;
|
|
26
|
+
pulled: number;
|
|
27
|
+
evicted: number;
|
|
28
|
+
/** More increments/baselines are queued than the limit allowed — sync again. */
|
|
29
|
+
pending: boolean;
|
|
30
|
+
/** A digest mismatch triggered a full winners + member-set reconcile. */
|
|
31
|
+
healed: boolean;
|
|
32
|
+
/** Site-file blob hashes the room lacks — the caller pushes their bytes
|
|
33
|
+
* through the chunked /owner/blob channel (room-peer.ts). */
|
|
34
|
+
needBlobs: string[];
|
|
35
|
+
shareState: ShareState;
|
|
36
|
+
}
|
|
37
|
+
/** Send a digest every this many rounds (plus after any grants change). */
|
|
38
|
+
export declare const ROOM_DIGEST_INTERVAL = 16;
|
|
39
|
+
/** Ensure the peers row exists (kind='room', cursors at 0). The full config
|
|
40
|
+
* (base/slug/ownerSecret) is written by share-create --room (C2); tests and
|
|
41
|
+
* the client itself only need the cursor columns. */
|
|
42
|
+
export declare function ensureRoomPeer(db: DbDriver, peerKey: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* One owner↔room sync round. Throws on transport/protocol errors (the caller —
|
|
45
|
+
* peers-status bookkeeping in C2, tests here — records and retries; nothing
|
|
46
|
+
* was committed, so a retry is a byte-identical resend). Returns pending:true
|
|
47
|
+
* when the limit truncated the payload — loop until quiescent.
|
|
48
|
+
*/
|
|
49
|
+
export declare function syncWithRoom(db: DbDriver, cfg: RoomClientConfig, transport: RoomTransport, opts?: RoomSyncOpts): Promise<RoomSyncResult>;
|
|
50
|
+
//# sourceMappingURL=room-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room-client.d.ts","sourceRoot":"","sources":["../../../src/core/sync/room-client.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAI7C,OAAO,EAWL,KAAK,cAAc,EAEpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAE5B,gFAAgF;AAChF,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAElF,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB;sFACkF;IAClF,KAAK,EAAE,cAAc,CAAC;IACtB;iDAC6C;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;yEACqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,MAAM,EAAE,OAAO,CAAC;IAChB;kEAC8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;sDAEsD;AACtD,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAIlE;AAiFD;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,aAAa,EACxB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,cAAc,CAAC,CAwEzB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { DbDriver } from "../driver.ts";
|
|
2
|
+
import { type ShareRow } from "../shares.ts";
|
|
3
|
+
import { type PeerRow, type RoomPeerConfig } from "./peers.ts";
|
|
4
|
+
import { type RoomTransport } from "./room-client.ts";
|
|
5
|
+
export { roomUrlOf } from "./room-url.ts";
|
|
6
|
+
export type RoomBlobResolver = (db: DbDriver, hash: string) => Promise<Uint8Array | null>;
|
|
7
|
+
/** Runtime adapter hook: Bun registers its blob resolver at server/CLI startup;
|
|
8
|
+
* browser replicas register spool→bucket resolution in db-worker. */
|
|
9
|
+
export declare function registerRoomBlobResolver(resolve: RoomBlobResolver): void;
|
|
10
|
+
/** Synthetic peers.url key of a share's room. */
|
|
11
|
+
export declare function roomPeerKey(slug: string): string;
|
|
12
|
+
/** The wire for room-client: POST /owner/sync with the owner secret. Statuses
|
|
13
|
+
* map onto MhError codes; a protocol-major mismatch is the room's 409. */
|
|
14
|
+
export declare function roomTransport(config: RoomPeerConfig): RoomTransport;
|
|
15
|
+
/**
|
|
16
|
+
* One owner→room sync pass for a kind='room' peer: derive the partition scope
|
|
17
|
+
* from the CURRENT share row (re-grants apply immediately), loop syncWithRoom
|
|
18
|
+
* until quiescent, then push any blob bytes the room asked for. Called from
|
|
19
|
+
* peers.ts syncPeerOnce (which records last_status) and from the first seed.
|
|
20
|
+
*
|
|
21
|
+
* Lifecycle self-healing: a room peer whose share row is gone (revoke should
|
|
22
|
+
* have torn it down — e.g. a crash in between) destroys the room best-effort
|
|
23
|
+
* and removes itself; an "expired" answer tears the peer down likewise.
|
|
24
|
+
*/
|
|
25
|
+
export declare function syncRoomPeer(db: DbDriver, peer: PeerRow, opts?: {
|
|
26
|
+
resolveBlob?: RoomBlobResolver;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
pushed: number;
|
|
29
|
+
pulled: number;
|
|
30
|
+
pendingPush: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Push blob bytes the room asked for (site_files rows with encoding='blob'
|
|
34
|
+
* store only the hash — the bytes never ride the oplog). Chunked ≤1MiB per
|
|
35
|
+
* POST; a hash whose bytes this node can't resolve right now is skipped (the
|
|
36
|
+
* room re-asks every round until someone answers). `resolve` is injectable
|
|
37
|
+
* for tests.
|
|
38
|
+
*/
|
|
39
|
+
export declare function pushRoomBlobs(db: DbDriver, config: RoomPeerConfig, hashes: string[], resolve?: (db: DbDriver, hash: string) => Promise<Uint8Array | null>): Promise<number>;
|
|
40
|
+
/**
|
|
41
|
+
* Provision a room for a freshly created share and run the first seed:
|
|
42
|
+
* POST /owner/provision (grants snapshot + password verifier + guest base +
|
|
43
|
+
* expiry) → register the kind='room' peer → sync rounds until quiescent.
|
|
44
|
+
* Returns the guest-facing room URL.
|
|
45
|
+
*/
|
|
46
|
+
export declare function provisionRoomForShare(db: DbDriver, share: ShareRow, edge: {
|
|
47
|
+
endpoint: string;
|
|
48
|
+
token: string;
|
|
49
|
+
}, resolveBlob?: RoomBlobResolver): Promise<{
|
|
50
|
+
url: string;
|
|
51
|
+
peerKey: string;
|
|
52
|
+
}>;
|
|
53
|
+
/** POST /owner/destroy — the room wipes its storage and drops every socket. */
|
|
54
|
+
export declare function destroyRoom(config: RoomPeerConfig): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Tear down the room bound to a share, if any: best-effort destroy (an
|
|
57
|
+
* unreachable edge must never block a revoke) + remove the peer row. Returns
|
|
58
|
+
* whether a room peer existed. Hooked into revokeShareAction so `mh share
|
|
59
|
+
* revoke` / the WebUI revoke both cascade (final decision 3).
|
|
60
|
+
*/
|
|
61
|
+
export declare function teardownRoomForShare(db: DbDriver, slug: string): Promise<"absent" | "destroyed" | "cleanup_pending">;
|
|
62
|
+
//# sourceMappingURL=room-peer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room-peer.d.ts","sourceRoot":"","sources":["../../../src/core/sync/room-peer.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAIL,KAAK,OAAO,EACZ,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;AAG1F;qEACqE;AACrE,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAExE;AAED,iDAAiD;AACjD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAkED;2EAC2E;AAC3E,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,aAAa,CAwBnE;AASD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,OAAO,EACb,IAAI,GAAE;IACJ,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAC3B,GACL,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CA8HnE;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,GACnE,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,QAAQ,EACf,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACzC,WAAW,CAAC,EAAE,gBAAgB,GAC7B,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAgD3C;AAED,+EAA+E;AAC/E,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,QAAQ,GAAG,WAAW,GAAG,iBAAiB,CAAC,CAYrD"}
|