@amalgm/live 0.2.26 → 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.
Files changed (197) hide show
  1. package/PURPOSE.md +52 -29
  2. package/README.md +1 -2
  3. package/dist/contracts/index.d.ts +0 -1
  4. package/dist/contracts/index.d.ts.map +1 -1
  5. package/dist/contracts/index.js +0 -1
  6. package/dist/contracts/index.js.map +1 -1
  7. package/dist/detection/record-proposal.d.ts +3 -2
  8. package/dist/detection/record-proposal.d.ts.map +1 -1
  9. package/dist/detection/record-proposal.js +9 -7
  10. package/dist/detection/record-proposal.js.map +1 -1
  11. package/dist/index.d.ts +3 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/machines/index.d.ts +0 -2
  16. package/dist/machines/index.d.ts.map +1 -1
  17. package/dist/machines/index.js +0 -2
  18. package/dist/machines/index.js.map +1 -1
  19. package/dist/records/authority.d.ts +23 -0
  20. package/dist/records/authority.d.ts.map +1 -0
  21. package/dist/records/authority.js +28 -0
  22. package/dist/records/authority.js.map +1 -0
  23. package/dist/records/codec.d.ts +6 -0
  24. package/dist/records/codec.d.ts.map +1 -0
  25. package/dist/records/codec.js +42 -0
  26. package/dist/records/codec.js.map +1 -0
  27. package/dist/records/contracts.d.ts +46 -0
  28. package/dist/records/contracts.d.ts.map +1 -0
  29. package/dist/records/contracts.js +114 -0
  30. package/dist/records/contracts.js.map +1 -0
  31. package/dist/records/inbox.d.ts +18 -0
  32. package/dist/records/inbox.d.ts.map +1 -0
  33. package/dist/records/inbox.js +34 -0
  34. package/dist/records/inbox.js.map +1 -0
  35. package/dist/records/index.d.ts +7 -0
  36. package/dist/records/index.d.ts.map +1 -0
  37. package/dist/records/index.js +7 -0
  38. package/dist/records/index.js.map +1 -0
  39. package/dist/records/outbox.d.ts +20 -0
  40. package/dist/records/outbox.d.ts.map +1 -0
  41. package/dist/records/outbox.js +44 -0
  42. package/dist/records/outbox.js.map +1 -0
  43. package/dist/registration/user-ground.d.ts +5 -2
  44. package/dist/registration/user-ground.d.ts.map +1 -1
  45. package/dist/registration/user-ground.js +14 -3
  46. package/dist/registration/user-ground.js.map +1 -1
  47. package/dist/watching/suspicion.d.ts.map +1 -1
  48. package/dist/watching/suspicion.js +29 -1
  49. package/dist/watching/suspicion.js.map +1 -1
  50. package/dist-cjs/contracts/binding.js +26 -0
  51. package/dist-cjs/contracts/binding.js.map +1 -0
  52. package/dist-cjs/contracts/content.js +99 -0
  53. package/dist-cjs/contracts/content.js.map +1 -0
  54. package/dist-cjs/contracts/docs.js +56 -0
  55. package/dist-cjs/contracts/docs.js.map +1 -0
  56. package/dist-cjs/contracts/events.js +44 -0
  57. package/dist-cjs/contracts/events.js.map +1 -0
  58. package/dist-cjs/contracts/frames/gateway.js +39 -0
  59. package/dist-cjs/contracts/frames/gateway.js.map +1 -0
  60. package/dist-cjs/contracts/frames/stream.js +20 -0
  61. package/dist-cjs/contracts/frames/stream.js.map +1 -0
  62. package/dist-cjs/contracts/frames/tunnel.js +60 -0
  63. package/dist-cjs/contracts/frames/tunnel.js.map +1 -0
  64. package/dist-cjs/contracts/index.js +34 -0
  65. package/dist-cjs/contracts/index.js.map +1 -0
  66. package/dist-cjs/contracts/presence.js +38 -0
  67. package/dist-cjs/contracts/presence.js.map +1 -0
  68. package/dist-cjs/contracts/resources.js +114 -0
  69. package/dist-cjs/contracts/resources.js.map +1 -0
  70. package/dist-cjs/contracts/snapshot.js +31 -0
  71. package/dist-cjs/contracts/snapshot.js.map +1 -0
  72. package/dist-cjs/contracts/validate.js +34 -0
  73. package/dist-cjs/contracts/validate.js.map +1 -0
  74. package/dist-cjs/detection/adapter-contract.js +168 -0
  75. package/dist-cjs/detection/adapter-contract.js.map +1 -0
  76. package/dist-cjs/detection/batch.js +71 -0
  77. package/dist-cjs/detection/batch.js.map +1 -0
  78. package/dist-cjs/detection/classify.js +48 -0
  79. package/dist-cjs/detection/classify.js.map +1 -0
  80. package/dist-cjs/detection/content.js +122 -0
  81. package/dist-cjs/detection/content.js.map +1 -0
  82. package/dist-cjs/detection/continuity.js +88 -0
  83. package/dist-cjs/detection/continuity.js.map +1 -0
  84. package/dist-cjs/detection/diff.js +193 -0
  85. package/dist-cjs/detection/diff.js.map +1 -0
  86. package/dist-cjs/detection/echo.js +25 -0
  87. package/dist-cjs/detection/echo.js.map +1 -0
  88. package/dist-cjs/detection/enrollment.js +189 -0
  89. package/dist-cjs/detection/enrollment.js.map +1 -0
  90. package/dist-cjs/detection/index.js +57 -0
  91. package/dist-cjs/detection/index.js.map +1 -0
  92. package/dist-cjs/detection/record-proposal.js +170 -0
  93. package/dist-cjs/detection/record-proposal.js.map +1 -0
  94. package/dist-cjs/detection/refusal.js +44 -0
  95. package/dist-cjs/detection/refusal.js.map +1 -0
  96. package/dist-cjs/detection/repository.js +67 -0
  97. package/dist-cjs/detection/repository.js.map +1 -0
  98. package/dist-cjs/detection/territory.js +82 -0
  99. package/dist-cjs/detection/territory.js.map +1 -0
  100. package/dist-cjs/detection/watch-boundary.js +51 -0
  101. package/dist-cjs/detection/watch-boundary.js.map +1 -0
  102. package/dist-cjs/entities/bytes.js +92 -0
  103. package/dist-cjs/entities/bytes.js.map +1 -0
  104. package/dist-cjs/entities/cloud.js +171 -0
  105. package/dist-cjs/entities/cloud.js.map +1 -0
  106. package/dist-cjs/entities/index.js +39 -0
  107. package/dist-cjs/entities/index.js.map +1 -0
  108. package/dist-cjs/entities/moves.js +85 -0
  109. package/dist-cjs/entities/moves.js.map +1 -0
  110. package/dist-cjs/entities/repo-states.js +256 -0
  111. package/dist-cjs/entities/repo-states.js.map +1 -0
  112. package/dist-cjs/entities/rules.js +216 -0
  113. package/dist-cjs/entities/rules.js.map +1 -0
  114. package/dist-cjs/entities/types.js +44 -0
  115. package/dist-cjs/entities/types.js.map +1 -0
  116. package/dist-cjs/files/commands.js +120 -0
  117. package/dist-cjs/files/commands.js.map +1 -0
  118. package/dist-cjs/files/index.js +19 -0
  119. package/dist-cjs/files/index.js.map +1 -0
  120. package/dist-cjs/index.js +39 -0
  121. package/dist-cjs/index.js.map +1 -0
  122. package/dist-cjs/machines/arbitration.js +41 -0
  123. package/dist-cjs/machines/arbitration.js.map +1 -0
  124. package/dist-cjs/machines/backoff.js +38 -0
  125. package/dist-cjs/machines/backoff.js.map +1 -0
  126. package/dist-cjs/machines/bootstrap.js +110 -0
  127. package/dist-cjs/machines/bootstrap.js.map +1 -0
  128. package/dist-cjs/machines/chunks.js +72 -0
  129. package/dist-cjs/machines/chunks.js.map +1 -0
  130. package/dist-cjs/machines/index.js +28 -0
  131. package/dist-cjs/machines/index.js.map +1 -0
  132. package/dist-cjs/machines/presence.js +53 -0
  133. package/dist-cjs/machines/presence.js.map +1 -0
  134. package/dist-cjs/merge/index.js +25 -0
  135. package/dist-cjs/merge/index.js.map +1 -0
  136. package/dist-cjs/merge/merge3.js +191 -0
  137. package/dist-cjs/merge/merge3.js.map +1 -0
  138. package/dist-cjs/package.json +1 -0
  139. package/dist-cjs/records/authority.js +31 -0
  140. package/dist-cjs/records/authority.js.map +1 -0
  141. package/dist-cjs/records/codec.js +48 -0
  142. package/dist-cjs/records/codec.js.map +1 -0
  143. package/dist-cjs/records/contracts.js +121 -0
  144. package/dist-cjs/records/contracts.js.map +1 -0
  145. package/dist-cjs/records/inbox.js +37 -0
  146. package/dist-cjs/records/inbox.js.map +1 -0
  147. package/dist-cjs/records/index.js +23 -0
  148. package/dist-cjs/records/index.js.map +1 -0
  149. package/dist-cjs/records/outbox.js +48 -0
  150. package/dist-cjs/records/outbox.js.map +1 -0
  151. package/dist-cjs/registration/index.js +19 -0
  152. package/dist-cjs/registration/index.js.map +1 -0
  153. package/dist-cjs/registration/user-ground.js +183 -0
  154. package/dist-cjs/registration/user-ground.js.map +1 -0
  155. package/dist-cjs/transfer/artifacts.js +75 -0
  156. package/dist-cjs/transfer/artifacts.js.map +1 -0
  157. package/dist-cjs/transfer/batches.js +44 -0
  158. package/dist-cjs/transfer/batches.js.map +1 -0
  159. package/dist-cjs/transfer/download.js +88 -0
  160. package/dist-cjs/transfer/download.js.map +1 -0
  161. package/dist-cjs/transfer/index.js +21 -0
  162. package/dist-cjs/transfer/index.js.map +1 -0
  163. package/dist-cjs/transfer/parallel.js +35 -0
  164. package/dist-cjs/transfer/parallel.js.map +1 -0
  165. package/dist-cjs/transfer/upload.js +161 -0
  166. package/dist-cjs/transfer/upload.js.map +1 -0
  167. package/dist-cjs/watching/coverage.js +54 -0
  168. package/dist-cjs/watching/coverage.js.map +1 -0
  169. package/dist-cjs/watching/index.js +24 -0
  170. package/dist-cjs/watching/index.js.map +1 -0
  171. package/dist-cjs/watching/suspicion.js +105 -0
  172. package/dist-cjs/watching/suspicion.js.map +1 -0
  173. package/dist-cjs/workspace/api.js +66 -0
  174. package/dist-cjs/workspace/api.js.map +1 -0
  175. package/dist-cjs/workspace/identity.js +32 -0
  176. package/dist-cjs/workspace/identity.js.map +1 -0
  177. package/dist-cjs/workspace/index.js +14 -0
  178. package/dist-cjs/workspace/index.js.map +1 -0
  179. package/dist-cjs/workspace/memory-store.js +21 -0
  180. package/dist-cjs/workspace/memory-store.js.map +1 -0
  181. package/dist-cjs/workspace/registry.js +98 -0
  182. package/dist-cjs/workspace/registry.js.map +1 -0
  183. package/dist-cjs/workspace/types.js +3 -0
  184. package/dist-cjs/workspace/types.js.map +1 -0
  185. package/package.json +30 -8
  186. package/dist/contracts/mutation.d.ts +0 -51
  187. package/dist/contracts/mutation.d.ts.map +0 -1
  188. package/dist/contracts/mutation.js +0 -55
  189. package/dist/contracts/mutation.js.map +0 -1
  190. package/dist/machines/journal.d.ts +0 -63
  191. package/dist/machines/journal.d.ts.map +0 -1
  192. package/dist/machines/journal.js +0 -71
  193. package/dist/machines/journal.js.map +0 -1
  194. package/dist/machines/ordering.d.ts +0 -53
  195. package/dist/machines/ordering.d.ts.map +0 -1
  196. package/dist/machines/ordering.js +0 -87
  197. package/dist/machines/ordering.js.map +0 -1
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ /**
3
+ * Login-time user-ground convergence.
4
+ *
5
+ * Core owns when this operation runs. Live owns what it means: either create
6
+ * one initial cloud graph or install the active graph, prove the local
7
+ * materialization carries the same portable records, then start Watch. Every
8
+ * filesystem, SQLite, content, and R2 effect is injected by the host.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.userGroundRecords = userGroundRecords;
12
+ exports.convergeUserGround = convergeUserGround;
13
+ const index_js_1 = require("../entities/index.js");
14
+ const HASH = /^[0-9a-f]{64}$/;
15
+ function required(value, name) {
16
+ const text = typeof value === 'string' ? value.trim() : '';
17
+ if (!text)
18
+ throw new Error(`user-ground ${name} is required`);
19
+ return text;
20
+ }
21
+ function normalizeIdentity(input) {
22
+ return {
23
+ userId: required(input?.userId, 'user id'),
24
+ userEmail: required(input?.userEmail, 'user email').toLowerCase(),
25
+ deviceId: required(input?.deviceId, 'device id'),
26
+ };
27
+ }
28
+ function assertCloud(value, resourceId, sha256Hex) {
29
+ const cloud = value;
30
+ if (!cloud || cloud.resourceId !== resourceId
31
+ || !Number.isSafeInteger(cloud.version) || Number(cloud.version) < 0
32
+ || typeof cloud.checksum !== 'string' || !HASH.test(cloud.checksum)) {
33
+ throw new Error('cloud user-ground head is invalid');
34
+ }
35
+ const snapshot = (0, index_js_1.snapshotFromRecords)(cloud.snapshot?.records || []);
36
+ if (snapshot.records.length === 0)
37
+ throw new Error('cloud user-ground graph is empty');
38
+ const checksum = sha256Hex((0, index_js_1.stableJson)(snapshot));
39
+ if (checksum !== cloud.checksum)
40
+ throw new Error('cloud user-ground snapshot checksum mismatch');
41
+ return {
42
+ resourceId,
43
+ version: Number(cloud.version),
44
+ checksum,
45
+ snapshot,
46
+ };
47
+ }
48
+ function controlPath(path) {
49
+ return path.replace(/\\/g, '/').split('/').includes('.amalgm');
50
+ }
51
+ function assertLocal(value, identity) {
52
+ const local = value;
53
+ if (!local
54
+ || local.userId !== identity.userId
55
+ || String(local.userEmail || '').toLowerCase() !== identity.userEmail
56
+ || local.deviceId !== identity.deviceId) {
57
+ throw new Error('local user-ground identity does not match login');
58
+ }
59
+ const localRoot = required(local.localRoot, 'local root');
60
+ const sqlitePath = required(local.sqlitePath, 'SQLite path');
61
+ if (!controlPath(sqlitePath)) {
62
+ throw new Error('local user-ground SQLite must live inside the reserved .amalgm control directory');
63
+ }
64
+ const snapshot = (0, index_js_1.snapshotFromRecords)(local.records || []);
65
+ if (snapshot.records.length === 0)
66
+ throw new Error('local user-ground graph is empty');
67
+ return {
68
+ ...identity,
69
+ localRoot,
70
+ sqlitePath,
71
+ records: snapshot.records,
72
+ ...(local.recordsBeyondSnapshot === true ? { recordsBeyondSnapshot: true } : {}),
73
+ };
74
+ }
75
+ /** Select the authenticated user's core root without materializing sibling
76
+ * workspace roots. The canonical email is the declared root name. */
77
+ function userGroundRecords(records, userEmail) {
78
+ const snapshot = (0, index_js_1.snapshotFromRecords)(records);
79
+ const canonicalEmail = required(userEmail, 'user email').toLowerCase();
80
+ const roots = snapshot.records.filter((record) => record.status === 'active'
81
+ && record.parentUUID === null
82
+ && record.type === 'workspace'
83
+ && record.name.toLowerCase() === canonicalEmail);
84
+ if (roots.length !== 1) {
85
+ throw new Error('cloud registry must contain exactly one authenticated user-ground root');
86
+ }
87
+ const included = new Set([roots[0].uuid]);
88
+ let changed = true;
89
+ while (changed) {
90
+ changed = false;
91
+ for (const record of snapshot.records) {
92
+ if (record.parentUUID !== null && included.has(record.parentUUID) && !included.has(record.uuid)) {
93
+ included.add(record.uuid);
94
+ changed = true;
95
+ }
96
+ }
97
+ }
98
+ return snapshot.records.filter((record) => included.has(record.uuid));
99
+ }
100
+ function assertSameGraph(local, cloud) {
101
+ if (!(0, index_js_1.sameRecords)((0, index_js_1.travelingRecords)(userGroundRecords(local.records, local.userEmail)), userGroundRecords(cloud.snapshot.records, local.userEmail))) {
102
+ throw new Error('local user-ground graph does not match the active cloud core graph');
103
+ }
104
+ }
105
+ function assertWatch(value) {
106
+ const watch = value;
107
+ if (!watch || watch.active !== true || !Number.isSafeInteger(watch.roots) || Number(watch.roots) < 1) {
108
+ throw new Error('user-ground watcher coverage is not active');
109
+ }
110
+ return { active: true, roots: Number(watch.roots) };
111
+ }
112
+ /**
113
+ * Converge one login. No caller selects "new" versus "existing": the active
114
+ * cloud head decides. Initial publication is conditional, so two first
115
+ * machines cannot create two authorities.
116
+ */
117
+ async function convergeUserGround(input, ports) {
118
+ const identity = normalizeIdentity(input);
119
+ const resourceId = (0, index_js_1.privateEntityResourceId)(identity.userId, ports.sha256Hex);
120
+ const found = await ports.cloud.lookup(resourceId);
121
+ let cloud;
122
+ let local;
123
+ let mode;
124
+ if (found === null || found === undefined) {
125
+ const initialized = assertLocal(await ports.local.initialize(identity), identity);
126
+ const snapshot = (0, index_js_1.snapshotFromRecords)((0, index_js_1.travelingRecords)(initialized.records));
127
+ const checksum = ports.sha256Hex((0, index_js_1.stableJson)(snapshot));
128
+ const published = await ports.cloud.publishInitial({ resourceId, checksum, snapshot });
129
+ cloud = assertCloud(published.current, resourceId, ports.sha256Hex);
130
+ if (published.created) {
131
+ assertSameGraph(initialized, cloud);
132
+ local = initialized;
133
+ mode = 'initialized';
134
+ }
135
+ else {
136
+ local = assertLocal(await ports.local.install(identity, cloud), identity);
137
+ assertSameGraph(local, cloud);
138
+ mode = 'materialized';
139
+ }
140
+ }
141
+ else {
142
+ cloud = assertCloud(found, resourceId, ports.sha256Hex);
143
+ const currentValue = await ports.local.inspect(identity);
144
+ if (currentValue !== null && currentValue !== undefined) {
145
+ const current = assertLocal(currentValue, identity);
146
+ if ((0, index_js_1.sameRecords)((0, index_js_1.travelingRecords)(userGroundRecords(current.records, identity.userEmail)), userGroundRecords(cloud.snapshot.records, identity.userEmail))) {
147
+ local = current;
148
+ mode = current.recordsBeyondSnapshot === true ? 'syncing' : 'current';
149
+ }
150
+ else if (current.recordsBeyondSnapshot === true) {
151
+ local = current;
152
+ mode = 'syncing';
153
+ }
154
+ else {
155
+ local = assertLocal(await ports.local.install(identity, cloud), identity);
156
+ assertSameGraph(local, cloud);
157
+ mode = 'materialized';
158
+ }
159
+ }
160
+ else {
161
+ local = assertLocal(await ports.local.install(identity, cloud), identity);
162
+ assertSameGraph(local, cloud);
163
+ mode = 'materialized';
164
+ }
165
+ }
166
+ const watch = assertWatch(await ports.local.watch(identity, cloud));
167
+ const finalLocal = assertLocal(await ports.local.inspect(identity), identity);
168
+ if (finalLocal.recordsBeyondSnapshot !== true)
169
+ assertSameGraph(finalLocal, cloud);
170
+ return {
171
+ ...identity,
172
+ localRoot: finalLocal.localRoot,
173
+ sqlitePath: finalLocal.sqlitePath,
174
+ cloudResourceId: cloud.resourceId,
175
+ cloudVersion: cloud.version,
176
+ cloudHead: cloud.checksum,
177
+ mode,
178
+ entityCount: userGroundRecords(cloud.snapshot.records, identity.userEmail).length,
179
+ watcherRoots: watch.roots,
180
+ watching: true,
181
+ };
182
+ }
183
+ //# sourceMappingURL=user-ground.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-ground.js","sourceRoot":"","sources":["../../src/registration/user-ground.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * The immutable content artifact selected by an entity record.
4
+ *
5
+ * Registration owns identity and the graph. Transfer sees only the exact
6
+ * content head registration already produced. Containers and references have
7
+ * no independent byte artifact: their portable state is already in the graph.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.artifactKindForType = artifactKindForType;
11
+ exports.assertTransferArtifact = assertTransferArtifact;
12
+ exports.artifactForRecord = artifactForRecord;
13
+ const CONTENT_HEAD = /^[0-9a-f]{64}$/;
14
+ /** Registration's type is the only transfer selector. */
15
+ function artifactKindForType(type) {
16
+ switch (type) {
17
+ case 'file.text':
18
+ case 'file.binary':
19
+ return 'file';
20
+ case 'link':
21
+ return 'link';
22
+ case 'repo.git':
23
+ return 'git';
24
+ case 'workspace':
25
+ case 'folder':
26
+ case 'reference':
27
+ return null;
28
+ }
29
+ }
30
+ function assertTransferArtifact(artifact) {
31
+ const expected = artifactKindForType(artifact.entityType);
32
+ if (expected === null || artifact.kind !== expected) {
33
+ throw new Error(`entity type ${artifact.entityType} cannot use ${artifact.kind} transfer`);
34
+ }
35
+ }
36
+ /**
37
+ * Select the one immutable artifact carried by a cloud entity record.
38
+ * Deleted records are tombstones. A not-yet-captured leaf has no artifact yet.
39
+ */
40
+ function artifactForRecord(record) {
41
+ if (record.status === 'deleted')
42
+ return null;
43
+ const kind = artifactKindForType(record.type);
44
+ if (kind === null)
45
+ return null;
46
+ let contentHash;
47
+ switch (record.type) {
48
+ case 'file.text':
49
+ case 'file.binary':
50
+ contentHash = record.payloadVersion;
51
+ break;
52
+ case 'link':
53
+ contentHash = record.payloadVersion;
54
+ break;
55
+ case 'repo.git':
56
+ contentHash = record.transportVersion;
57
+ break;
58
+ case 'workspace':
59
+ case 'folder':
60
+ case 'reference':
61
+ return null;
62
+ }
63
+ if (contentHash === null)
64
+ return null;
65
+ if (!CONTENT_HEAD.test(contentHash)) {
66
+ throw new Error(`entity ${record.uuid} has an invalid transfer head`);
67
+ }
68
+ return {
69
+ entityId: record.uuid,
70
+ entityType: record.type,
71
+ kind,
72
+ contentHash,
73
+ };
74
+ }
75
+ //# sourceMappingURL=artifacts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/transfer/artifacts.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAiBH,kDAcC;AAED,wDAKC;AAMD,8CAiCC;AAxED,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAWtC,yDAAyD;AACzD,SAAgB,mBAAmB,CAAC,IAAgB;IAClD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,WAAW,CAAC;QACjB,KAAK,aAAa;YAChB,OAAO,MAAM,CAAC;QAChB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,UAAU;YACb,OAAO,KAAK,CAAC;QACf,KAAK,WAAW,CAAC;QACjB,KAAK,QAAQ,CAAC;QACd,KAAK,WAAW;YACd,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,SAAgB,sBAAsB,CAAC,QAA0B;IAC/D,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC1D,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,UAAU,eAAe,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAC;IAC7F,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,MAAyB;IACzD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAE7C,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,WAA0B,CAAC;IAC/B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,WAAW,CAAC;QACjB,KAAK,aAAa;YAChB,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC;YACpC,MAAM;QACR,KAAK,MAAM;YACT,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC;YACpC,MAAM;QACR,KAAK,UAAU;YACb,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC;YACtC,MAAM;QACR,KAAK,WAAW,CAAC;QACjB,KAAK,QAAQ,CAAC;QACd,KAAK,WAAW;YACd,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,CAAC,IAAI,+BAA+B,CAAC,CAAC;IACxE,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,IAAI;QACrB,UAAU,EAAE,MAAM,CAAC,IAAI;QACvB,IAAI;QACJ,WAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * Cold file transport batches.
4
+ *
5
+ * Content blocks are durable identities. They are not network requests.
6
+ * Adjacent missing blocks may cross the wire together while remaining
7
+ * independently addressed and verified at both ends.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.FILE_TRANSFER_BATCH_BYTES = void 0;
11
+ exports.planChunkBatches = planChunkBatches;
12
+ /** One batch may fill the existing binary frame bound. Hosts lower transfer
13
+ * concurrency when their memory budget cannot admit eight full batches. */
14
+ exports.FILE_TRANSFER_BATCH_BYTES = 16 * 1024 * 1024;
15
+ /** Group ascending, adjacent chunk indexes without crossing the byte bound. */
16
+ function planChunkBatches(indexes, manifest) {
17
+ const offsets = [];
18
+ let offset = 0;
19
+ for (const chunk of manifest.chunks) {
20
+ offsets.push(offset);
21
+ offset += chunk.bytes;
22
+ }
23
+ const batches = [];
24
+ for (const index of [...indexes].sort((left, right) => left - right)) {
25
+ const chunk = manifest.chunks[index];
26
+ const chunkOffset = offsets[index];
27
+ if (!chunk || chunkOffset === undefined)
28
+ throw new Error(`content manifest has no chunk ${index}`);
29
+ const prior = batches.at(-1);
30
+ const adjacent = prior?.indexes.at(-1) === index - 1;
31
+ if (prior && adjacent && prior.bytes + chunk.bytes <= exports.FILE_TRANSFER_BATCH_BYTES) {
32
+ batches[batches.length - 1] = {
33
+ indexes: [...prior.indexes, index],
34
+ offset: prior.offset,
35
+ bytes: prior.bytes + chunk.bytes,
36
+ };
37
+ }
38
+ else {
39
+ batches.push({ indexes: [index], offset: chunkOffset, bytes: chunk.bytes });
40
+ }
41
+ }
42
+ return batches;
43
+ }
44
+ //# sourceMappingURL=batches.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batches.js","sourceRoot":"","sources":["../../src/transfer/batches.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAeH,4CA6BC;AAxCD;2EAC2E;AAC9D,QAAA,yBAAyB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAQ1D,+EAA+E;AAC/E,SAAgB,gBAAgB,CAC9B,OAA0B,EAC1B,QAAyB;IAEzB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC;IACxB,CAAC;IAED,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;QACrE,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,IAAI,WAAW,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,EAAE,CAAC,CAAC;QACnG,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC;QACrD,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,iCAAyB,EAAE,CAAC;YAChF,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG;gBAC5B,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC;gBAClC,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;aACjC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * Download one immutable artifact into a machine cache.
4
+ *
5
+ * The operation asks the cache which verified chunks it already owns, fetches
6
+ * only the gaps, verifies every fetched chunk, and then asks the cache to seal
7
+ * the complete artifact. Sealing must verify the whole byte count and content
8
+ * hash before returning a local handle.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.download = download;
12
+ const content_js_1 = require("../contracts/content.js");
13
+ const artifacts_js_1 = require("./artifacts.js");
14
+ const batches_js_1 = require("./batches.js");
15
+ const parallel_js_1 = require("./parallel.js");
16
+ const checkedBatchChunks = (bytes, indexes, manifest, sha256Hex) => {
17
+ const chunks = [];
18
+ let offset = 0;
19
+ for (const index of indexes) {
20
+ const expected = manifest.chunks[index];
21
+ if (!expected)
22
+ throw new Error(`download manifest named an unknown chunk index`);
23
+ const chunk = bytes.subarray(offset, offset + expected.bytes);
24
+ if (chunk.byteLength !== expected.bytes || sha256Hex(chunk) !== expected.sha256) {
25
+ throw new Error(`downloaded content chunk ${index} failed verification`);
26
+ }
27
+ chunks.push(chunk);
28
+ offset += expected.bytes;
29
+ }
30
+ if (offset !== bytes.byteLength)
31
+ throw new Error('downloaded file batch has the wrong byte count');
32
+ return chunks;
33
+ };
34
+ /** Download one immutable artifact, fetching only machine-cache gaps. */
35
+ async function download(artifact, ports, options = {}) {
36
+ (0, artifacts_js_1.assertTransferArtifact)(artifact);
37
+ const checked = (0, content_js_1.checkContentManifest)(await ports.getManifest(artifact), artifact.contentHash);
38
+ if (!checked.ok)
39
+ throw new Error(checked.error);
40
+ const manifest = checked.value;
41
+ const missing = [];
42
+ for (const [index, chunk] of manifest.chunks.entries()) {
43
+ if (!await ports.hasChunk(artifact, manifest, index))
44
+ missing.push(index);
45
+ }
46
+ if (artifact.kind === 'file' && ports.getChunkBatch) {
47
+ await (0, parallel_js_1.parallelForEach)((0, batches_js_1.planChunkBatches)(missing, manifest), options.concurrency ?? parallel_js_1.DEFAULT_TRANSFER_CONCURRENCY, async (batch) => {
48
+ const bytes = await ports.getChunkBatch(artifact, manifest, batch.indexes);
49
+ if (!(bytes instanceof Uint8Array))
50
+ throw new Error('downloaded file batch is not binary bytes');
51
+ const chunks = checkedBatchChunks(bytes, batch.indexes, manifest, ports.sha256Hex);
52
+ if (ports.putChunkBatch) {
53
+ await ports.putChunkBatch(artifact, manifest, batch.indexes, bytes);
54
+ }
55
+ else {
56
+ for (let offset = 0; offset < batch.indexes.length; offset += 1) {
57
+ await ports.putChunk(artifact, manifest, batch.indexes[offset], chunks[offset]);
58
+ }
59
+ }
60
+ });
61
+ }
62
+ else {
63
+ await (0, parallel_js_1.parallelForEach)(missing, options.concurrency ?? parallel_js_1.DEFAULT_TRANSFER_CONCURRENCY, async (index) => {
64
+ const expected = manifest.chunks[index];
65
+ if (expected === undefined)
66
+ throw new Error('download manifest named an unknown chunk index');
67
+ const bytes = await ports.getChunk(artifact, manifest, index);
68
+ if (!(bytes instanceof Uint8Array)
69
+ || bytes.byteLength !== expected.bytes
70
+ || ports.sha256Hex(bytes) !== expected.sha256) {
71
+ throw new Error(`downloaded content chunk ${index} failed verification`);
72
+ }
73
+ await ports.putChunk(artifact, manifest, index, bytes);
74
+ });
75
+ }
76
+ const sealed = await ports.sealArtifact(artifact, manifest);
77
+ if (sealed.bytes !== manifest.bytes || sealed.contentHash !== manifest.contentHash) {
78
+ throw new Error(`downloaded artifact ${artifact.contentHash} failed verification`);
79
+ }
80
+ return {
81
+ artifact,
82
+ manifest,
83
+ local: sealed.local,
84
+ downloadedChunks: missing.length,
85
+ reusedChunks: manifest.chunks.length - missing.length,
86
+ };
87
+ }
88
+ //# sourceMappingURL=download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/transfer/download.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /** Type-aware immutable content transfer operations. */
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ __exportStar(require("./artifacts.js"), exports);
19
+ __exportStar(require("./upload.js"), exports);
20
+ __exportStar(require("./download.js"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transfer/index.ts"],"names":[],"mappings":";AAAA,wDAAwD;;;;;;;;;;;;;;;;AAExD,iDAA+B;AAC/B,8CAA4B;AAC5B,gDAA8B"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_TRANSFER_CONCURRENCY = void 0;
4
+ exports.parallelForEach = parallelForEach;
5
+ /** Eight full-size frames keep one ordinary high-latency connection busy
6
+ * without making the SDK's default memory bound unreasonably large. Hosts
7
+ * with a stricter budget can always pass a smaller explicit window. */
8
+ exports.DEFAULT_TRANSFER_CONCURRENCY = 8;
9
+ /** Run a bounded number of independent effects without changing item order. */
10
+ async function parallelForEach(items, concurrency, effect) {
11
+ if (!Number.isSafeInteger(concurrency) || concurrency < 1) {
12
+ throw new Error('transfer concurrency must be a positive integer');
13
+ }
14
+ let cursor = 0;
15
+ let failure;
16
+ const worker = async () => {
17
+ while (failure === undefined && cursor < items.length) {
18
+ const index = cursor;
19
+ cursor += 1;
20
+ const item = items[index];
21
+ if (item === undefined)
22
+ continue;
23
+ try {
24
+ await effect(item);
25
+ }
26
+ catch (error) {
27
+ failure ??= error;
28
+ }
29
+ }
30
+ };
31
+ await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, worker));
32
+ if (failure !== undefined)
33
+ throw failure;
34
+ }
35
+ //# sourceMappingURL=parallel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel.js","sourceRoot":"","sources":["../../src/transfer/parallel.ts"],"names":[],"mappings":";;;AAMA,0CA2BC;AAjCD;;uEAEuE;AAC1D,QAAA,4BAA4B,GAAG,CAAC,CAAC;AAE9C,+EAA+E;AACxE,KAAK,UAAU,eAAe,CACnC,KAAmB,EACnB,WAAmB,EACnB,MAAkC;IAElC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,OAAgB,CAAC;IACrB,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,OAAO,OAAO,KAAK,SAAS,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YACtD,MAAM,KAAK,GAAG,MAAM,CAAC;YACrB,MAAM,IAAI,CAAC,CAAC;YACZ,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,IAAI,KAAK,SAAS;gBAAE,SAAS;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,KAAK,KAAK,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CACpE,CAAC;IACF,IAAI,OAAO,KAAK,SAAS;QAAE,MAAM,OAAO,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * Upload one already-captured immutable artifact.
4
+ *
5
+ * The source is seekable so the operation can first derive its manifest and
6
+ * then reread only the chunks the authority reports missing. The manifest is
7
+ * committed last; until then a partial upload is invisible.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.deriveUploadManifest = deriveUploadManifest;
11
+ exports.upload = upload;
12
+ const content_js_1 = require("../contracts/content.js");
13
+ const artifacts_js_1 = require("./artifacts.js");
14
+ const batches_js_1 = require("./batches.js");
15
+ const parallel_js_1 = require("./parallel.js");
16
+ const planChunks = (sizes) => {
17
+ let offset = 0;
18
+ return sizes.map((size, index) => {
19
+ const chunk = { index, offset, bytes: size };
20
+ offset += size;
21
+ return chunk;
22
+ });
23
+ };
24
+ const fixedSizes = (totalBytes, partBytes) => {
25
+ const sizes = [];
26
+ for (let remaining = totalBytes; remaining > 0; remaining -= partBytes) {
27
+ sizes.push(Math.min(remaining, partBytes));
28
+ }
29
+ return sizes;
30
+ };
31
+ /** Entity type selects semantic transfer framing. Files use reusable
32
+ * Syncthing-style blocks. A link target is one literal value. A Git transport
33
+ * is already a native incremental pack/state parcel, so the outer wire only
34
+ * segments it at the maximum frame size instead of re-chunking it as a file. */
35
+ const artifactPartSizes = (artifact, totalBytes) => {
36
+ switch (artifact.kind) {
37
+ case 'file':
38
+ return (0, content_js_1.chunkSizes)(totalBytes);
39
+ case 'link':
40
+ if (totalBytes > content_js_1.MAX_CONTENT_BLOCK_BYTES)
41
+ throw new Error('link target exceeds the transfer frame bound');
42
+ return totalBytes === 0 ? [] : [totalBytes];
43
+ case 'git':
44
+ return fixedSizes(totalBytes, content_js_1.MAX_CONTENT_BLOCK_BYTES);
45
+ }
46
+ };
47
+ /** Derive the reusable upload evidence while capture already owns the bytes.
48
+ * A host persists this beside its immutable artifact so upload never has to
49
+ * rediscover block boundaries or reread the complete source just to prove the
50
+ * work registration already completed. */
51
+ function deriveUploadManifest(artifact, bytes, sha256Hex) {
52
+ (0, artifacts_js_1.assertTransferArtifact)(artifact);
53
+ if (sha256Hex(bytes) !== artifact.contentHash) {
54
+ throw new Error(`captured artifact does not match ${artifact.contentHash}`);
55
+ }
56
+ const chunks = [];
57
+ let offset = 0;
58
+ for (const size of artifactPartSizes(artifact, bytes.byteLength)) {
59
+ const part = bytes.subarray(offset, offset + size);
60
+ chunks.push({ sha256: sha256Hex(part), bytes: size });
61
+ offset += size;
62
+ }
63
+ const checked = (0, content_js_1.manifestFromChunks)(artifact.contentHash, chunks);
64
+ if (!checked.ok)
65
+ throw new Error(checked.error);
66
+ return checked.value;
67
+ }
68
+ const readExact = async (source, chunk) => {
69
+ const bytes = await source.read(chunk.offset, chunk.bytes);
70
+ if (!(bytes instanceof Uint8Array) || bytes.byteLength !== chunk.bytes) {
71
+ throw new Error(`upload source returned the wrong byte count for chunk ${chunk.index}`);
72
+ }
73
+ return bytes;
74
+ };
75
+ const assertBatchChunks = (bytes, indexes, manifest, sha256Hex) => {
76
+ let offset = 0;
77
+ for (const index of indexes) {
78
+ const expected = manifest.chunks[index];
79
+ if (!expected)
80
+ throw new Error(`content manifest has no chunk ${index}`);
81
+ const chunk = bytes.subarray(offset, offset + expected.bytes);
82
+ if (chunk.byteLength !== expected.bytes || sha256Hex(chunk) !== expected.sha256) {
83
+ throw new Error(`upload source changed while reading chunk ${index}`);
84
+ }
85
+ offset += expected.bytes;
86
+ }
87
+ if (offset !== bytes.byteLength)
88
+ throw new Error('upload source returned the wrong byte count for a file batch');
89
+ };
90
+ /** Upload one immutable artifact, sending only authority-declared gaps. */
91
+ async function upload(artifact, source, ports, options = {}) {
92
+ (0, artifacts_js_1.assertTransferArtifact)(artifact);
93
+ if (!Number.isSafeInteger(source.bytes) || source.bytes < 0) {
94
+ throw new Error('upload source byte count is invalid');
95
+ }
96
+ if (source.manifestVerified === true && source.manifest === undefined) {
97
+ throw new Error('verified upload manifest is missing');
98
+ }
99
+ const expectedSizes = artifactPartSizes(artifact, source.bytes);
100
+ let plan;
101
+ let manifest;
102
+ if (source.manifest !== undefined) {
103
+ const cached = (0, content_js_1.checkContentManifest)(source.manifest, artifact.contentHash);
104
+ if (!cached.ok || cached.value.bytes !== source.bytes
105
+ || cached.value.chunks.some((chunk, index) => chunk.bytes !== expectedSizes[index])
106
+ || cached.value.chunks.length !== expectedSizes.length) {
107
+ throw new Error('cached upload manifest is invalid');
108
+ }
109
+ manifest = cached.value;
110
+ plan = planChunks(manifest.chunks.map((chunk) => chunk.bytes));
111
+ }
112
+ else {
113
+ plan = planChunks(expectedSizes);
114
+ const chunks = [];
115
+ for (const chunk of plan) {
116
+ const bytes = await readExact(source, chunk);
117
+ chunks.push({ sha256: ports.sha256Hex(bytes), bytes: chunk.bytes });
118
+ }
119
+ const checked = (0, content_js_1.manifestFromChunks)(artifact.contentHash, chunks);
120
+ if (!checked.ok)
121
+ throw new Error(checked.error);
122
+ manifest = checked.value;
123
+ }
124
+ const missing = [...await ports.missingChunks(artifact, manifest)];
125
+ if (new Set(missing).size !== missing.length
126
+ || missing.some((index) => !Number.isSafeInteger(index) || index < 0 || index >= plan.length)) {
127
+ throw new Error('upload authority returned invalid missing chunk indexes');
128
+ }
129
+ if (missing.length > 0 && source.manifestVerified !== true) {
130
+ if (!source.verify || !await source.verify(artifact.contentHash)) {
131
+ throw new Error(`upload source does not match ${artifact.contentHash}`);
132
+ }
133
+ }
134
+ if (artifact.kind === 'file' && ports.putChunkBatch) {
135
+ await (0, parallel_js_1.parallelForEach)((0, batches_js_1.planChunkBatches)(missing, manifest), options.concurrency ?? parallel_js_1.DEFAULT_TRANSFER_CONCURRENCY, async (batch) => {
136
+ const bytes = await readExact(source, { index: batch.indexes[0], offset: batch.offset, bytes: batch.bytes });
137
+ assertBatchChunks(bytes, batch.indexes, manifest, ports.sha256Hex);
138
+ await ports.putChunkBatch(artifact, manifest, batch.indexes, bytes);
139
+ });
140
+ }
141
+ else {
142
+ await (0, parallel_js_1.parallelForEach)(missing, options.concurrency ?? parallel_js_1.DEFAULT_TRANSFER_CONCURRENCY, async (index) => {
143
+ const chunk = plan[index];
144
+ if (chunk === undefined)
145
+ throw new Error('upload authority returned an unknown chunk index');
146
+ const bytes = await readExact(source, chunk);
147
+ if (ports.sha256Hex(bytes) !== manifest.chunks[index]?.sha256) {
148
+ throw new Error(`upload source changed while reading chunk ${index}`);
149
+ }
150
+ await ports.putChunk(artifact, manifest, index, bytes);
151
+ });
152
+ }
153
+ await ports.putManifest(artifact, manifest);
154
+ return {
155
+ artifact,
156
+ manifest,
157
+ uploadedChunks: missing.length,
158
+ reusedChunks: manifest.chunks.length - missing.length,
159
+ };
160
+ }
161
+ //# sourceMappingURL=upload.js.map