@opendatalabs/personal-server-ts-core 1.33.8 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/dist/additions/budget.d.ts +28 -0
  2. package/dist/additions/budget.d.ts.map +1 -0
  3. package/dist/additions/budget.js +119 -0
  4. package/dist/additions/budget.js.map +1 -0
  5. package/dist/additions/durable.d.ts +64 -0
  6. package/dist/additions/durable.d.ts.map +1 -0
  7. package/dist/additions/durable.js +591 -0
  8. package/dist/additions/durable.js.map +1 -0
  9. package/dist/additions/first-added.d.ts +32 -48
  10. package/dist/additions/first-added.d.ts.map +1 -1
  11. package/dist/additions/first-added.js +68 -123
  12. package/dist/additions/first-added.js.map +1 -1
  13. package/dist/additions/index.d.ts +3 -0
  14. package/dist/additions/index.d.ts.map +1 -1
  15. package/dist/additions/index.js +3 -0
  16. package/dist/additions/index.js.map +1 -1
  17. package/dist/additions/ledger-store.js +1 -1
  18. package/dist/additions/record-rules.d.ts.map +1 -1
  19. package/dist/additions/record-rules.js +7 -0
  20. package/dist/additions/record-rules.js.map +1 -1
  21. package/dist/additions/summary.d.ts +88 -1
  22. package/dist/additions/summary.d.ts.map +1 -1
  23. package/dist/additions/summary.js +173 -8
  24. package/dist/additions/summary.js.map +1 -1
  25. package/dist/additions/version-inventory.d.ts +80 -0
  26. package/dist/additions/version-inventory.d.ts.map +1 -0
  27. package/dist/additions/version-inventory.js +232 -0
  28. package/dist/additions/version-inventory.js.map +1 -0
  29. package/dist/api/index.d.ts +70 -2
  30. package/dist/api/index.d.ts.map +1 -1
  31. package/dist/api/index.js +506 -79
  32. package/dist/api/index.js.map +1 -1
  33. package/dist/auth/index.d.ts +1 -1
  34. package/dist/auth/index.d.ts.map +1 -1
  35. package/dist/auth/index.js +1 -1
  36. package/dist/auth/index.js.map +1 -1
  37. package/dist/auth/request.d.ts +6 -0
  38. package/dist/auth/request.d.ts.map +1 -1
  39. package/dist/auth/request.js +66 -4
  40. package/dist/auth/request.js.map +1 -1
  41. package/dist/contracts/data.d.ts +54 -8
  42. package/dist/contracts/data.d.ts.map +1 -1
  43. package/dist/contracts/data.js +178 -35
  44. package/dist/contracts/data.js.map +1 -1
  45. package/dist/contracts/index.d.ts +1 -1
  46. package/dist/contracts/index.d.ts.map +1 -1
  47. package/dist/derivatives/registration.d.ts +0 -5
  48. package/dist/derivatives/registration.d.ts.map +1 -1
  49. package/dist/derivatives/registration.js +2 -1
  50. package/dist/derivatives/registration.js.map +1 -1
  51. package/dist/errors/pdpp-catalog.d.ts +36 -0
  52. package/dist/errors/pdpp-catalog.d.ts.map +1 -0
  53. package/dist/errors/pdpp-catalog.js +86 -0
  54. package/dist/errors/pdpp-catalog.js.map +1 -0
  55. package/dist/grants/index.d.ts +2 -0
  56. package/dist/grants/index.d.ts.map +1 -1
  57. package/dist/grants/index.js +4 -0
  58. package/dist/grants/index.js.map +1 -1
  59. package/dist/grants/pdpp-binding-store.d.ts +89 -0
  60. package/dist/grants/pdpp-binding-store.d.ts.map +1 -0
  61. package/dist/grants/pdpp-binding-store.js +112 -0
  62. package/dist/grants/pdpp-binding-store.js.map +1 -0
  63. package/dist/grants/pdpp-binding.d.ts +169 -0
  64. package/dist/grants/pdpp-binding.d.ts.map +1 -0
  65. package/dist/grants/pdpp-binding.js +236 -0
  66. package/dist/grants/pdpp-binding.js.map +1 -0
  67. package/dist/legacy-projection/bindings.d.ts +28 -1
  68. package/dist/legacy-projection/bindings.d.ts.map +1 -1
  69. package/dist/legacy-projection/bindings.js +622 -189
  70. package/dist/legacy-projection/bindings.js.map +1 -1
  71. package/dist/legacy-projection/claude-source-record.d.ts +22 -0
  72. package/dist/legacy-projection/claude-source-record.d.ts.map +1 -0
  73. package/dist/legacy-projection/claude-source-record.js +360 -0
  74. package/dist/legacy-projection/claude-source-record.js.map +1 -0
  75. package/dist/legacy-projection/declared-text.d.ts +41 -0
  76. package/dist/legacy-projection/declared-text.d.ts.map +1 -0
  77. package/dist/legacy-projection/declared-text.js +199 -0
  78. package/dist/legacy-projection/declared-text.js.map +1 -0
  79. package/dist/legacy-projection/github-browser-bindings.d.ts.map +1 -1
  80. package/dist/legacy-projection/github-browser-bindings.js +89 -5
  81. package/dist/legacy-projection/github-browser-bindings.js.map +1 -1
  82. package/dist/legacy-projection/github-legacy-fields.d.ts +6 -0
  83. package/dist/legacy-projection/github-legacy-fields.d.ts.map +1 -0
  84. package/dist/legacy-projection/github-legacy-fields.js +202 -0
  85. package/dist/legacy-projection/github-legacy-fields.js.map +1 -0
  86. package/dist/legacy-projection/index.d.ts +3 -1
  87. package/dist/legacy-projection/index.d.ts.map +1 -1
  88. package/dist/legacy-projection/index.js +49 -1
  89. package/dist/legacy-projection/index.js.map +1 -1
  90. package/dist/legacy-projection/oura-browser-bindings.d.ts.map +1 -1
  91. package/dist/legacy-projection/oura-browser-bindings.js +43 -41
  92. package/dist/legacy-projection/oura-browser-bindings.js.map +1 -1
  93. package/dist/legacy-projection/types.d.ts +38 -2
  94. package/dist/legacy-projection/types.d.ts.map +1 -1
  95. package/dist/mcp/resources.d.ts.map +1 -1
  96. package/dist/mcp/resources.js +3 -0
  97. package/dist/mcp/resources.js.map +1 -1
  98. package/dist/mcp/tools.d.ts.map +1 -1
  99. package/dist/mcp/tools.js +11 -2
  100. package/dist/mcp/tools.js.map +1 -1
  101. package/dist/pdpp/approval.d.ts +271 -0
  102. package/dist/pdpp/approval.d.ts.map +1 -0
  103. package/dist/pdpp/approval.js +467 -0
  104. package/dist/pdpp/approval.js.map +1 -0
  105. package/dist/pdpp/client-identity.d.ts +88 -0
  106. package/dist/pdpp/client-identity.d.ts.map +1 -0
  107. package/dist/pdpp/client-identity.js +196 -0
  108. package/dist/pdpp/client-identity.js.map +1 -0
  109. package/dist/pdpp/client-metadata.d.ts +95 -0
  110. package/dist/pdpp/client-metadata.d.ts.map +1 -0
  111. package/dist/pdpp/client-metadata.js +96 -0
  112. package/dist/pdpp/client-metadata.js.map +1 -0
  113. package/dist/pdpp/commitments.d.ts +75 -0
  114. package/dist/pdpp/commitments.d.ts.map +1 -0
  115. package/dist/pdpp/commitments.js +113 -0
  116. package/dist/pdpp/commitments.js.map +1 -0
  117. package/dist/pdpp/declaration.d.ts +118 -0
  118. package/dist/pdpp/declaration.d.ts.map +1 -0
  119. package/dist/pdpp/declaration.js +836 -0
  120. package/dist/pdpp/declaration.js.map +1 -0
  121. package/dist/pdpp/index.d.ts +37 -0
  122. package/dist/pdpp/index.d.ts.map +1 -0
  123. package/dist/pdpp/index.js +23 -0
  124. package/dist/pdpp/index.js.map +1 -0
  125. package/dist/pdpp/issuance.d.ts +126 -0
  126. package/dist/pdpp/issuance.d.ts.map +1 -0
  127. package/dist/pdpp/issuance.js +180 -0
  128. package/dist/pdpp/issuance.js.map +1 -0
  129. package/dist/pdpp/pkce.d.ts +53 -0
  130. package/dist/pdpp/pkce.d.ts.map +1 -0
  131. package/dist/pdpp/pkce.js +109 -0
  132. package/dist/pdpp/pkce.js.map +1 -0
  133. package/dist/pdpp/redirect.d.ts +60 -0
  134. package/dist/pdpp/redirect.d.ts.map +1 -0
  135. package/dist/pdpp/redirect.js +125 -0
  136. package/dist/pdpp/redirect.js.map +1 -0
  137. package/dist/pdpp/resolve.d.ts +109 -0
  138. package/dist/pdpp/resolve.d.ts.map +1 -0
  139. package/dist/pdpp/resolve.js +393 -0
  140. package/dist/pdpp/resolve.js.map +1 -0
  141. package/dist/pdpp/review.d.ts +280 -0
  142. package/dist/pdpp/review.d.ts.map +1 -0
  143. package/dist/pdpp/review.js +205 -0
  144. package/dist/pdpp/review.js.map +1 -0
  145. package/dist/pdpp/selection.d.ts +50 -0
  146. package/dist/pdpp/selection.d.ts.map +1 -0
  147. package/dist/pdpp/selection.js +442 -0
  148. package/dist/pdpp/selection.js.map +1 -0
  149. package/dist/pdpp/store.d.ts +228 -0
  150. package/dist/pdpp/store.d.ts.map +1 -0
  151. package/dist/pdpp/store.js +542 -0
  152. package/dist/pdpp/store.js.map +1 -0
  153. package/dist/pdpp/tokens.d.ts +181 -0
  154. package/dist/pdpp/tokens.d.ts.map +1 -0
  155. package/dist/pdpp/tokens.js +419 -0
  156. package/dist/pdpp/tokens.js.map +1 -0
  157. package/dist/pdpp/types.d.ts +441 -0
  158. package/dist/pdpp/types.d.ts.map +1 -0
  159. package/dist/pdpp/types.js +61 -0
  160. package/dist/pdpp/types.js.map +1 -0
  161. package/dist/pdpp-version.d.ts +28 -0
  162. package/dist/pdpp-version.d.ts.map +1 -0
  163. package/dist/pdpp-version.js +28 -0
  164. package/dist/pdpp-version.js.map +1 -0
  165. package/dist/policy/data-read.d.ts +24 -0
  166. package/dist/policy/data-read.d.ts.map +1 -1
  167. package/dist/policy/data-read.js +60 -3
  168. package/dist/policy/data-read.js.map +1 -1
  169. package/dist/policy/data-write.d.ts.map +1 -1
  170. package/dist/policy/data-write.js +3 -0
  171. package/dist/policy/data-write.js.map +1 -1
  172. package/dist/policy/index.d.ts +1 -0
  173. package/dist/policy/index.d.ts.map +1 -1
  174. package/dist/policy/index.js +1 -0
  175. package/dist/policy/index.js.map +1 -1
  176. package/dist/policy/reserved-scopes.d.ts +3 -0
  177. package/dist/policy/reserved-scopes.d.ts.map +1 -0
  178. package/dist/policy/reserved-scopes.js +5 -0
  179. package/dist/policy/reserved-scopes.js.map +1 -0
  180. package/dist/ports/index.d.ts +63 -0
  181. package/dist/ports/index.d.ts.map +1 -1
  182. package/dist/ports/index.js.map +1 -1
  183. package/dist/ports/pdpp-auth.d.ts +129 -0
  184. package/dist/ports/pdpp-auth.d.ts.map +1 -0
  185. package/dist/ports/pdpp-auth.js +60 -0
  186. package/dist/ports/pdpp-auth.js.map +1 -0
  187. package/dist/ports/pdpp-auth.test-utils.d.ts +9 -0
  188. package/dist/ports/pdpp-auth.test-utils.d.ts.map +1 -0
  189. package/dist/ports/pdpp-auth.test-utils.js +18 -0
  190. package/dist/ports/pdpp-auth.test-utils.js.map +1 -0
  191. package/dist/schemas/server-config.d.ts +44 -0
  192. package/dist/schemas/server-config.d.ts.map +1 -1
  193. package/dist/schemas/server-config.js +91 -0
  194. package/dist/schemas/server-config.js.map +1 -1
  195. package/dist/signing/request-signer.d.ts +2 -0
  196. package/dist/signing/request-signer.d.ts.map +1 -1
  197. package/dist/signing/request-signer.js +1 -0
  198. package/dist/signing/request-signer.js.map +1 -1
  199. package/dist/storage/adapters/interface.d.ts +3 -0
  200. package/dist/storage/adapters/interface.d.ts.map +1 -1
  201. package/dist/storage/adapters/vana-audience.d.ts +12 -0
  202. package/dist/storage/adapters/vana-audience.d.ts.map +1 -0
  203. package/dist/storage/adapters/vana-audience.js +72 -0
  204. package/dist/storage/adapters/vana-audience.js.map +1 -0
  205. package/dist/storage/adapters/vana.d.ts +2 -0
  206. package/dist/storage/adapters/vana.d.ts.map +1 -1
  207. package/dist/storage/adapters/vana.js +53 -20
  208. package/dist/storage/adapters/vana.js.map +1 -1
  209. package/dist/storage/canonical-legacy-projection.d.ts +262 -0
  210. package/dist/storage/canonical-legacy-projection.d.ts.map +1 -0
  211. package/dist/storage/canonical-legacy-projection.js +761 -0
  212. package/dist/storage/canonical-legacy-projection.js.map +1 -0
  213. package/dist/storage/index/manager.d.ts +29 -2
  214. package/dist/storage/index/manager.d.ts.map +1 -1
  215. package/dist/storage/index/schema.d.ts +1 -1
  216. package/dist/storage/index/schema.js +1 -1
  217. package/dist/storage/index/types.d.ts +7 -1
  218. package/dist/storage/index/types.d.ts.map +1 -1
  219. package/dist/storage/legacy-projection.d.ts +4 -3
  220. package/dist/storage/legacy-projection.d.ts.map +1 -1
  221. package/dist/storage/legacy-projection.js +145 -42
  222. package/dist/storage/legacy-projection.js.map +1 -1
  223. package/dist/storage/legacy-scope-coverage-exceptions.d.ts +8 -0
  224. package/dist/storage/legacy-scope-coverage-exceptions.d.ts.map +1 -0
  225. package/dist/storage/legacy-scope-coverage-exceptions.js +10 -0
  226. package/dist/storage/legacy-scope-coverage-exceptions.js.map +1 -0
  227. package/dist/storage/legacy-scope-coverage.d.ts +53 -0
  228. package/dist/storage/legacy-scope-coverage.d.ts.map +1 -0
  229. package/dist/storage/legacy-scope-coverage.js +403 -0
  230. package/dist/storage/legacy-scope-coverage.js.map +1 -0
  231. package/dist/storage/legacy-scope-entity-keys.d.ts +4 -0
  232. package/dist/storage/legacy-scope-entity-keys.d.ts.map +1 -0
  233. package/dist/storage/legacy-scope-entity-keys.js +92 -0
  234. package/dist/storage/legacy-scope-entity-keys.js.map +1 -0
  235. package/dist/storage/legacy-scope-view.d.ts +154 -0
  236. package/dist/storage/legacy-scope-view.d.ts.map +1 -0
  237. package/dist/storage/legacy-scope-view.js +735 -0
  238. package/dist/storage/legacy-scope-view.js.map +1 -0
  239. package/dist/storage/pdpp-records/completion.d.ts +91 -0
  240. package/dist/storage/pdpp-records/completion.d.ts.map +1 -0
  241. package/dist/storage/pdpp-records/completion.js +170 -0
  242. package/dist/storage/pdpp-records/completion.js.map +1 -0
  243. package/dist/storage/pdpp-records/cursor.d.ts +41 -0
  244. package/dist/storage/pdpp-records/cursor.d.ts.map +1 -0
  245. package/dist/storage/pdpp-records/cursor.js +49 -0
  246. package/dist/storage/pdpp-records/cursor.js.map +1 -0
  247. package/dist/storage/pdpp-records/declaration-witness.d.ts +58 -0
  248. package/dist/storage/pdpp-records/declaration-witness.d.ts.map +1 -0
  249. package/dist/storage/pdpp-records/declaration-witness.js +230 -0
  250. package/dist/storage/pdpp-records/declaration-witness.js.map +1 -0
  251. package/dist/storage/pdpp-records/enforcement.d.ts +52 -0
  252. package/dist/storage/pdpp-records/enforcement.d.ts.map +1 -0
  253. package/dist/storage/pdpp-records/enforcement.js +116 -0
  254. package/dist/storage/pdpp-records/enforcement.js.map +1 -0
  255. package/dist/storage/pdpp-records/index.d.ts +5 -0
  256. package/dist/storage/pdpp-records/index.d.ts.map +1 -0
  257. package/dist/storage/pdpp-records/index.js +5 -0
  258. package/dist/storage/pdpp-records/index.js.map +1 -0
  259. package/dist/storage/pdpp-records/ingest-plan.d.ts +39 -0
  260. package/dist/storage/pdpp-records/ingest-plan.d.ts.map +1 -0
  261. package/dist/storage/pdpp-records/ingest-plan.js +103 -0
  262. package/dist/storage/pdpp-records/ingest-plan.js.map +1 -0
  263. package/dist/storage/pdpp-records/interface.d.ts +104 -0
  264. package/dist/storage/pdpp-records/interface.d.ts.map +1 -0
  265. package/dist/storage/pdpp-records/interface.js +2 -0
  266. package/dist/storage/pdpp-records/interface.js.map +1 -0
  267. package/dist/storage/pdpp-records/memory-store.d.ts +9 -0
  268. package/dist/storage/pdpp-records/memory-store.d.ts.map +1 -0
  269. package/dist/storage/pdpp-records/memory-store.js +466 -0
  270. package/dist/storage/pdpp-records/memory-store.js.map +1 -0
  271. package/dist/storage/pdpp-records/portable.d.ts +9 -0
  272. package/dist/storage/pdpp-records/portable.d.ts.map +1 -0
  273. package/dist/storage/pdpp-records/portable.js +8 -0
  274. package/dist/storage/pdpp-records/portable.js.map +1 -0
  275. package/dist/storage/pdpp-records/read-port.d.ts +44 -0
  276. package/dist/storage/pdpp-records/read-port.d.ts.map +1 -0
  277. package/dist/storage/pdpp-records/read-port.js +22 -0
  278. package/dist/storage/pdpp-records/read-port.js.map +1 -0
  279. package/dist/storage/pdpp-records/record-key.d.ts +52 -0
  280. package/dist/storage/pdpp-records/record-key.d.ts.map +1 -0
  281. package/dist/storage/pdpp-records/record-key.js +97 -0
  282. package/dist/storage/pdpp-records/record-key.js.map +1 -0
  283. package/dist/storage/pdpp-records/schema.d.ts +41 -0
  284. package/dist/storage/pdpp-records/schema.d.ts.map +1 -0
  285. package/dist/storage/pdpp-records/schema.js +153 -0
  286. package/dist/storage/pdpp-records/schema.js.map +1 -0
  287. package/dist/storage/pdpp-records/stream-declaration.d.ts +88 -0
  288. package/dist/storage/pdpp-records/stream-declaration.d.ts.map +1 -0
  289. package/dist/storage/pdpp-records/stream-declaration.js +57 -0
  290. package/dist/storage/pdpp-records/stream-declaration.js.map +1 -0
  291. package/dist/storage/pdpp-records/types.d.ts +138 -0
  292. package/dist/storage/pdpp-records/types.d.ts.map +1 -0
  293. package/dist/storage/pdpp-records/types.js +38 -0
  294. package/dist/storage/pdpp-records/types.js.map +1 -0
  295. package/dist/sync/data-point-feed.d.ts +3 -0
  296. package/dist/sync/data-point-feed.d.ts.map +1 -1
  297. package/dist/sync/data-point-feed.js +17 -1
  298. package/dist/sync/data-point-feed.js.map +1 -1
  299. package/dist/sync/delete-data-port.d.ts +2 -0
  300. package/dist/sync/delete-data-port.d.ts.map +1 -1
  301. package/dist/sync/delete-data-port.js +1 -1
  302. package/dist/sync/delete-data-port.js.map +1 -1
  303. package/dist/sync/engine/sync-manager.d.ts.map +1 -1
  304. package/dist/sync/engine/sync-manager.js +5 -2
  305. package/dist/sync/engine/sync-manager.js.map +1 -1
  306. package/dist/sync/index.d.ts +3 -1
  307. package/dist/sync/index.d.ts.map +1 -1
  308. package/dist/sync/index.js +4 -0
  309. package/dist/sync/index.js.map +1 -1
  310. package/dist/sync/pdpp-import.d.ts +192 -0
  311. package/dist/sync/pdpp-import.d.ts.map +1 -0
  312. package/dist/sync/pdpp-import.js +441 -0
  313. package/dist/sync/pdpp-import.js.map +1 -0
  314. package/dist/sync/scope-deletions.d.ts +10 -0
  315. package/dist/sync/scope-deletions.d.ts.map +1 -1
  316. package/dist/sync/scope-deletions.js +40 -7
  317. package/dist/sync/scope-deletions.js.map +1 -1
  318. package/dist/sync/workers/download.d.ts +14 -0
  319. package/dist/sync/workers/download.d.ts.map +1 -1
  320. package/dist/sync/workers/download.js +149 -13
  321. package/dist/sync/workers/download.js.map +1 -1
  322. package/dist/sync/workers/upload.d.ts +2 -0
  323. package/dist/sync/workers/upload.d.ts.map +1 -1
  324. package/dist/sync/workers/upload.js +7 -0
  325. package/dist/sync/workers/upload.js.map +1 -1
  326. package/dist/test-utils/memory-storage.d.ts +1 -1
  327. package/dist/test-utils/memory-storage.d.ts.map +1 -1
  328. package/dist/test-utils/memory-storage.js +11 -5
  329. package/dist/test-utils/memory-storage.js.map +1 -1
  330. package/package.json +54 -4
