@muhkoo/connect 0.10.14-alpha.0 → 0.12.0-alpha.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/dist/browser/index.js +2141 -78
- package/dist/browser/index.js.map +1 -1
- package/dist/connect.d.ts +822 -8
- package/dist/server/index.js +2141 -78
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/connect.d.ts
CHANGED
|
@@ -1897,7 +1897,7 @@ interface AuthSuccess {
|
|
|
1897
1897
|
/** A vault factor record (M1.0 + M2 gated types). Wrap/iv are base64 AES-GCM; absent for phrase. */
|
|
1898
1898
|
interface VaultFactor {
|
|
1899
1899
|
id: string;
|
|
1900
|
-
type: "password" | "passkey" | "phrase-marker" | "email" | "google";
|
|
1900
|
+
type: "password" | "passkey" | "phrase-marker" | "email" | "google" | "device";
|
|
1901
1901
|
wrap?: string;
|
|
1902
1902
|
iv?: string;
|
|
1903
1903
|
params?: Record<string, unknown>;
|
|
@@ -2009,7 +2009,9 @@ declare class AuthClient {
|
|
|
2009
2009
|
/** Passkeys are per-origin: naming the caller's host returns THAT origin's
|
|
2010
2010
|
* passkey instead of whichever was enrolled first (which WebAuthn would
|
|
2011
2011
|
* refuse). Ignored for other factor types. */
|
|
2012
|
-
rpId?: string
|
|
2012
|
+
rpId?: string,
|
|
2013
|
+
/** Device factors are selected by id — a user may have many machines. */
|
|
2014
|
+
factorId?: string): Promise<{
|
|
2013
2015
|
factor: VaultFactor | null;
|
|
2014
2016
|
}>;
|
|
2015
2017
|
/**
|
|
@@ -2966,10 +2968,6 @@ interface ZkAuthDeps {
|
|
|
2966
2968
|
circuits: CircuitUrls;
|
|
2967
2969
|
session: SessionState;
|
|
2968
2970
|
}
|
|
2969
|
-
/**
|
|
2970
|
-
* ZK identity auth. One instance per {@link Client}; reads/writes the shared
|
|
2971
|
-
* {@link SessionState}.
|
|
2972
|
-
*/
|
|
2973
2971
|
declare class ZkAuth {
|
|
2974
2972
|
private readonly deps;
|
|
2975
2973
|
constructor(deps: ZkAuthDeps);
|
|
@@ -3184,6 +3182,63 @@ declare class ZkAuth {
|
|
|
3184
3182
|
* passwordless (passkey) login, both of which recover the same seed.
|
|
3185
3183
|
*/
|
|
3186
3184
|
get seedBase64(): string | null;
|
|
3185
|
+
/**
|
|
3186
|
+
* Pair this machine, so it can unlock the account later without a password.
|
|
3187
|
+
*
|
|
3188
|
+
* Generates a random 32-byte key, wraps the master seed under it, and stores
|
|
3189
|
+
* only the CIPHERTEXT in the vault as a `device` factor. The key is returned
|
|
3190
|
+
* for the caller to persist locally and is never sent anywhere.
|
|
3191
|
+
*
|
|
3192
|
+
* The split is the point: the seed itself never lands on disk, and the
|
|
3193
|
+
* server holds a blob it cannot open. Revoking the factor
|
|
3194
|
+
* ({@link revokeDevice}) makes the local key inert immediately — which a
|
|
3195
|
+
* stored master seed can never be, because nothing can reach out and
|
|
3196
|
+
* un-store it.
|
|
3197
|
+
*
|
|
3198
|
+
* Returns the factor id and the key, both base64. Losing the key just means
|
|
3199
|
+
* re-pairing; leaking it is equivalent to leaking the seed for as long as
|
|
3200
|
+
* the factor exists, so store it with the tightest permissions available.
|
|
3201
|
+
*/
|
|
3202
|
+
enrollDevice(label: string): Promise<{
|
|
3203
|
+
factorId: string;
|
|
3204
|
+
deviceKey: string;
|
|
3205
|
+
}>;
|
|
3206
|
+
/**
|
|
3207
|
+
* Unlock using a key stored by {@link enrollDevice}.
|
|
3208
|
+
*
|
|
3209
|
+
* Fetches the wrapped seed and opens it locally. Establishes the encryption
|
|
3210
|
+
* identity only — pair it with a session for calls that need one.
|
|
3211
|
+
*
|
|
3212
|
+
* Throws distinguishably: a REVOKED factor is gone from the vault and says
|
|
3213
|
+
* so, where a wrong key fails to decrypt. A machine that has had its access
|
|
3214
|
+
* withdrawn should be able to tell you that, not just "decryption failed".
|
|
3215
|
+
*/
|
|
3216
|
+
unlockWithDevice(username: string, factorId: string, deviceKey: string): Promise<void>;
|
|
3217
|
+
/** Paired machines, newest first. Metadata only — never the ciphertext. */
|
|
3218
|
+
listDevices(): Promise<Array<{
|
|
3219
|
+
id: string;
|
|
3220
|
+
label?: string;
|
|
3221
|
+
createdAt?: number;
|
|
3222
|
+
}>>;
|
|
3223
|
+
/** Withdraw a machine's access. Its stored key stops working immediately. */
|
|
3224
|
+
revokeDevice(factorId: string): Promise<boolean>;
|
|
3225
|
+
/**
|
|
3226
|
+
* Restore an unlocked identity from a seed you already hold.
|
|
3227
|
+
*
|
|
3228
|
+
* The inverse of {@link seedBase64}, for hosts that persist the seed
|
|
3229
|
+
* themselves rather than re-deriving it from a password: a CLI holding it in
|
|
3230
|
+
* a 0600 file, a server-side tool, a test. Without this the seed is a
|
|
3231
|
+
* one-way export and anything outside a browser has to run a full ZK login —
|
|
3232
|
+
* circuits download and proof — on every single invocation.
|
|
3233
|
+
*
|
|
3234
|
+
* Establishes the ENCRYPTION identity only. It does not authenticate: pair
|
|
3235
|
+
* it with a session token (`sessionStore`) for calls that need one.
|
|
3236
|
+
*
|
|
3237
|
+
* Treat a persisted seed as the master credential it is. It is not scoped
|
|
3238
|
+
* and cannot be revoked server-side the way a session token can — rotating
|
|
3239
|
+
* it means moving the identity.
|
|
3240
|
+
*/
|
|
3241
|
+
unlockWithSeed(seedBase64: string): void;
|
|
3187
3242
|
/** Poseidon commitment for an identity — the value the server stores. */
|
|
3188
3243
|
private commitmentFor;
|
|
3189
3244
|
/**
|
|
@@ -3766,6 +3821,8 @@ interface StorageChangeEvent<T = unknown> {
|
|
|
3766
3821
|
}
|
|
3767
3822
|
declare class KvNamespace {
|
|
3768
3823
|
private readonly deps;
|
|
3824
|
+
/** One personal-space socket, shared by every subscriber. */
|
|
3825
|
+
private feed;
|
|
3769
3826
|
private cipher;
|
|
3770
3827
|
private cipherIdentity;
|
|
3771
3828
|
constructor(deps: KvNamespaceDeps);
|
|
@@ -3789,6 +3846,20 @@ declare class KvNamespace {
|
|
|
3789
3846
|
* Requires a `WebSocket` global (browsers; Node 22+ or a `ws` shim).
|
|
3790
3847
|
*/
|
|
3791
3848
|
on<T = unknown>(event: "change", handler: (e: StorageChangeEvent<T>) => void): () => void;
|
|
3849
|
+
/**
|
|
3850
|
+
* Subscribe to the personal space's raw change feed.
|
|
3851
|
+
*
|
|
3852
|
+
* Every key this user owns crosses this socket, not only `client.kv`
|
|
3853
|
+
* collections — the VFS keeps its directory records here too. Consumers get
|
|
3854
|
+
* the frame untouched: no decryption, no offline-cache merge, because the
|
|
3855
|
+
* value's meaning depends on which subsystem wrote it.
|
|
3856
|
+
*
|
|
3857
|
+
* The transport is SHARED and reference-counted. `WSTransport` namespaces
|
|
3858
|
+
* its events by `id`, so two transports with the same id would cross wires
|
|
3859
|
+
* on the shared event bus — and a second socket to the same Durable Object
|
|
3860
|
+
* would be pure waste besides.
|
|
3861
|
+
*/
|
|
3862
|
+
onRaw(handler: (frame: unknown) => void): () => void;
|
|
3792
3863
|
private dispatchChange;
|
|
3793
3864
|
/** Decode a stored value: decrypt envelopes, pass plaintext through. */
|
|
3794
3865
|
private decode;
|
|
@@ -4915,9 +4986,20 @@ declare class SpaceNamespace {
|
|
|
4915
4986
|
* Create a new space: generate its keypair (public key = id), register
|
|
4916
4987
|
* metadata, mint the initial group key, and connect. Returns the open Space.
|
|
4917
4988
|
*/
|
|
4989
|
+
/**
|
|
4990
|
+
* Create a space.
|
|
4991
|
+
*
|
|
4992
|
+
* `messaging: false` registers the space and stops there — no group-key
|
|
4993
|
+
* bootstrap and no WebSocket. Use it for a space that only ever holds
|
|
4994
|
+
* FILES: each chunk carries its own key inside its manifest, so the group
|
|
4995
|
+
* keyring buys nothing, and opening a socket makes the write path depend on
|
|
4996
|
+
* a live connection. That dependency is wrong for a CLI, and it is what made
|
|
4997
|
+
* the first VFS write hang forever instead of failing.
|
|
4998
|
+
*/
|
|
4918
4999
|
createSpace(opts?: {
|
|
4919
5000
|
historyPolicy?: HistoryPolicy;
|
|
4920
5001
|
private?: boolean;
|
|
5002
|
+
messaging?: boolean;
|
|
4921
5003
|
}): Promise<Space>;
|
|
4922
5004
|
/** Join an existing space by id (its public key). Resolves once a key arrives. */
|
|
4923
5005
|
joinSpace(spaceId: string, opts?: {
|
|
@@ -5379,6 +5461,693 @@ declare class AccessTokensNamespace {
|
|
|
5379
5461
|
private path;
|
|
5380
5462
|
}
|
|
5381
5463
|
|
|
5464
|
+
/**
|
|
5465
|
+
* Wire types for the VFS metadata layer.
|
|
5466
|
+
*
|
|
5467
|
+
* ## What is stored where
|
|
5468
|
+
*
|
|
5469
|
+
* The filesystem is a graph of **directory records**, one per directory, each
|
|
5470
|
+
* held under a personal-space KV key and encrypted client-side. A record lists
|
|
5471
|
+
* only its immediate children — so listing a directory is one fetch, and you
|
|
5472
|
+
* only ever pay for the subtrees you actually open.
|
|
5473
|
+
*
|
|
5474
|
+
* File CONTENT is not here. Bytes are written with `client.storage.writeFile`
|
|
5475
|
+
* exactly as any other file — into a real Space, with a real gated manifest —
|
|
5476
|
+
* and the entry keeps the returned `FileManifest` as its handle.
|
|
5477
|
+
* `client.storage.readByManifest(manifest)` then reconstructs the bytes from
|
|
5478
|
+
* the content-addressed shard store with no space membership or directory
|
|
5479
|
+
* lookup needed, which is what makes a file shareable by handing over the
|
|
5480
|
+
* handle alone.
|
|
5481
|
+
*
|
|
5482
|
+
* This supersedes the flat personal mirror `writeFile` already maintains
|
|
5483
|
+
* (`{spaceId, manifest}` keyed by manifest id): the same metadata, in the same
|
|
5484
|
+
* personal space, arranged as a tree you can actually navigate.
|
|
5485
|
+
*
|
|
5486
|
+
* ## Why directories have stable random ids, not content hashes
|
|
5487
|
+
*
|
|
5488
|
+
* A git-style tree keys each directory by the hash of its contents, so writing
|
|
5489
|
+
* one file rewrites every ancestor up to the root. Here a directory's id is
|
|
5490
|
+
* random and permanent, so a file write touches exactly ONE record — its
|
|
5491
|
+
* parent — and renaming or moving a directory edits only the parent entry
|
|
5492
|
+
* while the whole subtree underneath stays byte-identical and untouched.
|
|
5493
|
+
*
|
|
5494
|
+
* ## Why a directory's key lives in its parent
|
|
5495
|
+
*
|
|
5496
|
+
* Only the root key is derived from the master seed. Every other directory has
|
|
5497
|
+
* a random key stored in its parent's entry, which chains capabilities
|
|
5498
|
+
* downward: handing someone `{id, key}` for `/apps/my-app` lets them walk that
|
|
5499
|
+
* subtree and nothing else, because no key can be derived upward. That is the
|
|
5500
|
+
* seam group-sharing hangs off later without re-encrypting the filesystem.
|
|
5501
|
+
*/
|
|
5502
|
+
|
|
5503
|
+
/** Personal-space KV key holding a directory record. */
|
|
5504
|
+
declare const dirKey: (id: string) => string;
|
|
5505
|
+
/** Personal-space KV key holding one file's prior versions. */
|
|
5506
|
+
declare const historyKey: (id: string) => string;
|
|
5507
|
+
/** The root directory's id is fixed — it is the one node reached without a parent. */
|
|
5508
|
+
declare const ROOT_ID = "root";
|
|
5509
|
+
/** HKDF label for the seed-derived root key. Sits alongside `muhkoo-storage-v1`. */
|
|
5510
|
+
declare const VFS_ROOT_INFO = "muhkoo-vfs-v1";
|
|
5511
|
+
interface FileEntry {
|
|
5512
|
+
kind: "file";
|
|
5513
|
+
/**
|
|
5514
|
+
* Stable identity, independent of name and location. Renaming a file keeps
|
|
5515
|
+
* its id, so its version history follows it rather than being orphaned.
|
|
5516
|
+
*/
|
|
5517
|
+
id: string;
|
|
5518
|
+
/** The handle: everything needed to read the current bytes back. */
|
|
5519
|
+
manifest: FileManifest;
|
|
5520
|
+
/** Plaintext byte length. */
|
|
5521
|
+
size: number;
|
|
5522
|
+
mtime: number;
|
|
5523
|
+
/**
|
|
5524
|
+
* Count of prior versions in the file's history record.
|
|
5525
|
+
*
|
|
5526
|
+
* Denormalised on purpose: the file tree wants to show "3 versions" while
|
|
5527
|
+
* listing a directory, and fetching every history record to answer that
|
|
5528
|
+
* would defeat the point of keeping history out of the directory record.
|
|
5529
|
+
*/
|
|
5530
|
+
versions?: number;
|
|
5531
|
+
}
|
|
5532
|
+
interface DirEntry {
|
|
5533
|
+
kind: "dir";
|
|
5534
|
+
/** Record id — `dirKey(id)` locates it. Permanent for the directory's life. */
|
|
5535
|
+
id: string;
|
|
5536
|
+
/** Base64 AES-256 key for that record. See the capability note above. */
|
|
5537
|
+
key: string;
|
|
5538
|
+
mtime: number;
|
|
5539
|
+
}
|
|
5540
|
+
type Entry = FileEntry | DirEntry;
|
|
5541
|
+
/** One directory record, as stored (before encryption). */
|
|
5542
|
+
interface DirNode {
|
|
5543
|
+
v: 1;
|
|
5544
|
+
/** Child name → entry. Names are unique within a directory, per filesystem. */
|
|
5545
|
+
entries: Record<string, Entry>;
|
|
5546
|
+
mtime: number;
|
|
5547
|
+
}
|
|
5548
|
+
/** A file's prior versions, newest first. Fetched only when asked for. */
|
|
5549
|
+
interface HistoryRecord {
|
|
5550
|
+
v: 1;
|
|
5551
|
+
versions: Array<{
|
|
5552
|
+
manifest: FileManifest;
|
|
5553
|
+
size: number;
|
|
5554
|
+
mtime: number;
|
|
5555
|
+
}>;
|
|
5556
|
+
}
|
|
5557
|
+
/** What `stat` and `list` hand back — a path-addressed view of an entry. */
|
|
5558
|
+
interface VfsStat {
|
|
5559
|
+
path: string;
|
|
5560
|
+
name: string;
|
|
5561
|
+
kind: "file" | "dir";
|
|
5562
|
+
size: number;
|
|
5563
|
+
mtime: number;
|
|
5564
|
+
versions: number;
|
|
5565
|
+
}
|
|
5566
|
+
/** The encrypted envelope every record is stored in. */
|
|
5567
|
+
interface SealedRecord {
|
|
5568
|
+
v: 1;
|
|
5569
|
+
/** Base64 12-byte AES-GCM IV. */
|
|
5570
|
+
iv: string;
|
|
5571
|
+
/** Base64 ciphertext with the 16-byte tag appended. */
|
|
5572
|
+
ct: string;
|
|
5573
|
+
}
|
|
5574
|
+
/** The personal-space blob store the VFS persists through. */
|
|
5575
|
+
interface VfsStore {
|
|
5576
|
+
get(key: string): Promise<unknown | null>;
|
|
5577
|
+
put(key: string, value: unknown): Promise<void>;
|
|
5578
|
+
delete(key: string): Promise<void>;
|
|
5579
|
+
/** Every key in the store — the personal space is shared, so this is NOT VFS-only. */
|
|
5580
|
+
list(): Promise<string[]>;
|
|
5581
|
+
}
|
|
5582
|
+
/** Raised when the master seed is not held, so the root key cannot be derived. */
|
|
5583
|
+
declare class VfsLockedError extends Error {
|
|
5584
|
+
constructor();
|
|
5585
|
+
}
|
|
5586
|
+
/** Raised for a path that does not exist. */
|
|
5587
|
+
declare class VfsNotFoundError extends Error {
|
|
5588
|
+
constructor(path: string);
|
|
5589
|
+
}
|
|
5590
|
+
/** Raised when an operation would clobber or misuse an existing entry. */
|
|
5591
|
+
declare class VfsConflictError extends Error {
|
|
5592
|
+
constructor(message: string);
|
|
5593
|
+
}
|
|
5594
|
+
|
|
5595
|
+
/**
|
|
5596
|
+
* `client.vfs` — a real filesystem over Muhkoo primitives.
|
|
5597
|
+
*
|
|
5598
|
+
* Metadata is a graph of encrypted directory records in the user's personal
|
|
5599
|
+
* space; file content is chunked, encrypted and stored in the global
|
|
5600
|
+
* content-addressed shard store, referenced by manifest. See `types.ts` for
|
|
5601
|
+
* why directories carry stable random ids and why each key lives in its parent.
|
|
5602
|
+
*
|
|
5603
|
+
* ## Consistency
|
|
5604
|
+
*
|
|
5605
|
+
* Every mutation is a read-modify-write of exactly ONE directory record, so a
|
|
5606
|
+
* write cannot leave the tree half-updated: either the parent names the new
|
|
5607
|
+
* child or it does not. Two tabs writing different directories never conflict;
|
|
5608
|
+
* two tabs writing the SAME directory resolve last-writer-wins at the personal
|
|
5609
|
+
* space, which carries an HLC per key. Orphaned records (a directory whose
|
|
5610
|
+
* parent entry lost the race) are unreachable rather than corrupting — they
|
|
5611
|
+
* cost storage until a future sweep, which is the trade this shape makes in
|
|
5612
|
+
* exchange for single-record writes.
|
|
5613
|
+
*/
|
|
5614
|
+
|
|
5615
|
+
/**
|
|
5616
|
+
* The slice of content storage the VFS needs.
|
|
5617
|
+
*
|
|
5618
|
+
* Narrow on purpose: the VFS knows how to name and arrange things, not how to
|
|
5619
|
+
* shard them. The adapter in `Client` resolves the content space and calls
|
|
5620
|
+
* `client.storage.writeFile` unchanged — the file still lands in a real Space
|
|
5621
|
+
* with a real gated manifest, and the VFS just keeps the handle somewhere
|
|
5622
|
+
* better than a flat mirror.
|
|
5623
|
+
*/
|
|
5624
|
+
interface VfsContentStore {
|
|
5625
|
+
write(data: Uint8Array | Blob, meta: {
|
|
5626
|
+
name: string;
|
|
5627
|
+
type: string;
|
|
5628
|
+
}): Promise<{
|
|
5629
|
+
manifest: FileManifest;
|
|
5630
|
+
size: number;
|
|
5631
|
+
}>;
|
|
5632
|
+
read(manifest: FileManifest): Promise<Uint8Array>;
|
|
5633
|
+
/**
|
|
5634
|
+
* Drop a reference to a manifest's shards.
|
|
5635
|
+
*
|
|
5636
|
+
* Shards are content-addressed and reference-counted, so deleting a file is
|
|
5637
|
+
* not enough to reclaim the bytes — without this, every version ever
|
|
5638
|
+
* written stays on disk and billed forever. Best-effort: a failure here
|
|
5639
|
+
* leaks storage but must not block the delete.
|
|
5640
|
+
*/
|
|
5641
|
+
release(manifest: FileManifest): Promise<void>;
|
|
5642
|
+
/**
|
|
5643
|
+
* Take an ADDITIONAL reference to shards that already exist.
|
|
5644
|
+
*
|
|
5645
|
+
* Needed when a manifest gains a second owner without new bytes being
|
|
5646
|
+
* written — copying a file. Without it the copy holds a handle to shards
|
|
5647
|
+
* with a refcount of one, and deleting the original frees the bytes out
|
|
5648
|
+
* from under it.
|
|
5649
|
+
*/
|
|
5650
|
+
retain(manifest: FileManifest): Promise<void>;
|
|
5651
|
+
}
|
|
5652
|
+
interface VfsNamespaceDeps {
|
|
5653
|
+
store: VfsStore;
|
|
5654
|
+
/**
|
|
5655
|
+
* Subscribe to the personal space's raw change feed, if the runtime has one.
|
|
5656
|
+
* Optional so the VFS works headlessly (a CLI, a test) with no socket.
|
|
5657
|
+
*/
|
|
5658
|
+
subscribe?: (handler: (frame: unknown) => void) => () => void;
|
|
5659
|
+
content: VfsContentStore;
|
|
5660
|
+
/** The in-memory master seed, or null when locked. Read per call — it can change. */
|
|
5661
|
+
seed: () => Uint8Array | null;
|
|
5662
|
+
/** Versions retained per file. */
|
|
5663
|
+
historyLimit?: number;
|
|
5664
|
+
}
|
|
5665
|
+
declare class VfsNamespace {
|
|
5666
|
+
private readonly deps;
|
|
5667
|
+
private rootKey;
|
|
5668
|
+
/**
|
|
5669
|
+
* Decrypted directory records, by id.
|
|
5670
|
+
*
|
|
5671
|
+
* The file tree re-lists constantly while typing, and every miss is a
|
|
5672
|
+
* network round trip plus a decrypt. Invalidated precisely on write rather
|
|
5673
|
+
* than by TTL — a stale directory listing shows files that are not there.
|
|
5674
|
+
*/
|
|
5675
|
+
private cache;
|
|
5676
|
+
private readonly historyLimit;
|
|
5677
|
+
constructor(deps: VfsNamespaceDeps);
|
|
5678
|
+
/**
|
|
5679
|
+
* The working directory relative paths resolve against.
|
|
5680
|
+
*
|
|
5681
|
+
* Kept here rather than left to each caller so that a CLI, a script and the
|
|
5682
|
+
* SDK all agree on what "here" means — the alternative is every consumer
|
|
5683
|
+
* re-implementing `cd`, and disagreeing about `..`.
|
|
5684
|
+
*/
|
|
5685
|
+
private _cwd;
|
|
5686
|
+
/** The current working directory. Always absolute. */
|
|
5687
|
+
get cwd(): string;
|
|
5688
|
+
/**
|
|
5689
|
+
* Change the working directory.
|
|
5690
|
+
*
|
|
5691
|
+
* Verifies the target is a real directory, so a typo fails here — where the
|
|
5692
|
+
* message can name the path — rather than turning every later relative path
|
|
5693
|
+
* into a confusing "does not exist".
|
|
5694
|
+
*/
|
|
5695
|
+
cd(path: string): Promise<string>;
|
|
5696
|
+
/** Resolve a path against the working directory. Absolute paths pass through. */
|
|
5697
|
+
resolve(path: string): string;
|
|
5698
|
+
/** True when the master seed is held, so the root key can be derived. */
|
|
5699
|
+
get unlocked(): boolean;
|
|
5700
|
+
/**
|
|
5701
|
+
* Drop every cached record.
|
|
5702
|
+
*
|
|
5703
|
+
* Call after signing in as someone else — the cache is keyed by directory
|
|
5704
|
+
* id with no notion of whose filesystem it came from, and serving one
|
|
5705
|
+
* identity's directories to another would be the worst possible bug here.
|
|
5706
|
+
*/
|
|
5707
|
+
clearCache(): void;
|
|
5708
|
+
/**
|
|
5709
|
+
* Call `handler` when this filesystem changes somewhere else — another tab,
|
|
5710
|
+
* another machine, the CLI.
|
|
5711
|
+
*
|
|
5712
|
+
* Deliberately a NOTIFICATION, not a diff: the frame names a record id, and
|
|
5713
|
+
* a record id is not a path (paths are resolved by walking, and the walk is
|
|
5714
|
+
* what the change may have invalidated). So the cache is dropped and the
|
|
5715
|
+
* caller re-reads whatever it is actually showing. Trying to translate a
|
|
5716
|
+
* record id back into a path would mean maintaining a reverse index that
|
|
5717
|
+
* could itself go stale.
|
|
5718
|
+
*
|
|
5719
|
+
* Returns an unsubscribe function. A runtime with no socket (a CLI) gets a
|
|
5720
|
+
* no-op rather than an error — watching is an optimisation, never required
|
|
5721
|
+
* for correctness.
|
|
5722
|
+
*/
|
|
5723
|
+
watch(handler: () => void): () => void;
|
|
5724
|
+
exists(path: string): Promise<boolean>;
|
|
5725
|
+
stat(path: string): Promise<VfsStat>;
|
|
5726
|
+
/** Immediate children of a directory. One record fetch. */
|
|
5727
|
+
list(path?: string): Promise<VfsStat[]>;
|
|
5728
|
+
/**
|
|
5729
|
+
* Every file path under a prefix, depth-first.
|
|
5730
|
+
*
|
|
5731
|
+
* Deliberately separate from `list`: this walks the whole subtree, which is
|
|
5732
|
+
* one fetch per directory. Fine for a project, wrong as a default.
|
|
5733
|
+
*/
|
|
5734
|
+
walk(path?: string): Promise<string[]>;
|
|
5735
|
+
readFile(path: string): Promise<Uint8Array>;
|
|
5736
|
+
readText(path: string): Promise<string>;
|
|
5737
|
+
/**
|
|
5738
|
+
* A file's manifest — the handle itself, without reading the bytes.
|
|
5739
|
+
*
|
|
5740
|
+
* Version control records manifests rather than content, so a commit costs
|
|
5741
|
+
* a hash and a reference, not a copy.
|
|
5742
|
+
*/
|
|
5743
|
+
statManifest(path: string): Promise<{
|
|
5744
|
+
manifest: FileManifest;
|
|
5745
|
+
size: number;
|
|
5746
|
+
}>;
|
|
5747
|
+
/**
|
|
5748
|
+
* Read content by its manifest, wherever it came from.
|
|
5749
|
+
*
|
|
5750
|
+
* A merge needs the COMMON ANCESTOR's version of a file, which by definition
|
|
5751
|
+
* is not in the working tree any more. The manifest is the whole capability,
|
|
5752
|
+
* so no path lookup is involved.
|
|
5753
|
+
*/
|
|
5754
|
+
readByManifest(manifest: FileManifest): Promise<Uint8Array>;
|
|
5755
|
+
/**
|
|
5756
|
+
* Pin content that something other than the working tree depends on.
|
|
5757
|
+
*
|
|
5758
|
+
* A commit references a manifest, so it is an OWNER of that content — without
|
|
5759
|
+
* a reference of its own, deleting the file from the working tree would free
|
|
5760
|
+
* shards the history still needs, and checking out an older commit would find
|
|
5761
|
+
* the content gone. (It did, in test.)
|
|
5762
|
+
*/
|
|
5763
|
+
retainManifest(manifest: FileManifest): Promise<void>;
|
|
5764
|
+
/**
|
|
5765
|
+
* Point a path at content that already exists.
|
|
5766
|
+
*
|
|
5767
|
+
* This is how a checkout restores a file: the bytes are in the shard store
|
|
5768
|
+
* already, so nothing is uploaded or downloaded — only the directory record
|
|
5769
|
+
* changes. Takes a reference, because a second path now depends on those
|
|
5770
|
+
* shards.
|
|
5771
|
+
*/
|
|
5772
|
+
writeManifest(path: string, manifest: FileManifest, size: number): Promise<VfsStat>;
|
|
5773
|
+
/**
|
|
5774
|
+
* Create a directory. `recursive` creates missing parents and tolerates an
|
|
5775
|
+
* existing target, matching `mkdir -p`.
|
|
5776
|
+
*/
|
|
5777
|
+
mkdir(path: string, opts?: {
|
|
5778
|
+
recursive?: boolean;
|
|
5779
|
+
}): Promise<void>;
|
|
5780
|
+
/**
|
|
5781
|
+
* Write a file, creating parent directories as needed.
|
|
5782
|
+
*
|
|
5783
|
+
* The previous version is pushed onto the file's history record before the
|
|
5784
|
+
* directory entry is replaced, so an interrupted write can lose the new
|
|
5785
|
+
* version but never the old one.
|
|
5786
|
+
*/
|
|
5787
|
+
writeFile(path: string, data: Uint8Array | string | Blob, opts?: {
|
|
5788
|
+
type?: string;
|
|
5789
|
+
}): Promise<VfsStat>;
|
|
5790
|
+
/**
|
|
5791
|
+
* Delete a file, or a directory when `recursive`.
|
|
5792
|
+
*
|
|
5793
|
+
* `keepContent` removes the entry WITHOUT releasing its shards. A checkout
|
|
5794
|
+
* needs that: the file is leaving the working tree, but commits still
|
|
5795
|
+
* reference the same manifest, and releasing would free content history
|
|
5796
|
+
* depends on.
|
|
5797
|
+
*/
|
|
5798
|
+
delete(path: string, opts?: {
|
|
5799
|
+
recursive?: boolean;
|
|
5800
|
+
keepContent?: boolean;
|
|
5801
|
+
}): Promise<void>;
|
|
5802
|
+
/**
|
|
5803
|
+
* Move or rename. Works across directories, which is the same operation:
|
|
5804
|
+
* detach the entry from one parent and attach it to another.
|
|
5805
|
+
*
|
|
5806
|
+
* A directory move is O(1) regardless of how much is inside it — the
|
|
5807
|
+
* subtree records are not touched at all, because nothing in them records
|
|
5808
|
+
* its own path.
|
|
5809
|
+
*/
|
|
5810
|
+
rename(from: string, to: string): Promise<void>;
|
|
5811
|
+
/**
|
|
5812
|
+
* Copy a file or a directory tree.
|
|
5813
|
+
*
|
|
5814
|
+
* Copying a FILE moves no bytes: content is immutable and content-addressed,
|
|
5815
|
+
* so the new entry simply points at the same manifest. A 200 MB asset
|
|
5816
|
+
* duplicates in one small metadata write, and the shard store already holds
|
|
5817
|
+
* exactly one copy of it.
|
|
5818
|
+
*
|
|
5819
|
+
* The copy gets a NEW file id, so it does not inherit or share history —
|
|
5820
|
+
* two paths sharing an id would mean saving one silently pushed a version
|
|
5821
|
+
* onto the other.
|
|
5822
|
+
*/
|
|
5823
|
+
copy(from: string, to: string): Promise<VfsStat>;
|
|
5824
|
+
/**
|
|
5825
|
+
* Paths matching a glob, relative to the whole filesystem.
|
|
5826
|
+
*
|
|
5827
|
+
* Matching happens on the client over a walk of the tree, so the cost is one
|
|
5828
|
+
* record fetch per directory — cheap for a project, and worth knowing before
|
|
5829
|
+
* pointing it at the root of a large filesystem. Use `walk` when the prefix
|
|
5830
|
+
* already narrows it.
|
|
5831
|
+
*/
|
|
5832
|
+
glob(pattern: string, opts?: {
|
|
5833
|
+
from?: string;
|
|
5834
|
+
}): Promise<string[]>;
|
|
5835
|
+
/**
|
|
5836
|
+
* Delete metadata records no longer reachable from the root.
|
|
5837
|
+
*
|
|
5838
|
+
* These accumulate because every write touches exactly one record: if two
|
|
5839
|
+
* tabs edit the same directory, last-writer-wins at the personal space can
|
|
5840
|
+
* leave a child whose parent entry lost the race. Unreachable is harmless —
|
|
5841
|
+
* it is invisible and undecryptable-by-nobody — but it costs storage, so
|
|
5842
|
+
* this reclaims it.
|
|
5843
|
+
*
|
|
5844
|
+
* Deliberately explicit, never automatic: a sweep racing another tab's
|
|
5845
|
+
* in-flight write could delete a record that is about to be referenced.
|
|
5846
|
+
* Run it when the filesystem is idle.
|
|
5847
|
+
*/
|
|
5848
|
+
sweep(opts?: {
|
|
5849
|
+
force?: boolean;
|
|
5850
|
+
}): Promise<{
|
|
5851
|
+
removed: string[];
|
|
5852
|
+
}>;
|
|
5853
|
+
/** Prior versions of a file, newest first. One record fetch. */
|
|
5854
|
+
history(path: string): Promise<Array<{
|
|
5855
|
+
size: number;
|
|
5856
|
+
mtime: number;
|
|
5857
|
+
}>>;
|
|
5858
|
+
/**
|
|
5859
|
+
* Restore a prior version, where 0 is the most recent one.
|
|
5860
|
+
*
|
|
5861
|
+
* The current version is pushed onto history first, so restoring is itself
|
|
5862
|
+
* undoable rather than a one-way door.
|
|
5863
|
+
*/
|
|
5864
|
+
restore(path: string, index?: number): Promise<VfsStat>;
|
|
5865
|
+
private expectFile;
|
|
5866
|
+
/** Resolve a path to its entry, or null. Root has no entry of its own. */
|
|
5867
|
+
private find;
|
|
5868
|
+
private root;
|
|
5869
|
+
/** Open a directory by path, creating nothing. Throws if it is missing. */
|
|
5870
|
+
private openDir;
|
|
5871
|
+
private openDirOrNull;
|
|
5872
|
+
private openDirById;
|
|
5873
|
+
/** Apply an edit to a directory record and persist it. */
|
|
5874
|
+
private mutate;
|
|
5875
|
+
private createDir;
|
|
5876
|
+
/** Recursively remove a subtree's records. Best-effort: unreachable ≠ fatal. */
|
|
5877
|
+
private purge;
|
|
5878
|
+
/**
|
|
5879
|
+
* Deep-copy an entry. Files reuse their manifest (no bytes move); each
|
|
5880
|
+
* directory gets a fresh id and key, so the copy is independent of the
|
|
5881
|
+
* original — writing into one must never appear in the other.
|
|
5882
|
+
*/
|
|
5883
|
+
private cloneEntry;
|
|
5884
|
+
/**
|
|
5885
|
+
* Let go of a file's bytes: the current manifest and every retained
|
|
5886
|
+
* version, then the history record itself.
|
|
5887
|
+
*/
|
|
5888
|
+
private releaseFile;
|
|
5889
|
+
private readHistory;
|
|
5890
|
+
/** Push `entry` onto its history record. Returns the new version count. */
|
|
5891
|
+
private pushHistory;
|
|
5892
|
+
}
|
|
5893
|
+
|
|
5894
|
+
/**
|
|
5895
|
+
* Object model for version control over the VFS. See `docs/VCS_DESIGN.md`.
|
|
5896
|
+
*
|
|
5897
|
+
* Two spaces, deliberately: the WORKING TREE (`/apps/<slug>`) uses mutable
|
|
5898
|
+
* random directory ids so a file write touches one record; the REPOSITORY
|
|
5899
|
+
* (here) is content-addressed so a commit means the same thing forever. Git
|
|
5900
|
+
* makes the same split, for the same reason.
|
|
5901
|
+
*/
|
|
5902
|
+
|
|
5903
|
+
/** A file in a commit. The manifest IS the content — there are no blob objects. */
|
|
5904
|
+
interface TreeFile {
|
|
5905
|
+
kind: "file";
|
|
5906
|
+
manifest: FileManifest;
|
|
5907
|
+
size: number;
|
|
5908
|
+
}
|
|
5909
|
+
/** A subdirectory, by the hash of its own tree object. */
|
|
5910
|
+
interface TreeDir {
|
|
5911
|
+
kind: "tree";
|
|
5912
|
+
hash: string;
|
|
5913
|
+
}
|
|
5914
|
+
type TreeEntry = TreeFile | TreeDir;
|
|
5915
|
+
/**
|
|
5916
|
+
* One directory, frozen.
|
|
5917
|
+
*
|
|
5918
|
+
* Addressed by the hash of its canonical form, so an unchanged directory has
|
|
5919
|
+
* the same hash in every commit containing it — a deep project costs one new
|
|
5920
|
+
* tree per CHANGED directory, not one per commit.
|
|
5921
|
+
*/
|
|
5922
|
+
interface Tree {
|
|
5923
|
+
v: 1;
|
|
5924
|
+
entries: Record<string, TreeEntry>;
|
|
5925
|
+
}
|
|
5926
|
+
interface Commit {
|
|
5927
|
+
v: 1;
|
|
5928
|
+
/** Hash of the root tree. */
|
|
5929
|
+
tree: string;
|
|
5930
|
+
/**
|
|
5931
|
+
* Ancestors. An ARRAY because a merge has two; the first commit has none.
|
|
5932
|
+
* Order matters: `parents[0]` is the branch being committed onto.
|
|
5933
|
+
*/
|
|
5934
|
+
parents: string[];
|
|
5935
|
+
message: string;
|
|
5936
|
+
author: string;
|
|
5937
|
+
at: number;
|
|
5938
|
+
}
|
|
5939
|
+
/** Where a branch points, or a specific commit when detached. */
|
|
5940
|
+
type Head = {
|
|
5941
|
+
branch: string;
|
|
5942
|
+
} | {
|
|
5943
|
+
detached: string;
|
|
5944
|
+
};
|
|
5945
|
+
/** A single change between two trees. */
|
|
5946
|
+
interface Change {
|
|
5947
|
+
path: string;
|
|
5948
|
+
kind: "added" | "removed" | "modified";
|
|
5949
|
+
}
|
|
5950
|
+
/** A path the merge could not settle on its own. */
|
|
5951
|
+
interface Conflict {
|
|
5952
|
+
path: string;
|
|
5953
|
+
/** `binary` cannot be merged at all — the user picks a side. */
|
|
5954
|
+
reason: "content" | "binary" | "modify-delete";
|
|
5955
|
+
}
|
|
5956
|
+
interface MergeResult {
|
|
5957
|
+
/** "up-to-date" and "fast-forward" do no three-way work at all. */
|
|
5958
|
+
kind: "up-to-date" | "fast-forward" | "merged" | "conflicted";
|
|
5959
|
+
commit?: string;
|
|
5960
|
+
conflicts: Conflict[];
|
|
5961
|
+
}
|
|
5962
|
+
interface LogEntry {
|
|
5963
|
+
hash: string;
|
|
5964
|
+
message: string;
|
|
5965
|
+
author: string;
|
|
5966
|
+
at: number;
|
|
5967
|
+
parents: string[];
|
|
5968
|
+
}
|
|
5969
|
+
/** Storage keys. Namespaced per project so repositories cannot collide. */
|
|
5970
|
+
declare const objectKey: (slug: string, hash: string) => string;
|
|
5971
|
+
declare const refKey: (slug: string, name: string) => string;
|
|
5972
|
+
declare const headKey: (slug: string) => string;
|
|
5973
|
+
declare const refsIndexKey: (slug: string) => string;
|
|
5974
|
+
/** An in-progress merge: what to make the second parent when it is committed. */
|
|
5975
|
+
declare const mergeKey: (slug: string) => string;
|
|
5976
|
+
declare const DEFAULT_BRANCH = "main";
|
|
5977
|
+
declare class VcsError extends Error {
|
|
5978
|
+
constructor(message: string);
|
|
5979
|
+
}
|
|
5980
|
+
|
|
5981
|
+
/**
|
|
5982
|
+
* `client.vcs` — version control for a project in the VFS.
|
|
5983
|
+
*
|
|
5984
|
+
* Phase 1: objects, commit, log, checkout, diff. Branches and merge build on
|
|
5985
|
+
* this; see `docs/VCS_DESIGN.md` for the whole shape and for why the repository
|
|
5986
|
+
* is content-addressed while the working tree is not.
|
|
5987
|
+
*/
|
|
5988
|
+
|
|
5989
|
+
interface VcsNamespaceDeps {
|
|
5990
|
+
vfs: VfsNamespace;
|
|
5991
|
+
store: VfsStore;
|
|
5992
|
+
/** The key repository objects are sealed with — the project's own. */
|
|
5993
|
+
key: () => Promise<Uint8Array>;
|
|
5994
|
+
/** Who to record as the author. */
|
|
5995
|
+
author: () => string;
|
|
5996
|
+
}
|
|
5997
|
+
declare class Repo {
|
|
5998
|
+
private readonly slug;
|
|
5999
|
+
private readonly deps;
|
|
6000
|
+
constructor(slug: string, deps: VcsNamespaceDeps);
|
|
6001
|
+
private get root();
|
|
6002
|
+
/** Store an object, returning its hash and whether it was new. */
|
|
6003
|
+
private put;
|
|
6004
|
+
private get;
|
|
6005
|
+
private head;
|
|
6006
|
+
private setHead;
|
|
6007
|
+
private readRef;
|
|
6008
|
+
private writeRef;
|
|
6009
|
+
/** Every branch name in this repository. */
|
|
6010
|
+
branches(): Promise<string[]>;
|
|
6011
|
+
/** The commit HEAD resolves to, or null in an empty repository. */
|
|
6012
|
+
current(): Promise<string | null>;
|
|
6013
|
+
/**
|
|
6014
|
+
* Freeze the working tree as a commit.
|
|
6015
|
+
*
|
|
6016
|
+
* Trees are written bottom-up so a directory's hash is known before its
|
|
6017
|
+
* parent references it. Nothing is written to the shard store: file entries
|
|
6018
|
+
* carry the manifest the VFS already holds.
|
|
6019
|
+
*/
|
|
6020
|
+
commit(message: string, parents?: string[]): Promise<string>;
|
|
6021
|
+
/** Move whatever HEAD points at onto a new commit. */
|
|
6022
|
+
private advance;
|
|
6023
|
+
/** The other side of a merge that stopped for conflicts, if any. */
|
|
6024
|
+
pendingMerge(): Promise<{
|
|
6025
|
+
theirs: string;
|
|
6026
|
+
name: string;
|
|
6027
|
+
} | null>;
|
|
6028
|
+
/** Write a directory and everything under it, returning the tree's hash. */
|
|
6029
|
+
private writeTree;
|
|
6030
|
+
/** Commits reachable from HEAD, newest first. */
|
|
6031
|
+
log(limit?: number): Promise<LogEntry[]>;
|
|
6032
|
+
/**
|
|
6033
|
+
* What changed between two commits.
|
|
6034
|
+
*
|
|
6035
|
+
* Identical subtree hashes prune the walk: an untouched directory is one
|
|
6036
|
+
* comparison regardless of how much is inside it. That is the payoff for
|
|
6037
|
+
* content-addressing the trees.
|
|
6038
|
+
*/
|
|
6039
|
+
diff(fromHash: string | null, toHash: string): Promise<Change[]>;
|
|
6040
|
+
private diffTrees;
|
|
6041
|
+
/**
|
|
6042
|
+
* Replace the working tree with a commit's.
|
|
6043
|
+
*
|
|
6044
|
+
* Moves no file content: entries carry manifests the shard store already
|
|
6045
|
+
* holds, so this rewrites directory records only.
|
|
6046
|
+
*/
|
|
6047
|
+
/**
|
|
6048
|
+
* What has changed since the last commit.
|
|
6049
|
+
*
|
|
6050
|
+
* Compares the WORKING TREE against HEAD without writing any objects: the
|
|
6051
|
+
* common case is answering "anything to commit?", and a `status` that
|
|
6052
|
+
* quietly stored a tree object every time would litter the repository with
|
|
6053
|
+
* trees for states nobody chose to keep.
|
|
6054
|
+
*/
|
|
6055
|
+
status(): Promise<Change[]>;
|
|
6056
|
+
/** The working tree as a path → entry map, mirroring `filesOf`. */
|
|
6057
|
+
private workingFiles;
|
|
6058
|
+
/**
|
|
6059
|
+
* Turn what a person typed into a commit hash.
|
|
6060
|
+
*
|
|
6061
|
+
* Accepts a branch name, `HEAD`, a full or abbreviated hash, and trailing
|
|
6062
|
+
* `^` / `~n` to walk back through parents — so `show abc123^` means "the
|
|
6063
|
+
* commit before that one" the way anyone who has used git expects.
|
|
6064
|
+
*/
|
|
6065
|
+
resolve(rev: string): Promise<string>;
|
|
6066
|
+
/** A hash prefix a person typed → the one commit it names. */
|
|
6067
|
+
private expand;
|
|
6068
|
+
/**
|
|
6069
|
+
* Put one file back to the way it was in a commit.
|
|
6070
|
+
*
|
|
6071
|
+
* Scoped to a path on purpose — recovering one file you broke should not
|
|
6072
|
+
* mean rewinding everything else you have done since.
|
|
6073
|
+
*/
|
|
6074
|
+
restore(path: string, rev?: string): Promise<void>;
|
|
6075
|
+
checkout(commitHash: string, opts?: {
|
|
6076
|
+
discardChanges?: boolean;
|
|
6077
|
+
}): Promise<void>;
|
|
6078
|
+
/** Make the working tree match a commit, leaving HEAD alone. */
|
|
6079
|
+
private materialise;
|
|
6080
|
+
/** Project-relative path → absolute VFS path. */
|
|
6081
|
+
private abs;
|
|
6082
|
+
private collect;
|
|
6083
|
+
/** Create a branch at a commit (HEAD by default). Does not switch to it. */
|
|
6084
|
+
branch(name: string, at?: string): Promise<string>;
|
|
6085
|
+
/**
|
|
6086
|
+
* Move onto a branch, bringing the working tree with it.
|
|
6087
|
+
*
|
|
6088
|
+
* Unlike {@link checkout}, this ATTACHES head: later commits advance the
|
|
6089
|
+
* branch. Checking out a bare commit deliberately does not, so that
|
|
6090
|
+
* committing from a historical state cannot silently rewrite a branch.
|
|
6091
|
+
*/
|
|
6092
|
+
switchTo(name: string, opts?: {
|
|
6093
|
+
discardChanges?: boolean;
|
|
6094
|
+
}): Promise<void>;
|
|
6095
|
+
/**
|
|
6096
|
+
* Refuse to overwrite work that was never recorded.
|
|
6097
|
+
*
|
|
6098
|
+
* Moving between commits REPLACES the working tree, so uncommitted edits
|
|
6099
|
+
* are gone — and unlike an ordinary overwrite, the person did not ask for
|
|
6100
|
+
* anything to be written. Naming the files is the point: "you have changes"
|
|
6101
|
+
* with no list leaves them guessing what they are about to lose.
|
|
6102
|
+
*/
|
|
6103
|
+
private guardWorkingTree;
|
|
6104
|
+
/** The branch HEAD is on, or null when detached. */
|
|
6105
|
+
currentBranch(): Promise<string | null>;
|
|
6106
|
+
/**
|
|
6107
|
+
* Merge another branch into the current one.
|
|
6108
|
+
*
|
|
6109
|
+
* Every part of this is client-side by necessity: the server holds
|
|
6110
|
+
* ciphertext and cannot compare versions, so base, ours and theirs are all
|
|
6111
|
+
* fetched and decrypted here.
|
|
6112
|
+
*/
|
|
6113
|
+
merge(name: string, opts?: {
|
|
6114
|
+
discardChanges?: boolean;
|
|
6115
|
+
}): Promise<MergeResult>;
|
|
6116
|
+
/** Decide one path. Returns a conflict when it cannot be settled. */
|
|
6117
|
+
private settlePath;
|
|
6118
|
+
private take;
|
|
6119
|
+
/**
|
|
6120
|
+
* The most recent commit reachable from both.
|
|
6121
|
+
*
|
|
6122
|
+
* Walks our ancestry into a set, then theirs breadth-first until it hits it —
|
|
6123
|
+
* so the first match is the nearest, which is what a three-way merge needs.
|
|
6124
|
+
*/
|
|
6125
|
+
mergeBase(a: string, b: string): Promise<string | null>;
|
|
6126
|
+
/** Every file in a commit, by path. */
|
|
6127
|
+
private filesOf;
|
|
6128
|
+
}
|
|
6129
|
+
/**
|
|
6130
|
+
* Version control over the VFS.
|
|
6131
|
+
*
|
|
6132
|
+
* One repository per project, addressed by slug — the same slug the VFS stores
|
|
6133
|
+
* that project's working tree under. `client.vcs.open("my-app")` is the whole
|
|
6134
|
+
* entry point.
|
|
6135
|
+
*/
|
|
6136
|
+
declare class VcsNamespace {
|
|
6137
|
+
private readonly deps;
|
|
6138
|
+
private readonly repos;
|
|
6139
|
+
constructor(deps: VcsNamespaceOptions);
|
|
6140
|
+
/** The repository for one project. Cheap and cached; creates nothing. */
|
|
6141
|
+
open(slug: string): Repo;
|
|
6142
|
+
}
|
|
6143
|
+
interface VcsNamespaceOptions {
|
|
6144
|
+
vfs: VfsNamespace;
|
|
6145
|
+
store: VfsStore;
|
|
6146
|
+
/** The in-memory master seed, or null when locked. Read per call. */
|
|
6147
|
+
seed: () => Uint8Array | null;
|
|
6148
|
+
author: () => string;
|
|
6149
|
+
}
|
|
6150
|
+
|
|
5382
6151
|
/** The hosted Muhkoo Accelerator — the default {@link ClientOptions.baseUrl}. */
|
|
5383
6152
|
declare const DEFAULT_BASE_URL = "https://api.muhkoo.dev";
|
|
5384
6153
|
/** The hosted auth SPA — the default {@link ClientOptions.authBaseUrl}. */
|
|
@@ -5476,6 +6245,10 @@ declare class Client {
|
|
|
5476
6245
|
readonly db: DbNamespace;
|
|
5477
6246
|
/** File storage — `client.storage.writeFile(...)`, etc. */
|
|
5478
6247
|
readonly storage: StorageNamespace;
|
|
6248
|
+
/** Filesystem over the personal space: directories, versions, real paths. */
|
|
6249
|
+
readonly vfs: VfsNamespace;
|
|
6250
|
+
/** Version control over the VFS: commits, branches, merges — per project. */
|
|
6251
|
+
readonly vcs: VcsNamespace;
|
|
5479
6252
|
/** Realtime messaging — `client.message.subscribe(...)`, etc. */
|
|
5480
6253
|
readonly message: MessageNamespace;
|
|
5481
6254
|
/** Fan-out group spaces — `client.space.createSpace(...)`, etc. */
|
|
@@ -5669,6 +6442,47 @@ declare function loadDeviceIdentity(): Promise<PersistedDeviceIdentity | null>;
|
|
|
5669
6442
|
*/
|
|
5670
6443
|
declare function clearDeviceIdentity(): Promise<void>;
|
|
5671
6444
|
|
|
6445
|
+
/**
|
|
6446
|
+
* Three-way line merge.
|
|
6447
|
+
*
|
|
6448
|
+
* Given a common ancestor and two descendants, produce the combined text — or
|
|
6449
|
+
* report the regions where both sides changed the same lines differently.
|
|
6450
|
+
*
|
|
6451
|
+
* The server cannot do any of this: it holds ciphertext and cannot compare
|
|
6452
|
+
* versions, so merging is entirely a client concern and has to be exact here.
|
|
6453
|
+
*/
|
|
6454
|
+
interface MergeRegionConflict {
|
|
6455
|
+
/** 1-based line where the conflict begins in the merged output. */
|
|
6456
|
+
line: number;
|
|
6457
|
+
ours: string[];
|
|
6458
|
+
theirs: string[];
|
|
6459
|
+
}
|
|
6460
|
+
interface Merge3Result {
|
|
6461
|
+
lines: string[];
|
|
6462
|
+
conflicts: MergeRegionConflict[];
|
|
6463
|
+
}
|
|
6464
|
+
/**
|
|
6465
|
+
* Merge `ours` and `theirs` over their common ancestor `base`.
|
|
6466
|
+
*
|
|
6467
|
+
* A region where only one side changed takes that side. A region where both
|
|
6468
|
+
* made the SAME change takes it once — two people fixing the same typo is not a
|
|
6469
|
+
* conflict. Only genuinely divergent edits conflict.
|
|
6470
|
+
*/
|
|
6471
|
+
declare function merge3(base: string[], ours: string[], theirs: string[]): Merge3Result;
|
|
6472
|
+
/**
|
|
6473
|
+
* Merge to text, writing conflict markers where the sides diverged.
|
|
6474
|
+
*
|
|
6475
|
+
* Markers rather than a bespoke format: they are what every developer already
|
|
6476
|
+
* knows how to resolve, and resolving becomes ordinary editing.
|
|
6477
|
+
*/
|
|
6478
|
+
declare function merge3Text(base: string, ours: string, theirs: string, labels?: {
|
|
6479
|
+
ours: string;
|
|
6480
|
+
theirs: string;
|
|
6481
|
+
}): {
|
|
6482
|
+
text: string;
|
|
6483
|
+
conflicted: boolean;
|
|
6484
|
+
};
|
|
6485
|
+
|
|
5672
6486
|
/**
|
|
5673
6487
|
* Passkey factor (M1.1) — WebAuthn with the **PRF extension**.
|
|
5674
6488
|
*
|
|
@@ -6659,5 +7473,5 @@ declare class KeyringClient implements KeyringTransport {
|
|
|
6659
7473
|
fetchMetadata(): Promise<SpaceMetadata | null>;
|
|
6660
7474
|
}
|
|
6661
7475
|
|
|
6662
|
-
export { ACCESS_TOKEN_SCOPES, AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AccessTokensNamespace, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BlockEngine, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, ConnectivityManager, DEFAULT_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_FN_HOST_SUFFIX, DEVICE_IDENTITY_STORAGE_KEY, DEVICE_KEY_DB_NAME, DbCache, DevicePairingError, DeviceStoreUnavailableError, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HLC, HashKnowledge, HostedAuth, HttpClient, HttpError, IndexedDbStore, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvCache, KvNamespace, LocalEngineHost, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, NoopStore, OfflineLockedError, OfflineManager, OutboundQueue, P2P_SUBJECT, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PasskeyOriginError, PeerExchange, PeerNetwork, PersonalSpaceClient, Poseidon, PreimagePoK, ReauthRequiredError, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardCache, ShardClient, SharedSpaceClient, SnapshotNamespace, Space, SpaceCache, SpaceKeyring, SpaceNamespace, SpacePacketCipher, SpaceSignaler, StorageNamespace, SyncEngine, VaultUnavailableError, WSTransport, WebRtcTransport, WorkerEngineHost, ZERO_HLC, ZkAuth, buildCommitment, canonicalUserCode, clearDeviceIdentity, compareHlc, compilePrograms, configureDeviceStore, createPasskeyWithPrf, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultRpId, defaultSessionStore, deleteBeatsWrite, deleteRegister, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, deviceFingerprint, deviceIdentityIsEphemeral, deviceIdentityKey, ejectAgentPrompt, ejectAgentTools, emptyMap, emptyRegister, encodeToHex, encryptAesGcm, evaluatePasskeyPrf, exportPublicKeyBase64, exportPublicKeyHex, formatUserCode, generateAuthProof, generateDevicePairingKeypair, generatePkce, getMuhkooAppDescriptor, getSubtle, hasDeviceIdentity, initBn128Wasm, initRsWasm, initializeCircuits, isNewer, isOfflineCapable, isP2pCapable, isProvisional, loadDeviceIdentity, manifestToStat, materializeRow, mergeMap, mergeMessage, mergeRegister, messageKey, pack, padHandle, pairingVerificationCode, passkeyPrfCapable, passkeySupported, passkeyUsableFromOrigin, persistDeviceIdentity, poseidonHash, protocol, provisionalHandle, quickVerify, randomBytes, randomState, rpIdUsableForOrigin, sealSeed, sealSeedToDevice, shardHash, signMessage, stableStringify, toField, tombstoneRow, unpack, unsealSeed, unsealSeedFromDevice, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase, writeBeatsTombstone, writeColumns, writeRegister };
|
|
6663
|
-
export type { ACL, ACLEntry, AccessTokenInfo, AccessTokensNamespaceDeps, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, AttachedPeerNetwork, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, BlockStore, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, CachedSpaceMessage, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, ConnectivityOptions, ConnectivityState, CreateAccessTokenInput, CreatedAccessToken, DbQueryPage, DehydratedKeypair, DehydratedKeys, DeviceBlobStore, DeviceKeyVault, DevicePairingErrorReason, DevicePairingKeypair, DevicePairingPoll, DevicePairingRequest, DevicePairingSession, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EngineHandle, EngineHost, EphemeralEvent, EpochKeyProvider, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, Hasher, HlcParts, HlcState, HostedAuthDeps, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvEntry, KvNamespaceDeps, LoginOptions, LwwMap, LwwRegister, MessageBody, MessageDeletedEvent, MessageEntry, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOp, MessageOptions, MessageSubscription, MuhkooAgentMeta, MuhkooAgentToolsConfig, MuhkooAppDescriptor, MuhkooDBAccess, MuhkooDBMeta, MuhkooFunctionMeta, MuhkooKeystoreBridge, MuhkooMessageEvent, MuhkooSpaceMeta, OAuthProvider, OfflineEntry, OfflineManagerDeps, OfflineStore, OutSink, PacketCipher, PacketHeaders, PacketOptions, PairedDevice, Participant, PasskeyEnrollment, PeerBlockSource, PeerExchangeOptions, PeerId, PeerNetworkOptions, PeerTransport, Permission, PermissionCheckRequest, PermissionCheckResult, PersistedDeviceIdentity, PersonalSpaceClientOptions, Pkce, PublicKeyJWK, QueueEntry, RatchetState, ReceiveResult, RegisterParams, Replayer, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, Scope, SealedHandoff, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardByteCache, ShardClientOptions, SharedSpaceClientOptions, SignalEnvelope, SignalKind, SignalingSpace, SnapshotNamespaceDeps, SnarkProof, SpaceChannelLike, SpaceCursor, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, SpaceOfflineAdapter, StartDevicePairingOptions, StorageChangeEvent, StoreName, StoredSession, Subscription, UserIdentity, VaultFactor, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WaitForDevicePairingOptions, WebRtcTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|
|
7476
|
+
export { ACCESS_TOKEN_SCOPES, AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AccessTokensNamespace, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BlockEngine, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, ConnectivityManager, DEFAULT_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_BRANCH, DEFAULT_FN_HOST_SUFFIX, DEVICE_IDENTITY_STORAGE_KEY, DEVICE_KEY_DB_NAME, DbCache, DevicePairingError, DeviceStoreUnavailableError, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HLC, HashKnowledge, HostedAuth, HttpClient, HttpError, IndexedDbStore, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvCache, KvNamespace, LocalEngineHost, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, NoopStore, OfflineLockedError, OfflineManager, OutboundQueue, P2P_SUBJECT, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PasskeyOriginError, PeerExchange, PeerNetwork, PersonalSpaceClient, Poseidon, PreimagePoK, ROOT_ID, ReauthRequiredError, ReedSolomonCodec, Repo, Space as Room, SerializeMessage, SessionState, ShardCache, ShardClient, SharedSpaceClient, SnapshotNamespace, Space, SpaceCache, SpaceKeyring, SpaceNamespace, SpacePacketCipher, SpaceSignaler, StorageNamespace, SyncEngine, VFS_ROOT_INFO, VaultUnavailableError, VcsError, VcsNamespace, VfsConflictError, VfsLockedError, VfsNamespace, VfsNotFoundError, WSTransport, WebRtcTransport, WorkerEngineHost, ZERO_HLC, ZkAuth, buildCommitment, canonicalUserCode, clearDeviceIdentity, compareHlc, compilePrograms, configureDeviceStore, createPasskeyWithPrf, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultRpId, defaultSessionStore, deleteBeatsWrite, deleteRegister, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, deviceFingerprint, deviceIdentityIsEphemeral, deviceIdentityKey, dirKey, ejectAgentPrompt, ejectAgentTools, emptyMap, emptyRegister, encodeToHex, encryptAesGcm, evaluatePasskeyPrf, exportPublicKeyBase64, exportPublicKeyHex, formatUserCode, generateAuthProof, generateDevicePairingKeypair, generatePkce, getMuhkooAppDescriptor, getSubtle, hasDeviceIdentity, headKey, historyKey, initBn128Wasm, initRsWasm, initializeCircuits, isNewer, isOfflineCapable, isP2pCapable, isProvisional, loadDeviceIdentity, manifestToStat, materializeRow, merge3, merge3Text, mergeKey, mergeMap, mergeMessage, mergeRegister, messageKey, objectKey, pack, padHandle, pairingVerificationCode, passkeyPrfCapable, passkeySupported, passkeyUsableFromOrigin, persistDeviceIdentity, poseidonHash, protocol, provisionalHandle, quickVerify, randomBytes, randomState, refKey, refsIndexKey, rpIdUsableForOrigin, sealSeed, sealSeedToDevice, shardHash, signMessage, stableStringify, toField, tombstoneRow, unpack, unsealSeed, unsealSeedFromDevice, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase, writeBeatsTombstone, writeColumns, writeRegister };
|
|
7477
|
+
export type { ACL, ACLEntry, AccessTokenInfo, AccessTokensNamespaceDeps, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, AttachedPeerNetwork, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, BlockStore, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, CachedSpaceMessage, Change, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, Commit, Conflict, ConnectivityOptions, ConnectivityState, CreateAccessTokenInput, CreatedAccessToken, DbQueryPage, DehydratedKeypair, DehydratedKeys, DeviceBlobStore, DeviceKeyVault, DevicePairingErrorReason, DevicePairingKeypair, DevicePairingPoll, DevicePairingRequest, DevicePairingSession, DirEntry, DirNode, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EngineHandle, EngineHost, Entry, EphemeralEvent, EpochKeyProvider, FileEntry, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, Hasher, Head, HistoryRecord, HlcParts, HlcState, HostedAuthDeps, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvEntry, KvNamespaceDeps, LogEntry, LoginOptions, LwwMap, LwwRegister, Merge3Result, MergeRegionConflict, MergeResult, MessageBody, MessageDeletedEvent, MessageEntry, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOp, MessageOptions, MessageSubscription, MuhkooAgentMeta, MuhkooAgentToolsConfig, MuhkooAppDescriptor, MuhkooDBAccess, MuhkooDBMeta, MuhkooFunctionMeta, MuhkooKeystoreBridge, MuhkooMessageEvent, MuhkooSpaceMeta, OAuthProvider, OfflineEntry, OfflineManagerDeps, OfflineStore, OutSink, PacketCipher, PacketHeaders, PacketOptions, PairedDevice, Participant, PasskeyEnrollment, PeerBlockSource, PeerExchangeOptions, PeerId, PeerNetworkOptions, PeerTransport, Permission, PermissionCheckRequest, PermissionCheckResult, PersistedDeviceIdentity, PersonalSpaceClientOptions, Pkce, PublicKeyJWK, QueueEntry, RatchetState, ReceiveResult, RegisterParams, Replayer, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, Scope, SealedHandoff, SealedHeaders, SealedRecord, Session, SessionOptions, SessionStore, SetOptions, ShardByteCache, ShardClientOptions, SharedSpaceClientOptions, SignalEnvelope, SignalKind, SignalingSpace, SnapshotNamespaceDeps, SnarkProof, SpaceChannelLike, SpaceCursor, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, SpaceOfflineAdapter, StartDevicePairingOptions, StorageChangeEvent, StoreName, StoredSession, Subscription, Tree, TreeDir, TreeEntry, TreeFile, UserIdentity, VaultFactor, VcsNamespaceDeps, VcsNamespaceOptions, VerificationKey, VfsContentStore, VfsNamespaceDeps, VfsStat, VfsStore, WSMessage, WSMessageType, WSTransportOptions, WaitForDevicePairingOptions, WebRtcTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|