@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,193 @@
1
+ "use strict";
2
+ /**
3
+ * The compare core of detection: rows (store memory) + entries (disk
4
+ * truth) in, actions out. Pure — the walk that produces entries and the
5
+ * hashing that settles `checks` are the host's; only the identity
6
+ * verdicts live here. Ratified by entity-model.md ("Address and
7
+ * physical continuity are the two identity witnesses, never content").
8
+ *
9
+ * Three kinds of channel, three orderings of the same two witnesses:
10
+ *
11
+ * - Files: path claims first; the inode rescues path-orphans. An
12
+ * atomic save replaces a file's inode in place, so the address must
13
+ * absorb that churn.
14
+ * - Directories: physical continuity claims first; the address claims
15
+ * what no inode claims. Nothing replaces a directory's inode in
16
+ * place, so a known inode at a new address IS the directory, moved.
17
+ * - Links: the FILE rules, for the file reason — retarget is unlink +
18
+ * recreate, so the inode churns exactly like an atomic save.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.matchByInode = matchByInode;
22
+ exports.diffDirState = diffDirState;
23
+ exports.diffLinkState = diffLinkState;
24
+ exports.diffRootState = diffRootState;
25
+ /**
26
+ * Physical continuity, shared by every compare function: a row and an
27
+ * entry sharing one device:inode, exactly one match on both sides → the
28
+ * same physical thing. Anything else is never guessed. For files this
29
+ * rescues path-orphans (rule 2); for directories it IS rule 1.
30
+ */
31
+ function matchByInode(rows, entries) {
32
+ const one = (map, key, value) => map.set(key, map.has(key) ? 'many' : value);
33
+ const rowsByInode = new Map();
34
+ for (const row of rows)
35
+ one(rowsByInode, `${row.device}:${row.inode}`, row);
36
+ const entriesByInode = new Map();
37
+ for (const entry of entries)
38
+ one(entriesByInode, `${entry.device}:${entry.inode}`, entry);
39
+ const matched = [];
40
+ for (const [key, entry] of entriesByInode) {
41
+ const row = rowsByInode.get(key);
42
+ if (entry === 'many' || !row || row === 'many')
43
+ continue;
44
+ matched.push({ row, entry });
45
+ }
46
+ return matched;
47
+ }
48
+ /**
49
+ * The compare function for directory channels — the file identity rules
50
+ * in the OPPOSITE order, for the mirror reason. A file's inode is
51
+ * replaced in place on every atomic save, so for files the path must
52
+ * absorb that churn (path claims first). Nothing replaces a directory's
53
+ * inode in place: a known inode at a new address IS the directory,
54
+ * moved — even when its old address was reoccupied in the same interval
55
+ * (two directories exchanging names cross by inode, exactly like the
56
+ * files inside them). So:
57
+ * 1. Physical continuity claims the channel: unique device:inode,
58
+ * whole sets, same path = continued, new path = rename.
59
+ * 2. The address claims what no inode claims — recreation under an old
60
+ * name is the same channel wearing a never-seen inode (`retags`,
61
+ * silent).
62
+ * 3. Leftover entries are creates; leftover rows are deletes.
63
+ * No content: a directory's membership is derived state upstream, so
64
+ * there is nothing to hash or check.
65
+ */
66
+ function diffDirState(rows, entries) {
67
+ const matched = matchByInode(rows, entries);
68
+ const matchedRowIds = new Set(matched.map((pair) => pair.row.dirId));
69
+ const matchedEntries = new Set(matched.map((pair) => pair.entry));
70
+ const rowsByPath = new Map(rows.filter((row) => !matchedRowIds.has(row.dirId))
71
+ .map((row) => [row.relPath, row]));
72
+ const retags = [];
73
+ const creates = [];
74
+ for (const entry of entries) {
75
+ if (matchedEntries.has(entry))
76
+ continue;
77
+ const row = rowsByPath.get(entry.relPath);
78
+ if (row) {
79
+ rowsByPath.delete(entry.relPath);
80
+ retags.push({ row, entry });
81
+ }
82
+ else {
83
+ creates.push(entry);
84
+ }
85
+ }
86
+ return {
87
+ renames: matched.filter((pair) => pair.row.relPath !== pair.entry.relPath),
88
+ retags,
89
+ creates,
90
+ deletes: [...rowsByPath.values()],
91
+ };
92
+ }
93
+ /**
94
+ * The compare function for link channels — the FILE identity rules, for
95
+ * the file reason: a symlink cannot be retargeted in place (retarget is
96
+ * unlink + recreate, so the inode churns exactly like an atomic save),
97
+ * so the path absorbs that churn and claims first; the inode rescues
98
+ * path-orphans (`mv` preserves a symlink's inode). The payload is the
99
+ * target string, already read at walk time. A consequence worth naming:
100
+ * two links exchanging names read as two retargets (path-first),
101
+ * because a link's meaning IS its written address.
102
+ * 1. Path claims the channel: same target = continued (a new inode
103
+ * under an old name with an old target is silent recreation),
104
+ * different target = changed.
105
+ * 2. Inode rescues path-orphans: renames.
106
+ * 3. Leftover entries are creates; leftover rows are deletes.
107
+ */
108
+ function diffLinkState(rows, entries) {
109
+ const changes = [];
110
+ const retags = [];
111
+ const rowsByPath = new Map(rows.map((row) => [row.relPath, row]));
112
+ const claimedRowIds = new Set();
113
+ const orphanEntries = [];
114
+ for (const entry of entries) {
115
+ const row = rowsByPath.get(entry.relPath);
116
+ if (!row) {
117
+ orphanEntries.push(entry);
118
+ continue;
119
+ }
120
+ claimedRowIds.add(row.linkId);
121
+ if (row.target !== entry.target)
122
+ changes.push({ row, entry });
123
+ else if (row.device !== entry.device || row.inode !== entry.inode)
124
+ retags.push({ row, entry });
125
+ }
126
+ const orphanRows = rows.filter((row) => !claimedRowIds.has(row.linkId));
127
+ const renames = matchByInode(orphanRows, orphanEntries);
128
+ const renamedRowIds = new Set(renames.map((pair) => pair.row.linkId));
129
+ const renamedEntries = new Set(renames.map((pair) => pair.entry));
130
+ return {
131
+ changes,
132
+ retags,
133
+ renames,
134
+ creates: orphanEntries.filter((entry) => !renamedEntries.has(entry)),
135
+ deletes: orphanRows.filter((row) => !renamedRowIds.has(row.linkId)),
136
+ };
137
+ }
138
+ /**
139
+ * The compare function for file channels — doorbells and rescans both
140
+ * end here. Hashing is deferred — `checks` name the files whose content
141
+ * must be read before an event may be claimed.
142
+ *
143
+ * Identity rules, in order:
144
+ * 1. Path claims the channel. Same rel_path = same file_id, even when
145
+ * the inode changed (atomic saves replace the inode on every
146
+ * write).
147
+ * 2. Inode rescues path-orphans (matchByInode above).
148
+ * 3. Leftover entries are creates; leftover rows are deletes.
149
+ *
150
+ * The single stat-shortcut this layer allows itself: a (size, mtime,
151
+ * inode) triple identical to the stored one is skipped as unchanged —
152
+ * but only when the mtime is strictly older than the moment we last
153
+ * read the bytes (git's "racily clean" rule), and only when nothing
154
+ * pointed at the file. A path named in `mustCheck` (the doorbell said
155
+ * so) is always hashed: timestamps can lie, a delivered watcher event
156
+ * cannot be un-delivered.
157
+ */
158
+ function diffRootState(rows, entries, mustCheck = null) {
159
+ const checks = [];
160
+ let unchanged = 0;
161
+ const rowsByPath = new Map(rows.map((row) => [row.relPath, row]));
162
+ const claimedRowIds = new Set();
163
+ const orphanEntries = [];
164
+ for (const entry of entries) {
165
+ const row = rowsByPath.get(entry.relPath);
166
+ if (!row) {
167
+ orphanEntries.push(entry);
168
+ continue;
169
+ }
170
+ claimedRowIds.add(row.fileId);
171
+ const statsMatch = row.size === entry.size && row.mtimeMs === entry.mtimeMs && row.inode === entry.inode;
172
+ const provablyBeforeLastRead = typeof row.hashedAtMs === 'number' && entry.mtimeMs < row.hashedAtMs;
173
+ const named = mustCheck !== null && mustCheck.has(entry.relPath);
174
+ if (statsMatch && provablyBeforeLastRead && !named) {
175
+ unchanged += 1;
176
+ }
177
+ else {
178
+ checks.push({ row, entry });
179
+ }
180
+ }
181
+ const orphanRows = rows.filter((row) => !claimedRowIds.has(row.fileId));
182
+ const renames = matchByInode(orphanRows, orphanEntries);
183
+ const renamedRowIds = new Set(renames.map((pair) => pair.row.fileId));
184
+ const renamedEntries = new Set(renames.map((pair) => pair.entry));
185
+ return {
186
+ checks,
187
+ renames,
188
+ creates: orphanEntries.filter((entry) => !renamedEntries.has(entry)),
189
+ deletes: orphanRows.filter((row) => !renamedRowIds.has(row.fileId)),
190
+ unchanged,
191
+ };
192
+ }
193
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/detection/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * Echo judgment after incoming Apply.
4
+ *
5
+ * Apply never suppresses watcher rings. It verifies the incoming result and
6
+ * advances the notebook. The retained suspicion then uses this ordinary
7
+ * equality judgment: exact expected truth is silence; different truth is a
8
+ * real local change; unreadable truth is retried.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.echoDecision = echoDecision;
12
+ function echoDecision(notebook, observed) {
13
+ if (observed.truth === 'unsettled') {
14
+ return observed.reason === undefined
15
+ ? { kind: 'retry' }
16
+ : { kind: 'retry', reason: observed.reason };
17
+ }
18
+ if (notebook.status === 'active') {
19
+ return observed.truth === 'present' && observed.version === notebook.version
20
+ ? { kind: 'silence' }
21
+ : { kind: 'detect' };
22
+ }
23
+ return observed.truth === 'absent' ? { kind: 'silence' } : { kind: 'detect' };
24
+ }
25
+ //# sourceMappingURL=echo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"echo.js","sourceRoot":"","sources":["../../src/detection/echo.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAUH,oCAeC;AAfD,SAAgB,YAAY,CAC1B,QAAuD,EACvD,QAAsB;IAEtB,IAAI,QAAQ,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;QACnC,OAAO,QAAQ,CAAC,MAAM,KAAK,SAAS;YAClC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;YACnB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACjD,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,QAAQ,CAAC,KAAK,KAAK,SAAS,IAAI,QAAQ,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO;YAC1E,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;YACrB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAChF,CAAC"}
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * Detection enrollment policies.
4
+ *
5
+ * Current user ground enrolls everything except structural local state and
6
+ * paths excluded by its tracked `.amalgmignore`; `.gitignore` never decides
7
+ * Amalgm enrollment. `defaultShouldEnroll` remains only as the older explicit
8
+ * registration policy for additive API compatibility. Current portable paths
9
+ * already use `/`; a host converts its native separator at the boundary. A
10
+ * literal `\\` is therefore preserved as part of a POSIX filename. Both
11
+ * policies are pure strings-in/verdict-out and never inspect disk themselves.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.defaultShouldEnroll = exports.DEFAULT_AMALGM_IGNORE = exports.AMALGM_IGNORE_FILE = exports.AMALGM_CONTROL_DIRECTORY = void 0;
15
+ exports.normalizedRelativePath = normalizedRelativePath;
16
+ exports.isAmalgmControlPath = isAmalgmControlPath;
17
+ exports.isSqliteCoordinationPath = isSqliteCoordinationPath;
18
+ exports.createUserGroundEnrollmentPolicy = createUserGroundEnrollmentPolicy;
19
+ /** The Git-like names at the user-ground boundary. Structural coordination
20
+ * state is local and cannot be negated by user policy; the ignore file is
21
+ * portable ground so every machine evaluates the same user rules. */
22
+ exports.AMALGM_CONTROL_DIRECTORY = '.amalgm';
23
+ exports.AMALGM_IGNORE_FILE = '.amalgmignore';
24
+ exports.DEFAULT_AMALGM_IGNORE = `/${exports.AMALGM_CONTROL_DIRECTORY}/\n`;
25
+ const EXCLUDED_DIRECTORIES = new Set([
26
+ '.amalgm',
27
+ '.aws',
28
+ '.cache',
29
+ '.docker',
30
+ '.git',
31
+ '.gnupg',
32
+ '.gradle',
33
+ '.hg',
34
+ '.kube',
35
+ '.mypy_cache',
36
+ '.next',
37
+ '.nyc_output',
38
+ '.parcel-cache',
39
+ '.pytest_cache',
40
+ '.ruff_cache',
41
+ '.ssh',
42
+ '.svn',
43
+ '.svelte-kit',
44
+ '.turbo',
45
+ '.venv',
46
+ '.vite',
47
+ '__pycache__',
48
+ 'coverage',
49
+ 'node_modules',
50
+ ]);
51
+ const EXCLUDED_FILES = new Set([
52
+ '.DS_Store',
53
+ 'Thumbs.db',
54
+ 'desktop.ini',
55
+ 'npm-debug.log',
56
+ 'pnpm-debug.log',
57
+ 'yarn-debug.log',
58
+ 'yarn-error.log',
59
+ ]);
60
+ function isPrivateFile(name) {
61
+ if (/^\.env(?:\..+)?$/i.test(name) && !/\.example$/i.test(name))
62
+ return true;
63
+ if (/^(?:credentials?|secrets?)\.(?:json|ya?ml|toml|ini|conf)$/i.test(name))
64
+ return true;
65
+ if (/^id_(?:dsa|ecdsa|ed25519|rsa)$/i.test(name))
66
+ return true;
67
+ if (/^(?:\.git-credentials|\.netrc|\.npmrc|\.pypirc|auth\.json|kubeconfig)$/i.test(name))
68
+ return true;
69
+ if (/^(?:service[-_]?account|firebase[-_]?adminsdk).*\.json$/i.test(name))
70
+ return true;
71
+ return /\.(?:key|keystore|kdbx|p12|pfx|pem|ppk|tfstate(?:\.backup)?)$/i.test(name);
72
+ }
73
+ /**
74
+ * Legacy explicit-registration policy retained for additive API compatibility.
75
+ * Current user-ground Detect must use {@link createUserGroundEnrollmentPolicy};
76
+ * there are no implicit dependency, dotfile, cache, or secret exclusions.
77
+ * Both separators are path separators; '' never enrolls.
78
+ *
79
+ * @deprecated Use `createUserGroundEnrollmentPolicy` for current user ground.
80
+ */
81
+ const defaultShouldEnroll = (relPath) => {
82
+ const parts = normalizedRelativePath(String(relPath || '').replace(/\\/g, '/')).split('/').filter(Boolean);
83
+ const name = parts.at(-1);
84
+ if (name === undefined || parts.some((part) => EXCLUDED_DIRECTORIES.has(part)))
85
+ return false;
86
+ if (EXCLUDED_FILES.has(name) || isPrivateFile(name))
87
+ return false;
88
+ return !name.startsWith('.#') && !name.endsWith('~') && !/\.(?:swp|swo)$/i.test(name);
89
+ };
90
+ exports.defaultShouldEnroll = defaultShouldEnroll;
91
+ /**
92
+ * One normalized spelling for portable enrollment and ignore matching.
93
+ * Portable relative paths use `/`. Do not reinterpret `\\`: it is a valid,
94
+ * literal filename character on POSIX after the host has crossed this seam.
95
+ */
96
+ function normalizedRelativePath(relPath) {
97
+ return String(relPath || '')
98
+ .replace(/^\.\//, '')
99
+ .replace(/^\/+|\/+$/g, '')
100
+ .replace(/\/{2,}/g, '/');
101
+ }
102
+ /** `.amalgm/` is local control state wherever a registered tree is rooted. */
103
+ function isAmalgmControlPath(relPath) {
104
+ return normalizedRelativePath(relPath).split('/').includes(exports.AMALGM_CONTROL_DIRECTORY);
105
+ }
106
+ /** SQLite's WAL, shared-memory, and rollback-journal files coordinate one
107
+ * local database connection. They are not a portable copy of the database. */
108
+ function isSqliteCoordinationPath(relPath) {
109
+ const name = normalizedRelativePath(relPath).split('/').at(-1) ?? '';
110
+ return /\.(?:db|sqlite|sqlite3)-(?:wal|shm|journal)$/i.test(name);
111
+ }
112
+ function escapeRegex(character) {
113
+ return /[\\^$.*+?()[\]{}|]/.test(character) ? `\\${character}` : character;
114
+ }
115
+ function globExpression(pattern) {
116
+ let expression = '';
117
+ for (let index = 0; index < pattern.length; index += 1) {
118
+ const character = pattern[index];
119
+ if (character === '*') {
120
+ if (pattern[index + 1] === '*') {
121
+ while (pattern[index + 1] === '*')
122
+ index += 1;
123
+ if (pattern[index + 1] === '/') {
124
+ index += 1;
125
+ expression += '(?:.*/)?';
126
+ }
127
+ else {
128
+ expression += '.*';
129
+ }
130
+ }
131
+ else {
132
+ expression += '[^/]*';
133
+ }
134
+ }
135
+ else if (character === '?') {
136
+ expression += '[^/]';
137
+ }
138
+ else {
139
+ expression += escapeRegex(character);
140
+ }
141
+ }
142
+ return expression;
143
+ }
144
+ function ignoreRule(line) {
145
+ let value = line.trim();
146
+ if (!value || value.startsWith('#'))
147
+ return null;
148
+ let negated = false;
149
+ if (value.startsWith('!')) {
150
+ negated = true;
151
+ value = value.slice(1);
152
+ }
153
+ if (!value)
154
+ return null;
155
+ const anchored = value.startsWith('/');
156
+ if (anchored)
157
+ value = value.slice(1);
158
+ if (value.endsWith('/'))
159
+ value = value.slice(0, -1);
160
+ if (!value)
161
+ return null;
162
+ const body = globExpression(value);
163
+ const prefix = anchored || value.includes('/') ? '^' : '(?:^|/)';
164
+ return { negated, expression: new RegExp(`${prefix}${body}(?:$|/)`) };
165
+ }
166
+ /**
167
+ * Compile the root `.amalgmignore` for a user ground. Rules are ordered and
168
+ * `!` negates a prior user rule. Reserved control state and transient SQLite
169
+ * coordination files are structural exclusions; every other path is ground
170
+ * unless this tracked file excludes it. The policy file itself always travels.
171
+ */
172
+ function createUserGroundEnrollmentPolicy(ignoreText = '') {
173
+ const rules = String(ignoreText).split(/\r?\n/).map(ignoreRule)
174
+ .filter((rule) => rule !== null);
175
+ return (input) => {
176
+ const relPath = normalizedRelativePath(input);
177
+ if (!relPath || isAmalgmControlPath(relPath) || isSqliteCoordinationPath(relPath))
178
+ return false;
179
+ if (relPath === exports.AMALGM_IGNORE_FILE)
180
+ return true;
181
+ let excluded = false;
182
+ for (const rule of rules) {
183
+ if (rule.expression.test(relPath))
184
+ excluded = !rule.negated;
185
+ }
186
+ return !excluded;
187
+ };
188
+ }
189
+ //# sourceMappingURL=enrollment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrollment.js","sourceRoot":"","sources":["../../src/detection/enrollment.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AA+EH,wDAKC;AAGD,kDAEC;AAID,4DAGC;AA4DD,4EAaC;AApKD;;qEAEqE;AACxD,QAAA,wBAAwB,GAAG,SAAS,CAAC;AACrC,QAAA,kBAAkB,GAAG,eAAe,CAAC;AACrC,QAAA,qBAAqB,GAAG,IAAI,gCAAwB,KAAK,CAAC;AAEvE,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,SAAS;IACT,MAAM;IACN,QAAQ;IACR,SAAS;IACT,MAAM;IACN,QAAQ;IACR,SAAS;IACT,KAAK;IACL,OAAO;IACP,aAAa;IACb,OAAO;IACP,aAAa;IACb,eAAe;IACf,eAAe;IACf,aAAa;IACb,MAAM;IACN,MAAM;IACN,aAAa;IACb,QAAQ;IACR,OAAO;IACP,OAAO;IACP,aAAa;IACb,UAAU;IACV,cAAc;CACf,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,WAAW;IACX,WAAW;IACX,aAAa;IACb,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;CACjB,CAAC,CAAC;AAEH,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,IAAI,4DAA4D,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACzF,IAAI,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,yEAAyE,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtG,IAAI,0DAA0D,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvF,OAAO,gEAAgE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrF,CAAC;AAED;;;;;;;GAOG;AACI,MAAM,mBAAmB,GAAqB,CAAC,OAAO,EAAE,EAAE;IAC/D,MAAM,KAAK,GAAG,sBAAsB,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3G,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7F,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAClE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxF,CAAC,CAAC;AANW,QAAA,mBAAmB,uBAM9B;AAEF;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,OAAe;IACpD,OAAO,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;SACzB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;SACpB,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,8EAA8E;AAC9E,SAAgB,mBAAmB,CAAC,OAAe;IACjD,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,gCAAwB,CAAC,CAAC;AACvF,CAAC;AAED;8EAC8E;AAC9E,SAAgB,wBAAwB,CAAC,OAAe;IACtD,MAAM,IAAI,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,OAAO,+CAA+C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpE,CAAC;AAOD,SAAS,WAAW,CAAC,SAAiB;IACpC,OAAO,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7E,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAW,CAAC;QAC3C,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC/B,OAAO,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;oBAAE,KAAK,IAAI,CAAC,CAAC;gBAC9C,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBAC/B,KAAK,IAAI,CAAC,CAAC;oBACX,UAAU,IAAI,UAAU,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACN,UAAU,IAAI,IAAI,CAAC;gBACrB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,UAAU,IAAI,OAAO,CAAC;YACxB,CAAC;QACH,CAAC;aAAM,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YAC7B,UAAU,IAAI,MAAM,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,UAAU,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACxB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACjD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,GAAG,IAAI,CAAC;QACf,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,QAAQ;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,CAAC;AACxE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,gCAAgC,CAAC,UAAU,GAAG,EAAE;IAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC;SAC5D,MAAM,CAAC,CAAC,IAAI,EAAsB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,OAAO,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,IAAI,wBAAwB,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;QAChG,IAAI,OAAO,KAAK,0BAAkB;YAAE,OAAO,IAAI,CAAC;QAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,QAAQ,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,QAAQ,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * Detection — the pure cores that turn observed disk evidence into facts.
4
+ * Normative sources:
5
+ * entity-model.md (identity axioms, the two continuity witnesses) and
6
+ * entity-implementation.md (the ratified seams).
7
+ *
8
+ * Everything here is rows and entries in, verdicts out. Walking disk,
9
+ * hashing bytes, opening watchers, and writing stores are the host's;
10
+ * the host injects its answers (a stat, a channel row, an enrollment
11
+ * policy) and this layer owns only the laws:
12
+ *
13
+ * - `adapter-contract` the seven rails, the ratified detect socket,
14
+ * descriptor conviction, the three-state truth
15
+ * judgment, the reference adapter
16
+ * - `classify` text/binary from detect's own read evidence
17
+ * - `refusal` declined-vs-broken, named where it is raised
18
+ * - `enrollment` tracked user-ground policy plus legacy compatibility
19
+ * - `diff` the compare functions: file/dir/link identity
20
+ * - `continuity` root rescue and the undecided address
21
+ * - `watch-boundary` generation-safe Watch input and coverage output
22
+ * - `territory` nearest-repository recorder selection
23
+ * - `record-proposal` the exact Detect-to-Record contract
24
+ * - `batch` net-difference planning and Watch settlement
25
+ * - `content` text/binary/link replay from one captured read
26
+ * - `repository` verified Card + Checkpoint transport planning
27
+ * - `echo` incoming Apply equality without suppression
28
+ */
29
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
33
+ desc = { enumerable: true, get: function() { return m[k]; } };
34
+ }
35
+ Object.defineProperty(o, k2, desc);
36
+ }) : (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ o[k2] = m[k];
39
+ }));
40
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
41
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ __exportStar(require("./adapter-contract.js"), exports);
45
+ __exportStar(require("./batch.js"), exports);
46
+ __exportStar(require("./classify.js"), exports);
47
+ __exportStar(require("./continuity.js"), exports);
48
+ __exportStar(require("./content.js"), exports);
49
+ __exportStar(require("./diff.js"), exports);
50
+ __exportStar(require("./echo.js"), exports);
51
+ __exportStar(require("./enrollment.js"), exports);
52
+ __exportStar(require("./record-proposal.js"), exports);
53
+ __exportStar(require("./repository.js"), exports);
54
+ __exportStar(require("./refusal.js"), exports);
55
+ __exportStar(require("./territory.js"), exports);
56
+ __exportStar(require("./watch-boundary.js"), exports);
57
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/detection/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;;;;;;;;;;;;;;AAEH,wDAAsC;AACtC,6CAA2B;AAC3B,gDAA8B;AAC9B,kDAAgC;AAChC,+CAA6B;AAC7B,4CAA0B;AAC1B,4CAA0B;AAC1B,kDAAgC;AAChC,uDAAqC;AACrC,kDAAgC;AAChC,+CAA6B;AAC7B,iDAA+B;AAC/B,sDAAoC"}
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ /**
3
+ * Detect's exact output to Record.
4
+ *
5
+ * A proposal is not yet a durable mutation: Record assigns one mutation id,
6
+ * appends it atomically, and then Send publishes that same row. Detect owns the
7
+ * complete result and replay instruction so no later rail has to rediscover
8
+ * filesystem truth.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.DETECTED_CHANGE_CONTRACT = void 0;
12
+ exports.detectedChangeProposal = detectedChangeProposal;
13
+ const cloud_js_1 = require("../entities/cloud.js");
14
+ const types_js_1 = require("../entities/types.js");
15
+ const content_js_1 = require("../contracts/content.js");
16
+ const artifacts_js_1 = require("../transfer/artifacts.js");
17
+ exports.DETECTED_CHANGE_CONTRACT = 'amalgm.detected-change-proposal@1';
18
+ const HASH = /^[0-9a-f]{64}$/;
19
+ const facetsOf = (base, result) => {
20
+ if (base === null)
21
+ return ['create'];
22
+ const facets = [];
23
+ if (base.parentUUID !== result.parentUUID)
24
+ facets.push('move');
25
+ if (base.name !== result.name)
26
+ facets.push('rename');
27
+ if (base.type !== result.type)
28
+ facets.push('retype');
29
+ const resultCarriesPayload = !types_js_1.DERIVED_PAYLOAD.has(result.type) && result.type !== 'reference';
30
+ if ((base.payloadVersion !== result.payloadVersion && resultCarriesPayload)
31
+ || (result.type === 'repo.git' && base.transportVersion !== result.transportVersion)) {
32
+ facets.push('content');
33
+ }
34
+ if (base.status !== result.status) {
35
+ if (result.status === 'trashed')
36
+ facets.push('trash');
37
+ else if (base.status === 'trashed' && result.status === 'active')
38
+ facets.push('restore');
39
+ }
40
+ return facets;
41
+ };
42
+ const contentRequired = (base, result, facets) => result.status === 'active'
43
+ && (base === null || facets.includes('content') || facets.includes('retype'))
44
+ && ['file.text', 'file.binary', 'link', 'repo.git'].includes(result.type);
45
+ const validateReplay = (base, result, facets, replay) => {
46
+ const needsContent = contentRequired(base, result, facets);
47
+ if (!needsContent) {
48
+ if (replay.kind !== 'structure') {
49
+ throw new Error('a structural-only detected change must use structure replay');
50
+ }
51
+ return;
52
+ }
53
+ if (replay.kind !== result.type) {
54
+ throw new Error(`detected ${result.type} content cannot use ${replay.kind} replay`);
55
+ }
56
+ if (result.payloadVersion === null) {
57
+ throw new Error(`detected ${result.type} content has no result payload version`);
58
+ }
59
+ switch (replay.kind) {
60
+ case 'file.text':
61
+ const expectedTextBase = base?.payloadVersion && HASH.test(base.payloadVersion)
62
+ ? base.payloadVersion
63
+ : null;
64
+ if (replay.basePayloadVersion !== expectedTextBase
65
+ || replay.resultPayloadVersion !== result.payloadVersion
66
+ || (replay.mode === 'delta') !== (replay.delta !== null)) {
67
+ throw new Error('text replay does not name the detected base and result');
68
+ }
69
+ if (!HASH.test(replay.resultPayloadVersion)
70
+ || (replay.basePayloadVersion !== null && !HASH.test(replay.basePayloadVersion))) {
71
+ throw new Error('text replay has an invalid content head');
72
+ }
73
+ if (replay.delta !== null
74
+ && (!Number.isSafeInteger(replay.delta.offset) || replay.delta.offset < 0
75
+ || !Number.isSafeInteger(replay.delta.deleteBytes) || replay.delta.deleteBytes < 0
76
+ || !(replay.delta.insert instanceof Uint8Array))) {
77
+ throw new Error('text replay has an invalid byte splice');
78
+ }
79
+ break;
80
+ case 'file.binary': {
81
+ const manifest = (0, content_js_1.checkContentManifest)(replay.manifest, result.payloadVersion);
82
+ if (!manifest.ok || replay.resultPayloadVersion !== result.payloadVersion) {
83
+ throw new Error('binary replay does not name the detected result');
84
+ }
85
+ break;
86
+ }
87
+ case 'link':
88
+ if (typeof replay.target !== 'string'
89
+ || replay.resultPayloadVersion !== result.payloadVersion
90
+ || !HASH.test(replay.resultPayloadVersion)) {
91
+ throw new Error('link replay does not name the detected result');
92
+ }
93
+ break;
94
+ case 'repo.git':
95
+ if (replay.stateId !== result.payloadVersion
96
+ || result.transportVersion === null
97
+ || replay.transportVersion !== result.transportVersion
98
+ || ![replay.stateId, replay.cardId, replay.checkpointId, replay.transportVersion]
99
+ .every((head) => HASH.test(head))
100
+ || (replay.parentTransportVersion !== null && !HASH.test(replay.parentTransportVersion))
101
+ || typeof replay.cardChanged !== 'boolean'
102
+ || typeof replay.checkpointChanged !== 'boolean') {
103
+ throw new Error('repository replay does not name the detected state and transport');
104
+ }
105
+ break;
106
+ }
107
+ };
108
+ /**
109
+ * Build one immutable proposal, or null when settled truth has the same logical
110
+ * version. A repository transport-only change is still meaningful because its
111
+ * worktree identity map is part of exact reproduction even when Card and
112
+ * Checkpoint IDs did not move.
113
+ */
114
+ function detectedChangeProposal(input) {
115
+ const result = (0, cloud_js_1.assertRecord)(input.result);
116
+ const base = input.base === null ? null : (0, cloud_js_1.assertRecord)(input.base);
117
+ if (base !== null && base.uuid !== result.uuid) {
118
+ throw new Error('a detected transition cannot change entity UUID');
119
+ }
120
+ if (base === null && result.status !== 'active') {
121
+ throw new Error('newly detected ground must begin active');
122
+ }
123
+ if (base?.status === 'deleted') {
124
+ throw new Error('Detection cannot resurrect a deleted tombstone');
125
+ }
126
+ if (result.status === 'deleted') {
127
+ throw new Error('Detection cannot propose permanent deletion; deletion is an explicit lifecycle operation');
128
+ }
129
+ if (base?.version === result.version && base.transportVersion === result.transportVersion) {
130
+ const sameState = base.type === result.type
131
+ && base.parentUUID === result.parentUUID
132
+ && base.name === result.name
133
+ && base.status === result.status
134
+ && base.payloadVersion === result.payloadVersion;
135
+ if (!sameState)
136
+ throw new Error('one logical version cannot name two different entity states');
137
+ return null;
138
+ }
139
+ const facets = facetsOf(base, result);
140
+ if (facets.length === 0) {
141
+ if (base !== null && types_js_1.DERIVED_PAYLOAD.has(base.type) && base.type === result.type) {
142
+ // Container membership is a projection of child parentUUID values. Its
143
+ // derived version can move locally, but it is never an independent write
144
+ // that could erase a sibling created by another machine.
145
+ return null;
146
+ }
147
+ throw new Error('detected records disagree without a named logical facet');
148
+ }
149
+ if (facets.includes('trash') && input.lifecycleAuthorization !== 'trash') {
150
+ throw new Error('filesystem absence is not authority to Trash an entity');
151
+ }
152
+ if (facets.includes('restore') && input.lifecycleAuthorization !== 'restore') {
153
+ throw new Error('restoring an entity requires explicit lifecycle authority');
154
+ }
155
+ validateReplay(base, result, facets, input.replay);
156
+ const artifact = input.replay.kind === 'structure' ? null : (0, artifacts_js_1.artifactForRecord)(result);
157
+ if (input.replay.kind !== 'structure' && artifact === null) {
158
+ throw new Error(`detected ${result.type} replay has no transferable artifact`);
159
+ }
160
+ return Object.freeze({
161
+ contract: exports.DETECTED_CHANGE_CONTRACT,
162
+ entityId: result.uuid,
163
+ basedOnState: base?.version ?? null,
164
+ result,
165
+ facets: Object.freeze(facets),
166
+ replay: input.replay,
167
+ cargo: Object.freeze(artifact === null ? [] : [artifact]),
168
+ });
169
+ }
170
+ //# sourceMappingURL=record-proposal.js.map