@amalgm/live 0.2.81-preview.34176839240 → 0.2.81-preview.34320593070
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/PURPOSE.md +26 -6
- package/README.md +3 -2
- package/dist/files/api.d.ts +3 -0
- package/dist/files/api.d.ts.map +1 -0
- package/dist/files/api.js +83 -0
- package/dist/files/api.js.map +1 -0
- package/dist/files/browse-types.d.ts +65 -0
- package/dist/files/browse-types.d.ts.map +1 -0
- package/dist/files/browse-types.js +46 -0
- package/dist/files/browse-types.js.map +1 -0
- package/dist/files/browser.d.ts +44 -0
- package/dist/files/browser.d.ts.map +1 -0
- package/dist/files/browser.js +100 -0
- package/dist/files/browser.js.map +1 -0
- package/dist/files/cloud-files.d.ts +55 -0
- package/dist/files/cloud-files.d.ts.map +1 -0
- package/dist/files/cloud-files.js +152 -0
- package/dist/files/cloud-files.js.map +1 -0
- package/dist/files/index.d.ts +9 -0
- package/dist/files/index.d.ts.map +1 -1
- package/dist/files/index.js +5 -0
- package/dist/files/index.js.map +1 -1
- package/dist/files/local-files.d.ts +39 -0
- package/dist/files/local-files.d.ts.map +1 -0
- package/dist/files/local-files.js +95 -0
- package/dist/files/local-files.js.map +1 -0
- package/dist/workspace/api.d.ts.map +1 -1
- package/dist/workspace/api.js +62 -18
- package/dist/workspace/api.js.map +1 -1
- package/dist/workspace/entries.d.ts +9 -0
- package/dist/workspace/entries.d.ts.map +1 -0
- package/dist/workspace/entries.js +77 -0
- package/dist/workspace/entries.js.map +1 -0
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +1 -0
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/memory-store.d.ts +3 -5
- package/dist/workspace/memory-store.d.ts.map +1 -1
- package/dist/workspace/memory-store.js +10 -11
- package/dist/workspace/memory-store.js.map +1 -1
- package/dist/workspace/registry.d.ts +13 -5
- package/dist/workspace/registry.d.ts.map +1 -1
- package/dist/workspace/registry.js +159 -27
- package/dist/workspace/registry.js.map +1 -1
- package/dist/workspace/types.d.ts +36 -5
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist-cjs/files/api.js +85 -0
- package/dist-cjs/files/api.js.map +1 -0
- package/dist-cjs/files/browse-types.js +54 -0
- package/dist-cjs/files/browse-types.js.map +1 -0
- package/dist-cjs/files/browser.js +104 -0
- package/dist-cjs/files/browser.js.map +1 -0
- package/dist-cjs/files/cloud-files.js +158 -0
- package/dist-cjs/files/cloud-files.js.map +1 -0
- package/dist-cjs/files/index.js +13 -0
- package/dist-cjs/files/index.js.map +1 -1
- package/dist-cjs/files/local-files.js +99 -0
- package/dist-cjs/files/local-files.js.map +1 -0
- package/dist-cjs/workspace/api.js +62 -18
- package/dist-cjs/workspace/api.js.map +1 -1
- package/dist-cjs/workspace/entries.js +82 -0
- package/dist-cjs/workspace/entries.js.map +1 -0
- package/dist-cjs/workspace/index.js +4 -1
- package/dist-cjs/workspace/index.js.map +1 -1
- package/dist-cjs/workspace/memory-store.js +10 -11
- package/dist-cjs/workspace/memory-store.js.map +1 -1
- package/dist-cjs/workspace/registry.js +159 -27
- package/dist-cjs/workspace/registry.js.map +1 -1
- package/package.json +4 -4
package/PURPOSE.md
CHANGED
|
@@ -13,8 +13,9 @@ The public developer model is `live.bind(surface, channel)` and is specified
|
|
|
13
13
|
in `docs/realtime-sdk.md`. The shipped wire behavior this package encodes is
|
|
14
14
|
`docs/local-live-protocol.md`. The entity Record/Send/Receive discipline is
|
|
15
15
|
`docs/entity-record-queues.md`. This repository is where those documents
|
|
16
|
-
become executable and testable
|
|
17
|
-
|
|
16
|
+
become executable and testable. The deprecated Engine repository is read-only
|
|
17
|
+
reference evidence; released Live surfaces run only from this package through
|
|
18
|
+
Shell.
|
|
18
19
|
|
|
19
20
|
## Axioms
|
|
20
21
|
|
|
@@ -63,10 +64,15 @@ or certify a new wire contract, cloud migration or cross-machine rollout.
|
|
|
63
64
|
released surface.
|
|
64
65
|
10. **Amalgm's own surfaces use this SDK.** A private shortcut in the Amalgm
|
|
65
66
|
UI or runtime means the SDK is incomplete — fix the SDK, not the caller.
|
|
66
|
-
11. **A workspace has one portable identity
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
11. **A workspace link has one portable identity; a group composes links.** A
|
|
68
|
+
direct external folder keeps its UUIDv7 and computer URI across renames and
|
|
69
|
+
rediscovery. Two or more links may have one separately identified, named
|
|
70
|
+
workspace group on one computer with one selected default child. Promotion into that group
|
|
71
|
+
retains each child link's identity and never copies its filesystem ground;
|
|
72
|
+
a child belongs to at most one group. Link and group UUIDs are unique across
|
|
73
|
+
the complete registry; a rejected replacement changes neither state nor
|
|
74
|
+
events. Every registry change emits both
|
|
75
|
+
legacy `projects` and canonical `workspaces` resource events.
|
|
70
76
|
12. **User ground is portable; coordination state is not.** Everything beneath
|
|
71
77
|
an authenticated `users/<email>/` home is registrable except the reserved
|
|
72
78
|
`.amalgm/` directory, transient SQLite journal/lock sidecars, and paths
|
|
@@ -390,6 +396,18 @@ or certify a new wire contract, cloud migration or cross-machine rollout.
|
|
|
390
396
|
is placement work, never correctness work: placement rows converge
|
|
391
397
|
first-wins, a source missing at repack time is skipped silently, and the
|
|
392
398
|
immutable content readers prove never changes even when its locator does.
|
|
399
|
+
45. **Files browse opens one active cloud head.** The authenticated user's
|
|
400
|
+
`/amalgm/users/<canonical-email>` path is one complete logical namespace.
|
|
401
|
+
Every browse or content request opens the current cloud graph head once,
|
|
402
|
+
and all cloud membership and content resolved by that request remains
|
|
403
|
+
pinned to that immutable head.
|
|
404
|
+
46. **Local is materialization on one computer.** A machine-local path is
|
|
405
|
+
evidence that one entity is materialized on one computer, never a second
|
|
406
|
+
file identity or a second directory tree. A present local entity is read
|
|
407
|
+
through that computer's filesystem port even when legitimate unsynced work
|
|
408
|
+
makes its version differ from cloud; an absent entity is read from the
|
|
409
|
+
request's pinned cloud head. Arbitrary machine paths remain an explicit
|
|
410
|
+
local-only scope and never enter the Amalgm namespace by accident.
|
|
393
411
|
47. **An unfinished merge is settled Checkpoint truth.** Git lock files and
|
|
394
412
|
Amalgm's own reveal marker are transient and may never be captured. A
|
|
395
413
|
stable merge in progress is different: its merge heads, message and mode,
|
|
@@ -463,6 +481,8 @@ src/apply/ the Apply/Verify pure submodule: durable intent phases,
|
|
|
463
481
|
proof, type-adapter sockets, and crash-resumable orchestration.
|
|
464
482
|
src/registration/ the login-time user-ground convergence operation and its
|
|
465
483
|
injected local/cloud effect ports.
|
|
484
|
+
src/files/ Files Register/Add decisions plus active-head browse and
|
|
485
|
+
per-computer local-source selection behind injected ports.
|
|
466
486
|
src/transfer/ type-aware immutable artifact upload/download operations;
|
|
467
487
|
chunk storage, R2, files, and Git remain injected effects.
|
|
468
488
|
tests/ unit tests per module, in the engine wall's style: each
|
package/README.md
CHANGED
|
@@ -21,8 +21,9 @@ normative specifications live in [docs/](docs/):
|
|
|
21
21
|
| [local-live-protocol.md](docs/local-live-protocol.md) | the shipped local wire protocol (v1/v2) |
|
|
22
22
|
| [entity-record-queues.md](docs/entity-record-queues.md) | filesystem Record, local Outbox, per-UUID cloud order, and local Inbox |
|
|
23
23
|
| [entity-apply.md](docs/entity-apply.md) | intent-first, type-sensitive Apply and exact Verify |
|
|
24
|
+
| [files-browsing.md](docs/files-browsing.md) | one active-cloud namespace with per-computer local materialization |
|
|
24
25
|
| [shared-realtime-wave-3.md](docs/shared-realtime-wave-3.md) | the multi-user cloud-authority design |
|
|
25
|
-
| [extraction-boundary.md](docs/extraction-boundary.md) |
|
|
26
|
+
| [extraction-boundary.md](docs/extraction-boundary.md) | current Live/Shell ownership and the remaining cutover boundary |
|
|
26
27
|
| [user-ground-login.md](docs/user-ground-login.md) | first-login publication, later-device materialization, and Watch readiness |
|
|
27
28
|
| [watching.md](docs/watching.md) | Watch purpose, axioms, lifecycle matrix, and cross-platform test layers |
|
|
28
29
|
|
|
@@ -43,7 +44,7 @@ Every surface delegates to `src/`. One behavior, five doors.
|
|
|
43
44
|
```bash
|
|
44
45
|
npm install
|
|
45
46
|
npm test # build + unit tests
|
|
46
|
-
npm run test:parity # golden vectors vs
|
|
47
|
+
npm run test:parity # golden vectors vs read-only historical Engine evidence
|
|
47
48
|
```
|
|
48
49
|
|
|
49
50
|
## Managed candidate delivery
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/files/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA8C7F"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { FilesBrowser } from './browser.js';
|
|
2
|
+
import { FilesRequestError } from './browse-types.js';
|
|
3
|
+
export function createFilesApi(browser) {
|
|
4
|
+
return async (request) => {
|
|
5
|
+
const url = new URL(request.url);
|
|
6
|
+
if (request.method !== 'GET')
|
|
7
|
+
return json(405, 'method_not_allowed', 'Files endpoints are read-only');
|
|
8
|
+
try {
|
|
9
|
+
if (url.pathname === '/files/amalgm') {
|
|
10
|
+
return jsonValue(200, await browser.listAmalgm({
|
|
11
|
+
...optional(url, 'parentId'),
|
|
12
|
+
...optional(url, 'computerId'),
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
if (url.pathname === '/files/amalgm/content') {
|
|
16
|
+
const read = await browser.readAmalgm({
|
|
17
|
+
entityId: required(url, 'entityId'),
|
|
18
|
+
...optional(url, 'computerId'),
|
|
19
|
+
});
|
|
20
|
+
return content(read.content, {
|
|
21
|
+
'x-amalgm-scope': read.scope,
|
|
22
|
+
'x-amalgm-source': read.source,
|
|
23
|
+
'x-amalgm-cloud-head': read.head.checksum,
|
|
24
|
+
'x-amalgm-entity-id': read.entityId,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (url.pathname === '/files/local') {
|
|
28
|
+
return jsonValue(200, await browser.listLocal({
|
|
29
|
+
computerId: required(url, 'computerId'),
|
|
30
|
+
...optional(url, 'path'),
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
if (url.pathname === '/files/local/content') {
|
|
34
|
+
const read = await browser.readLocal({
|
|
35
|
+
computerId: required(url, 'computerId'),
|
|
36
|
+
path: required(url, 'path'),
|
|
37
|
+
});
|
|
38
|
+
return content(read.content, {
|
|
39
|
+
'x-amalgm-scope': read.scope,
|
|
40
|
+
'x-amalgm-source': read.source,
|
|
41
|
+
'x-amalgm-computer-id': read.computerId,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return json(404, 'not_found', 'Files route not found');
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof FilesRequestError)
|
|
48
|
+
return json(error.status, error.code, error.message);
|
|
49
|
+
return json(500, 'files_error', error instanceof Error ? error.message : 'Files request failed');
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function required(url, key) {
|
|
54
|
+
const value = url.searchParams.get(key)?.trim() ?? '';
|
|
55
|
+
if (!value)
|
|
56
|
+
throw new FilesRequestError(400, 'invalid_request', `${key} is required`);
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
function optional(url, key) {
|
|
60
|
+
const value = url.searchParams.get(key)?.trim() ?? '';
|
|
61
|
+
return value ? { [key]: value } : {};
|
|
62
|
+
}
|
|
63
|
+
function content(value, extra) {
|
|
64
|
+
const headers = {
|
|
65
|
+
'content-type': value.mediaType,
|
|
66
|
+
'cache-control': 'no-store',
|
|
67
|
+
etag: `\"${value.version}\"`,
|
|
68
|
+
...extra,
|
|
69
|
+
};
|
|
70
|
+
if (value.bytes !== null)
|
|
71
|
+
headers['content-length'] = String(value.bytes);
|
|
72
|
+
return new Response(value.body, { status: 200, headers });
|
|
73
|
+
}
|
|
74
|
+
function json(status, code, message) {
|
|
75
|
+
return jsonValue(status, { error: { code, message } });
|
|
76
|
+
}
|
|
77
|
+
function jsonValue(status, body) {
|
|
78
|
+
return new Response(JSON.stringify(body), {
|
|
79
|
+
status,
|
|
80
|
+
headers: { 'content-type': 'application/json', 'cache-control': 'no-store' },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/files/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { EntityStatus, EntityType } from '../entities/index.js';
|
|
2
|
+
export type FilesBody = Uint8Array | ReadableStream<Uint8Array>;
|
|
3
|
+
/** A host-owned byte source. Streams stay streams across the Live boundary. */
|
|
4
|
+
export interface FilesContent {
|
|
5
|
+
readonly body: FilesBody;
|
|
6
|
+
readonly mediaType: string;
|
|
7
|
+
readonly bytes: number | null;
|
|
8
|
+
/** Exact entity version, or a host-derived version for raw local files. */
|
|
9
|
+
readonly version: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FilesHeadDescriptor {
|
|
12
|
+
readonly checksum: string;
|
|
13
|
+
readonly version: number;
|
|
14
|
+
}
|
|
15
|
+
export interface AmalgmFileEntry {
|
|
16
|
+
/** Stable id of this directory placement; differs from entityId for a reference mount. */
|
|
17
|
+
readonly entryId: string;
|
|
18
|
+
readonly entityId: string;
|
|
19
|
+
readonly parentEntityId: string | null;
|
|
20
|
+
readonly referenceId: string | null;
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly type: EntityType;
|
|
23
|
+
readonly status: EntityStatus;
|
|
24
|
+
readonly version: string;
|
|
25
|
+
readonly availability: {
|
|
26
|
+
readonly cloud: true;
|
|
27
|
+
readonly local: boolean;
|
|
28
|
+
readonly localVersion: string | null;
|
|
29
|
+
readonly matchesCloud: boolean | null;
|
|
30
|
+
};
|
|
31
|
+
/** The route a content request will prefer for the selected computer. */
|
|
32
|
+
readonly source: 'cloud' | 'local';
|
|
33
|
+
}
|
|
34
|
+
export interface AmalgmDirectoryListing {
|
|
35
|
+
readonly scope: 'amalgm';
|
|
36
|
+
readonly root: string;
|
|
37
|
+
readonly head: FilesHeadDescriptor;
|
|
38
|
+
readonly computerId: string | null;
|
|
39
|
+
readonly directory: AmalgmFileEntry;
|
|
40
|
+
readonly entries: readonly AmalgmFileEntry[];
|
|
41
|
+
}
|
|
42
|
+
export type LocalPathKind = 'directory' | 'file' | 'symlink' | 'other';
|
|
43
|
+
export interface LocalPathEntry {
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly path: string;
|
|
46
|
+
readonly kind: LocalPathKind;
|
|
47
|
+
readonly bytes: number | null;
|
|
48
|
+
readonly modifiedAt: string | null;
|
|
49
|
+
}
|
|
50
|
+
export interface LocalDirectoryListing {
|
|
51
|
+
readonly scope: 'local-only';
|
|
52
|
+
readonly computerId: string;
|
|
53
|
+
readonly directory: LocalPathEntry;
|
|
54
|
+
readonly entries: readonly LocalPathEntry[];
|
|
55
|
+
}
|
|
56
|
+
export declare class FilesRequestError extends Error {
|
|
57
|
+
readonly status: number;
|
|
58
|
+
readonly code: string;
|
|
59
|
+
constructor(status: number, code: string, message: string);
|
|
60
|
+
}
|
|
61
|
+
export declare function requiredText(value: unknown, label: string): string;
|
|
62
|
+
export declare function canonicalUserEmail(value: unknown): string;
|
|
63
|
+
export declare function amalgmUserRoot(userEmail: string): string;
|
|
64
|
+
export declare function assertFilesContent(value: unknown, label: string): FilesContent;
|
|
65
|
+
//# sourceMappingURL=browse-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browse-types.d.ts","sourceRoot":"","sources":["../../src/files/browse-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export class FilesRequestError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
code;
|
|
4
|
+
constructor(status, code, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = 'FilesRequestError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function requiredText(value, label) {
|
|
12
|
+
const text = typeof value === 'string' ? value.trim() : '';
|
|
13
|
+
if (!text)
|
|
14
|
+
throw new FilesRequestError(400, 'invalid_request', `${label} is required`);
|
|
15
|
+
return text;
|
|
16
|
+
}
|
|
17
|
+
export function canonicalUserEmail(value) {
|
|
18
|
+
const email = requiredText(value, 'authenticated user email').toLowerCase();
|
|
19
|
+
if (email.includes('/') || email.includes('\\') || email.includes('\0')) {
|
|
20
|
+
throw new FilesRequestError(400, 'invalid_identity', 'authenticated user email is not path-safe');
|
|
21
|
+
}
|
|
22
|
+
return email;
|
|
23
|
+
}
|
|
24
|
+
export function amalgmUserRoot(userEmail) {
|
|
25
|
+
return `/amalgm/users/${canonicalUserEmail(userEmail)}`;
|
|
26
|
+
}
|
|
27
|
+
export function assertFilesContent(value, label) {
|
|
28
|
+
const content = value;
|
|
29
|
+
const stream = content?.body;
|
|
30
|
+
const validBody = content?.body instanceof Uint8Array
|
|
31
|
+
|| (stream !== undefined && typeof stream.getReader === 'function');
|
|
32
|
+
if (!content || !validBody
|
|
33
|
+
|| typeof content.mediaType !== 'string' || !content.mediaType.trim()
|
|
34
|
+
|| (content.bytes !== null
|
|
35
|
+
&& (!Number.isSafeInteger(content.bytes) || Number(content.bytes) < 0))
|
|
36
|
+
|| typeof content.version !== 'string' || !content.version) {
|
|
37
|
+
throw new Error(`${label} returned invalid content`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
body: content.body,
|
|
41
|
+
mediaType: content.mediaType,
|
|
42
|
+
bytes: content.bytes === null ? null : Number(content.bytes),
|
|
43
|
+
version: content.version,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=browse-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browse-types.js","sourceRoot":"","sources":["../../src/files/browse-types.ts"],"names":[],"mappings":"AAgEA,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAE/B;IACA;IAFX,YACW,MAAc,EACd,IAAY,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAJN,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,KAAa;IACxD,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,iBAAiB,CAAC,GAAG,EAAE,iBAAiB,EAAE,GAAG,KAAK,cAAc,CAAC,CAAC;IACvF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,0BAA0B,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5E,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,EAAE,2CAA2C,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,iBAAiB,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc,EAAE,KAAa;IAC9D,MAAM,OAAO,GAAG,KAAqC,CAAC;IACtD,MAAM,MAAM,GAAG,OAAO,EAAE,IAA2C,CAAC;IACpE,MAAM,SAAS,GAAG,OAAO,EAAE,IAAI,YAAY,UAAU;WAChD,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC;IACtE,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS;WACrB,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE;WAClE,CAAC,OAAO,CAAC,KAAK,KAAK,IAAI;eACrB,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;WACtE,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,2BAA2B,CAAC,CAAC;IACvD,CAAC;IACD,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAiB;QAC/B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;QAC5D,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type AmalgmDirectoryListing, type FilesContent, type FilesHeadDescriptor, type LocalDirectoryListing } from './browse-types.js';
|
|
2
|
+
import { type CloudFilesPort } from './cloud-files.js';
|
|
3
|
+
import { type LocalFilesPort } from './local-files.js';
|
|
4
|
+
export interface FilesBrowserOptions {
|
|
5
|
+
readonly userEmail: string;
|
|
6
|
+
readonly cloud: CloudFilesPort;
|
|
7
|
+
readonly local: LocalFilesPort;
|
|
8
|
+
}
|
|
9
|
+
export interface AmalgmFileRead {
|
|
10
|
+
readonly scope: 'amalgm';
|
|
11
|
+
readonly head: FilesHeadDescriptor;
|
|
12
|
+
readonly entityId: string;
|
|
13
|
+
readonly source: 'cloud' | 'local';
|
|
14
|
+
readonly content: FilesContent;
|
|
15
|
+
}
|
|
16
|
+
export interface LocalFileRead {
|
|
17
|
+
readonly scope: 'local-only';
|
|
18
|
+
readonly computerId: string;
|
|
19
|
+
readonly source: 'local';
|
|
20
|
+
readonly content: FilesContent;
|
|
21
|
+
}
|
|
22
|
+
export declare class FilesBrowser {
|
|
23
|
+
readonly root: string;
|
|
24
|
+
private readonly cloud;
|
|
25
|
+
private readonly local;
|
|
26
|
+
constructor(options: FilesBrowserOptions);
|
|
27
|
+
listAmalgm(input?: {
|
|
28
|
+
readonly parentId?: string;
|
|
29
|
+
readonly computerId?: string;
|
|
30
|
+
}): Promise<AmalgmDirectoryListing>;
|
|
31
|
+
readAmalgm(input: {
|
|
32
|
+
readonly entityId: string;
|
|
33
|
+
readonly computerId?: string;
|
|
34
|
+
}): Promise<AmalgmFileRead>;
|
|
35
|
+
listLocal(input: {
|
|
36
|
+
readonly computerId: string;
|
|
37
|
+
readonly path?: string;
|
|
38
|
+
}): Promise<LocalDirectoryListing>;
|
|
39
|
+
readLocal(input: {
|
|
40
|
+
readonly computerId: string;
|
|
41
|
+
readonly path: string;
|
|
42
|
+
}): Promise<LocalFileRead>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/files/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,sBAAsB,EAE3B,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEnE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;CAChC;AAED,qBAAa,YAAY;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;gBAEvB,OAAO,EAAE,mBAAmB;IAMlC,UAAU,CAAC,KAAK,GAAE;QACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAkBlC,UAAU,CAAC,KAAK,EAAE;QACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;KAC9B,GAAG,OAAO,CAAC,cAAc,CAAC;IA0B3B,SAAS,CAAC,KAAK,EAAE;QACf,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAI5B,SAAS,CAAC,KAAK,EAAE;QACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,aAAa,CAAC;CAQ3B"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { amalgmUserRoot, } from './browse-types.js';
|
|
2
|
+
import { CloudFiles, assertReadable, } from './cloud-files.js';
|
|
3
|
+
import { LocalFiles } from './local-files.js';
|
|
4
|
+
export class FilesBrowser {
|
|
5
|
+
root;
|
|
6
|
+
cloud;
|
|
7
|
+
local;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.cloud = new CloudFiles(options.userEmail, options.cloud);
|
|
10
|
+
this.local = new LocalFiles(options.local);
|
|
11
|
+
this.root = amalgmUserRoot(options.userEmail);
|
|
12
|
+
}
|
|
13
|
+
async listAmalgm(input = {}) {
|
|
14
|
+
const cloud = await this.cloud.open();
|
|
15
|
+
const listed = await cloud.list(input.parentId);
|
|
16
|
+
const records = uniqueEntities([listed.directory, ...listed.entries]);
|
|
17
|
+
const computerId = optionalText(input.computerId);
|
|
18
|
+
const local = computerId === null
|
|
19
|
+
? new Map()
|
|
20
|
+
: await this.local.available(computerId, cloud.head, records);
|
|
21
|
+
return {
|
|
22
|
+
scope: 'amalgm',
|
|
23
|
+
root: this.root,
|
|
24
|
+
head: headDescriptor(cloud.head),
|
|
25
|
+
computerId,
|
|
26
|
+
directory: present(listed.directory, local),
|
|
27
|
+
entries: listed.entries.map((entry) => present(entry, local)),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async readAmalgm(input) {
|
|
31
|
+
const cloud = await this.cloud.open();
|
|
32
|
+
const entity = await cloud.entry(input.entityId);
|
|
33
|
+
assertReadable(entity);
|
|
34
|
+
const computerId = optionalText(input.computerId);
|
|
35
|
+
if (computerId !== null) {
|
|
36
|
+
const local = await this.local.readIfMaterialized(computerId, cloud.head, entity);
|
|
37
|
+
if (local !== null) {
|
|
38
|
+
return {
|
|
39
|
+
scope: 'amalgm',
|
|
40
|
+
head: headDescriptor(cloud.head),
|
|
41
|
+
entityId: entity.uuid,
|
|
42
|
+
source: 'local',
|
|
43
|
+
content: local,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
scope: 'amalgm',
|
|
49
|
+
head: headDescriptor(cloud.head),
|
|
50
|
+
entityId: entity.uuid,
|
|
51
|
+
source: 'cloud',
|
|
52
|
+
content: await cloud.read(entity),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
listLocal(input) {
|
|
56
|
+
return this.local.list(input.computerId, input.path);
|
|
57
|
+
}
|
|
58
|
+
async readLocal(input) {
|
|
59
|
+
return {
|
|
60
|
+
scope: 'local-only',
|
|
61
|
+
computerId: input.computerId.trim(),
|
|
62
|
+
source: 'local',
|
|
63
|
+
content: await this.local.read(input.computerId, input.path),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function present(node, local) {
|
|
68
|
+
const localVersion = local.get(node.entity.uuid) ?? null;
|
|
69
|
+
const isLocal = localVersion !== null;
|
|
70
|
+
return {
|
|
71
|
+
entryId: node.entryId,
|
|
72
|
+
entityId: node.entity.uuid,
|
|
73
|
+
parentEntityId: node.parentEntityId,
|
|
74
|
+
referenceId: node.referenceId,
|
|
75
|
+
name: node.name,
|
|
76
|
+
type: node.entity.type,
|
|
77
|
+
status: node.entity.status,
|
|
78
|
+
version: node.entity.version,
|
|
79
|
+
availability: {
|
|
80
|
+
cloud: true,
|
|
81
|
+
local: isLocal,
|
|
82
|
+
localVersion,
|
|
83
|
+
matchesCloud: localVersion === null ? null : localVersion === node.entity.version,
|
|
84
|
+
},
|
|
85
|
+
source: isLocal ? 'local' : 'cloud',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function uniqueEntities(nodes) {
|
|
89
|
+
return [...new Map(nodes.map((node) => [node.entity.uuid, node.entity])).values()];
|
|
90
|
+
}
|
|
91
|
+
function headDescriptor(head) {
|
|
92
|
+
return { checksum: head.checksum, version: head.version };
|
|
93
|
+
}
|
|
94
|
+
function optionalText(value) {
|
|
95
|
+
if (value === undefined)
|
|
96
|
+
return null;
|
|
97
|
+
const text = value.trim();
|
|
98
|
+
return text || null;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/files/browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type CloudEntityRecord } from '../entities/index.js';
|
|
2
|
+
import { type FilesContent } from './browse-types.js';
|
|
3
|
+
export interface CloudFilesHead {
|
|
4
|
+
readonly checksum: string;
|
|
5
|
+
readonly version: number;
|
|
6
|
+
readonly root: CloudEntityRecord;
|
|
7
|
+
}
|
|
8
|
+
export interface CloudFilesPort {
|
|
9
|
+
/** Resolve the authority's current head. Every later call receives this
|
|
10
|
+
* exact immutable descriptor rather than consulting the pointer again. */
|
|
11
|
+
openActiveHead(): Promise<unknown>;
|
|
12
|
+
list(input: {
|
|
13
|
+
readonly head: CloudFilesHead;
|
|
14
|
+
readonly parentId: string;
|
|
15
|
+
}): Promise<unknown>;
|
|
16
|
+
entry(input: {
|
|
17
|
+
readonly head: CloudFilesHead;
|
|
18
|
+
readonly entityId: string;
|
|
19
|
+
}): Promise<unknown>;
|
|
20
|
+
read(input: {
|
|
21
|
+
readonly head: CloudFilesHead;
|
|
22
|
+
readonly entity: CloudEntityRecord;
|
|
23
|
+
}): Promise<unknown>;
|
|
24
|
+
}
|
|
25
|
+
export interface CloudDirectory {
|
|
26
|
+
readonly directory: CloudFileNode;
|
|
27
|
+
readonly entries: readonly CloudFileNode[];
|
|
28
|
+
}
|
|
29
|
+
/** One placement in the logical Amalgm tree. A reference keeps its own entry
|
|
30
|
+
* id and name while its target supplies entity identity, type, and version. */
|
|
31
|
+
export interface CloudFileNode {
|
|
32
|
+
readonly entryId: string;
|
|
33
|
+
readonly entity: CloudEntityRecord;
|
|
34
|
+
readonly parentEntityId: string | null;
|
|
35
|
+
readonly name: string;
|
|
36
|
+
readonly referenceId: string | null;
|
|
37
|
+
}
|
|
38
|
+
export declare class CloudFiles {
|
|
39
|
+
private readonly port;
|
|
40
|
+
readonly userEmail: string;
|
|
41
|
+
constructor(userEmail: string, port: CloudFilesPort);
|
|
42
|
+
open(): Promise<CloudFilesView>;
|
|
43
|
+
}
|
|
44
|
+
export declare class CloudFilesView {
|
|
45
|
+
readonly head: CloudFilesHead;
|
|
46
|
+
private readonly port;
|
|
47
|
+
constructor(head: CloudFilesHead, port: CloudFilesPort);
|
|
48
|
+
list(parentId?: string): Promise<CloudDirectory>;
|
|
49
|
+
entry(value: string): Promise<CloudEntityRecord>;
|
|
50
|
+
private resolveNode;
|
|
51
|
+
private referenceNode;
|
|
52
|
+
read(entry: CloudEntityRecord): Promise<FilesContent>;
|
|
53
|
+
}
|
|
54
|
+
export declare function assertReadable(entry: CloudEntityRecord): void;
|
|
55
|
+
//# sourceMappingURL=cloud-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-files.d.ts","sourceRoot":"","sources":["../../src/files/cloud-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAK3B,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B;8EAC0E;IAC1E,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5F,KAAK,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7F,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtG;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED;+EAC+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,qBAAa,UAAU;IAKnB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAGzB,SAAS,EAAE,MAAM,EACA,IAAI,EAAE,cAAc;IAKjC,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC;CAGtC;AAED,qBAAa,cAAc;IAEvB,QAAQ,CAAC,IAAI,EAAE,cAAc;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADZ,IAAI,EAAE,cAAc,EACZ,IAAI,EAAE,cAAc;IAGjC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAmChD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAKxC,WAAW;YAYX,aAAa;IAyBrB,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;CAS5D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAI7D"}
|