@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.
Files changed (70) hide show
  1. package/PURPOSE.md +26 -6
  2. package/README.md +3 -2
  3. package/dist/files/api.d.ts +3 -0
  4. package/dist/files/api.d.ts.map +1 -0
  5. package/dist/files/api.js +83 -0
  6. package/dist/files/api.js.map +1 -0
  7. package/dist/files/browse-types.d.ts +65 -0
  8. package/dist/files/browse-types.d.ts.map +1 -0
  9. package/dist/files/browse-types.js +46 -0
  10. package/dist/files/browse-types.js.map +1 -0
  11. package/dist/files/browser.d.ts +44 -0
  12. package/dist/files/browser.d.ts.map +1 -0
  13. package/dist/files/browser.js +100 -0
  14. package/dist/files/browser.js.map +1 -0
  15. package/dist/files/cloud-files.d.ts +55 -0
  16. package/dist/files/cloud-files.d.ts.map +1 -0
  17. package/dist/files/cloud-files.js +152 -0
  18. package/dist/files/cloud-files.js.map +1 -0
  19. package/dist/files/index.d.ts +9 -0
  20. package/dist/files/index.d.ts.map +1 -1
  21. package/dist/files/index.js +5 -0
  22. package/dist/files/index.js.map +1 -1
  23. package/dist/files/local-files.d.ts +39 -0
  24. package/dist/files/local-files.d.ts.map +1 -0
  25. package/dist/files/local-files.js +95 -0
  26. package/dist/files/local-files.js.map +1 -0
  27. package/dist/workspace/api.d.ts.map +1 -1
  28. package/dist/workspace/api.js +62 -18
  29. package/dist/workspace/api.js.map +1 -1
  30. package/dist/workspace/entries.d.ts +9 -0
  31. package/dist/workspace/entries.d.ts.map +1 -0
  32. package/dist/workspace/entries.js +77 -0
  33. package/dist/workspace/entries.js.map +1 -0
  34. package/dist/workspace/index.d.ts +1 -0
  35. package/dist/workspace/index.d.ts.map +1 -1
  36. package/dist/workspace/index.js +1 -0
  37. package/dist/workspace/index.js.map +1 -1
  38. package/dist/workspace/memory-store.d.ts +3 -5
  39. package/dist/workspace/memory-store.d.ts.map +1 -1
  40. package/dist/workspace/memory-store.js +10 -11
  41. package/dist/workspace/memory-store.js.map +1 -1
  42. package/dist/workspace/registry.d.ts +13 -5
  43. package/dist/workspace/registry.d.ts.map +1 -1
  44. package/dist/workspace/registry.js +159 -27
  45. package/dist/workspace/registry.js.map +1 -1
  46. package/dist/workspace/types.d.ts +36 -5
  47. package/dist/workspace/types.d.ts.map +1 -1
  48. package/dist-cjs/files/api.js +85 -0
  49. package/dist-cjs/files/api.js.map +1 -0
  50. package/dist-cjs/files/browse-types.js +54 -0
  51. package/dist-cjs/files/browse-types.js.map +1 -0
  52. package/dist-cjs/files/browser.js +104 -0
  53. package/dist-cjs/files/browser.js.map +1 -0
  54. package/dist-cjs/files/cloud-files.js +158 -0
  55. package/dist-cjs/files/cloud-files.js.map +1 -0
  56. package/dist-cjs/files/index.js +13 -0
  57. package/dist-cjs/files/index.js.map +1 -1
  58. package/dist-cjs/files/local-files.js +99 -0
  59. package/dist-cjs/files/local-files.js.map +1 -0
  60. package/dist-cjs/workspace/api.js +62 -18
  61. package/dist-cjs/workspace/api.js.map +1 -1
  62. package/dist-cjs/workspace/entries.js +82 -0
  63. package/dist-cjs/workspace/entries.js.map +1 -0
  64. package/dist-cjs/workspace/index.js +4 -1
  65. package/dist-cjs/workspace/index.js.map +1 -1
  66. package/dist-cjs/workspace/memory-store.js +10 -11
  67. package/dist-cjs/workspace/memory-store.js.map +1 -1
  68. package/dist-cjs/workspace/registry.js +159 -27
  69. package/dist-cjs/workspace/registry.js.map +1 -1
  70. 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; the engine repo keeps read-only copies until
17
- its cutover.
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.** Its UUIDv7 and computer URI
67
- survive renames and rediscovery; local paths locate it but never become a
68
- second identity, and every registry change emits both legacy `projects`
69
- and canonical `workspaces` resource events.
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) | what moves here from the engine runtime, and in what order |
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 the shipped engine runtime
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,3 @@
1
+ import { FilesBrowser } from './browser.js';
2
+ export declare function createFilesApi(browser: FilesBrowser): (request: Request) => Promise<Response>;
3
+ //# sourceMappingURL=api.d.ts.map
@@ -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"}