@@ -0,0 +1,761 @@
1
+ import { ANTHROPIC_CONNECTOR_ID, CLAUDE_SOURCE_ID, } from "../legacy-projection/bindings.js";
2
+ import { hasAlternateLegacyProfile, LEGACY_SCOPE_BINDINGS, legacyBindingsForProfile, legacyScopeToPdppSelection, projectPdppRecordsToLegacyPayload, } from "../legacy-projection/index.js";
3
+ import { declaredBlobField, declaredBlobReference, decodeDeclaredBlob, decodeDeclaredRecordsForProjection, DeclaredTextError, } from "../legacy-projection/declared-text.js";
4
+ import { claudeBlobReference, ClaudeSourceRecordError, decodeClaudeSourceRecord, mergeClaudeSourceRecords, } from "../legacy-projection/claude-source-record.js";
5
+ import { CursorExpiredError, InvalidCursorError, } from "./pdpp-records/portable.js";
6
+ /**
7
+ * Read-time legacy view over canonical PDPP records.
8
+ *
9
+ * Reads ONE legacy scope from exactly one selected connection and returns the
10
+ * legacy `DataFileEnvelope`. Nothing is stored: every call recomputes the body
11
+ * from the live rows, so a repaired source is visible on the next read. The
12
+ * reader only calls read methods on the store.
13
+ *
14
+ * Out of scope here: manual/unbound scope files (no binding => `unbound_scope`),
15
+ * auth/grants, and the sync `DataStoragePort` listing surface.
16
+ */
17
+ /** Bump when a change to this module (or the pure projector) can change bytes. */
18
+ export const CANONICAL_LEGACY_VIEW_CONTRACT = "canonical-legacy-view/7";
19
+ const DEFAULT_PAGE_SIZE = 500;
20
+ const MAX_BLOB_READ_BYTES = 64 * 1024 * 1024;
21
+ const SOURCE_ID_PREFIX = "https://registry.pdpp.dev/sources/";
22
+ const CONNECTOR_ID_PREFIX = "https://registry.pdpp.dev/connectors/";
23
+ /**
24
+ * Pick the single connection for a source. Several matches is an error, never
25
+ * a union: a legacy body is one account's data.
26
+ */
27
+ export function resolveCanonicalConnection(store, query) {
28
+ const matches = store
29
+ .listConnections(query.sourceId)
30
+ .filter((binding) => binding.deletedAt === null &&
31
+ binding.method !== null &&
32
+ (query.method === undefined || binding.method === query.method) &&
33
+ (query.instance === undefined || binding.instance === query.instance));
34
+ const [only] = matches;
35
+ if (matches.length === 1 && only?.method) {
36
+ return {
37
+ ok: true,
38
+ selection: {
39
+ instance: only.instance,
40
+ sourceId: query.sourceId,
41
+ method: only.method,
42
+ },
43
+ };
44
+ }
45
+ return {
46
+ ok: false,
47
+ status: "unavailable",
48
+ reason: matches.length === 0 ? "no_connection" : "ambiguous_connection",
49
+ candidates: matches.length,
50
+ };
51
+ }
52
+ export async function readCanonicalLegacyScope(input) {
53
+ const { store, scope, selection } = input;
54
+ const pageSize = Math.max(1, Math.floor(input.pageSize ?? DEFAULT_PAGE_SIZE));
55
+ const byMethod = input.profileBy === "method";
56
+ const profileKey = byMethod
57
+ ? profileKeyForMethod(selection.method)
58
+ : profileKeyForSource(selection.sourceId);
59
+ const lookup = legacyScopeToPdppSelection(scope, { profileKey });
60
+ const binding = bindingFor(scope, profileKey);
61
+ if (!lookup.ok || !binding) {
62
+ return unavailable({
63
+ reason: "unbound_scope",
64
+ lookup: lookup.ok ? { kind: "unknown_scope", scope } : lookup.error,
65
+ });
66
+ }
67
+ if ((binding.pdppSource === ANTHROPIC_CONNECTOR_ID &&
68
+ selection.sourceId !== CLAUDE_SOURCE_ID) ||
69
+ (byMethod
70
+ ? legacyConnectorKey(binding.pdppSource) !== selection.method
71
+ : canonicalSourceId(binding.pdppSource) !== selection.sourceId)) {
72
+ return unavailable({ reason: "scope_source_mismatch", scope });
73
+ }
74
+ const streams = lookup.selection.streams;
75
+ const snapshot = store.readSnapshot(() => input.confirmSelection && !input.confirmSelection()
76
+ ? {
77
+ ok: false,
78
+ result: inconsistent("the connection selection changed"),
79
+ }
80
+ : collectSnapshot(store, selection, streams, binding.rowOrder === "arrival", pageSize, Number.isSafeInteger(input.blobByteBudget) &&
81
+ (input.blobByteBudget ?? -1) >= 0
82
+ ? Math.min(MAX_BLOB_READ_BYTES, input.blobByteBudget)
83
+ : MAX_BLOB_READ_BYTES, input.requireCompletionReceipts === true, input.allowUnprovenEmptyStreams === true, input.declarations));
84
+ if (!snapshot.ok)
85
+ return snapshot.result;
86
+ return projectSnapshot({
87
+ scope,
88
+ selection,
89
+ streams,
90
+ binding,
91
+ profileKey,
92
+ includeStreamTimes: input.includeStreamTimes === true,
93
+ requireClaudeRawBlobs: input.requireClaudeRawBlobs === true,
94
+ ...snapshot,
95
+ });
96
+ }
97
+ /** Everything the view needs from the store, read inside one snapshot. */
98
+ function collectSnapshot(store, selection, streams, arrivalOrder, pageSize, blobByteBudget, requireCompletionReceipts, allowUnprovenEmptyStreams, declarations) {
99
+ const { instance } = selection;
100
+ const binding = store.getInstanceBinding(instance);
101
+ const rejected = checkConnection(binding, selection);
102
+ if (rejected)
103
+ return { ok: false, result: unavailable(rejected) };
104
+ const listing = store.listStreams([instance]);
105
+ const streamSchemas = {};
106
+ for (const stream of streams) {
107
+ const schema = declarations?.forInstance(instance, stream)?.schema;
108
+ if (schema)
109
+ streamSchemas[stream] = structuredClone(schema);
110
+ }
111
+ const rowsByStream = new Map();
112
+ const orderGaps = [];
113
+ try {
114
+ for (const stream of streams) {
115
+ const rows = readAllRows(store, instance, stream, pageSize);
116
+ if (typeof rows === "string") {
117
+ return { ok: false, result: inconsistent(rows) };
118
+ }
119
+ // Inside one snapshot the listing and the pages must agree.
120
+ if (rows.length !== countFor(listing, stream)) {
121
+ return {
122
+ ok: false,
123
+ result: inconsistent(`${stream} pages disagree with the listing`),
124
+ };
125
+ }
126
+ const ordered = arrivalOrder
127
+ ? inArrivalOrder(rows, store.listArrivalOrder(instance, stream))
128
+ : rows;
129
+ if (!ordered)
130
+ orderGaps.push(stream);
131
+ rowsByStream.set(stream, ordered ?? rows);
132
+ }
133
+ }
134
+ catch (error) {
135
+ if (error instanceof CursorExpiredError ||
136
+ error instanceof InvalidCursorError) {
137
+ return { ok: false, result: inconsistent("store cursor rejected") };
138
+ }
139
+ throw error;
140
+ }
141
+ const completions = new Map();
142
+ for (const receipt of store.listStreamCompletions(instance)) {
143
+ if (streams.includes(receipt.stream) &&
144
+ receipt.method === binding.method &&
145
+ receipt.generation === binding.generation &&
146
+ receipt.liveCount === rowsByStream.get(receipt.stream)?.length) {
147
+ completions.set(receipt.stream, receipt);
148
+ }
149
+ }
150
+ // Refuse missing source evidence before reading even one blob body.
151
+ // Binding, rows, receipts, metadata and the later copies share this snapshot.
152
+ if (completions.size === 0 &&
153
+ listing.every((entry) => entry.recordCount === 0)) {
154
+ return { ok: false, result: unavailable({ reason: "no_synced_data" }) };
155
+ }
156
+ const unprovenStreams = streams.filter((stream) => !completions.has(stream) &&
157
+ (requireCompletionReceipts ||
158
+ (!allowUnprovenEmptyStreams &&
159
+ (rowsByStream.get(stream)?.length ?? 0) === 0)));
160
+ if (unprovenStreams.length > 0) {
161
+ return {
162
+ ok: false,
163
+ result: unavailable({
164
+ reason: "stream_completion_unknown",
165
+ streams: unprovenStreams,
166
+ }),
167
+ };
168
+ }
169
+ // Preflight all unique references/metadata before copying ANY body. A legal
170
+ // per-blob size does not bound fan-in; excluded raw fields still cost bytes.
171
+ const blobContents = new Map();
172
+ let requiredBytes = 0;
173
+ const declaredBlobIds = new Set();
174
+ for (const stream of streams) {
175
+ const blobBacked = (rowsByStream.get(stream) ?? []).filter(hasBlobRef);
176
+ if (blobBacked.length === 0)
177
+ continue;
178
+ const projectedField = declaredBlobField(streamSchemas[stream]);
179
+ if (!projectedField &&
180
+ (selection.sourceId !== CLAUDE_SOURCE_ID ||
181
+ (stream !== "conversations" && stream !== "projects"))) {
182
+ return {
183
+ ok: false,
184
+ result: unavailable({
185
+ reason: "blob_backed_records_unsupported",
186
+ stream,
187
+ recordCount: blobBacked.length,
188
+ blocker: "This scope/stream has no verified mapping from blob bytes to its legacy fields.",
189
+ }),
190
+ };
191
+ }
192
+ for (const row of blobBacked) {
193
+ let ref;
194
+ try {
195
+ ref = projectedField
196
+ ? declaredBlobReference(row.data, projectedField)
197
+ : claudeBlobReference(row.data);
198
+ }
199
+ catch (error) {
200
+ if (!projectedField && !(error instanceof ClaudeSourceRecordError))
201
+ throw error;
202
+ return {
203
+ ok: false,
204
+ result: unavailable({
205
+ reason: "blob_backed_record_invalid",
206
+ detail: projectedField
207
+ ? "invalid_blob_reference"
208
+ : error.message,
209
+ }),
210
+ };
211
+ }
212
+ if (projectedField)
213
+ declaredBlobIds.add(ref.blobId);
214
+ const prior = blobContents.get(ref.blobId);
215
+ let meta;
216
+ try {
217
+ meta = prior?.meta ?? store.getBlobMeta?.(ref.blobId);
218
+ }
219
+ catch (error) {
220
+ if (!projectedField)
221
+ throw error;
222
+ return {
223
+ ok: false,
224
+ result: unavailable({ reason: "missing_raw_blob", recordCount: 1 }),
225
+ };
226
+ }
227
+ if (!meta && projectedField) {
228
+ return {
229
+ ok: false,
230
+ result: unavailable({ reason: "missing_raw_blob", recordCount: 1 }),
231
+ };
232
+ }
233
+ if (!meta)
234
+ return {
235
+ ok: false,
236
+ result: unavailable({
237
+ reason: "blob_backed_record_invalid",
238
+ detail: "blob_missing_or_corrupt",
239
+ }),
240
+ };
241
+ if (meta.blobId !== ref.blobId ||
242
+ meta.mimeType !== "application/json" ||
243
+ meta.sizeBytes !== ref.size ||
244
+ meta.sha256 !== ref.sha256) {
245
+ return {
246
+ ok: false,
247
+ result: unavailable({
248
+ reason: "blob_backed_record_invalid",
249
+ detail: "blob_metadata_mismatch",
250
+ }),
251
+ };
252
+ }
253
+ if (prior)
254
+ continue;
255
+ const sum = requiredBytes + ref.size;
256
+ // Never accumulate an inexact integer, even for a refused request.
257
+ if (!Number.isSafeInteger(sum)) {
258
+ return {
259
+ ok: false,
260
+ result: unavailable({
261
+ reason: "blob_read_budget_exceeded",
262
+ maxBytes: blobByteBudget,
263
+ }),
264
+ };
265
+ }
266
+ requiredBytes = sum;
267
+ blobContents.set(ref.blobId, { meta: { ...meta }, bytes: undefined });
268
+ }
269
+ }
270
+ if (requiredBytes > blobByteBudget) {
271
+ return {
272
+ ok: false,
273
+ result: unavailable({
274
+ reason: "blob_read_budget_exceeded",
275
+ requiredBytes,
276
+ maxBytes: blobByteBudget,
277
+ }),
278
+ };
279
+ }
280
+ for (const [blobId, contents] of blobContents) {
281
+ let bytes;
282
+ try {
283
+ bytes = store.getBlobBytes?.(blobId);
284
+ }
285
+ catch (error) {
286
+ if (!declaredBlobIds.has(blobId))
287
+ throw error;
288
+ return {
289
+ ok: false,
290
+ result: unavailable({ reason: "missing_raw_blob", recordCount: 1 }),
291
+ };
292
+ }
293
+ if (!bytes && declaredBlobIds.has(blobId)) {
294
+ return {
295
+ ok: false,
296
+ result: unavailable({ reason: "missing_raw_blob", recordCount: 1 }),
297
+ };
298
+ }
299
+ if (!bytes)
300
+ return {
301
+ ok: false,
302
+ result: unavailable({
303
+ reason: "blob_backed_record_invalid",
304
+ detail: "blob_missing_or_corrupt",
305
+ }),
306
+ };
307
+ if (bytes.byteLength !== contents.meta?.sizeBytes) {
308
+ return {
309
+ ok: false,
310
+ result: unavailable({
311
+ reason: "blob_backed_record_invalid",
312
+ detail: "blob_metadata_mismatch",
313
+ }),
314
+ };
315
+ }
316
+ contents.bytes = bytes.slice();
317
+ }
318
+ return {
319
+ ok: true,
320
+ generation: binding.generation,
321
+ listing,
322
+ rowsByStream,
323
+ orderGaps,
324
+ completions,
325
+ blobContents,
326
+ streamSchemas,
327
+ };
328
+ }
329
+ /**
330
+ * `rows` in the order their keys last arrived, or null when a row has no
331
+ * arrival to place it by. Zero or one row has only one order.
332
+ */
333
+ function inArrivalOrder(rows, arrival) {
334
+ if (rows.length < 2)
335
+ return rows;
336
+ const position = new Map(arrival.map((key, index) => [key, index]));
337
+ const placed = rows.map((row) => ({ row, at: position.get(row.recordKey) }));
338
+ if (placed.some(({ at }) => at === undefined))
339
+ return null;
340
+ return placed.sort((a, b) => (a.at ?? 0) - (b.at ?? 0)).map(({ row }) => row);
341
+ }
342
+ async function projectSnapshot(input) {
343
+ const { scope, selection, streams, binding, profileKey, rowsByStream, orderGaps, completions, } = input;
344
+ const listingBefore = input.listing;
345
+ const sourceRowCounts = Object.fromEntries(streams.map((stream) => [stream, rowsByStream.get(stream)?.length ?? 0]));
346
+ const expanded = [];
347
+ const canExpandClaude = selection.sourceId === CLAUDE_SOURCE_ID &&
348
+ (scope === "claude.conversations" || scope === "claude.projects");
349
+ if (canExpandClaude && input.requireClaudeRawBlobs) {
350
+ const parentStream = scope === "claude.conversations" ? "conversations" : "projects";
351
+ const missingRawBlobCount = (rowsByStream.get(parentStream) ?? []).filter((row) => !hasBlobRef(row)).length;
352
+ if (missingRawBlobCount > 0) {
353
+ return unavailable({
354
+ reason: "missing_raw_blob",
355
+ recordCount: missingRawBlobCount,
356
+ });
357
+ }
358
+ }
359
+ for (const stream of streams) {
360
+ const blobBacked = (rowsByStream.get(stream) ?? []).filter(hasBlobRef);
361
+ if (canExpandClaude &&
362
+ !declaredBlobField(input.streamSchemas[stream]) &&
363
+ (stream === "conversations" || stream === "projects")) {
364
+ try {
365
+ for (const row of blobBacked) {
366
+ const ref = claudeBlobReference(row.data);
367
+ expanded.push(...(await decodeClaudeSourceRecord(row, input.blobContents.get(ref.blobId) ?? {
368
+ meta: undefined,
369
+ bytes: undefined,
370
+ })));
371
+ }
372
+ }
373
+ catch (error) {
374
+ if (!(error instanceof ClaudeSourceRecordError))
375
+ throw error;
376
+ return unavailable({
377
+ reason: "blob_backed_record_invalid",
378
+ detail: error.message,
379
+ });
380
+ }
381
+ continue;
382
+ }
383
+ if (blobBacked.length > 0 &&
384
+ !declaredBlobField(input.streamSchemas[stream])) {
385
+ return unavailable({
386
+ reason: "blob_backed_records_unsupported",
387
+ stream,
388
+ recordCount: blobBacked.length,
389
+ blocker: "This scope/stream has no verified mapping from blob bytes to its legacy fields.",
390
+ });
391
+ }
392
+ }
393
+ const collectedAt = latestCollectedAt(rowsByStream, completions, listingBefore);
394
+ if (collectedAt === null)
395
+ return unavailable({ reason: "invalid_collected_at" });
396
+ let records = streams.flatMap((stream) => (rowsByStream.get(stream) ?? []).map((row) => ({ stream, data: row.data })));
397
+ // Decode inline fields before inserting raw blob text; a JSON-string blob
398
+ // is already source text and must never be decoded a second time.
399
+ let decodeDiagnostics = [];
400
+ try {
401
+ const decoded = decodeDeclaredRecordsForProjection(records, input.streamSchemas, scope, true);
402
+ records = decoded.records;
403
+ decodeDiagnostics = decoded.diagnostics;
404
+ }
405
+ catch (error) {
406
+ if (!(error instanceof DeclaredTextError))
407
+ throw error;
408
+ return rejection(error.projectionError(scope), sourceRowCounts);
409
+ }
410
+ for (let i = 0; i < records.length; i++) {
411
+ const record = records[i];
412
+ const field = declaredBlobField(input.streamSchemas[record.stream]);
413
+ if (!field || !Object.hasOwn(record.data, "blob_ref"))
414
+ continue;
415
+ try {
416
+ const ref = declaredBlobReference(record.data, field);
417
+ records[i] = {
418
+ ...record,
419
+ data: await decodeDeclaredBlob(record.data, field, input.blobContents.get(ref.blobId) ?? {
420
+ meta: undefined,
421
+ bytes: undefined,
422
+ }),
423
+ };
424
+ }
425
+ catch {
426
+ return unavailable({
427
+ reason: "blob_backed_record_invalid",
428
+ detail: "invalid_declared_blob",
429
+ });
430
+ }
431
+ }
432
+ if (expanded.length > 0) {
433
+ try {
434
+ records = mergeClaudeSourceRecords(records.filter((record) => !Object.hasOwn(record.data, "blob_ref")), expanded);
435
+ }
436
+ catch (error) {
437
+ if (!(error instanceof ClaudeSourceRecordError))
438
+ throw error;
439
+ return unavailable({
440
+ reason: "blob_backed_record_invalid",
441
+ detail: error.message,
442
+ });
443
+ }
444
+ }
445
+ const projected = projectPdppRecordsToLegacyPayload(scope, records, {
446
+ fetchedStreams: streams,
447
+ ...(profileKey ? { profileKey } : {}),
448
+ now: collectedAt,
449
+ // Rows of an arrival-ordered scope go in as read. Without proof of that
450
+ // order the view falls back to key order and reports the gap below.
451
+ orderByPrimaryKey: binding.rowOrder !== "arrival" || orderGaps.length > 0,
452
+ });
453
+ if (!projected.ok) {
454
+ return rejection(projected.error, sourceRowCounts);
455
+ }
456
+ const omissions = [];
457
+ const partialRows = [];
458
+ const unproven = orderGaps.map((stream) => ({
459
+ stream,
460
+ aspect: "record_order",
461
+ reason: "some rows were stored before the store kept arrival order; they are served in primary-key order",
462
+ }));
463
+ for (const diagnostic of [
464
+ ...decodeDiagnostics,
465
+ ...(projected.diagnostics ?? []),
466
+ ]) {
467
+ if (diagnostic.kind === "records_dropped" ||
468
+ diagnostic.kind === "records_partial") {
469
+ (diagnostic.kind === "records_dropped" ? omissions : partialRows).push({
470
+ stream: diagnostic.stream,
471
+ count: diagnostic.count,
472
+ reasons: diagnostic.reasons,
473
+ });
474
+ }
475
+ }
476
+ // Valid peers may deliver with omissions. If NOTHING of the item stream
477
+ // survived, a "successful empty" would read as an empty history.
478
+ const itemStream = streams[0];
479
+ const sourceRows = itemStream ? (sourceRowCounts[itemStream] ?? 0) : 0;
480
+ const itemOmissions = omissions.filter((entry) => entry.stream === itemStream);
481
+ const itemDropped = itemOmissions.reduce((sum, entry) => sum + entry.count, 0);
482
+ if (itemStream && sourceRows > 0 && itemDropped >= sourceRows) {
483
+ return {
484
+ ok: false,
485
+ status: "incomplete",
486
+ reason: "all_rows_rejected",
487
+ stream: itemStream,
488
+ sourceRows,
489
+ dropped: itemDropped,
490
+ reasons: [...new Set(itemOmissions.flatMap((entry) => entry.reasons))],
491
+ sourceRowCounts,
492
+ };
493
+ }
494
+ const revision = await viewRevision({
495
+ scope,
496
+ selection,
497
+ binding,
498
+ profileKey,
499
+ generation: input.generation,
500
+ streams,
501
+ rowsByStream,
502
+ orderGaps,
503
+ completions,
504
+ listing: listingBefore,
505
+ streamSchemas: input.streamSchemas,
506
+ });
507
+ return {
508
+ ok: true,
509
+ ...(projected.projectionOmissions
510
+ ? { projectionOmissions: projected.projectionOmissions }
511
+ : {}),
512
+ envelope: {
513
+ version: "1.0",
514
+ scope,
515
+ collectedAt,
516
+ data: projected.payload,
517
+ },
518
+ ...(input.includeStreamTimes
519
+ ? {
520
+ streamCollectionTimes: Object.fromEntries(streams.map((stream) => {
521
+ const receipt = completions.get(stream);
522
+ if (receipt)
523
+ return [stream, receipt.sourceCollectedAt];
524
+ const rows = rowsByStream.get(stream) ?? [];
525
+ const times = rows.map((row) => Date.parse(row.emittedAt));
526
+ if (times.length === 0 ||
527
+ times.some((time) => !Number.isFinite(time))) {
528
+ return [stream, null];
529
+ }
530
+ return [
531
+ stream,
532
+ new Date(times.reduce((oldest, time) => Math.min(oldest, time), Number.POSITIVE_INFINITY)).toISOString(),
533
+ ];
534
+ })),
535
+ streamCompletionTimes: Object.fromEntries(streams.map((stream) => [
536
+ stream,
537
+ completions.get(stream)?.sourceCollectedAt ?? null,
538
+ ])),
539
+ }
540
+ : {}),
541
+ revision,
542
+ complete: omissions.length === 0 &&
543
+ partialRows.length === 0 &&
544
+ unproven.length === 0,
545
+ diagnostics: {
546
+ sourceRowCounts,
547
+ omissions,
548
+ partialRows,
549
+ ...(unproven.length > 0 ? { unproven } : {}),
550
+ },
551
+ };
552
+ }
553
+ function inconsistent(detail) {
554
+ return unavailable({ reason: "store_inconsistent_read", detail });
555
+ }
556
+ /** All live rows of one stream, or a detail string if the pages are not one clean listing. */
557
+ function readAllRows(store, instance, stream, limit) {
558
+ const rows = [];
559
+ const seen = new Set();
560
+ let cursor;
561
+ let horizon;
562
+ let first = true;
563
+ for (;;) {
564
+ const page = store.listRecords(stream, {
565
+ instanceIds: [instance],
566
+ limit,
567
+ cursor,
568
+ order: "asc",
569
+ });
570
+ if (first)
571
+ horizon = page.horizon;
572
+ else if (page.horizon !== horizon)
573
+ return `${stream} page horizon moved`;
574
+ first = false;
575
+ for (const row of page.data) {
576
+ const identity = `${row.instance}\u0000${row.recordKey}`;
577
+ if (row.instance !== instance ||
578
+ row.stream !== stream ||
579
+ seen.has(identity)) {
580
+ return `${stream} returned a foreign or repeated row`;
581
+ }
582
+ seen.add(identity);
583
+ rows.push(row);
584
+ }
585
+ if (!page.hasMore)
586
+ return rows;
587
+ if (!page.nextCursor || page.nextCursor === cursor) {
588
+ return `${stream} cursor did not advance`;
589
+ }
590
+ cursor = page.nextCursor;
591
+ }
592
+ }
593
+ function checkConnection(binding, selection) {
594
+ const detail = (value) => ({ reason: "connection_unavailable", detail: value });
595
+ if (binding.sourceId === null)
596
+ return detail("not_registered");
597
+ if (binding.deletedAt !== null)
598
+ return detail("deleted");
599
+ if (binding.sourceId !== selection.sourceId)
600
+ return detail("source_mismatch");
601
+ if (binding.method !== selection.method)
602
+ return detail("method_mismatch");
603
+ return null;
604
+ }
605
+ function rejection(error, sourceRowCounts) {
606
+ if (error.kind === "unknown_scope" ||
607
+ error.kind === "unsupported_profile" ||
608
+ error.kind === "gap") {
609
+ return unavailable({
610
+ reason: "unbound_scope",
611
+ lookup: error.kind === "gap"
612
+ ? {
613
+ kind: "gap",
614
+ scope: error.scope,
615
+ reason: error.reason,
616
+ provenanceChecked: [],
617
+ }
618
+ : error,
619
+ });
620
+ }
621
+ return {
622
+ ok: false,
623
+ status: "incomplete",
624
+ reason: "projection_rejected",
625
+ error,
626
+ sourceRowCounts,
627
+ };
628
+ }
629
+ function unavailable(failure) {
630
+ return {
631
+ ok: false,
632
+ status: "unavailable",
633
+ ...failure,
634
+ };
635
+ }
636
+ function hasBlobRef(row) {
637
+ return Object.hasOwn(row.data, "blob_ref");
638
+ }
639
+ function countFor(listing, stream) {
640
+ return listing.find((entry) => entry.stream === stream)?.recordCount ?? 0;
641
+ }
642
+ /**
643
+ * Newest collection time over the bound streams. A stream with a valid
644
+ * receipt was collected at its producer `sourceCollectedAt`, even when an
645
+ * unchanged replace left older rows. A stream without one falls back to its
646
+ * rows' `emittedAt` (the importer stamps it from the original envelope
647
+ * `collectedAt`). With neither, the newest stream horizon of the connection.
648
+ * Never the wall clock.
649
+ */
650
+ function latestCollectedAt(rowsByStream, completions, listing) {
651
+ const stamps = [];
652
+ for (const [stream, rows] of rowsByStream) {
653
+ const receipt = completions.get(stream);
654
+ if (receipt)
655
+ stamps.push(receipt.sourceCollectedAt);
656
+ else
657
+ for (const row of rows)
658
+ stamps.push(row.emittedAt);
659
+ }
660
+ if (stamps.length === 0) {
661
+ for (const entry of listing) {
662
+ if (entry.lastUpdated !== null)
663
+ stamps.push(entry.lastUpdated);
664
+ }
665
+ }
666
+ let newest = Number.NEGATIVE_INFINITY;
667
+ for (const stamp of stamps) {
668
+ const ms = Date.parse(stamp);
669
+ if (Number.isNaN(ms))
670
+ return null;
671
+ newest = Math.max(newest, ms);
672
+ }
673
+ return Number.isFinite(newest) ? new Date(newest).toISOString() : null;
674
+ }
675
+ async function viewRevision(input) {
676
+ const horizons = [];
677
+ for (const stream of input.streams) {
678
+ const rows = input.rowsByStream.get(stream) ?? [];
679
+ horizons.push({
680
+ stream,
681
+ count: rows.length,
682
+ lastUpdated: input.listing.find((entry) => entry.stream === stream)?.lastUpdated ??
683
+ null,
684
+ rows: await sha256Hex(rows
685
+ .map((row) => `${row.recordKey}\u0000${row.version}\u0000${row.emittedAt}`)
686
+ .join("\n")),
687
+ });
688
+ }
689
+ const digest = await sha256Hex(JSON.stringify({
690
+ contract: CANONICAL_LEGACY_VIEW_CONTRACT,
691
+ streamSchemas: input.streamSchemas,
692
+ scope: input.scope,
693
+ instance: input.selection.instance,
694
+ sourceId: input.selection.sourceId,
695
+ method: input.selection.method,
696
+ generation: input.generation,
697
+ profileKey: input.profileKey ?? null,
698
+ declaration: {
699
+ pdppSource: input.binding.pdppSource,
700
+ streams: input.binding.pdppStreams,
701
+ primaryKey: input.binding.primaryKey,
702
+ fieldsRead: input.binding.fieldsRead,
703
+ provenance: input.binding.provenance,
704
+ },
705
+ // Rows hash in served order, so a re-delivery that moves a key moves
706
+ // the revision even though no row version changed.
707
+ horizons,
708
+ orderGaps: input.orderGaps,
709
+ // Not `writeSeq`: the write clock is shared by all connections, so an
710
+ // unchanged replay after another account's write would move it.
711
+ completions: input.streams.flatMap((stream) => {
712
+ const receipt = input.completions.get(stream);
713
+ return receipt
714
+ ? [
715
+ {
716
+ stream,
717
+ method: receipt.method,
718
+ generation: receipt.generation,
719
+ sourceCollectedAt: receipt.sourceCollectedAt,
720
+ liveCount: receipt.liveCount,
721
+ },
722
+ ]
723
+ : [];
724
+ }),
725
+ }));
726
+ return `v1:${digest}`;
727
+ }
728
+ async function sha256Hex(text) {
729
+ const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
730
+ return Array.from(new Uint8Array(bytes), (b) => b.toString(16).padStart(2, "0")).join("");
731
+ }
732
+ function bindingFor(scope, profileKey) {
733
+ return profileKey
734
+ ? legacyBindingsForProfile(profileKey).get(scope)
735
+ : LEGACY_SCOPE_BINDINGS.get(scope);
736
+ }
737
+ /** Alternate declaration profiles are keyed by the source's last segment. */
738
+ function profileKeyForSource(sourceId) {
739
+ const key = sourceId.slice(sourceId.lastIndexOf("/") + 1);
740
+ return hasAlternateLegacyProfile(key) ? key : undefined;
741
+ }
742
+ /** A method id names its alternate profile directly; others use the default. */
743
+ function profileKeyForMethod(method) {
744
+ return hasAlternateLegacyProfile(method) ? method : undefined;
745
+ }
746
+ /**
747
+ * The connector key a binding was reviewed for: the last segment of its
748
+ * `pdppSource`. It is a profile name, not a store source id.
749
+ */
750
+ export function legacyConnectorKey(pdppSource) {
751
+ return pdppSource.slice(pdppSource.lastIndexOf("/") + 1);
752
+ }
753
+ /** Most connector keys match their public source; Anthropic collects Claude. */
754
+ function canonicalSourceId(pdppSource) {
755
+ if (pdppSource === ANTHROPIC_CONNECTOR_ID)
756
+ return CLAUDE_SOURCE_ID;
757
+ return pdppSource.startsWith(CONNECTOR_ID_PREFIX)
758
+ ? SOURCE_ID_PREFIX + pdppSource.slice(CONNECTOR_ID_PREFIX.length)
759
+ : pdppSource;
760
+ }
761
+ //# sourceMappingURL=canonical-legacy-projection.js.map