@amalgm/live 0.2.27 → 0.2.28
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 +38 -25
- package/README.md +1 -2
- package/dist/contracts/index.d.ts +0 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +0 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/detection/record-proposal.d.ts +3 -2
- package/dist/detection/record-proposal.d.ts.map +1 -1
- package/dist/detection/record-proposal.js +9 -7
- package/dist/detection/record-proposal.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/machines/index.d.ts +0 -2
- package/dist/machines/index.d.ts.map +1 -1
- package/dist/machines/index.js +0 -2
- package/dist/machines/index.js.map +1 -1
- package/dist/records/authority.d.ts +23 -0
- package/dist/records/authority.d.ts.map +1 -0
- package/dist/records/authority.js +28 -0
- package/dist/records/authority.js.map +1 -0
- package/dist/records/codec.d.ts +6 -0
- package/dist/records/codec.d.ts.map +1 -0
- package/dist/records/codec.js +42 -0
- package/dist/records/codec.js.map +1 -0
- package/dist/records/contracts.d.ts +46 -0
- package/dist/records/contracts.d.ts.map +1 -0
- package/dist/records/contracts.js +114 -0
- package/dist/records/contracts.js.map +1 -0
- package/dist/records/inbox.d.ts +18 -0
- package/dist/records/inbox.d.ts.map +1 -0
- package/dist/records/inbox.js +34 -0
- package/dist/records/inbox.js.map +1 -0
- package/dist/records/index.d.ts +7 -0
- package/dist/records/index.d.ts.map +1 -0
- package/dist/records/index.js +7 -0
- package/dist/records/index.js.map +1 -0
- package/dist/records/outbox.d.ts +20 -0
- package/dist/records/outbox.d.ts.map +1 -0
- package/dist/records/outbox.js +44 -0
- package/dist/records/outbox.js.map +1 -0
- package/dist/registration/user-ground.d.ts +4 -4
- package/dist/registration/user-ground.d.ts.map +1 -1
- package/dist/registration/user-ground.js +8 -3
- package/dist/registration/user-ground.js.map +1 -1
- package/dist-cjs/contracts/binding.js +26 -0
- package/dist-cjs/contracts/binding.js.map +1 -0
- package/dist-cjs/contracts/content.js +99 -0
- package/dist-cjs/contracts/content.js.map +1 -0
- package/dist-cjs/contracts/docs.js +56 -0
- package/dist-cjs/contracts/docs.js.map +1 -0
- package/dist-cjs/contracts/events.js +44 -0
- package/dist-cjs/contracts/events.js.map +1 -0
- package/dist-cjs/contracts/frames/gateway.js +39 -0
- package/dist-cjs/contracts/frames/gateway.js.map +1 -0
- package/dist-cjs/contracts/frames/stream.js +20 -0
- package/dist-cjs/contracts/frames/stream.js.map +1 -0
- package/dist-cjs/contracts/frames/tunnel.js +60 -0
- package/dist-cjs/contracts/frames/tunnel.js.map +1 -0
- package/dist-cjs/contracts/index.js +34 -0
- package/dist-cjs/contracts/index.js.map +1 -0
- package/dist-cjs/contracts/presence.js +38 -0
- package/dist-cjs/contracts/presence.js.map +1 -0
- package/dist-cjs/contracts/resources.js +114 -0
- package/dist-cjs/contracts/resources.js.map +1 -0
- package/dist-cjs/contracts/snapshot.js +31 -0
- package/dist-cjs/contracts/snapshot.js.map +1 -0
- package/dist-cjs/contracts/validate.js +34 -0
- package/dist-cjs/contracts/validate.js.map +1 -0
- package/dist-cjs/detection/adapter-contract.js +168 -0
- package/dist-cjs/detection/adapter-contract.js.map +1 -0
- package/dist-cjs/detection/batch.js +71 -0
- package/dist-cjs/detection/batch.js.map +1 -0
- package/dist-cjs/detection/classify.js +48 -0
- package/dist-cjs/detection/classify.js.map +1 -0
- package/dist-cjs/detection/content.js +122 -0
- package/dist-cjs/detection/content.js.map +1 -0
- package/dist-cjs/detection/continuity.js +88 -0
- package/dist-cjs/detection/continuity.js.map +1 -0
- package/dist-cjs/detection/diff.js +193 -0
- package/dist-cjs/detection/diff.js.map +1 -0
- package/dist-cjs/detection/echo.js +25 -0
- package/dist-cjs/detection/echo.js.map +1 -0
- package/dist-cjs/detection/enrollment.js +189 -0
- package/dist-cjs/detection/enrollment.js.map +1 -0
- package/dist-cjs/detection/index.js +57 -0
- package/dist-cjs/detection/index.js.map +1 -0
- package/dist-cjs/detection/record-proposal.js +170 -0
- package/dist-cjs/detection/record-proposal.js.map +1 -0
- package/dist-cjs/detection/refusal.js +44 -0
- package/dist-cjs/detection/refusal.js.map +1 -0
- package/dist-cjs/detection/repository.js +67 -0
- package/dist-cjs/detection/repository.js.map +1 -0
- package/dist-cjs/detection/territory.js +82 -0
- package/dist-cjs/detection/territory.js.map +1 -0
- package/dist-cjs/detection/watch-boundary.js +51 -0
- package/dist-cjs/detection/watch-boundary.js.map +1 -0
- package/dist-cjs/entities/bytes.js +92 -0
- package/dist-cjs/entities/bytes.js.map +1 -0
- package/dist-cjs/entities/cloud.js +171 -0
- package/dist-cjs/entities/cloud.js.map +1 -0
- package/dist-cjs/entities/index.js +39 -0
- package/dist-cjs/entities/index.js.map +1 -0
- package/dist-cjs/entities/moves.js +85 -0
- package/dist-cjs/entities/moves.js.map +1 -0
- package/dist-cjs/entities/repo-states.js +256 -0
- package/dist-cjs/entities/repo-states.js.map +1 -0
- package/dist-cjs/entities/rules.js +216 -0
- package/dist-cjs/entities/rules.js.map +1 -0
- package/dist-cjs/entities/types.js +44 -0
- package/dist-cjs/entities/types.js.map +1 -0
- package/dist-cjs/files/commands.js +120 -0
- package/dist-cjs/files/commands.js.map +1 -0
- package/dist-cjs/files/index.js +19 -0
- package/dist-cjs/files/index.js.map +1 -0
- package/dist-cjs/index.js +39 -0
- package/dist-cjs/index.js.map +1 -0
- package/dist-cjs/machines/arbitration.js +41 -0
- package/dist-cjs/machines/arbitration.js.map +1 -0
- package/dist-cjs/machines/backoff.js +38 -0
- package/dist-cjs/machines/backoff.js.map +1 -0
- package/dist-cjs/machines/bootstrap.js +110 -0
- package/dist-cjs/machines/bootstrap.js.map +1 -0
- package/dist-cjs/machines/chunks.js +72 -0
- package/dist-cjs/machines/chunks.js.map +1 -0
- package/dist-cjs/machines/index.js +28 -0
- package/dist-cjs/machines/index.js.map +1 -0
- package/dist-cjs/machines/presence.js +53 -0
- package/dist-cjs/machines/presence.js.map +1 -0
- package/dist-cjs/merge/index.js +25 -0
- package/dist-cjs/merge/index.js.map +1 -0
- package/dist-cjs/merge/merge3.js +191 -0
- package/dist-cjs/merge/merge3.js.map +1 -0
- package/dist-cjs/package.json +1 -0
- package/dist-cjs/records/authority.js +31 -0
- package/dist-cjs/records/authority.js.map +1 -0
- package/dist-cjs/records/codec.js +48 -0
- package/dist-cjs/records/codec.js.map +1 -0
- package/dist-cjs/records/contracts.js +121 -0
- package/dist-cjs/records/contracts.js.map +1 -0
- package/dist-cjs/records/inbox.js +37 -0
- package/dist-cjs/records/inbox.js.map +1 -0
- package/dist-cjs/records/index.js +23 -0
- package/dist-cjs/records/index.js.map +1 -0
- package/dist-cjs/records/outbox.js +48 -0
- package/dist-cjs/records/outbox.js.map +1 -0
- package/dist-cjs/registration/index.js +19 -0
- package/dist-cjs/registration/index.js.map +1 -0
- package/dist-cjs/registration/user-ground.js +183 -0
- package/dist-cjs/registration/user-ground.js.map +1 -0
- package/dist-cjs/transfer/artifacts.js +75 -0
- package/dist-cjs/transfer/artifacts.js.map +1 -0
- package/dist-cjs/transfer/batches.js +44 -0
- package/dist-cjs/transfer/batches.js.map +1 -0
- package/dist-cjs/transfer/download.js +88 -0
- package/dist-cjs/transfer/download.js.map +1 -0
- package/dist-cjs/transfer/index.js +21 -0
- package/dist-cjs/transfer/index.js.map +1 -0
- package/dist-cjs/transfer/parallel.js +35 -0
- package/dist-cjs/transfer/parallel.js.map +1 -0
- package/dist-cjs/transfer/upload.js +161 -0
- package/dist-cjs/transfer/upload.js.map +1 -0
- package/dist-cjs/watching/coverage.js +54 -0
- package/dist-cjs/watching/coverage.js.map +1 -0
- package/dist-cjs/watching/index.js +24 -0
- package/dist-cjs/watching/index.js.map +1 -0
- package/dist-cjs/watching/suspicion.js +105 -0
- package/dist-cjs/watching/suspicion.js.map +1 -0
- package/dist-cjs/workspace/api.js +66 -0
- package/dist-cjs/workspace/api.js.map +1 -0
- package/dist-cjs/workspace/identity.js +32 -0
- package/dist-cjs/workspace/identity.js.map +1 -0
- package/dist-cjs/workspace/index.js +14 -0
- package/dist-cjs/workspace/index.js.map +1 -0
- package/dist-cjs/workspace/memory-store.js +21 -0
- package/dist-cjs/workspace/memory-store.js.map +1 -0
- package/dist-cjs/workspace/registry.js +98 -0
- package/dist-cjs/workspace/registry.js.map +1 -0
- package/dist-cjs/workspace/types.js +3 -0
- package/dist-cjs/workspace/types.js.map +1 -0
- package/package.json +30 -8
- package/dist/contracts/mutation.d.ts +0 -51
- package/dist/contracts/mutation.d.ts.map +0 -1
- package/dist/contracts/mutation.js +0 -55
- package/dist/contracts/mutation.js.map +0 -1
- package/dist/machines/journal.d.ts +0 -63
- package/dist/machines/journal.d.ts.map +0 -1
- package/dist/machines/journal.js +0 -71
- package/dist/machines/journal.js.map +0 -1
- package/dist/machines/ordering.d.ts +0 -53
- package/dist/machines/ordering.d.ts.map +0 -1
- package/dist/machines/ordering.js +0 -87
- package/dist/machines/ordering.js.map +0 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exact entity record carried from local Record to cloud acceptance.
|
|
3
|
+
*
|
|
4
|
+
* The entity UUID is the channel. `globalSequence` exists only after the
|
|
5
|
+
* authority accepts the record and is dense independently on that UUID.
|
|
6
|
+
*/
|
|
7
|
+
import { assertRecord } from '../entities/cloud.js';
|
|
8
|
+
import { UUID_PATTERN } from '../entities/types.js';
|
|
9
|
+
import { DETECTED_CHANGE_CONTRACT, } from '../detection/record-proposal.js';
|
|
10
|
+
import { failShape, isNonEmptyString, isRecord, pass } from '../contracts/validate.js';
|
|
11
|
+
export const ENTITY_RECORD_CONTRACT = 'amalgm.entity-record@1';
|
|
12
|
+
const HASH = /^[0-9a-f]{64}$/;
|
|
13
|
+
const FACETS = new Set(['create', 'rename', 'move', 'retype', 'content', 'trash', 'restore']);
|
|
14
|
+
const EXAMPLE = '{ contract: "amalgm.entity-record@1", mutationId: "m1", authorityId: "pvr_...", entityId: "UUID", basedOnState: null, resultState: "64-hex", lastSeenGlobalSequence: 0, change: {...}, createdAt: 1 }';
|
|
15
|
+
const isSequence = (value, positive = false) => typeof value === 'number'
|
|
16
|
+
&& Number.isSafeInteger(value)
|
|
17
|
+
&& value >= (positive ? 1 : 0);
|
|
18
|
+
function checkChange(value) {
|
|
19
|
+
if (!isRecord(value) || value.contract !== DETECTED_CHANGE_CONTRACT) {
|
|
20
|
+
return failShape(EXAMPLE, `change.contract was not ${DETECTED_CHANGE_CONTRACT}`);
|
|
21
|
+
}
|
|
22
|
+
if (typeof value.entityId !== 'string' || !UUID_PATTERN.test(value.entityId)) {
|
|
23
|
+
return failShape(EXAMPLE, 'change.entityId was not an entity UUID');
|
|
24
|
+
}
|
|
25
|
+
if (value.basedOnState !== null
|
|
26
|
+
&& (typeof value.basedOnState !== 'string' || !HASH.test(value.basedOnState))) {
|
|
27
|
+
return failShape(EXAMPLE, 'change.basedOnState was not null or lowercase SHA-256');
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(value.facets) || value.facets.length === 0
|
|
30
|
+
|| value.facets.some((facet) => typeof facet !== 'string' || !FACETS.has(facet))
|
|
31
|
+
|| new Set(value.facets).size !== value.facets.length) {
|
|
32
|
+
return failShape(EXAMPLE, 'change.facets was empty, duplicated, or unknown');
|
|
33
|
+
}
|
|
34
|
+
if (!isRecord(value.replay) || !isNonEmptyString(value.replay.kind)) {
|
|
35
|
+
return failShape(EXAMPLE, 'change.replay was not a replay instruction');
|
|
36
|
+
}
|
|
37
|
+
if (!Array.isArray(value.cargo)) {
|
|
38
|
+
return failShape(EXAMPLE, 'change.cargo was not an array');
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const result = assertRecord(value.result);
|
|
42
|
+
if (result.uuid !== value.entityId) {
|
|
43
|
+
return failShape(EXAMPLE, 'change.entityId did not equal change.result.uuid');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return failShape(EXAMPLE, `change.result was invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
+
}
|
|
49
|
+
return pass(value);
|
|
50
|
+
}
|
|
51
|
+
export function checkLocalEntityRecord(value) {
|
|
52
|
+
if (!isRecord(value) || value.contract !== ENTITY_RECORD_CONTRACT) {
|
|
53
|
+
return failShape(EXAMPLE, `contract was not ${ENTITY_RECORD_CONTRACT}`);
|
|
54
|
+
}
|
|
55
|
+
for (const field of ['mutationId', 'authorityId']) {
|
|
56
|
+
if (!isNonEmptyString(value[field])) {
|
|
57
|
+
return failShape(EXAMPLE, `${field} was not a non-empty string`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (typeof value.entityId !== 'string' || !UUID_PATTERN.test(value.entityId)) {
|
|
61
|
+
return failShape(EXAMPLE, 'entityId was not an entity UUID');
|
|
62
|
+
}
|
|
63
|
+
if (value.basedOnState !== null
|
|
64
|
+
&& (typeof value.basedOnState !== 'string' || !HASH.test(value.basedOnState))) {
|
|
65
|
+
return failShape(EXAMPLE, 'basedOnState was not null or lowercase SHA-256');
|
|
66
|
+
}
|
|
67
|
+
if (typeof value.resultState !== 'string' || !HASH.test(value.resultState)) {
|
|
68
|
+
return failShape(EXAMPLE, 'resultState was not lowercase SHA-256');
|
|
69
|
+
}
|
|
70
|
+
if (!isSequence(value.lastSeenGlobalSequence) || !isSequence(value.createdAt)) {
|
|
71
|
+
return failShape(EXAMPLE, 'lastSeenGlobalSequence and createdAt must be non-negative safe integers');
|
|
72
|
+
}
|
|
73
|
+
const change = checkChange(value.change);
|
|
74
|
+
if (!change.ok)
|
|
75
|
+
return change;
|
|
76
|
+
if (change.value.entityId !== value.entityId
|
|
77
|
+
|| change.value.basedOnState !== value.basedOnState
|
|
78
|
+
|| change.value.result.version !== value.resultState) {
|
|
79
|
+
return failShape(EXAMPLE, 'record lineage did not exactly match its Detect proposal');
|
|
80
|
+
}
|
|
81
|
+
return pass(value);
|
|
82
|
+
}
|
|
83
|
+
export function checkAcceptedEntityRecord(value) {
|
|
84
|
+
const local = checkLocalEntityRecord(value);
|
|
85
|
+
if (!local.ok)
|
|
86
|
+
return local;
|
|
87
|
+
if (!isRecord(value)
|
|
88
|
+
|| !isSequence(value.globalSequence, true)
|
|
89
|
+
|| !isNonEmptyString(value.actorId)
|
|
90
|
+
|| !isNonEmptyString(value.originDeviceId)
|
|
91
|
+
|| !isSequence(value.acceptedAt)) {
|
|
92
|
+
return failShape(EXAMPLE, 'accepted record lacked a positive sequence or server-derived acceptance fields');
|
|
93
|
+
}
|
|
94
|
+
return pass(value);
|
|
95
|
+
}
|
|
96
|
+
export const entityMutationKey = (record) => `${record.entityId}\u0000${record.mutationId}`;
|
|
97
|
+
export function createLocalEntityRecord(input) {
|
|
98
|
+
const record = Object.freeze({
|
|
99
|
+
contract: ENTITY_RECORD_CONTRACT,
|
|
100
|
+
mutationId: input.mutationId,
|
|
101
|
+
authorityId: input.authorityId,
|
|
102
|
+
entityId: input.change.entityId,
|
|
103
|
+
basedOnState: input.change.basedOnState,
|
|
104
|
+
resultState: input.change.result.version,
|
|
105
|
+
lastSeenGlobalSequence: input.lastSeenGlobalSequence,
|
|
106
|
+
change: input.change,
|
|
107
|
+
createdAt: input.createdAt,
|
|
108
|
+
});
|
|
109
|
+
const checked = checkLocalEntityRecord(record);
|
|
110
|
+
if (!checked.ok)
|
|
111
|
+
throw new Error(checked.error);
|
|
112
|
+
return record;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=contracts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../src/records/contracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Pure per-entity Receive law. Apply is intentionally absent. */
|
|
2
|
+
import type { AcceptedEntityRecord } from './contracts.js';
|
|
3
|
+
export type InboxDecision = {
|
|
4
|
+
readonly kind: 'insert';
|
|
5
|
+
readonly record: AcceptedEntityRecord;
|
|
6
|
+
readonly receivedThrough: number;
|
|
7
|
+
} | {
|
|
8
|
+
readonly kind: 'duplicate';
|
|
9
|
+
readonly record: AcceptedEntityRecord;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: 'gap';
|
|
12
|
+
readonly entityId: string;
|
|
13
|
+
readonly after: number;
|
|
14
|
+
readonly received: number;
|
|
15
|
+
};
|
|
16
|
+
/** Decide one durable inbox transaction against this entity's own cursor. */
|
|
17
|
+
export declare function receiveEntityRecord(receivedThrough: number, record: AcceptedEntityRecord, existing: AcceptedEntityRecord | null): InboxDecision;
|
|
18
|
+
//# sourceMappingURL=inbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../../src/records/inbox.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAElE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG3D,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GACpG;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GACrE;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3G,6EAA6E;AAC7E,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,EAAE,oBAAoB,GAAG,IAAI,GACpC,aAAa,CA6Bf"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Pure per-entity Receive law. Apply is intentionally absent. */
|
|
2
|
+
import { encodeEntityRecord } from './codec.js';
|
|
3
|
+
/** Decide one durable inbox transaction against this entity's own cursor. */
|
|
4
|
+
export function receiveEntityRecord(receivedThrough, record, existing) {
|
|
5
|
+
if (!Number.isSafeInteger(receivedThrough) || receivedThrough < 0) {
|
|
6
|
+
throw new Error('received-through must be a non-negative safe integer');
|
|
7
|
+
}
|
|
8
|
+
if (record.globalSequence <= receivedThrough) {
|
|
9
|
+
if (existing === null) {
|
|
10
|
+
throw new Error('entity cursor claims a sequence that is absent from the inbox');
|
|
11
|
+
}
|
|
12
|
+
if (encodeEntityRecord(existing) !== encodeEntityRecord(record)) {
|
|
13
|
+
throw new Error('one entity sequence cannot name two accepted record bytes');
|
|
14
|
+
}
|
|
15
|
+
return Object.freeze({ kind: 'duplicate', record: existing });
|
|
16
|
+
}
|
|
17
|
+
if (existing !== null) {
|
|
18
|
+
throw new Error('inbox contains a sequence beyond its entity cursor');
|
|
19
|
+
}
|
|
20
|
+
if (record.globalSequence !== receivedThrough + 1) {
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
kind: 'gap',
|
|
23
|
+
entityId: record.entityId,
|
|
24
|
+
after: receivedThrough,
|
|
25
|
+
received: record.globalSequence,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
kind: 'insert',
|
|
30
|
+
record,
|
|
31
|
+
receivedThrough: record.globalSequence,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=inbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox.js","sourceRoot":"","sources":["../../src/records/inbox.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAGlE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAOhD,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CACjC,eAAuB,EACvB,MAA4B,EAC5B,QAAqC;IAErC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,MAAM,CAAC,cAAc,IAAI,eAAe,EAAE,CAAC;QAC7C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,kBAAkB,CAAC,QAAQ,CAAC,KAAK,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,MAAM,CAAC,cAAc,KAAK,eAAe,GAAG,CAAC,EAAE,CAAC;QAClD,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,KAAK,EAAE,eAAe;YACtB,QAAQ,EAAE,MAAM,CAAC,cAAc;SAChC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,IAAI,EAAE,QAAQ;QACd,MAAM;QACN,eAAe,EAAE,MAAM,CAAC,cAAc;KACvC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Entity Record, local Outbox, cloud acceptance, and local Inbox laws. */
|
|
2
|
+
export * from './authority.js';
|
|
3
|
+
export * from './codec.js';
|
|
4
|
+
export * from './contracts.js';
|
|
5
|
+
export * from './inbox.js';
|
|
6
|
+
export * from './outbox.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/records/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Entity Record, local Outbox, cloud acceptance, and local Inbox laws. */
|
|
2
|
+
export * from './authority.js';
|
|
3
|
+
export * from './codec.js';
|
|
4
|
+
export * from './contracts.js';
|
|
5
|
+
export * from './inbox.js';
|
|
6
|
+
export * from './outbox.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/records/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Pure laws for the local-only record outbox. */
|
|
2
|
+
import type { AcceptedEntityRecord, LocalEntityRecord } from './contracts.js';
|
|
3
|
+
export type OutboxRow = {
|
|
4
|
+
readonly record: LocalEntityRecord;
|
|
5
|
+
readonly status: 'pending';
|
|
6
|
+
readonly globalSequence: null;
|
|
7
|
+
} | {
|
|
8
|
+
readonly record: LocalEntityRecord;
|
|
9
|
+
readonly status: 'accepted';
|
|
10
|
+
readonly globalSequence: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Choose at most one pending row per entity. This preserves causality within
|
|
14
|
+
* one UUID channel while allowing unrelated entity channels to send in
|
|
15
|
+
* parallel. Input must be in durable local insertion order.
|
|
16
|
+
*/
|
|
17
|
+
export declare function nextEntitySends(rows: readonly OutboxRow[]): readonly OutboxRow[];
|
|
18
|
+
/** Stamp a retained local receipt from the authority's exact acknowledgement. */
|
|
19
|
+
export declare function acceptOutboxRow(row: OutboxRow, accepted: AcceptedEntityRecord): OutboxRow;
|
|
20
|
+
//# sourceMappingURL=outbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/records/outbox.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAElD,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE,IAAI,CAAA;CAAE,GACjG;IAAE,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzG;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,SAAS,EAAE,CAUhF;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,GAAG,SAAS,CA2BzF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Pure laws for the local-only record outbox. */
|
|
2
|
+
import { encodeEntityRecord } from './codec.js';
|
|
3
|
+
/**
|
|
4
|
+
* Choose at most one pending row per entity. This preserves causality within
|
|
5
|
+
* one UUID channel while allowing unrelated entity channels to send in
|
|
6
|
+
* parallel. Input must be in durable local insertion order.
|
|
7
|
+
*/
|
|
8
|
+
export function nextEntitySends(rows) {
|
|
9
|
+
const selected = [];
|
|
10
|
+
const seen = new Set();
|
|
11
|
+
for (const row of rows) {
|
|
12
|
+
if (row.status !== 'pending')
|
|
13
|
+
continue;
|
|
14
|
+
if (seen.has(row.record.entityId))
|
|
15
|
+
continue;
|
|
16
|
+
seen.add(row.record.entityId);
|
|
17
|
+
selected.push(row);
|
|
18
|
+
}
|
|
19
|
+
return Object.freeze(selected);
|
|
20
|
+
}
|
|
21
|
+
/** Stamp a retained local receipt from the authority's exact acknowledgement. */
|
|
22
|
+
export function acceptOutboxRow(row, accepted) {
|
|
23
|
+
if (row.record.authorityId !== accepted.authorityId
|
|
24
|
+
|| row.record.entityId !== accepted.entityId
|
|
25
|
+
|| row.record.mutationId !== accepted.mutationId) {
|
|
26
|
+
throw new Error('an acknowledgement cannot stamp a different outbox record');
|
|
27
|
+
}
|
|
28
|
+
const { globalSequence: _globalSequence, actorId: _actorId, originDeviceId: _originDeviceId, acceptedAt: _acceptedAt, ...acceptedLocal } = accepted;
|
|
29
|
+
if (encodeEntityRecord(row.record) !== encodeEntityRecord(acceptedLocal)) {
|
|
30
|
+
throw new Error('an acknowledgement cannot change the outbox record bytes');
|
|
31
|
+
}
|
|
32
|
+
if (row.status === 'accepted') {
|
|
33
|
+
if (row.globalSequence !== accepted.globalSequence) {
|
|
34
|
+
throw new Error('one outbox record cannot have two official entity sequences');
|
|
35
|
+
}
|
|
36
|
+
return row;
|
|
37
|
+
}
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
record: row.record,
|
|
40
|
+
status: 'accepted',
|
|
41
|
+
globalSequence: accepted.globalSequence,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=outbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/records/outbox.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAGlD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMhD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAA0B;IACxD,MAAM,QAAQ,GAAgB,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;YAAE,SAAS;QACvC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,SAAS;QAC5C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,eAAe,CAAC,GAAc,EAAE,QAA8B;IAC5E,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW;WAC9C,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ;WACzC,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,EACJ,cAAc,EAAE,eAAe,EAC/B,OAAO,EAAE,QAAQ,EACjB,cAAc,EAAE,eAAe,EAC/B,UAAU,EAAE,WAAW,EACvB,GAAG,aAAa,EACjB,GAAG,QAAQ,CAAC;IACb,IAAI,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC9B,IAAI,GAAG,CAAC,cAAc,KAAK,QAAQ,CAAC,cAAc,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACjF,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,MAAM,EAAE,UAAU;QAClB,cAAc,EAAE,QAAQ,CAAC,cAAc;KACxC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -19,9 +19,9 @@ export interface CloudUserGround {
|
|
|
19
19
|
readonly checksum: string;
|
|
20
20
|
readonly snapshot: CloudSnapshot;
|
|
21
21
|
}
|
|
22
|
-
/** The
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
/** The local materialization plus machine-only placement. A cold snapshot may
|
|
23
|
+
* lag durable entity records; recordsBeyondSnapshot prevents login from
|
|
24
|
+
* replacing that newer local truth with the older bootstrap image. */
|
|
25
25
|
export interface LocalUserGround {
|
|
26
26
|
readonly userId: string;
|
|
27
27
|
readonly userEmail: string;
|
|
@@ -29,7 +29,7 @@ export interface LocalUserGround {
|
|
|
29
29
|
readonly localRoot: string;
|
|
30
30
|
readonly sqlitePath: string;
|
|
31
31
|
readonly records: readonly CloudEntityRecord[];
|
|
32
|
-
readonly
|
|
32
|
+
readonly recordsBeyondSnapshot?: boolean;
|
|
33
33
|
}
|
|
34
34
|
export interface UserGroundWatchEvidence {
|
|
35
35
|
readonly active: true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-ground.d.ts","sourceRoot":"","sources":["../../src/registration/user-ground.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAML,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;CAClC;AAED;;
|
|
1
|
+
{"version":3,"file":"user-ground.d.ts","sourceRoot":"","sources":["../../src/registration/user-ground.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,7 +65,7 @@ function assertLocal(value, identity) {
|
|
|
65
65
|
localRoot,
|
|
66
66
|
sqlitePath,
|
|
67
67
|
records: snapshot.records,
|
|
68
|
-
...(local.
|
|
68
|
+
...(local.recordsBeyondSnapshot === true ? { recordsBeyondSnapshot: true } : {}),
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
/** Select the authenticated user's core root without materializing sibling
|
|
@@ -141,7 +141,11 @@ export async function convergeUserGround(input, ports) {
|
|
|
141
141
|
const current = assertLocal(currentValue, identity);
|
|
142
142
|
if (sameRecords(travelingRecords(userGroundRecords(current.records, identity.userEmail)), userGroundRecords(cloud.snapshot.records, identity.userEmail))) {
|
|
143
143
|
local = current;
|
|
144
|
-
mode = current.
|
|
144
|
+
mode = current.recordsBeyondSnapshot === true ? 'syncing' : 'current';
|
|
145
|
+
}
|
|
146
|
+
else if (current.recordsBeyondSnapshot === true) {
|
|
147
|
+
local = current;
|
|
148
|
+
mode = 'syncing';
|
|
145
149
|
}
|
|
146
150
|
else {
|
|
147
151
|
local = assertLocal(await ports.local.install(identity, cloud), identity);
|
|
@@ -157,7 +161,8 @@ export async function convergeUserGround(input, ports) {
|
|
|
157
161
|
}
|
|
158
162
|
const watch = assertWatch(await ports.local.watch(identity, cloud));
|
|
159
163
|
const finalLocal = assertLocal(await ports.local.inspect(identity), identity);
|
|
160
|
-
|
|
164
|
+
if (finalLocal.recordsBeyondSnapshot !== true)
|
|
165
|
+
assertSameGraph(finalLocal, cloud);
|
|
161
166
|
return {
|
|
162
167
|
...identity,
|
|
163
168
|
localRoot: finalLocal.localRoot,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-ground.js","sourceRoot":"","sources":["../../src/registration/user-ground.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"user-ground.js","sourceRoot":"","sources":["../../src/registration/user-ground.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The binding — `live.bind(surface, channel)` (realtime-sdk.md).
|
|
4
|
+
*
|
|
5
|
+
* The surface knows what the user is looking at and what counts as one
|
|
6
|
+
* edit. The channel identifies the shared thing and its delivery class.
|
|
7
|
+
* The binding moves changes both ways and exposes honest lifecycle state:
|
|
8
|
+
* a socket being open is never `live` (axiom 7).
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.BINDING_STATES = void 0;
|
|
12
|
+
/**
|
|
13
|
+
* Connection lifecycle vocabulary (realtime-sdk.md §7). `live` means the
|
|
14
|
+
* snapshot is applied AND ordered delivery is confirmed; `degraded` means
|
|
15
|
+
* delivery works through a slower fallback (e.g. gateway poll).
|
|
16
|
+
*/
|
|
17
|
+
exports.BINDING_STATES = [
|
|
18
|
+
'connecting',
|
|
19
|
+
'hydrating',
|
|
20
|
+
'live',
|
|
21
|
+
'degraded',
|
|
22
|
+
'reconnecting',
|
|
23
|
+
'blocked',
|
|
24
|
+
'revoked',
|
|
25
|
+
];
|
|
26
|
+
//# sourceMappingURL=binding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding.js","sourceRoot":"","sources":["../../src/contracts/binding.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAaH;;;;GAIG;AACU,QAAA,cAAc,GAAG;IAC5B,YAAY;IACZ,WAAW;IACX,MAAM;IACN,UAAU;IACV,cAAc;IACd,SAAS;IACT,SAAS;CACD,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Content manifests — the wire shape of immutable entity bytes.
|
|
4
|
+
*
|
|
5
|
+
* An entity artifact travels as bounded, content-addressed blocks named by a
|
|
6
|
+
* manifest. Every writer derives the same manifest from
|
|
7
|
+
* the same bytes, so manifests are idempotent facts, never negotiations.
|
|
8
|
+
*
|
|
9
|
+
* The chunk rule is `bytes <= CHUNK_BYTES`, never an exact size — older,
|
|
10
|
+
* smaller chunks remain valid forever. Capture, storage, and upload are
|
|
11
|
+
* host effects; this file owns the shape and its laws.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.MAX_CONTENT_BLOCKS = exports.CHUNK_BYTES = exports.MAX_CONTENT_BLOCK_BYTES = exports.MIN_CONTENT_BLOCK_BYTES = exports.CONTENT_CONTRACT = exports.LEGACY_CONTENT_CONTRACT = void 0;
|
|
15
|
+
exports.checkContentManifest = checkContentManifest;
|
|
16
|
+
exports.manifestFromChunks = manifestFromChunks;
|
|
17
|
+
exports.contentBlockSize = contentBlockSize;
|
|
18
|
+
exports.chunkSizes = chunkSizes;
|
|
19
|
+
const validate_js_1 = require("./validate.js");
|
|
20
|
+
/** Version 3 changes only the canonical writer layout: ordinary files begin
|
|
21
|
+
* at Dropbox's stable 4 MiB block size. Version 2 remains readable because
|
|
22
|
+
* immutable cloud heads created by an older writer must still materialize. */
|
|
23
|
+
exports.LEGACY_CONTENT_CONTRACT = 'amalgm-entity-content@2';
|
|
24
|
+
exports.CONTENT_CONTRACT = 'amalgm-entity-content@3';
|
|
25
|
+
exports.MIN_CONTENT_BLOCK_BYTES = 4 * 1024 * 1024;
|
|
26
|
+
exports.MAX_CONTENT_BLOCK_BYTES = 16 * 1024 * 1024;
|
|
27
|
+
/** Maximum accepted block size. Kept as the public manifest-bound name. */
|
|
28
|
+
exports.CHUNK_BYTES = exports.MAX_CONTENT_BLOCK_BYTES;
|
|
29
|
+
exports.MAX_CONTENT_BLOCKS = 2_000;
|
|
30
|
+
const HASH = /^[0-9a-f]{64}$/;
|
|
31
|
+
/**
|
|
32
|
+
* Validate and normalize a manifest. The laws, in conviction order:
|
|
33
|
+
* well-formed hashes; every chunk within the bound; chunk bytes sum to the
|
|
34
|
+
* total; empty content has no chunks. Error phrases match the engine's —
|
|
35
|
+
* they are the ratified refusal text.
|
|
36
|
+
*/
|
|
37
|
+
function checkContentManifest(input, expectedHash = null) {
|
|
38
|
+
if (!(0, validate_js_1.isRecord)(input) ||
|
|
39
|
+
(input.contract !== exports.CONTENT_CONTRACT && input.contract !== exports.LEGACY_CONTENT_CONTRACT) ||
|
|
40
|
+
typeof input.contentHash !== 'string' ||
|
|
41
|
+
!HASH.test(input.contentHash) ||
|
|
42
|
+
(expectedHash !== null && input.contentHash !== expectedHash) ||
|
|
43
|
+
!Number.isSafeInteger(input.bytes) ||
|
|
44
|
+
input.bytes < 0 ||
|
|
45
|
+
!Array.isArray(input.chunks) ||
|
|
46
|
+
input.chunks.length > exports.MAX_CONTENT_BLOCKS ||
|
|
47
|
+
input.chunks.some((chunk) => !(0, validate_js_1.isRecord)(chunk) ||
|
|
48
|
+
!HASH.test(String(chunk.sha256 ?? '')) ||
|
|
49
|
+
!Number.isSafeInteger(chunk.bytes) ||
|
|
50
|
+
chunk.bytes < 1 ||
|
|
51
|
+
chunk.bytes > exports.CHUNK_BYTES)) {
|
|
52
|
+
return (0, validate_js_1.fail)('entity content manifest is invalid');
|
|
53
|
+
}
|
|
54
|
+
const bytes = input.bytes;
|
|
55
|
+
const chunks = input.chunks;
|
|
56
|
+
if (chunks.reduce((total, chunk) => total + chunk.bytes, 0) !== bytes) {
|
|
57
|
+
return (0, validate_js_1.fail)('entity content manifest byte count is invalid');
|
|
58
|
+
}
|
|
59
|
+
if (bytes === 0 && chunks.length !== 0) {
|
|
60
|
+
return (0, validate_js_1.fail)('empty entity content must have no chunks');
|
|
61
|
+
}
|
|
62
|
+
return (0, validate_js_1.pass)({
|
|
63
|
+
contract: input.contract,
|
|
64
|
+
contentHash: input.contentHash,
|
|
65
|
+
bytes,
|
|
66
|
+
chunks: chunks.map((chunk) => ({ sha256: chunk.sha256, bytes: chunk.bytes })),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Derive the manifest for already-chunked bytes. The host reads and hashes
|
|
71
|
+
* (sha256 hex of each part and of the whole); this seals the shape so a
|
|
72
|
+
* hand-built manifest cannot drift from the derivation rule.
|
|
73
|
+
*/
|
|
74
|
+
function manifestFromChunks(contentHash, chunks) {
|
|
75
|
+
return checkContentManifest({ contract: exports.CONTENT_CONTRACT, contentHash, bytes: chunks.reduce((t, c) => t + c.bytes, 0), chunks }, contentHash);
|
|
76
|
+
}
|
|
77
|
+
/** Dropbox-sized cold blocks, widened by Syncthing's adaptive rule only when
|
|
78
|
+
* needed to keep a manifest below 2,000 blocks, capped at 16 MiB. */
|
|
79
|
+
function contentBlockSize(totalBytes) {
|
|
80
|
+
if (!Number.isSafeInteger(totalBytes) || totalBytes < 0)
|
|
81
|
+
return exports.MIN_CONTENT_BLOCK_BYTES;
|
|
82
|
+
let bytes = exports.MIN_CONTENT_BLOCK_BYTES;
|
|
83
|
+
while (Math.ceil(totalBytes / bytes) > exports.MAX_CONTENT_BLOCKS && bytes < exports.MAX_CONTENT_BLOCK_BYTES) {
|
|
84
|
+
bytes *= 2;
|
|
85
|
+
}
|
|
86
|
+
return bytes;
|
|
87
|
+
}
|
|
88
|
+
/** Split a byte count into adaptive full blocks, then the remainder. */
|
|
89
|
+
function chunkSizes(totalBytes) {
|
|
90
|
+
if (!Number.isSafeInteger(totalBytes) || totalBytes < 0)
|
|
91
|
+
return [];
|
|
92
|
+
const blockBytes = contentBlockSize(totalBytes);
|
|
93
|
+
const sizes = [];
|
|
94
|
+
for (let remaining = totalBytes; remaining > 0; remaining -= blockBytes) {
|
|
95
|
+
sizes.push(Math.min(remaining, blockBytes));
|
|
96
|
+
}
|
|
97
|
+
return sizes;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/contracts/content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Versioned document writes — the contract for whole-text writers.
|
|
4
|
+
*
|
|
5
|
+
* `POST /state/docs/write` (local-live-protocol.md §8) exists so a writer
|
|
6
|
+
* that read a file, thought, and wrote back does not clobber live edits
|
|
7
|
+
* that landed in between. `baseHash` is the lowercase sha256 hex of the
|
|
8
|
+
* EXACT text the writer read; every response's `sha256` chains into the
|
|
9
|
+
* next write.
|
|
10
|
+
*
|
|
11
|
+
* The governing invariant — never a silent winner: any path that durably
|
|
12
|
+
* discards text preserves it in a sidecar first and surfaces the decision
|
|
13
|
+
* on the doc event.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.isSha256Hex = exports.CONFLICT_REASONS = exports.MERGE_OUTCOMES = exports.RETENTION_RING = exports.MAX_DOC_BYTES = void 0;
|
|
17
|
+
exports.checkDocWriteRequest = checkDocWriteRequest;
|
|
18
|
+
const validate_js_1 = require("./validate.js");
|
|
19
|
+
/** Text over this limit is refused with 413. */
|
|
20
|
+
exports.MAX_DOC_BYTES = 2 * 1024 * 1024;
|
|
21
|
+
/** Bases resolve against a ring of recent disk-sync states, per doc. */
|
|
22
|
+
exports.RETENTION_RING = { states: 8, maxTotalBytes: 4 * 1024 * 1024, maxAgeMs: 10 * 60_000 };
|
|
23
|
+
/**
|
|
24
|
+
* Every outcome states what happens to the losing text in the same breath.
|
|
25
|
+
*
|
|
26
|
+
* | outcome | when | losing text |
|
|
27
|
+
* |----------------|----------------------------------------|------------------------|
|
|
28
|
+
* | `noop` | incoming === live | none exists |
|
|
29
|
+
* | `clean` | base === live | none exists |
|
|
30
|
+
* | `merged` | base in retention, no overlapping hunks| both sides land |
|
|
31
|
+
* | `conflict` | overlapping hunks | full incoming → sidecar|
|
|
32
|
+
* | `base-unknown` | base outside the retention window | live text → sidecar |
|
|
33
|
+
* | `overwrite` | no base sent | replaced text → sidecar unless nothing was deleted |
|
|
34
|
+
*/
|
|
35
|
+
exports.MERGE_OUTCOMES = ['noop', 'clean', 'merged', 'conflict', 'base-unknown', 'overwrite'];
|
|
36
|
+
exports.CONFLICT_REASONS = ['overlapping-hunks', 'unversioned-overwrite', 'base-unknown'];
|
|
37
|
+
const REQUEST_EXAMPLE = '{ path: "notes.md", text: "full new contents", baseHash: "9f86d0…" }';
|
|
38
|
+
const isSha256Hex = (value) => typeof value === 'string' && /^[0-9a-f]{64}$/.test(value);
|
|
39
|
+
exports.isSha256Hex = isSha256Hex;
|
|
40
|
+
function checkDocWriteRequest(input) {
|
|
41
|
+
if (!(0, validate_js_1.isRecord)(input))
|
|
42
|
+
return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, `got ${input === null ? 'null' : typeof input}`);
|
|
43
|
+
if (!(0, validate_js_1.isNonEmptyString)(input.path)) {
|
|
44
|
+
return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, `path was ${JSON.stringify(input.path)}, not a non-empty string`);
|
|
45
|
+
}
|
|
46
|
+
if (typeof input.text !== 'string')
|
|
47
|
+
return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, 'text was missing — this is a whole-text write');
|
|
48
|
+
if (input.baseHash !== undefined && !(0, exports.isSha256Hex)(input.baseHash)) {
|
|
49
|
+
return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, `baseHash was ${JSON.stringify(input.baseHash)}, not lowercase sha256 hex`);
|
|
50
|
+
}
|
|
51
|
+
if (input.baseSeq !== undefined && !(0, validate_js_1.isSeq)(input.baseSeq)) {
|
|
52
|
+
return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, `baseSeq was ${JSON.stringify(input.baseSeq)}, not a non-negative integer`);
|
|
53
|
+
}
|
|
54
|
+
return (0, validate_js_1.pass)(input);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=docs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/contracts/docs.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AA2DH,oDAaC;AAtED,+CAAiG;AAEjG,gDAAgD;AACnC,QAAA,aAAa,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7C,wEAAwE;AAC3D,QAAA,cAAc,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,EAAW,CAAC;AAY5G;;;;;;;;;;;GAWG;AACU,QAAA,cAAc,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,CAAU,CAAC;AAG/F,QAAA,gBAAgB,GAAG,CAAC,mBAAmB,EAAE,uBAAuB,EAAE,cAAc,CAAU,CAAC;AAmBxG,MAAM,eAAe,GAAG,sEAAsE,CAAC;AAExF,MAAM,WAAW,GAAG,CAAC,KAAc,EAAmB,EAAE,CAC7D,OAAO,KAAK,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAD/C,QAAA,WAAW,eACoC;AAE5D,SAAgB,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC,IAAA,sBAAQ,EAAC,KAAK,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IACzG,IAAI,CAAC,IAAA,8BAAgB,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACtG,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,+CAA+C,CAAC,CAAC;IACvH,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,IAAA,mBAAW,EAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjE,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,gBAAgB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC;IAChH,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,IAAA,mBAAK,EAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACzD,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,eAAe,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAChH,CAAC;IACD,OAAO,IAAA,kBAAI,EAAC,KAAmC,CAAC,CAAC;AACnD,CAAC"}
|