@tensorix/metahub 0.1.11 → 0.2.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.
Files changed (67) hide show
  1. package/dist/cli.js +23711 -17899
  2. package/dist/cli.js.map +84 -59
  3. package/dist/core/blobs-core.d.ts +106 -0
  4. package/dist/core/blobs-core.d.ts.map +1 -0
  5. package/dist/core/blobs.d.ts +110 -0
  6. package/dist/core/blobs.d.ts.map +1 -0
  7. package/dist/core/cache.d.ts +23 -1
  8. package/dist/core/cache.d.ts.map +1 -1
  9. package/dist/core/compact.d.ts +4 -3
  10. package/dist/core/compact.d.ts.map +1 -1
  11. package/dist/core/config.d.ts +7 -0
  12. package/dist/core/config.d.ts.map +1 -1
  13. package/dist/core/crdt.d.ts +32 -2
  14. package/dist/core/crdt.d.ts.map +1 -1
  15. package/dist/core/schema-init.d.ts +62 -2
  16. package/dist/core/schema-init.d.ts.map +1 -1
  17. package/dist/core/schema.d.ts +1 -1
  18. package/dist/core/schema.d.ts.map +1 -1
  19. package/dist/core/shares.d.ts +60 -0
  20. package/dist/core/shares.d.ts.map +1 -0
  21. package/dist/core/sites-core.d.ts +35 -0
  22. package/dist/core/sites-core.d.ts.map +1 -1
  23. package/dist/core/sites.d.ts +4 -13
  24. package/dist/core/sites.d.ts.map +1 -1
  25. package/dist/core/sync/auth.d.ts +15 -2
  26. package/dist/core/sync/auth.d.ts.map +1 -1
  27. package/dist/core/sync/blob-routes.d.ts +12 -0
  28. package/dist/core/sync/blob-routes.d.ts.map +1 -0
  29. package/dist/core/sync/client.d.ts +7 -0
  30. package/dist/core/sync/client.d.ts.map +1 -1
  31. package/dist/core/sync/e2ee.d.ts +31 -0
  32. package/dist/core/sync/e2ee.d.ts.map +1 -0
  33. package/dist/core/sync/enroll.d.ts +21 -0
  34. package/dist/core/sync/enroll.d.ts.map +1 -0
  35. package/dist/core/sync/peers-routes.d.ts.map +1 -1
  36. package/dist/core/sync/peers.d.ts +74 -2
  37. package/dist/core/sync/peers.d.ts.map +1 -1
  38. package/dist/core/sync/publisher-lease.d.ts +8 -0
  39. package/dist/core/sync/publisher-lease.d.ts.map +1 -0
  40. package/dist/core/sync/routes.d.ts.map +1 -1
  41. package/dist/core/sync/server.d.ts +1 -0
  42. package/dist/core/sync/server.d.ts.map +1 -1
  43. package/dist/core/sync/share-actions.d.ts +69 -0
  44. package/dist/core/sync/share-actions.d.ts.map +1 -0
  45. package/dist/core/sync/share-export.d.ts +72 -0
  46. package/dist/core/sync/share-export.d.ts.map +1 -0
  47. package/dist/core/sync/share-render.d.ts +10 -0
  48. package/dist/core/sync/share-render.d.ts.map +1 -0
  49. package/dist/core/sync/share-serve.d.ts +3 -0
  50. package/dist/core/sync/share-serve.d.ts.map +1 -0
  51. package/dist/core/sync/sites-serve.d.ts.map +1 -1
  52. package/dist/core/sync/storage-s3-bun.d.ts +35 -0
  53. package/dist/core/sync/storage-s3-bun.d.ts.map +1 -0
  54. package/dist/core/sync/storage-url.d.ts +18 -0
  55. package/dist/core/sync/storage-url.d.ts.map +1 -0
  56. package/dist/core/sync/storage.d.ts +170 -0
  57. package/dist/core/sync/storage.d.ts.map +1 -0
  58. package/dist/db-worker.js +212 -66
  59. package/dist/index.js +13546 -8960
  60. package/dist/index.js.map +46 -27
  61. package/dist/sw.js +1 -1
  62. package/dist/webui/server/assets.d.ts.map +1 -1
  63. package/dist/webui/server/routes.d.ts.map +1 -1
  64. package/dist/webui/server/share-routes.d.ts +3 -0
  65. package/dist/webui/server/share-routes.d.ts.map +1 -0
  66. package/dist/webui.js +93 -61
  67. package/package.json +5 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-s3-bun.d.ts","sourceRoot":"","sources":["../../../src/core/sync/storage-s3-bun.ts"],"names":[],"mappings":"AAUA,OAAO,EAML,KAAK,QAAQ,EACd,MAAM,cAAc,CAAC;AAiHtB,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAE1C;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAanG;AAED,+EAA+E;AAC/E,wBAAsB,aAAa,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAS5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAoB/F;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,IAAI,CAAC,CAmBf"}
