@amalgm/live 0.1.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/PURPOSE.md +103 -0
- package/README.md +45 -0
- package/dist/contracts/binding.d.ts +49 -0
- package/dist/contracts/binding.d.ts.map +1 -0
- package/dist/contracts/binding.js +23 -0
- package/dist/contracts/binding.js.map +1 -0
- package/dist/contracts/content.d.ts +42 -0
- package/dist/contracts/content.d.ts.map +1 -0
- package/dist/contracts/content.js +72 -0
- package/dist/contracts/content.js.map +1 -0
- package/dist/contracts/docs.d.ts +64 -0
- package/dist/contracts/docs.d.ts.map +1 -0
- package/dist/contracts/docs.js +51 -0
- package/dist/contracts/docs.js.map +1 -0
- package/dist/contracts/events.d.ts +45 -0
- package/dist/contracts/events.d.ts.map +1 -0
- package/dist/contracts/events.js +38 -0
- package/dist/contracts/events.js.map +1 -0
- package/dist/contracts/frames/gateway.d.ts +83 -0
- package/dist/contracts/frames/gateway.d.ts.map +1 -0
- package/dist/contracts/frames/gateway.js +35 -0
- package/dist/contracts/frames/gateway.js.map +1 -0
- package/dist/contracts/frames/stream.d.ts +59 -0
- package/dist/contracts/frames/stream.d.ts.map +1 -0
- package/dist/contracts/frames/stream.js +17 -0
- package/dist/contracts/frames/stream.js.map +1 -0
- package/dist/contracts/frames/tunnel.d.ts +78 -0
- package/dist/contracts/frames/tunnel.d.ts.map +1 -0
- package/dist/contracts/frames/tunnel.js +55 -0
- package/dist/contracts/frames/tunnel.js.map +1 -0
- package/dist/contracts/index.d.ts +19 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +19 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/mutation.d.ts +51 -0
- package/dist/contracts/mutation.d.ts.map +1 -0
- package/dist/contracts/mutation.js +55 -0
- package/dist/contracts/mutation.js.map +1 -0
- package/dist/contracts/presence.d.ts +46 -0
- package/dist/contracts/presence.d.ts.map +1 -0
- package/dist/contracts/presence.js +34 -0
- package/dist/contracts/presence.js.map +1 -0
- package/dist/contracts/resources.d.ts +52 -0
- package/dist/contracts/resources.d.ts.map +1 -0
- package/dist/contracts/resources.js +105 -0
- package/dist/contracts/resources.js.map +1 -0
- package/dist/contracts/snapshot.d.ts +24 -0
- package/dist/contracts/snapshot.d.ts.map +1 -0
- package/dist/contracts/snapshot.js +28 -0
- package/dist/contracts/snapshot.js.map +1 -0
- package/dist/contracts/validate.d.ts +32 -0
- package/dist/contracts/validate.d.ts.map +1 -0
- package/dist/contracts/validate.js +25 -0
- package/dist/contracts/validate.js.map +1 -0
- package/dist/detection/adapter-contract.d.ts +134 -0
- package/dist/detection/adapter-contract.d.ts.map +1 -0
- package/dist/detection/adapter-contract.js +162 -0
- package/dist/detection/adapter-contract.js.map +1 -0
- package/dist/detection/classify.d.ts +31 -0
- package/dist/detection/classify.d.ts.map +1 -0
- package/dist/detection/classify.js +28 -0
- package/dist/detection/classify.js.map +1 -0
- package/dist/detection/continuity.d.ts +54 -0
- package/dist/detection/continuity.d.ts.map +1 -0
- package/dist/detection/continuity.js +85 -0
- package/dist/detection/continuity.js.map +1 -0
- package/dist/detection/coverage.d.ts +43 -0
- package/dist/detection/coverage.d.ts.map +1 -0
- package/dist/detection/coverage.js +60 -0
- package/dist/detection/coverage.js.map +1 -0
- package/dist/detection/diff.d.ts +144 -0
- package/dist/detection/diff.d.ts.map +1 -0
- package/dist/detection/diff.js +187 -0
- package/dist/detection/diff.js.map +1 -0
- package/dist/detection/enrollment.d.ts +16 -0
- package/dist/detection/enrollment.d.ts.map +1 -0
- package/dist/detection/enrollment.js +70 -0
- package/dist/detection/enrollment.js.map +1 -0
- package/dist/detection/index.d.ts +29 -0
- package/dist/detection/index.d.ts.map +1 -0
- package/dist/detection/index.js +29 -0
- package/dist/detection/index.js.map +1 -0
- package/dist/detection/refusal.d.ts +33 -0
- package/dist/detection/refusal.d.ts.map +1 -0
- package/dist/detection/refusal.js +40 -0
- package/dist/detection/refusal.js.map +1 -0
- package/dist/entities/bytes.d.ts +18 -0
- package/dist/entities/bytes.d.ts.map +1 -0
- package/dist/entities/bytes.js +61 -0
- package/dist/entities/bytes.js.map +1 -0
- package/dist/entities/cloud.d.ts +82 -0
- package/dist/entities/cloud.d.ts.map +1 -0
- package/dist/entities/cloud.js +136 -0
- package/dist/entities/cloud.js.map +1 -0
- package/dist/entities/index.d.ts +23 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +23 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/entities/moves.d.ts +42 -0
- package/dist/entities/moves.d.ts.map +1 -0
- package/dist/entities/moves.js +82 -0
- package/dist/entities/moves.js.map +1 -0
- package/dist/entities/repo-states.d.ts +121 -0
- package/dist/entities/repo-states.d.ts.map +1 -0
- package/dist/entities/repo-states.js +127 -0
- package/dist/entities/repo-states.js.map +1 -0
- package/dist/entities/rules.d.ts +104 -0
- package/dist/entities/rules.d.ts.map +1 -0
- package/dist/entities/rules.js +200 -0
- package/dist/entities/rules.js.map +1 -0
- package/dist/entities/types.d.ts +94 -0
- package/dist/entities/types.d.ts.map +1 -0
- package/dist/entities/types.js +40 -0
- package/dist/entities/types.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/machines/arbitration.d.ts +36 -0
- package/dist/machines/arbitration.d.ts.map +1 -0
- package/dist/machines/arbitration.js +37 -0
- package/dist/machines/arbitration.js.map +1 -0
- package/dist/machines/backoff.d.ts +36 -0
- package/dist/machines/backoff.d.ts.map +1 -0
- package/dist/machines/backoff.js +34 -0
- package/dist/machines/backoff.js.map +1 -0
- package/dist/machines/bootstrap.d.ts +65 -0
- package/dist/machines/bootstrap.d.ts.map +1 -0
- package/dist/machines/bootstrap.js +105 -0
- package/dist/machines/bootstrap.js.map +1 -0
- package/dist/machines/chunks.d.ts +51 -0
- package/dist/machines/chunks.d.ts.map +1 -0
- package/dist/machines/chunks.js +67 -0
- package/dist/machines/chunks.js.map +1 -0
- package/dist/machines/index.d.ts +14 -0
- package/dist/machines/index.d.ts.map +1 -0
- package/dist/machines/index.js +14 -0
- package/dist/machines/index.js.map +1 -0
- package/dist/machines/journal.d.ts +63 -0
- package/dist/machines/journal.d.ts.map +1 -0
- package/dist/machines/journal.js +71 -0
- package/dist/machines/journal.js.map +1 -0
- package/dist/machines/ordering.d.ts +53 -0
- package/dist/machines/ordering.d.ts.map +1 -0
- package/dist/machines/ordering.js +87 -0
- package/dist/machines/ordering.js.map +1 -0
- package/dist/machines/presence.d.ts +26 -0
- package/dist/machines/presence.d.ts.map +1 -0
- package/dist/machines/presence.js +49 -0
- package/dist/machines/presence.js.map +1 -0
- package/dist/merge/index.d.ts +9 -0
- package/dist/merge/index.d.ts.map +1 -0
- package/dist/merge/index.js +9 -0
- package/dist/merge/index.js.map +1 -0
- package/dist/merge/merge3.d.ts +39 -0
- package/dist/merge/merge3.d.ts.map +1 -0
- package/dist/merge/merge3.js +188 -0
- package/dist/merge/merge3.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource identity — the names of live channels.
|
|
3
|
+
*
|
|
4
|
+
* A resource is a named channel over the local wire
|
|
5
|
+
* (local-live-protocol.md §1). The shipped grammar:
|
|
6
|
+
*
|
|
7
|
+
* tasks a well-known singleton table
|
|
8
|
+
* app:<appId>:<name> a per-app named resource
|
|
9
|
+
* doc:<base64url(path)> a live document, addressed by local path
|
|
10
|
+
* files:<base64url(dir)> a directory listing channel
|
|
11
|
+
*
|
|
12
|
+
* A shared resource additionally receives a cloud id that never depends on
|
|
13
|
+
* any user's local path (realtime-sdk.md §2); path-derived ids are for
|
|
14
|
+
* unshared local resources only. Renaming a materialized file must not
|
|
15
|
+
* change a shared id — which is exactly why the shared id is not derived
|
|
16
|
+
* from the path.
|
|
17
|
+
*/
|
|
18
|
+
import { type Checked } from './validate.js';
|
|
19
|
+
export type ResourceName = string;
|
|
20
|
+
export type ParsedResource = {
|
|
21
|
+
readonly kind: 'table';
|
|
22
|
+
readonly name: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly kind: 'app';
|
|
25
|
+
readonly appId: string;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
} | {
|
|
28
|
+
readonly kind: 'doc';
|
|
29
|
+
readonly path: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly kind: 'files';
|
|
32
|
+
readonly dir: string;
|
|
33
|
+
};
|
|
34
|
+
/** `doc:<base64url(path)>` for the document at a local path. */
|
|
35
|
+
export declare const docResource: (path: string) => ResourceName;
|
|
36
|
+
/** `files:<base64url(dir)>` for the listing channel of a directory. */
|
|
37
|
+
export declare const filesResource: (dir: string) => ResourceName;
|
|
38
|
+
/** `app:<appId>:<name>` for an app-scoped resource. */
|
|
39
|
+
export declare const appResource: (appId: string, name: string) => ResourceName;
|
|
40
|
+
/**
|
|
41
|
+
* Parse any shipped resource name into its addressed form.
|
|
42
|
+
*
|
|
43
|
+
* parseResource('doc:bm90ZXMubWQ') → { kind: 'doc', path: 'notes.md' }
|
|
44
|
+
* parseResource('app:canvas:board') → { kind: 'app', appId: 'canvas', name: 'board' }
|
|
45
|
+
* parseResource('tasks') → { kind: 'table', name: 'tasks' }
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseResource(resource: unknown): Checked<ParsedResource>;
|
|
48
|
+
/** UTF-8 → unpadded base64url. Portable: no Buffer, no btoa. */
|
|
49
|
+
export declare function encodeBase64Url(text: string): string;
|
|
50
|
+
/** Unpadded base64url → UTF-8, or null when the input is not base64url. */
|
|
51
|
+
export declare function decodeBase64Url(encoded: string): string | null;
|
|
52
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/contracts/resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAqC,MAAM,eAAe,CAAC;AAEhF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,gEAAgE;AAChE,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,YAA8C,CAAC;AAE1F,uEAAuE;AACvE,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,KAAG,YAA+C,CAAC;AAE5F,uDAAuD;AACvD,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,EAAE,MAAM,MAAM,KAAG,YAAsC,CAAC;AAEjG;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CA6BxE;AAID,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAapD;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoB9D"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource identity — the names of live channels.
|
|
3
|
+
*
|
|
4
|
+
* A resource is a named channel over the local wire
|
|
5
|
+
* (local-live-protocol.md §1). The shipped grammar:
|
|
6
|
+
*
|
|
7
|
+
* tasks a well-known singleton table
|
|
8
|
+
* app:<appId>:<name> a per-app named resource
|
|
9
|
+
* doc:<base64url(path)> a live document, addressed by local path
|
|
10
|
+
* files:<base64url(dir)> a directory listing channel
|
|
11
|
+
*
|
|
12
|
+
* A shared resource additionally receives a cloud id that never depends on
|
|
13
|
+
* any user's local path (realtime-sdk.md §2); path-derived ids are for
|
|
14
|
+
* unshared local resources only. Renaming a materialized file must not
|
|
15
|
+
* change a shared id — which is exactly why the shared id is not derived
|
|
16
|
+
* from the path.
|
|
17
|
+
*/
|
|
18
|
+
import { pass, failShape, isNonEmptyString } from './validate.js';
|
|
19
|
+
/** `doc:<base64url(path)>` for the document at a local path. */
|
|
20
|
+
export const docResource = (path) => `doc:${encodeBase64Url(path)}`;
|
|
21
|
+
/** `files:<base64url(dir)>` for the listing channel of a directory. */
|
|
22
|
+
export const filesResource = (dir) => `files:${encodeBase64Url(dir)}`;
|
|
23
|
+
/** `app:<appId>:<name>` for an app-scoped resource. */
|
|
24
|
+
export const appResource = (appId, name) => `app:${appId}:${name}`;
|
|
25
|
+
/**
|
|
26
|
+
* Parse any shipped resource name into its addressed form.
|
|
27
|
+
*
|
|
28
|
+
* parseResource('doc:bm90ZXMubWQ') → { kind: 'doc', path: 'notes.md' }
|
|
29
|
+
* parseResource('app:canvas:board') → { kind: 'app', appId: 'canvas', name: 'board' }
|
|
30
|
+
* parseResource('tasks') → { kind: 'table', name: 'tasks' }
|
|
31
|
+
*/
|
|
32
|
+
export function parseResource(resource) {
|
|
33
|
+
if (!isNonEmptyString(resource)) {
|
|
34
|
+
return failShape(`a resource name like "tasks" or "doc:${encodeBase64Url('notes.md')}"`, `got ${typeof resource}`);
|
|
35
|
+
}
|
|
36
|
+
const [prefix] = resource.split(':', 1);
|
|
37
|
+
switch (prefix) {
|
|
38
|
+
case 'doc': {
|
|
39
|
+
const path = decodeBase64Url(resource.slice('doc:'.length));
|
|
40
|
+
return path === null
|
|
41
|
+
? failShape(`"doc:${encodeBase64Url('notes.md')}" (base64url of the path)`, `"${resource}" carries invalid base64url`)
|
|
42
|
+
: pass({ kind: 'doc', path });
|
|
43
|
+
}
|
|
44
|
+
case 'files': {
|
|
45
|
+
const dir = decodeBase64Url(resource.slice('files:'.length));
|
|
46
|
+
return dir === null
|
|
47
|
+
? failShape(`"files:${encodeBase64Url('notes')}" (base64url of the directory)`, `"${resource}" carries invalid base64url`)
|
|
48
|
+
: pass({ kind: 'files', dir });
|
|
49
|
+
}
|
|
50
|
+
case 'app': {
|
|
51
|
+
const rest = resource.slice('app:'.length);
|
|
52
|
+
const divider = rest.indexOf(':');
|
|
53
|
+
if (divider <= 0 || divider === rest.length - 1) {
|
|
54
|
+
return failShape('"app:<appId>:<name>", e.g. "app:canvas:board"', `"${resource}" is missing the appId or name segment`);
|
|
55
|
+
}
|
|
56
|
+
return pass({ kind: 'app', appId: rest.slice(0, divider), name: rest.slice(divider + 1) });
|
|
57
|
+
}
|
|
58
|
+
default:
|
|
59
|
+
return pass({ kind: 'table', name: resource });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
63
|
+
/** UTF-8 → unpadded base64url. Portable: no Buffer, no btoa. */
|
|
64
|
+
export function encodeBase64Url(text) {
|
|
65
|
+
const bytes = new TextEncoder().encode(text);
|
|
66
|
+
let out = '';
|
|
67
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
68
|
+
const a = bytes[i];
|
|
69
|
+
const b = bytes[i + 1];
|
|
70
|
+
const c = bytes[i + 2];
|
|
71
|
+
out += BASE64_ALPHABET[a >> 2];
|
|
72
|
+
out += BASE64_ALPHABET[((a & 0x03) << 4) | ((b ?? 0) >> 4)];
|
|
73
|
+
if (b !== undefined)
|
|
74
|
+
out += BASE64_ALPHABET[((b & 0x0f) << 2) | ((c ?? 0) >> 6)];
|
|
75
|
+
if (c !== undefined)
|
|
76
|
+
out += BASE64_ALPHABET[c & 0x3f];
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/** Unpadded base64url → UTF-8, or null when the input is not base64url. */
|
|
81
|
+
export function decodeBase64Url(encoded) {
|
|
82
|
+
if (encoded.length % 4 === 1)
|
|
83
|
+
return null;
|
|
84
|
+
const bytes = [];
|
|
85
|
+
let buffer = 0;
|
|
86
|
+
let bits = 0;
|
|
87
|
+
for (const char of encoded) {
|
|
88
|
+
const value = BASE64_ALPHABET.indexOf(char);
|
|
89
|
+
if (value === -1)
|
|
90
|
+
return null;
|
|
91
|
+
buffer = (buffer << 6) | value;
|
|
92
|
+
bits += 6;
|
|
93
|
+
if (bits >= 8) {
|
|
94
|
+
bits -= 8;
|
|
95
|
+
bytes.push((buffer >> bits) & 0xff);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(new Uint8Array(bytes));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/contracts/resources.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshots — the resume point of every recovery path.
|
|
3
|
+
*
|
|
4
|
+
* A snapshot is the full value of each requested resource, consistent as of
|
|
5
|
+
* `seq` when `stable` is true (local-live-protocol.md §1). The builder may
|
|
6
|
+
* race writes; after 3 attempts it serves best-effort data with
|
|
7
|
+
* `stable: false`. Clients may treat an unstable snapshot like a stable one
|
|
8
|
+
* — the live stream repairs staleness — but must never cache it as a
|
|
9
|
+
* consistency proof.
|
|
10
|
+
*
|
|
11
|
+
* Recovery rule: every failure path (seq gap beyond the buffer, `reset`
|
|
12
|
+
* frame, failed backfill) converges on "fetch a snapshot and resume from
|
|
13
|
+
* its seq".
|
|
14
|
+
*/
|
|
15
|
+
import { type Checked } from './validate.js';
|
|
16
|
+
import type { ResourceName } from './resources.js';
|
|
17
|
+
export interface Snapshot {
|
|
18
|
+
readonly seq: number;
|
|
19
|
+
/** False when every build attempt raced a write; data is best-effort at this seq. */
|
|
20
|
+
readonly stable: boolean;
|
|
21
|
+
readonly resources: Readonly<Record<ResourceName, unknown>>;
|
|
22
|
+
}
|
|
23
|
+
export declare function checkSnapshot(input: unknown): Checked<Snapshot>;
|
|
24
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/contracts/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAoC,MAAM,eAAe,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;CAC7D;AAID,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAQ/D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshots — the resume point of every recovery path.
|
|
3
|
+
*
|
|
4
|
+
* A snapshot is the full value of each requested resource, consistent as of
|
|
5
|
+
* `seq` when `stable` is true (local-live-protocol.md §1). The builder may
|
|
6
|
+
* race writes; after 3 attempts it serves best-effort data with
|
|
7
|
+
* `stable: false`. Clients may treat an unstable snapshot like a stable one
|
|
8
|
+
* — the live stream repairs staleness — but must never cache it as a
|
|
9
|
+
* consistency proof.
|
|
10
|
+
*
|
|
11
|
+
* Recovery rule: every failure path (seq gap beyond the buffer, `reset`
|
|
12
|
+
* frame, failed backfill) converges on "fetch a snapshot and resume from
|
|
13
|
+
* its seq".
|
|
14
|
+
*/
|
|
15
|
+
import { pass, failShape, isRecord, isSeq } from './validate.js';
|
|
16
|
+
const EXAMPLE = '{ seq: 42, stable: true, resources: { tasks: [...] } }';
|
|
17
|
+
export function checkSnapshot(input) {
|
|
18
|
+
if (!isRecord(input))
|
|
19
|
+
return failShape(EXAMPLE, `got ${input === null ? 'null' : typeof input}`);
|
|
20
|
+
if (!isSeq(input.seq))
|
|
21
|
+
return failShape(EXAMPLE, `seq was ${JSON.stringify(input.seq)}, not a non-negative integer`);
|
|
22
|
+
if (!isRecord(input.resources)) {
|
|
23
|
+
return failShape(EXAMPLE, `resources was ${JSON.stringify(input.resources)}, not an object`);
|
|
24
|
+
}
|
|
25
|
+
const stable = input.stable !== false; // absent means stable: the field is an additive escape hatch
|
|
26
|
+
return pass({ seq: input.seq, stable, resources: input.resources });
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/contracts/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAgB,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAU/E,MAAM,OAAO,GAAG,wDAAwD,CAAC;AAEzE,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC,OAAO,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IACjG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC,OAAO,EAAE,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IACrH,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC,OAAO,EAAE,iBAAiB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,6DAA6D;IACpG,OAAO,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared validation vocabulary for every contract in this package.
|
|
3
|
+
*
|
|
4
|
+
* Validators never throw on bad wire data — a peer being wrong is an
|
|
5
|
+
* expected condition, not an exception (local-live-protocol.md: unknown
|
|
6
|
+
* frames mean "the other end is older"). They return a `Checked<T>` so
|
|
7
|
+
* callers must look at the outcome, and every failure message is
|
|
8
|
+
* example-first: it states what a correct value looks like before saying
|
|
9
|
+
* what was wrong (see PURPOSE.md axiom on error shape).
|
|
10
|
+
*/
|
|
11
|
+
export type Checked<T> = {
|
|
12
|
+
readonly ok: true;
|
|
13
|
+
readonly value: T;
|
|
14
|
+
} | {
|
|
15
|
+
readonly ok: false;
|
|
16
|
+
readonly error: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const pass: <T>(value: T) => Checked<T>;
|
|
19
|
+
export declare const fail: <T = never>(error: string) => Checked<T>;
|
|
20
|
+
/**
|
|
21
|
+
* Build an example-first failure: `expected` is a correct literal shape,
|
|
22
|
+
* `problem` names the specific field that broke it.
|
|
23
|
+
*
|
|
24
|
+
* failShape('{ seq: 42, resource: "tasks", op: "update" }',
|
|
25
|
+
* 'op was "patch", not one of insert|update|delete|replace|invalidate')
|
|
26
|
+
*/
|
|
27
|
+
export declare const failShape: <T = never>(expected: string, problem: string) => Checked<T>;
|
|
28
|
+
export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
|
|
29
|
+
export declare const isNonEmptyString: (value: unknown) => value is string;
|
|
30
|
+
/** A strictly increasing wire sequence number: a non-negative safe integer. */
|
|
31
|
+
export declare const isSeq: (value: unknown) => value is number;
|
|
32
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/contracts/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,OAAO,CAAC,CAAC,IACjB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACxC;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,OAAO,CAAC,CAAC,CAA0B,CAAC;AAEvE,eAAO,MAAM,IAAI,GAAI,CAAC,GAAG,KAAK,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,CAAC,CAA2B,CAAC;AAErF;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,GAAG,KAAK,EAAE,UAAU,MAAM,EAAE,SAAS,MAAM,KAAG,OAAO,CAAC,CAAC,CACvC,CAAC;AAE5C,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAC;AAEvE,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACZ,CAAC;AAEhD,+EAA+E;AAC/E,eAAO,MAAM,KAAK,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACwB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared validation vocabulary for every contract in this package.
|
|
3
|
+
*
|
|
4
|
+
* Validators never throw on bad wire data — a peer being wrong is an
|
|
5
|
+
* expected condition, not an exception (local-live-protocol.md: unknown
|
|
6
|
+
* frames mean "the other end is older"). They return a `Checked<T>` so
|
|
7
|
+
* callers must look at the outcome, and every failure message is
|
|
8
|
+
* example-first: it states what a correct value looks like before saying
|
|
9
|
+
* what was wrong (see PURPOSE.md axiom on error shape).
|
|
10
|
+
*/
|
|
11
|
+
export const pass = (value) => ({ ok: true, value });
|
|
12
|
+
export const fail = (error) => ({ ok: false, error });
|
|
13
|
+
/**
|
|
14
|
+
* Build an example-first failure: `expected` is a correct literal shape,
|
|
15
|
+
* `problem` names the specific field that broke it.
|
|
16
|
+
*
|
|
17
|
+
* failShape('{ seq: 42, resource: "tasks", op: "update" }',
|
|
18
|
+
* 'op was "patch", not one of insert|update|delete|replace|invalidate')
|
|
19
|
+
*/
|
|
20
|
+
export const failShape = (expected, problem) => fail(`expected ${expected} — ${problem}`);
|
|
21
|
+
export const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
22
|
+
export const isNonEmptyString = (value) => typeof value === 'string' && value.length > 0;
|
|
23
|
+
/** A strictly increasing wire sequence number: a non-negative safe integer. */
|
|
24
|
+
export const isSeq = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
25
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/contracts/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAI,KAAQ,EAAc,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,IAAI,GAAG,CAAY,KAAa,EAAc,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAErF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAY,QAAgB,EAAE,OAAe,EAAc,EAAE,CACpF,IAAI,CAAC,YAAY,QAAQ,MAAM,OAAO,EAAE,CAAC,CAAC;AAE5C,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CAC3E,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAmB,EAAE,CAClE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AAEhD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAc,EAAmB,EAAE,CACvD,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The adapter contract: what it means to serve an entity type.
|
|
3
|
+
*
|
|
4
|
+
* An adapter is how a KIND of entity rides the seven realtime rails —
|
|
5
|
+
* watch, detect, record, send, receive, apply, verify. The entity's
|
|
6
|
+
* `type` selects its adapter; the rails themselves never change shape
|
|
7
|
+
* per type. Ratified by entity-model.md ("Type selects the complete
|
|
8
|
+
* adapter cycle") and entity-implementation.md.
|
|
9
|
+
*
|
|
10
|
+
* Axioms:
|
|
11
|
+
*
|
|
12
|
+
* 1. ONE ADAPTER PER TYPE, EVERY TYPE ASSIGNED. The alphabet belongs
|
|
13
|
+
* to the registry; the adapter map covers it exactly — no type
|
|
14
|
+
* unassigned, no type claimed twice. Assigned is not yet
|
|
15
|
+
* operational: operational is earned rail by rail, and absence is
|
|
16
|
+
* honest.
|
|
17
|
+
* 2. ADAPTERS OWN BEHAVIOR; THE REGISTRY OWNS IDENTITY. A lawful type
|
|
18
|
+
* transition that crosses adapters is a handoff on the same UUID,
|
|
19
|
+
* never a rebirth.
|
|
20
|
+
* 3. ONE SOCKET PER RAIL. A rail's callable surfaces are ratified
|
|
21
|
+
* HERE, once, for every adapter (`SOCKETS`). An adapter binds a
|
|
22
|
+
* rail's whole socket or binds the rail not at all; a rail whose
|
|
23
|
+
* socket is not yet ratified cannot be bound by anyone.
|
|
24
|
+
* 4. A TRUTH READ NEVER LIES. Every assigned type names its source of
|
|
25
|
+
* truth in one sentence, and reading it produces exactly one of
|
|
26
|
+
* three honest answers: present, absent, or unsettled.
|
|
27
|
+
* 5. RAILS ARE HONEST. An unbound rail is absent — never stubbed,
|
|
28
|
+
* never defaulted.
|
|
29
|
+
* 6. THE CONTRACT IS FROZEN GROUND. A descriptor is immutable from
|
|
30
|
+
* the moment it loads.
|
|
31
|
+
* 7. THE BASE ORCHESTRATES; THE ADAPTER SUPPLIES TYPE BEHAVIOR. A
|
|
32
|
+
* rail earns a socket only when a real consumer proves that
|
|
33
|
+
* behavior differs by type — never for symmetry.
|
|
34
|
+
*/
|
|
35
|
+
export type Rail = 'watch' | 'detect' | 'record' | 'send' | 'receive' | 'apply' | 'verify';
|
|
36
|
+
/** The seven rails, declared once, frozen, in journey order. */
|
|
37
|
+
export declare const RAILS: readonly Rail[];
|
|
38
|
+
/**
|
|
39
|
+
* The three honest answers of a truth read (axiom 4):
|
|
40
|
+
*
|
|
41
|
+
* { truth: 'present', version, state } the complete current truth,
|
|
42
|
+
* fingerprinted — equal versions mean equal truth, and equal truth
|
|
43
|
+
* is silence
|
|
44
|
+
* { truth: 'absent' } the ground genuinely is not
|
|
45
|
+
* there — a fact, not an error
|
|
46
|
+
* { truth: 'unsettled', reason } changing or unreadable right
|
|
47
|
+
* now; read again — never a partial answer
|
|
48
|
+
*/
|
|
49
|
+
export type TruthReading = {
|
|
50
|
+
readonly truth: 'present';
|
|
51
|
+
readonly version: string;
|
|
52
|
+
readonly state?: unknown;
|
|
53
|
+
} | {
|
|
54
|
+
readonly truth: 'absent';
|
|
55
|
+
} | {
|
|
56
|
+
readonly truth: 'unsettled';
|
|
57
|
+
readonly reason?: string | undefined;
|
|
58
|
+
};
|
|
59
|
+
/** The one ratified socket: detect's truth read. */
|
|
60
|
+
export interface DetectSocket {
|
|
61
|
+
readonly truth: (path: string) => TruthReading;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The ratified sockets — for each rail that has one, the exact surfaces
|
|
65
|
+
* every binding adapter implements. Same names, same shapes, every
|
|
66
|
+
* type. ONE socket is ratified today (detect), derived from what
|
|
67
|
+
* production detection actually asks of a type. Watch and record earn
|
|
68
|
+
* none at this bar (adjudicated 2026-08-03 under axiom 7); apply and
|
|
69
|
+
* verify join only when the Send/Receive rails exist to derive their
|
|
70
|
+
* socket from. A socket is ratified from a real consumer or it is not
|
|
71
|
+
* ratified at all.
|
|
72
|
+
*/
|
|
73
|
+
export declare const SOCKETS: Readonly<Partial<Record<Rail, readonly string[]>>>;
|
|
74
|
+
/**
|
|
75
|
+
* A lawful adapter descriptor. The shape is deliberately tiny:
|
|
76
|
+
*
|
|
77
|
+
* name — the adapter's one name
|
|
78
|
+
* types — { entityType: 'one-sentence source of truth', ... }
|
|
79
|
+
* rails — only rails whose ratified socket the adapter fully
|
|
80
|
+
* implements; detect is the only bindable rail today
|
|
81
|
+
*/
|
|
82
|
+
export interface AdapterDescriptor {
|
|
83
|
+
readonly name: string;
|
|
84
|
+
readonly types: Readonly<Record<string, string>>;
|
|
85
|
+
readonly rails: {
|
|
86
|
+
readonly detect?: DetectSocket;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Convict a malformed adapter descriptor loudly; return it (typed)
|
|
91
|
+
* unchanged when lawful.
|
|
92
|
+
*/
|
|
93
|
+
export declare function assertAdapter(adapter: unknown): AdapterDescriptor;
|
|
94
|
+
/**
|
|
95
|
+
* Build the one type → adapter map, convicting every violation of
|
|
96
|
+
* axiom 1: an adapter claiming a type outside the alphabet, two
|
|
97
|
+
* adapters claiming one type, or an alphabet type left unassigned.
|
|
98
|
+
* Pure — callers own where the alphabet and adapters come from.
|
|
99
|
+
*/
|
|
100
|
+
export declare function buildTypeMap(adapters: Iterable<unknown>, alphabet: Iterable<string>): Map<string, AdapterDescriptor>;
|
|
101
|
+
/** A failed look at ground, as the host's filesystem reported it. */
|
|
102
|
+
export interface FailedLook {
|
|
103
|
+
readonly code?: string | undefined;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The three-state vocabulary's one shared judgment: what a FAILED look
|
|
107
|
+
* at ground means. ENOENT and ENOTDIR are the filesystem itself saying
|
|
108
|
+
* "not there" — honest ABSENCE, a fact. Every other failure means the
|
|
109
|
+
* LOOK failed, not the ground — UNSETTLED: no conclusion, read again.
|
|
110
|
+
*
|
|
111
|
+
* Every truth reader classifies through this one function so no reader
|
|
112
|
+
* can invent its own idea of absence: mistaking "could not look" for
|
|
113
|
+
* "not there" is the exact lie axiom 4 forbids. Only present or absent
|
|
114
|
+
* may produce a conclusion.
|
|
115
|
+
*/
|
|
116
|
+
export declare const lookFailed: (error: FailedLook) => {
|
|
117
|
+
readonly truth: "absent";
|
|
118
|
+
} | {
|
|
119
|
+
readonly truth: "unsettled";
|
|
120
|
+
readonly reason: string | undefined;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* The managed-reference adapter: Amalgm's OWN link kind, never
|
|
124
|
+
* conflated with a filesystem symlink. Its payload is the target
|
|
125
|
+
* entity's UUID — identity, not an address — and its local rendering as
|
|
126
|
+
* an ordinary symlink is DERIVED state (entity-model.md, symlink
|
|
127
|
+
* section). ASSIGNED, NOT YET OPERATIONAL — and that absence is the
|
|
128
|
+
* honest truth: no rail is bound because the projection lives inside
|
|
129
|
+
* the registration boundary today; its detect socket is ratified from
|
|
130
|
+
* that seam when the projection is extracted, never invented ahead of
|
|
131
|
+
* it.
|
|
132
|
+
*/
|
|
133
|
+
export declare const referenceAdapter: AdapterDescriptor;
|
|
134
|
+
//# sourceMappingURL=adapter-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-contract.d.ts","sourceRoot":"","sources":["../../src/detection/adapter-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE3F,gEAAgE;AAChE,eAAO,MAAM,KAAK,EAAE,SAAS,IAAI,EAE/B,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACjF;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAE1E,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;CAChD;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAErE,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC;CACpD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CA6CjE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAC3B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,GACzB,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAsBhC;AAED,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,UAAU,KAChB;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAGpD,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,EAAE,iBAM7B,CAAC"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The adapter contract: what it means to serve an entity type.
|
|
3
|
+
*
|
|
4
|
+
* An adapter is how a KIND of entity rides the seven realtime rails —
|
|
5
|
+
* watch, detect, record, send, receive, apply, verify. The entity's
|
|
6
|
+
* `type` selects its adapter; the rails themselves never change shape
|
|
7
|
+
* per type. Ratified by entity-model.md ("Type selects the complete
|
|
8
|
+
* adapter cycle") and entity-implementation.md.
|
|
9
|
+
*
|
|
10
|
+
* Axioms:
|
|
11
|
+
*
|
|
12
|
+
* 1. ONE ADAPTER PER TYPE, EVERY TYPE ASSIGNED. The alphabet belongs
|
|
13
|
+
* to the registry; the adapter map covers it exactly — no type
|
|
14
|
+
* unassigned, no type claimed twice. Assigned is not yet
|
|
15
|
+
* operational: operational is earned rail by rail, and absence is
|
|
16
|
+
* honest.
|
|
17
|
+
* 2. ADAPTERS OWN BEHAVIOR; THE REGISTRY OWNS IDENTITY. A lawful type
|
|
18
|
+
* transition that crosses adapters is a handoff on the same UUID,
|
|
19
|
+
* never a rebirth.
|
|
20
|
+
* 3. ONE SOCKET PER RAIL. A rail's callable surfaces are ratified
|
|
21
|
+
* HERE, once, for every adapter (`SOCKETS`). An adapter binds a
|
|
22
|
+
* rail's whole socket or binds the rail not at all; a rail whose
|
|
23
|
+
* socket is not yet ratified cannot be bound by anyone.
|
|
24
|
+
* 4. A TRUTH READ NEVER LIES. Every assigned type names its source of
|
|
25
|
+
* truth in one sentence, and reading it produces exactly one of
|
|
26
|
+
* three honest answers: present, absent, or unsettled.
|
|
27
|
+
* 5. RAILS ARE HONEST. An unbound rail is absent — never stubbed,
|
|
28
|
+
* never defaulted.
|
|
29
|
+
* 6. THE CONTRACT IS FROZEN GROUND. A descriptor is immutable from
|
|
30
|
+
* the moment it loads.
|
|
31
|
+
* 7. THE BASE ORCHESTRATES; THE ADAPTER SUPPLIES TYPE BEHAVIOR. A
|
|
32
|
+
* rail earns a socket only when a real consumer proves that
|
|
33
|
+
* behavior differs by type — never for symmetry.
|
|
34
|
+
*/
|
|
35
|
+
/** The seven rails, declared once, frozen, in journey order. */
|
|
36
|
+
export const RAILS = Object.freeze([
|
|
37
|
+
'watch', 'detect', 'record', 'send', 'receive', 'apply', 'verify',
|
|
38
|
+
]);
|
|
39
|
+
/**
|
|
40
|
+
* The ratified sockets — for each rail that has one, the exact surfaces
|
|
41
|
+
* every binding adapter implements. Same names, same shapes, every
|
|
42
|
+
* type. ONE socket is ratified today (detect), derived from what
|
|
43
|
+
* production detection actually asks of a type. Watch and record earn
|
|
44
|
+
* none at this bar (adjudicated 2026-08-03 under axiom 7); apply and
|
|
45
|
+
* verify join only when the Send/Receive rails exist to derive their
|
|
46
|
+
* socket from. A socket is ratified from a real consumer or it is not
|
|
47
|
+
* ratified at all.
|
|
48
|
+
*/
|
|
49
|
+
export const SOCKETS = Object.freeze({
|
|
50
|
+
detect: Object.freeze(['truth']),
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Convict a malformed adapter descriptor loudly; return it (typed)
|
|
54
|
+
* unchanged when lawful.
|
|
55
|
+
*/
|
|
56
|
+
export function assertAdapter(adapter) {
|
|
57
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
58
|
+
const where = isRecord(adapter) && typeof adapter['name'] === 'string' && adapter['name']
|
|
59
|
+
? `adapter ${JSON.stringify(adapter['name'])}` : 'adapter';
|
|
60
|
+
if (!isRecord(adapter) || !Object.isFrozen(adapter)) {
|
|
61
|
+
throw new Error(`${where}: a descriptor is a frozen object`);
|
|
62
|
+
}
|
|
63
|
+
if (typeof adapter['name'] !== 'string' || adapter['name'].length === 0) {
|
|
64
|
+
throw new Error('adapter: name is a non-empty string');
|
|
65
|
+
}
|
|
66
|
+
const { types, rails } = adapter;
|
|
67
|
+
if (!isRecord(types) || !Object.isFrozen(types) || Object.keys(types).length === 0) {
|
|
68
|
+
throw new Error(`${where}: types is a frozen, non-empty map of entity type → declared truth`);
|
|
69
|
+
}
|
|
70
|
+
for (const [type, truth] of Object.entries(types)) {
|
|
71
|
+
if (typeof truth !== 'string' || truth.length === 0) {
|
|
72
|
+
throw new Error(`${where}: type ${JSON.stringify(type)} must declare its source of truth in one sentence`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!isRecord(rails) || !Object.isFrozen(rails)) {
|
|
76
|
+
throw new Error(`${where}: rails is a frozen map (empty is honest; a stub is not)`);
|
|
77
|
+
}
|
|
78
|
+
for (const [rail, surfaces] of Object.entries(rails)) {
|
|
79
|
+
if (!RAILS.includes(rail)) {
|
|
80
|
+
throw new Error(`${where}: ${JSON.stringify(rail)} is not a rail — the rails are ${RAILS.join(', ')}`);
|
|
81
|
+
}
|
|
82
|
+
const socket = SOCKETS[rail];
|
|
83
|
+
if (!socket) {
|
|
84
|
+
throw new Error(`${where}: rail ${rail} has no ratified socket yet — a socket is ratified in the contract before anyone binds it`);
|
|
85
|
+
}
|
|
86
|
+
if (!isRecord(surfaces) || !Object.isFrozen(surfaces)) {
|
|
87
|
+
throw new Error(`${where}: rail ${rail} must bind a frozen map of its socket's surfaces`);
|
|
88
|
+
}
|
|
89
|
+
const bound = Object.keys(surfaces).sort();
|
|
90
|
+
if (bound.join(',') !== [...socket].sort().join(',')) {
|
|
91
|
+
throw new Error(`${where}: rail ${rail} binds [${bound.join(', ')}] but its socket is [${socket.join(', ')}] — an adapter implements a rail's whole socket or binds the rail not at all`);
|
|
92
|
+
}
|
|
93
|
+
for (const [surface, fn] of Object.entries(surfaces)) {
|
|
94
|
+
if (typeof fn !== 'function') {
|
|
95
|
+
throw new Error(`${where}: ${rail}.${surface} must be a function — an unbuilt surface is left absent, never stubbed`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return adapter;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build the one type → adapter map, convicting every violation of
|
|
103
|
+
* axiom 1: an adapter claiming a type outside the alphabet, two
|
|
104
|
+
* adapters claiming one type, or an alphabet type left unassigned.
|
|
105
|
+
* Pure — callers own where the alphabet and adapters come from.
|
|
106
|
+
*/
|
|
107
|
+
export function buildTypeMap(adapters, alphabet) {
|
|
108
|
+
const known = new Set(alphabet);
|
|
109
|
+
const byType = new Map();
|
|
110
|
+
for (const candidate of adapters) {
|
|
111
|
+
const adapter = assertAdapter(candidate);
|
|
112
|
+
for (const type of Object.keys(adapter.types)) {
|
|
113
|
+
if (!known.has(type)) {
|
|
114
|
+
throw new Error(`adapter ${adapter.name} claims ${JSON.stringify(type)}, which is not in the registry's alphabet`);
|
|
115
|
+
}
|
|
116
|
+
const holder = byType.get(type);
|
|
117
|
+
if (holder) {
|
|
118
|
+
throw new Error(`entity type ${JSON.stringify(type)} is claimed by both ${holder.name} and ${adapter.name} — one adapter per type`);
|
|
119
|
+
}
|
|
120
|
+
byType.set(type, adapter);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const type of known) {
|
|
124
|
+
if (!byType.has(type)) {
|
|
125
|
+
throw new Error(`entity type ${JSON.stringify(type)} has no adapter — every type is assigned`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return byType;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The three-state vocabulary's one shared judgment: what a FAILED look
|
|
132
|
+
* at ground means. ENOENT and ENOTDIR are the filesystem itself saying
|
|
133
|
+
* "not there" — honest ABSENCE, a fact. Every other failure means the
|
|
134
|
+
* LOOK failed, not the ground — UNSETTLED: no conclusion, read again.
|
|
135
|
+
*
|
|
136
|
+
* Every truth reader classifies through this one function so no reader
|
|
137
|
+
* can invent its own idea of absence: mistaking "could not look" for
|
|
138
|
+
* "not there" is the exact lie axiom 4 forbids. Only present or absent
|
|
139
|
+
* may produce a conclusion.
|
|
140
|
+
*/
|
|
141
|
+
export const lookFailed = (error) => error.code === 'ENOENT' || error.code === 'ENOTDIR'
|
|
142
|
+
? { truth: 'absent' }
|
|
143
|
+
: { truth: 'unsettled', reason: error.code };
|
|
144
|
+
/**
|
|
145
|
+
* The managed-reference adapter: Amalgm's OWN link kind, never
|
|
146
|
+
* conflated with a filesystem symlink. Its payload is the target
|
|
147
|
+
* entity's UUID — identity, not an address — and its local rendering as
|
|
148
|
+
* an ordinary symlink is DERIVED state (entity-model.md, symlink
|
|
149
|
+
* section). ASSIGNED, NOT YET OPERATIONAL — and that absence is the
|
|
150
|
+
* honest truth: no rail is bound because the projection lives inside
|
|
151
|
+
* the registration boundary today; its detect socket is ratified from
|
|
152
|
+
* that seam when the projection is extracted, never invented ahead of
|
|
153
|
+
* it.
|
|
154
|
+
*/
|
|
155
|
+
export const referenceAdapter = Object.freeze({
|
|
156
|
+
name: 'reference',
|
|
157
|
+
types: Object.freeze({
|
|
158
|
+
reference: "its target entity's UUID — identity, not an address; the rendered symlink is derived state",
|
|
159
|
+
}),
|
|
160
|
+
rails: Object.freeze({}),
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=adapter-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-contract.js","sourceRoot":"","sources":["../../src/detection/adapter-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amalgm's file-type policy: which physical type a piece of file ground
|
|
3
|
+
* takes. Ratified by entity-model.md ("Formats live underneath the file
|
|
4
|
+
* types"; text versus binary is an adapter difference, never an
|
|
5
|
+
* identity difference).
|
|
6
|
+
*
|
|
7
|
+
* Classification is an INJECTED policy everywhere it is used — the
|
|
8
|
+
* registration boundary and its lenses refuse to construct without one
|
|
9
|
+
* — so it lives in its own file with no other job: one definition,
|
|
10
|
+
* replaceable whole.
|
|
11
|
+
*
|
|
12
|
+
* The rule is the oldest one there is: bytes holding a NUL are not
|
|
13
|
+
* text. What matters is WHERE that fact comes from. It is not read
|
|
14
|
+
* here. Detect's single read of a file produces its fingerprint and
|
|
15
|
+
* this report together, and this policy only names the type that report
|
|
16
|
+
* implies.
|
|
17
|
+
*
|
|
18
|
+
* That is the whole law: A FILE'S HASH AND ITS TYPE DESCRIBE THE SAME
|
|
19
|
+
* OBSERVED BYTES. A policy that opened the ground itself would be a
|
|
20
|
+
* second look — and a second look can meet different bytes, or none at
|
|
21
|
+
* all, leaving a record whose type contradicts the payload beside it.
|
|
22
|
+
* So this function takes evidence, never a path; it is total, and it
|
|
23
|
+
* cannot fail.
|
|
24
|
+
*/
|
|
25
|
+
export type FileType = 'file.binary' | 'file.text';
|
|
26
|
+
/** Detect's report on the bytes it hashed — the same read as the fingerprint. */
|
|
27
|
+
export interface ClassifyEvidence {
|
|
28
|
+
readonly binary: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function classifyFile({ binary }: ClassifyEvidence): FileType;
|
|
31
|
+
//# sourceMappingURL=classify.d.ts.map
|