@@ -0,0 +1,18 @@
1
+ export declare const DEFAULT_STORAGE_PREFIX = "metahub";
2
+ /**
3
+ * Stable host[:port] component of an S3 endpoint for the peer key. The default
4
+ * port is stripped so `https://h` and `https://h:443` collapse to one key (that
5
+ * spurious split is exactly the collision class the endpoint-qualified key is
6
+ * meant to remove). Falls back to a parsed-by-hand host when the endpoint isn't
7
+ * a valid URL, kept deterministic.
8
+ */
9
+ export declare function storageEndpointHost(endpoint: string): string;
10
+ /**
11
+ * Synthetic peer key for a storage peer: one per endpoint+bucket+prefix. The
12
+ * endpoint host is part of the identity because bucket names are only unique
13
+ * within a single S3-compatible endpoint (R2/MinIO/COS each have their own
14
+ * namespace) — without it, the same bucket+prefix on a second endpoint would
15
+ * collide with (and silently overwrite, keeping the stale cursor) the first.
16
+ */
17
+ export declare function storageUrl(endpoint: string, bucket: string, prefix?: string): string;
18
+ //# sourceMappingURL=storage-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-url.d.ts","sourceRoot":"","sources":["../../../src/core/sync/storage-url.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAIhD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY5D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAGpF"}
@@ -0,0 +1,170 @@
1
+ import type { DbDriver } from "../driver.ts";
2
+ import type { SyncResult } from "./client.ts";
3
+ /** An 's3' peer's bucket settings, stored as JSON in peers.config (local-only). */
4
+ export interface S3Config {
5
+ endpoint: string;
6
+ region: string;
7
+ bucket: string;
8
+ prefix: string;
9
+ accessKeyId: string;
10
+ secretAccessKey: string;
11
+ /** When false (`--no-encrypt`) segments are gzip-only plaintext. */
12
+ encrypt: boolean;
13
+ /** base64 raw 32-byte master key; present iff encrypt. Resolved at peer-add. */
14
+ masterKey?: string;
15
+ /**
16
+ * Address objects as `<bucket>.<host>/<key>` (virtual-hosted) instead of
17
+ * `<host>/<bucket>/<key>` (path-style). Some providers (Tencent COS) reject
18
+ * path-style outright. When undefined, the clients auto-detect: virtual-hosted
19
+ * iff the endpoint host already starts with `<bucket>.` (see
20
+ * isVirtualHostedStyle). R2/MinIO keep using path-style.
21
+ */
22
+ virtualHostedStyle?: boolean;
23
+ /**
24
+ * Node-role hints for this bucket (not bucket settings — safe to omit from
25
+ * shared enroll links). `publish` marks this node as the bucket's publisher
26
+ * (writes whole-hub snapshots; see StorageSyncOpts.publish): true for the data
27
+ * home (a server, or a no-origin PWA which IS the home), false for a replica
28
+ * that only attaches the bucket for its own away-sync. `priority` ranks
29
+ * publisher candidates when several are eligible (higher wins; see
30
+ * publisher-lease.ts) — server > desktop > laptop > phone.
31
+ */
32
+ publish?: boolean;
33
+ priority?: number;
34
+ }
35
+ /** Whether to use virtual-hosted addressing for this bucket: the explicit flag,
36
+ * or — when unset — auto-detected from an endpoint whose host already carries
37
+ * the bucket (e.g. COS's `<bucket>.cos.<region>.myqcloud.com`). */
38
+ export declare function isVirtualHostedStyle(config: S3Config): boolean;
39
+ export interface StorageObject {
40
+ key: string;
41
+ etag?: string;
42
+ }
43
+ /** Options for a conditional/typed put. */
44
+ export interface StoragePutOpts {
45
+ contentType?: string;
46
+ /** Conditional create (S3 `If-None-Match: *`): fail with an MhError("conflict")
47
+ * if the object already exists. Used to make first-time key provisioning a
48
+ * compare-and-set so two devices initializing the same empty bucket can't
49
+ * clobber each other's master key. */
50
+ ifNoneMatch?: boolean;
51
+ }
52
+ /** The minimal object-store surface storage-sync needs. Implemented per runtime:
53
+ * Bun.S3Client on the CLI/desktop (storage-s3-bun.ts), aws4fetch SigV4 + fetch
54
+ * in the browser worker (webui/data/storage-s3-browser.ts). */
55
+ export interface StorageClient {
56
+ /** Keys under `prefix`, ascending, optionally only those strictly after
57
+ * `startAfter`. With `delimiter` ("/"), collapse one level into common
58
+ * prefixes (their keys end in the delimiter) to discover child "folders"
59
+ * cheaply — callers must tolerate a backend that ignores it and returns full
60
+ * keys instead. */
61
+ list(prefix: string, startAfter?: string, delimiter?: string): Promise<StorageObject[]>;
62
+ get(key: string): Promise<Uint8Array | null>;
63
+ put(key: string, body: Uint8Array, opts?: StoragePutOpts): Promise<void>;
64
+ del(key: string): Promise<void>;
65
+ }
66
+ type StorageClientFactory = (config: S3Config) => StorageClient;
67
+ /** Register this runtime's S3 client builder (see the per-runtime impls). */
68
+ export declare function setStorageClientFactory(f: StorageClientFactory): void;
69
+ export declare function storageClientFor(config: S3Config): StorageClient;
70
+ /** Upload a blob's bytes to the bucket (idempotent via If-None-Match). Returns
71
+ * true when actually written, false when the object already existed. */
72
+ export declare function putBucketBlob(config: S3Config, hash: string, bytes: Uint8Array): Promise<boolean>;
73
+ /** Every blob hash the bucket durably holds — one paginated LIST of the `blobs/`
74
+ * namespace (the client loops continuation tokens internally). Used by the
75
+ * on-demand clear-presence verify; keeps the `<base>/blobs/<hash>` key layout
76
+ * encapsulated here. Throws if the bucket is unreachable (caller treats the
77
+ * anchor as unverified). */
78
+ export declare function listBucketBlobHashes(config: S3Config): Promise<Set<string>>;
79
+ /** Fetch a blob's bytes from the bucket, or null if absent. */
80
+ export declare function getBucketBlob(config: S3Config, hash: string): Promise<Uint8Array | null>;
81
+ /**
82
+ * Resolve the master key for a bucket at setup: read keys/main.json and unwrap
83
+ * with the passphrase, or — when absent — generate a key, wrap it, and upload
84
+ * it with `If-None-Match` so a concurrent first-time init can't clobber it.
85
+ * Returns base64(K) to store in peers.config, or null when encryption is off.
86
+ * Wrong passphrase throws `auth` (from unwrapMasterKey).
87
+ */
88
+ export declare function provisionMasterKey(client: StorageClient, config: S3Config, passphrase: string): Promise<string | null>;
89
+ /**
90
+ * Publish a winners-only snapshot and truncate this node's own segments. Every
91
+ * op we've uploaded so far is in the local oplog now, so the snapshot (winners
92
+ * as-of the current max HLC) supersedes all of them — they're safe to delete
93
+ * (compact.ts invariant #1). Ops produced afterwards go into fresh, higher-keyed
94
+ * segments; consumers reach our pre-snapshot state via the snapshot itself.
95
+ *
96
+ * The key is `<maxHlc>~<hash>.snap`: the hash (over the canonical winner set)
97
+ * makes converged nodes collide on one key (deduped) while two nodes that are
98
+ * NOT converged — same max HLC but different extra winners — get distinct keys,
99
+ * so neither overwrites the other. Then old snapshots (strictly lower max-HLC)
100
+ * are GC'd so the bucket stays bounded.
101
+ *
102
+ * Returns the snapshot key + change count, or null when the oplog is empty.
103
+ */
104
+ export declare function publishSnapshot(db: DbDriver, client: StorageClient, config: S3Config, retainSegments?: number): Promise<{
105
+ key: string;
106
+ changes: number;
107
+ } | null>;
108
+ export interface StorageSyncOpts {
109
+ /** Publisher-only: publish a snapshot + truncate once this node's own segment
110
+ * count reaches this. Default 200 — high enough that typical use rarely hits
111
+ * it, low enough to keep the bucket bounded under heavy publisher use. */
112
+ snapshotEverySegments?: number;
113
+ /** Push-batching: defer uploading our pending own ops until there are at least
114
+ * this many (default 1 → push every round). Coalesces a burst of edits into
115
+ * one segment instead of many tiny ones (each is a billed request + a GET for
116
+ * every puller). */
117
+ minPushChanges?: number;
118
+ /** …or until the oldest pending op is at least this old (ms; default 0 → no
119
+ * age bound). Bounds how long a small trailing edit waits. */
120
+ maxPushAgeMs?: number;
121
+ /** Push regardless of the batching thresholds — use on explicit "sync now" and
122
+ * when the app is backgrounding, so edits are never stranded unsynced. */
123
+ forcePush?: boolean;
124
+ /**
125
+ * This node is the bucket's *publisher*: keep the bucket a complete, fresh
126
+ * whole-hub mirror by writing a winners-only snapshot (publishSnapshot) when
127
+ * the bucket has none yet, or when the hub has advanced past the newest
128
+ * snapshot. The snapshot spans the WHOLE db, so it captures data authored by
129
+ * nodes that never push to this bucket themselves (e.g. a server holding the
130
+ * canonical hub while browsers only window onto it) — fixing the "attached a
131
+ * bucket but it stays empty" footgun. Any node holding the full hub can be the
132
+ * publisher; who is one is decided above storage.ts (server by default, else a
133
+ * lease — see publisher-lease.ts). Correctness never depends on a single
134
+ * publisher: concurrent snapshots are content-addressed and idempotent. */
135
+ publish?: boolean;
136
+ /** ⑤d snapshot trigger: publish once the whole-hub delta since the newest
137
+ * snapshot reaches max(`snapshotMinDelta` ops, hub_size × `snapshotDeltaRatio`),
138
+ * or `snapshotMaxIntervalMs` elapsed (safety). Replaces the old 60s timer that
139
+ * re-checkpointed constantly and forced full re-downloads. Defaults:
140
+ * DEFAULT_SNAPSHOT_MIN_DELTA / _DELTA_RATIO / _MAX_INTERVAL_MS. */
141
+ snapshotMinDelta?: number;
142
+ snapshotDeltaRatio?: number;
143
+ snapshotMaxIntervalMs?: number;
144
+ /** ⑤c: keep this many of our most recent own segments alive past a snapshot so
145
+ * slightly-behind consumers catch up incrementally (default DEFAULT_RETAIN_SEGMENTS). */
146
+ snapshotRetainSegments?: number;
147
+ /** This node's publisher priority when contending for the lease (higher wins;
148
+ * see publisher-lease.ts). Only consulted when `publish` is set. */
149
+ priority?: number;
150
+ /**
151
+ * Run the PULL half of the round (snapshots + other nodes' segments). Default
152
+ * true. Set false for *push-only* rounds: an origin-backed replica gets a
153
+ * superset of bucket contents from its origin (HTTP), so it never needs to PULL
154
+ * the bucket — it only PUSHes its own segments here for independent durability
155
+ * (Path B). Skipping PULL drops the two unconditional LISTs (snapshot/, oplog/)
156
+ * that dominate idle bucket cost. A *publisher* must always PULL (it needs the
157
+ * snapshot list to decide what to publish), so callers never combine
158
+ * `publish:true` with `pull:false`.
159
+ */
160
+ pull?: boolean;
161
+ }
162
+ /**
163
+ * One push/pull round against a storage peer. Push uploads this node's pending
164
+ * own ops as a new segment (subject to batching); pull hydrates from snapshots
165
+ * (any not yet ingested) then drains each other node's new segments. Returns
166
+ * { pushed, pulled } like syncWithPeer so peers.ts status writeback is shared.
167
+ */
168
+ export declare function syncWithStorage(db: DbDriver, peerUrl: string, client: StorageClient, config: S3Config, opts?: StorageSyncOpts): Promise<SyncResult>;
169
+ export {};
170
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/core/sync/storage.ts"],"names":[],"mappings":"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"}