@slicedearth/whoisleuth-cli 2.3.0 → 2.4.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 (264) hide show
  1. package/NOTICE +6 -0
  2. package/README.md +19 -15
  3. package/bin/whoisleuth.mjs +8 -1
  4. package/cli/archive-content-digest.mjs +14 -0
  5. package/cli/archive-inspect.mjs +20 -53
  6. package/cli/arguments.mjs +68 -6
  7. package/cli/artifact-validation/case-response.mjs +47 -41
  8. package/cli/artifact-validation/investigation-capsule.mjs +52 -31
  9. package/cli/artifact-validation/lookup-evidence.mjs +12 -6
  10. package/cli/artifact-validation/signed-review.mjs +83 -61
  11. package/cli/artifact-validation/structure-primitives.mjs +1 -105
  12. package/cli/artifact-verify.mjs +90 -24
  13. package/cli/assurance-command-runner.mjs +39 -4
  14. package/cli/bagit-folder.mjs +57 -0
  15. package/cli/bagit-review.mjs +23 -0
  16. package/cli/bounded-json.mjs +1 -1
  17. package/cli/bulk-command-runner.mjs +2 -0
  18. package/cli/bulk-output.mjs +29 -3
  19. package/cli/case-command.mjs +195 -0
  20. package/cli/case-pack.mjs +39 -31
  21. package/cli/command-argument-grammar.mjs +4 -0
  22. package/cli/command-reference.mjs +145 -75
  23. package/cli/completion.mjs +298 -79
  24. package/cli/config-profile.mjs +7 -5
  25. package/cli/ct-event-intake.mjs +12 -12
  26. package/cli/discover.mjs +4 -3
  27. package/cli/discovery-observation-snapshot.mjs +44 -16
  28. package/cli/discovery-scan-command-runner.mjs +2 -0
  29. package/cli/discovery-scan.mjs +7 -4
  30. package/cli/discovery-workflow.mjs +1 -1
  31. package/cli/domain-control-monitor.mjs +50 -20
  32. package/cli/domain-control-observations.mjs +149 -102
  33. package/cli/errors.mjs +33 -1
  34. package/cli/evidence-command-runner.mjs +16 -5
  35. package/cli/evidence-signing.mjs +2 -2
  36. package/cli/external-observation-mapping.mjs +7 -8
  37. package/cli/formatters/terminal-lookup.mjs +18 -46
  38. package/cli/formatters/terminal-shared.mjs +5 -3
  39. package/cli/formatters/terminal-technology.mjs +114 -0
  40. package/cli/inline-command-families.mjs +1 -0
  41. package/cli/interactive-launcher.mjs +2 -160
  42. package/cli/interchange-report.mjs +4 -8
  43. package/cli/investigation-artifacts.mjs +176 -0
  44. package/cli/investigation-checkpoint.mjs +10 -0
  45. package/cli/investigation-folder.mjs +114 -0
  46. package/cli/investigation-manifest.mjs +1 -112
  47. package/cli/investigation-package-review.mjs +103 -0
  48. package/cli/investigation-plan.mjs +32 -19
  49. package/cli/investigation-run.mjs +188 -33
  50. package/cli/local-document-checkpoint.mjs +170 -0
  51. package/cli/lookup-browser.mjs +5 -12
  52. package/cli/lookup-command-runner.mjs +17 -6
  53. package/cli/lookup-diff.mjs +37 -3
  54. package/cli/lookup-plan.mjs +11 -5
  55. package/cli/lookup-reconcile.mjs +10 -8
  56. package/cli/mail-review.mjs +66 -14
  57. package/cli/manual.mjs +1 -0
  58. package/cli/network-command-runner.mjs +2 -0
  59. package/cli/output-file.mjs +31 -5
  60. package/cli/registry-doctor.mjs +3 -1
  61. package/cli/registry-support.mjs +1 -1
  62. package/cli/retained-artifact-diff.mjs +2 -2
  63. package/cli/review-command-runner.mjs +45 -0
  64. package/cli/runner.mjs +55 -6
  65. package/cli/sharing-review.mjs +21 -34
  66. package/cli/signer-trust.mjs +104 -0
  67. package/cli/source-reliability.mjs +74 -83
  68. package/cli/terminal-input.mjs +168 -0
  69. package/cli/workflow-command-runner.mjs +68 -13
  70. package/docs/cli-reference.md +76 -30
  71. package/docs/cli.md +289 -25
  72. package/lib/abort.mjs +4 -1
  73. package/lib/availability.mjs +161 -112
  74. package/lib/bounded-file.mjs +3 -1
  75. package/lib/bounded-html-document.mjs +125 -0
  76. package/lib/bounded-json.mjs +43 -3
  77. package/lib/browser-library-profile.mjs +42 -51
  78. package/lib/browser-library-worker.mjs +112 -0
  79. package/lib/client-behavior-profile.mjs +3 -3
  80. package/lib/ct-search.mjs +11 -4
  81. package/lib/dns-delegation-health.mjs +24 -14
  82. package/lib/dns-inheritance-review.mjs +217 -0
  83. package/lib/dns-intelligence.mjs +36 -23
  84. package/lib/dns-mx.mjs +1 -1
  85. package/lib/dnssec-evidence-validation.mjs +11 -3
  86. package/lib/domain-change-packet.mjs +7 -2
  87. package/lib/domain-change-review.mjs +14 -8
  88. package/lib/domain-control-flight-recorder.mjs +219 -118
  89. package/lib/domain-control-manifest.mjs +117 -57
  90. package/lib/domain-posture-analysis.mjs +20 -7
  91. package/lib/domain-posture-parsers.mjs +88 -6
  92. package/lib/domain-posture.mjs +113 -26
  93. package/lib/evidence-export-privacy.mjs +561 -0
  94. package/lib/evidence-export.mjs +11 -592
  95. package/lib/evidence-report.mjs +3 -1
  96. package/lib/favicon.mjs +117 -41
  97. package/lib/generated/cisa-kev-catalog.mjs +12 -4
  98. package/lib/generated/registrar-standing-catalogue.mjs +7 -7
  99. package/lib/generated/retire-browser-catalog.mjs +2335 -940
  100. package/lib/hostname.mjs +2 -38
  101. package/lib/html-signals.mjs +82 -85
  102. package/lib/http-evidence-bounds.mjs +2 -1
  103. package/lib/http-intelligence.mjs +39 -26
  104. package/lib/lookup-cache.mjs +34 -11
  105. package/lib/lookup-child-profile-contract.mjs +24 -15
  106. package/lib/lookup-diagnostics.mjs +24 -1
  107. package/lib/lookup-progress.mjs +30 -13
  108. package/lib/lookup-response-contract.mjs +1048 -0
  109. package/lib/lookup-response.mjs +7 -10
  110. package/lib/lookup-source-progress.mjs +17 -14
  111. package/lib/lookup.mjs +48 -27
  112. package/lib/observed-network-context.mjs +43 -27
  113. package/lib/outbound-request-bounds.mjs +4 -1
  114. package/lib/page-fingerprints.mjs +69 -133
  115. package/lib/page-role-profile.mjs +12 -8
  116. package/lib/perceptual-hash.mjs +43 -14
  117. package/lib/rdap-bootstrap.mjs +72 -22
  118. package/lib/rdap-client.mjs +15 -7
  119. package/lib/rdap-normalization.mjs +33 -18
  120. package/lib/rdap-search-workbench.mjs +1 -1
  121. package/lib/rdap-transport.mjs +47 -20
  122. package/lib/rdap-types.mjs +2 -1
  123. package/lib/rdap.mjs +18 -3
  124. package/lib/registrable-domain.mjs +21 -1
  125. package/lib/registrar-standing-contract.mjs +1 -0
  126. package/lib/registry-capabilities.mjs +5 -1
  127. package/lib/registry-capability-catalogue.mjs +2 -1
  128. package/lib/registry-dates.mjs +2 -190
  129. package/lib/registry-insights.mjs +56 -18
  130. package/lib/response-policy.mjs +86 -48
  131. package/lib/risk-calibration-summary.mjs +17 -3
  132. package/lib/rpki-evidence.mjs +8 -5
  133. package/lib/safe-fetch.mjs +54 -47
  134. package/lib/security-txt.mjs +10 -7
  135. package/lib/service-binding-dns.mjs +8 -0
  136. package/lib/smtp-transport-review.mjs +27 -15
  137. package/lib/sslbl-certificates.generated.mjs +136 -135
  138. package/lib/static-html-analysis.mjs +117 -146
  139. package/lib/structured-data-identity.mjs +1 -0
  140. package/lib/technology-markup.mjs +100 -0
  141. package/lib/threat-intelligence-manifests.mjs +11 -139
  142. package/lib/threat-intelligence-runtime.mjs +24 -52
  143. package/lib/threat-intelligence-targets.mjs +6 -34
  144. package/lib/threat-intelligence-types.mjs +39 -0
  145. package/lib/typosquat-generator.mjs +19 -15
  146. package/lib/urlhaus-intelligence.mjs +8 -3
  147. package/lib/urlscan-intelligence.mjs +1 -1
  148. package/lib/website-security-posture.mjs +30 -6
  149. package/lib/website-technology.mjs +111 -71
  150. package/lib/whois-authority.mjs +14 -2
  151. package/lib/whois-chain.mjs +9 -2
  152. package/lib/whois-common-formats.mjs +5 -5
  153. package/lib/whois-contracts.mjs +2 -1
  154. package/lib/whois-dialects.mjs +2 -2
  155. package/lib/whois-parser-support.mjs +8 -1
  156. package/lib/whois-transport.mjs +23 -7
  157. package/package.json +2 -1
  158. package/packages/cases/case-attachment-model.mjs +83 -0
  159. package/packages/cases/case-evidence-model.mjs +156 -62
  160. package/packages/cases/case-export-input.mjs +53 -0
  161. package/packages/cases/case-migration-model.mjs +59 -25
  162. package/packages/cases/case-model.mjs +1 -0
  163. package/packages/cases/case-recheck-model.mjs +91 -0
  164. package/packages/cases/case-record-core.mjs +3 -2
  165. package/packages/cases/case-record-operations.mjs +89 -27
  166. package/packages/cases/case-record-projection.mjs +23 -20
  167. package/packages/cases/case-report.mjs +63 -52
  168. package/packages/cases/case-response-actions.mjs +654 -0
  169. package/packages/cases/case-response-model.mjs +41 -1129
  170. package/packages/cases/case-response-outcomes.mjs +317 -0
  171. package/packages/cases/case-response-packet-render.mjs +153 -0
  172. package/packages/cases/case-response-packet-types.mjs +1 -0
  173. package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
  174. package/packages/cases/case-response-packet.mjs +38 -587
  175. package/packages/cases/case-response-records.mjs +94 -0
  176. package/packages/cases/case-response-review-inputs.mjs +371 -0
  177. package/packages/cases/case-response-values.mjs +85 -0
  178. package/packages/cases/case-review-package.mjs +45 -0
  179. package/packages/cases/case-selection.mjs +46 -0
  180. package/packages/cases/case-storage-model.mjs +25 -0
  181. package/packages/cases/response-route-freshness.mjs +16 -0
  182. package/packages/comparison/capture-context.mjs +61 -0
  183. package/packages/comparison/comparison-ledger-bulk.mjs +4 -1
  184. package/packages/comparison/page-similarity.mjs +2 -1
  185. package/packages/comparison/relationship-evidence.mjs +85 -8
  186. package/packages/comparison/relationship-provenance.mjs +93 -0
  187. package/packages/contracts/analyst-interchange.mjs +216 -0
  188. package/packages/contracts/analyst-review-state-contract.mjs +11 -0
  189. package/packages/contracts/analyst-review-state.mjs +5 -4
  190. package/packages/contracts/case-portability.mjs +279 -116
  191. package/packages/contracts/case-views-contract.mjs +12 -0
  192. package/packages/contracts/case-views.mjs +26 -0
  193. package/packages/contracts/cli-command-semantics.mjs +1 -0
  194. package/packages/contracts/cli-lookup.mjs +0 -1
  195. package/packages/contracts/domain-control-flight-recorder.mjs +80 -42
  196. package/packages/contracts/domain-control-manifest.mjs +127 -73
  197. package/packages/contracts/domain-control-monitor.mjs +48 -45
  198. package/packages/contracts/domain-control-review.mjs +179 -121
  199. package/packages/contracts/domain-name.mjs +21 -0
  200. package/packages/contracts/external-observation-interchange.mjs +2 -1
  201. package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
  202. package/packages/contracts/http-url.mjs +2 -0
  203. package/packages/contracts/investigation-package-limits.mjs +24 -0
  204. package/packages/contracts/investigation-portability.mjs +56 -27
  205. package/packages/contracts/investigation-run.mjs +16 -0
  206. package/packages/contracts/ip-address.mjs +34 -0
  207. package/packages/contracts/lookup-evidence.mjs +35 -0
  208. package/packages/contracts/offline-comparison.mjs +1 -2
  209. package/packages/contracts/page-fingerprints.mjs +15 -0
  210. package/packages/contracts/risk-calibration.mjs +0 -1
  211. package/packages/contracts/schema-compatibility.mjs +1 -1
  212. package/packages/contracts/schema-lifecycle.mjs +83 -153
  213. package/packages/contracts/selected-file-limits.mjs +6 -0
  214. package/packages/contracts/vulnerability-identifiers.mjs +5 -0
  215. package/packages/contracts/web-capture.mjs +10 -0
  216. package/packages/contracts/workspace-portability.mjs +160 -225
  217. package/packages/evidence/artifact-integrity.mjs +8 -2
  218. package/packages/evidence/artifact-structure.mjs +105 -0
  219. package/packages/evidence/decision-fact.mjs +4 -1
  220. package/packages/evidence/domain-control-runtime.mjs +155 -27
  221. package/packages/evidence/domain-posture-context.mjs +79 -0
  222. package/packages/evidence/image-regions.mjs +56 -0
  223. package/packages/evidence/latest-observations.mjs +28 -0
  224. package/packages/evidence/lookup-target.mjs +104 -0
  225. package/packages/evidence/observation.mjs +16 -1
  226. package/packages/evidence/passphrase-encryption.mjs +44 -0
  227. package/packages/evidence/record-identity.mjs +72 -0
  228. package/packages/evidence/registry-dates.mjs +190 -0
  229. package/packages/evidence/retained-file.mjs +69 -0
  230. package/packages/evidence/review-age.mjs +14 -0
  231. package/packages/interchange/bagit.mjs +342 -0
  232. package/packages/interchange/bounded-zip-extraction.mjs +278 -0
  233. package/packages/interchange/crc32.mjs +17 -0
  234. package/packages/interchange/external-findings-import.mjs +89 -49
  235. package/packages/interchange/web-capture-import.mjs +382 -0
  236. package/packages/investigation/bulk-mail-exposure.mjs +2 -1
  237. package/packages/investigation/investigation-bagit.mjs +14 -0
  238. package/packages/investigation/investigation-capsule.mjs +33 -5
  239. package/packages/investigation/investigation-manifest.mjs +200 -0
  240. package/packages/investigation/investigation-package-crypto.mjs +98 -0
  241. package/packages/investigation/investigation-package.mjs +200 -0
  242. package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
  243. package/packages/investigation/lookup-asset-graph.mjs +161 -141
  244. package/packages/investigation/lookup-claim-readiness.mjs +18 -0
  245. package/packages/investigation/lookup-investigation-brief.mjs +3 -3
  246. package/packages/investigation/lookup-task-guidance.mjs +58 -0
  247. package/packages/monitoring/analyst-review-state.mjs +38 -90
  248. package/packages/workspace/brand-profile-model.mjs +158 -20
  249. package/packages/workspace/bulk-columns.mjs +17 -0
  250. package/packages/workspace/bulk-review-model.mjs +11 -5
  251. package/packages/workspace/bulk-session-model.mjs +95 -20
  252. package/packages/workspace/bulk-sort.mjs +1 -0
  253. package/packages/workspace/case-views.mjs +102 -0
  254. package/packages/workspace/hostile-input.mjs +29 -8
  255. package/packages/workspace/investigation-template-model.mjs +19 -4
  256. package/packages/workspace/page-baseline.mjs +18 -15
  257. package/packages/workspace/relationship-observation-model.mjs +41 -26
  258. package/packages/workspace/watchlist-history.mjs +2 -2
  259. package/packages/workspace/watchlist-store.mjs +8 -2
  260. package/packages/workspace/website-snapshot-model.mjs +70 -30
  261. package/packages/workspace/workspace-archive-crypto.mjs +41 -61
  262. package/packages/workspace/workspace-archive.mjs +85 -21
  263. package/third-party-notices.txt +29 -2
  264. package/packages/investigation/bulk-review-export.mjs +0 -63
@@ -1,22 +1,25 @@
1
1
  import { WORKSPACE_ARCHIVE_SECTION_IDS } from "./case-portability.mjs";
2
+ export { PAGE_FINGERPRINT_VERSION, PAGE_FINGERPRINT_PARSERS } from "./page-fingerprints.mjs";
2
3
  import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
3
4
  import { defineSchemaLifecycleFamily, } from "./schema-lifecycle.mjs";
4
5
  export const WORKSPACE_CONTRACT_OWNER = 'packages/contracts/workspace-portability.mts';
5
6
  export const BRAND_PROFILE_SCHEMA = 'whoisleuth.brand-profiles';
6
7
  export const PUBLIC_BRAND_PROFILE_SCHEMA_VERSION = 6;
7
- export const LATEST_PUBLIC_BRAND_PROFILE_SCHEMA_VERSION = 7;
8
- export const BRAND_PROFILE_SCHEMA_VERSION = 8;
8
+ // Historical readers retain a fixed published identity when the writer moves.
9
+ export const PUBLISHED_V2_2_BRAND_PROFILE_SCHEMA_VERSION = 7;
10
+ export const BRAND_PROFILE_SCHEMA_VERSION = 9;
9
11
  export const BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS = Object.freeze([
10
12
  PUBLIC_BRAND_PROFILE_SCHEMA_VERSION,
11
- LATEST_PUBLIC_BRAND_PROFILE_SCHEMA_VERSION,
13
+ PUBLISHED_V2_2_BRAND_PROFILE_SCHEMA_VERSION,
14
+ 8,
12
15
  BRAND_PROFILE_SCHEMA_VERSION,
13
16
  ]);
14
17
  export const SUPPORTED_BRAND_PROFILE_SCHEMA_VERSIONS = BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS;
15
18
  export const MAX_PROFILES = 100;
16
19
  export const MAX_PROFILE_VALUES = 200;
17
20
  export const MAX_PROFILE_VALUE_INPUTS = MAX_PROFILE_VALUES * 4;
18
- export const MAX_PROFILE_STORE_BYTES = 1024 * 1024;
19
- export const MAX_PROFILE_IMPORT_BYTES = 2 * 1024 * 1024;
21
+ export const MAX_PROFILE_STORE_BYTES = 4 * 1024 * 1024;
22
+ export const MAX_PROFILE_IMPORT_BYTES = MAX_PROFILE_STORE_BYTES * 8;
20
23
  export const MAX_PROFILE_NAME_LENGTH = 100;
21
24
  export const MAX_PROFILE_TEXT_LENGTH = 200;
22
25
  export const MAX_PROFILE_DOMAIN_LENGTH = 253;
@@ -93,9 +96,9 @@ export const MAX_CONDITION_VALUE_LENGTH = 200;
93
96
  export const MAX_RULE_RISK_DELTA = 25;
94
97
  export const MAX_CUSTOM_RISK_TOTAL = 50;
95
98
  export const RELATIONSHIP_OBSERVATION_SCHEMA = 'whoisleuth.relationship-observations';
96
- export const RELATIONSHIP_OBSERVATION_SCHEMA_VERSION = 1;
97
- export const RELATIONSHIP_OBSERVATION_BROWSER_SUPPORTED_VERSIONS = Object.freeze([RELATIONSHIP_OBSERVATION_SCHEMA_VERSION]);
98
- export const RELATIONSHIP_OBSERVATION_EXPORT_SUPPORTED_VERSIONS = Object.freeze([RELATIONSHIP_OBSERVATION_SCHEMA_VERSION]);
99
+ export const RELATIONSHIP_OBSERVATION_SCHEMA_VERSION = 2;
100
+ export const RELATIONSHIP_OBSERVATION_BROWSER_SUPPORTED_VERSIONS = Object.freeze([1, RELATIONSHIP_OBSERVATION_SCHEMA_VERSION]);
101
+ export const RELATIONSHIP_OBSERVATION_EXPORT_SUPPORTED_VERSIONS = RELATIONSHIP_OBSERVATION_BROWSER_SUPPORTED_VERSIONS;
99
102
  export const MAX_RELATIONSHIP_OBSERVATIONS = 300;
100
103
  export const MAX_RELATIONSHIP_OBSERVATION_INPUTS = 1_200;
101
104
  export const MAX_RELATIONSHIP_OBSERVATION_DOMAINS = 50;
@@ -103,16 +106,16 @@ export const MAX_RELATIONSHIP_OBSERVATION_LIMITATIONS = 8;
103
106
  export const MAX_RELATIONSHIP_OBSERVATION_VALUE_LENGTH = 20_000;
104
107
  export const MAX_RELATIONSHIP_OBSERVATION_STORE_BYTES = 768 * 1024;
105
108
  export const WEBSITE_SNAPSHOT_SCHEMA = 'whoisleuth.website-profile-snapshots';
106
- export const WEBSITE_SNAPSHOT_SCHEMA_VERSION = 5;
107
- export const WEBSITE_SNAPSHOT_BROWSER_SUPPORTED_VERSIONS = Object.freeze([4, WEBSITE_SNAPSHOT_SCHEMA_VERSION]);
109
+ export const WEBSITE_SNAPSHOT_SCHEMA_VERSION = 6;
110
+ export const WEBSITE_SNAPSHOT_BROWSER_SUPPORTED_VERSIONS = Object.freeze([4, 5, WEBSITE_SNAPSHOT_SCHEMA_VERSION]);
108
111
  export const SUPPORTED_WEBSITE_SNAPSHOT_SCHEMA_VERSIONS = WEBSITE_SNAPSHOT_BROWSER_SUPPORTED_VERSIONS;
109
112
  export const MAX_WEBSITE_SNAPSHOTS = 60;
110
113
  export const MAX_WEBSITE_SNAPSHOTS_PER_DOMAIN = 12;
111
114
  export const MAX_WEBSITE_SNAPSHOT_STORE_BYTES = 512 * 1024;
112
115
  export const MAX_WEBSITE_SNAPSHOT_IMPORT_BYTES = 768 * 1024;
113
116
  export const BULK_SESSION_SCHEMA = 'whoisleuth.bulk-sessions';
114
- export const BULK_SESSION_SCHEMA_VERSION = 4;
115
- export const BULK_SESSION_BROWSER_SUPPORTED_VERSIONS = Object.freeze([BULK_SESSION_SCHEMA_VERSION]);
117
+ export const BULK_SESSION_SCHEMA_VERSION = 5;
118
+ export const BULK_SESSION_BROWSER_SUPPORTED_VERSIONS = Object.freeze([4, BULK_SESSION_SCHEMA_VERSION]);
116
119
  export const SUPPORTED_BULK_SESSION_SCHEMA_VERSIONS = BULK_SESSION_BROWSER_SUPPORTED_VERSIONS;
117
120
  export const MAX_BULK_SESSIONS = 10;
118
121
  export const MAX_BULK_SESSION_ROWS = 2_000;
@@ -129,9 +132,9 @@ export const MAX_INVESTIGATION_TEMPLATES = 20;
129
132
  export const MAX_INVESTIGATION_TEMPLATE_STORE_BYTES = 256 * 1024;
130
133
  export const MAX_INVESTIGATION_TEMPLATE_IMPORT_BYTES = 384 * 1024;
131
134
  export const BULK_REVIEW_SCHEMA = 'whoisleuth.bulk-review';
132
- export const BULK_REVIEW_SCHEMA_VERSION = 1;
133
- export const BULK_REVIEW_BROWSER_SUPPORTED_VERSIONS = Object.freeze([BULK_REVIEW_SCHEMA_VERSION]);
134
- export const BULK_REVIEW_EXPORT_SUPPORTED_VERSIONS = Object.freeze([BULK_REVIEW_SCHEMA_VERSION]);
135
+ export const BULK_REVIEW_SCHEMA_VERSION = 2;
136
+ export const BULK_REVIEW_BROWSER_SUPPORTED_VERSIONS = Object.freeze([1, BULK_REVIEW_SCHEMA_VERSION]);
137
+ export const BULK_REVIEW_EXPORT_SUPPORTED_VERSIONS = Object.freeze([1, BULK_REVIEW_SCHEMA_VERSION]);
135
138
  export const MAX_BULK_REVIEW_PRESETS = 24;
136
139
  export const MAX_BULK_REVIEW_ROWS = 1_900;
137
140
  export const MAX_BULK_REVIEW_STORE_BYTES = 512 * 1024;
@@ -143,7 +146,6 @@ export const MAX_WORKSPACE_INPUT_OBJECT_KEYS = 4_096;
143
146
  export const MAX_WORKSPACE_INPUT_STRING_CODE_UNITS = 8 * 1024 * 1024;
144
147
  export const PAGE_BASELINE_VERSION = 1;
145
148
  export const PAGE_IDENTITY_VERSION = 3;
146
- export const PAGE_FINGERPRINT_VERSION = 1;
147
149
  export const MAX_BASELINE_TITLE_LENGTH = 200;
148
150
  export const MAX_BASELINE_RESOURCE_HOSTS = 30;
149
151
  export const MAX_BASELINE_IDENTIFIERS = 30;
@@ -159,7 +161,7 @@ export const BULK_SESSION_BROWSER_STORE_LIFECYCLE_SCHEMA = 'whoisleuth.browser.b
159
161
  export const INVESTIGATION_TEMPLATE_BROWSER_STORE_LIFECYCLE_SCHEMA = 'whoisleuth.browser.investigation-template-store';
160
162
  export const BULK_REVIEW_BROWSER_STORE_LIFECYCLE_SCHEMA = 'whoisleuth.browser.bulk-review-store';
161
163
  export const WORKSPACE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
162
- 'BRAND_PROFILE_SCHEMA', 'PUBLIC_BRAND_PROFILE_SCHEMA_VERSION', 'LATEST_PUBLIC_BRAND_PROFILE_SCHEMA_VERSION', 'BRAND_PROFILE_SCHEMA_VERSION', 'BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS', 'SUPPORTED_BRAND_PROFILE_SCHEMA_VERSIONS',
164
+ 'BRAND_PROFILE_SCHEMA', 'PUBLIC_BRAND_PROFILE_SCHEMA_VERSION', 'PUBLISHED_V2_2_BRAND_PROFILE_SCHEMA_VERSION', 'BRAND_PROFILE_SCHEMA_VERSION', 'BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS', 'SUPPORTED_BRAND_PROFILE_SCHEMA_VERSIONS',
163
165
  'CAMPAIGN_SCHEMA', 'CAMPAIGN_SCHEMA_VERSION', 'CAMPAIGN_BROWSER_SUPPORTED_VERSIONS', 'CAMPAIGN_EXPORT_SUPPORTED_VERSIONS',
164
166
  'WATCHLIST_SCHEMA', 'WATCHLIST_SCHEMA_VERSION', 'WATCHLIST_BROWSER_SUPPORTED_VERSIONS', 'WATCHLIST_EXPORT_SUPPORTED_VERSIONS',
165
167
  'SHORTLIST_SCHEMA', 'SHORTLIST_SCHEMA_VERSION', 'SHORTLIST_BROWSER_SUPPORTED_VERSIONS', 'SUPPORTED_SHORTLIST_SCHEMA_VERSIONS',
@@ -200,7 +202,7 @@ export const WORKSPACE_PORTABILITY_BOUND_CONSTANTS = Object.freeze([
200
202
  'MAX_BULK_REVIEW_PRESETS', 'MAX_BULK_REVIEW_ROWS', 'MAX_BULK_REVIEW_STORE_BYTES', 'MAX_BULK_REVIEW_NAME_LENGTH',
201
203
  'MAX_WORKSPACE_INPUT_GRAPH_DEPTH', 'MAX_WORKSPACE_INPUT_GRAPH_NODES', 'MAX_WORKSPACE_INPUT_ARRAY_LENGTH',
202
204
  'MAX_WORKSPACE_INPUT_OBJECT_KEYS', 'MAX_WORKSPACE_INPUT_STRING_CODE_UNITS',
203
- 'PAGE_BASELINE_VERSION', 'PAGE_IDENTITY_VERSION', 'PAGE_FINGERPRINT_VERSION', 'MAX_BASELINE_TITLE_LENGTH',
205
+ 'PAGE_BASELINE_VERSION', 'PAGE_IDENTITY_VERSION', 'PAGE_FINGERPRINT_VERSION', 'PAGE_FINGERPRINT_PARSERS', 'MAX_BASELINE_TITLE_LENGTH',
204
206
  'MAX_BASELINE_RESOURCE_HOSTS', 'MAX_BASELINE_IDENTIFIERS',
205
207
  ]);
206
208
  export const WORKSPACE_DOMAIN_COMPATIBILITY_FACADES = Object.freeze([
@@ -227,7 +229,7 @@ export const BRAND_PROFILE_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
227
229
  supportedVersions: BRAND_PROFILE_BROWSER_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
228
230
  futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current', writeSemantics: 'normalized_rewrite',
229
231
  byteBudget: MAX_PROFILE_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
230
- note: 'Supported version 6 and 7 profiles migrate directly to version 8. Version 7 gives each approved change window a durable opaque identity; version 8 adds bounded official-channel and rights-reference records.',
232
+ note: 'Published versions 6, 7 and 8 migrate directly to the current profile envelope. Version 9 preserves earlier page baselines while admitting independently versioned native-tree fingerprints; incompatible fingerprint algorithms are not compared.',
231
233
  });
232
234
  export const CAMPAIGN_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
233
235
  id: 'browser.campaigns', kind: 'browser_store', schema: null, currentVersion: CAMPAIGN_SCHEMA_VERSION,
@@ -267,23 +269,23 @@ export const DETECTION_RULE_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
267
269
  export const RELATIONSHIP_OBSERVATION_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
268
270
  id: 'browser.relationship-observations', kind: 'browser_store', schema: null,
269
271
  currentVersion: RELATIONSHIP_OBSERVATION_SCHEMA_VERSION, supportedVersions: RELATIONSHIP_OBSERVATION_BROWSER_SUPPORTED_VERSIONS,
270
- acceptsUnversionedLegacy: false, futureVersionBehavior: 'preserve_without_write', migration: 'exact_current_only',
272
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current',
271
273
  writeSemantics: 'normalized_rewrite', byteBudget: MAX_RELATIONSHIP_OBSERVATION_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
272
- note: 'Only explicit analyst selections from bounded Bulk relationship evidence are retained; identities are re-derived from normalised values and members.',
274
+ note: 'Public version 1 observations migrate with unknown contributing-source provenance. Version 2 retains bounded per-domain source states and times; identities remain derived from normalised values and members.',
273
275
  });
274
276
  export const WEBSITE_SNAPSHOT_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
275
277
  id: 'browser.website-snapshots', kind: 'browser_store', schema: null,
276
278
  currentVersion: WEBSITE_SNAPSHOT_SCHEMA_VERSION, supportedVersions: WEBSITE_SNAPSHOT_BROWSER_SUPPORTED_VERSIONS,
277
279
  acceptsUnversionedLegacy: false, futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current',
278
280
  writeSemantics: 'normalized_rewrite', byteBudget: MAX_WEBSITE_SNAPSHOT_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
279
- note: 'Public version 4 records normalise to version 5 with explicit unknown profile provenance; raw lookup payloads, contact data, certificate bytes, and fetched bodies remain excluded.',
281
+ note: 'Public versions 4 and 5 retain their original fingerprint algorithm on migration. Current snapshots record fingerprint provenance so parser changes cannot become target changes; raw lookup payloads, contact data, certificate bytes and fetched bodies remain excluded.',
280
282
  });
281
283
  export const BULK_SESSION_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
282
284
  id: 'browser.bulk-sessions', kind: 'browser_store', schema: null, currentVersion: BULK_SESSION_SCHEMA_VERSION,
283
285
  supportedVersions: BULK_SESSION_BROWSER_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
284
- futureVersionBehavior: 'preserve_without_write', migration: 'exact_current_only', writeSemantics: 'normalized_rewrite',
286
+ futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current', writeSemantics: 'normalized_rewrite',
285
287
  byteBudget: MAX_BULK_SESSION_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
286
- note: 'The unchanged public schema 4 writer is the exact browser baseline with bounded Brand Profile provenance; unsupported local checkpoints are preserved without rewrite.',
288
+ note: 'Public schema 4 sessions migrate with unknown row and per-source observation times and contributing-source provenance. Schema 5 retains those bounded fields without retaining raw lookup payloads; future versions are preserved without rewrite.',
287
289
  });
288
290
  export const INVESTIGATION_TEMPLATE_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
289
291
  id: 'browser.investigation-templates', kind: 'browser_store', schema: null,
@@ -295,7 +297,7 @@ export const INVESTIGATION_TEMPLATE_BROWSER_COMPATIBILITY = defineSchemaCompatib
295
297
  export const BULK_REVIEW_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
296
298
  id: 'browser.bulk-review', kind: 'browser_store', schema: null, currentVersion: BULK_REVIEW_SCHEMA_VERSION,
297
299
  supportedVersions: BULK_REVIEW_BROWSER_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
298
- futureVersionBehavior: 'preserve_without_write', migration: 'exact_current_only', writeSemantics: 'normalized_rewrite',
300
+ futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current', writeSemantics: 'normalized_rewrite',
299
301
  byteBudget: MAX_BULK_REVIEW_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
300
302
  note: 'Bounded saved Bulk filter views and per-domain review states only; scan results, contacts, notes, and case disposition remain separate.',
301
303
  });
@@ -337,23 +339,23 @@ export const DETECTION_RULE_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
337
339
  export const RELATIONSHIP_OBSERVATION_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
338
340
  id: 'export.relationship-observations', kind: 'export', schema: RELATIONSHIP_OBSERVATION_SCHEMA,
339
341
  currentVersion: RELATIONSHIP_OBSERVATION_SCHEMA_VERSION, supportedVersions: RELATIONSHIP_OBSERVATION_EXPORT_SUPPORTED_VERSIONS,
340
- acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only',
342
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'normalize_to_current',
341
343
  writeSemantics: 'non_destructive_merge', byteBudget: MAX_RELATIONSHIP_OBSERVATION_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
342
- note: 'Workspace-archive section for bounded analyst-selected derived pivots; raw scan and lookup responses are excluded.',
344
+ note: 'Version 1 archive sections migrate to version 2 without inventing source provenance. Bounded contributing-source states and times survive current export; raw lookup responses remain excluded.',
343
345
  });
344
346
  export const WEBSITE_SNAPSHOT_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
345
347
  id: 'export.website-snapshots', kind: 'export', schema: WEBSITE_SNAPSHOT_SCHEMA,
346
348
  currentVersion: WEBSITE_SNAPSHOT_SCHEMA_VERSION, supportedVersions: SUPPORTED_WEBSITE_SNAPSHOT_SCHEMA_VERSIONS,
347
349
  acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'normalize_to_current',
348
350
  writeSemantics: 'non_destructive_merge', byteBudget: MAX_WEBSITE_SNAPSHOT_IMPORT_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
349
- note: 'Public schema 4 archive sections normalise to version 5 with explicit unknown profile provenance and merge non-destructively without retaining fetched bodies.',
351
+ note: 'Public schema 4 and 5 sections migrate non-destructively with preserved fingerprint provenance. Unknown detector versions remain unknown; fetched bodies remain excluded.',
350
352
  });
351
353
  export const BULK_SESSION_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
352
354
  id: 'export.bulk-sessions', kind: 'export', schema: BULK_SESSION_SCHEMA, currentVersion: BULK_SESSION_SCHEMA_VERSION,
353
355
  supportedVersions: SUPPORTED_BULK_SESSION_SCHEMA_VERSIONS, acceptsUnversionedLegacy: false,
354
- futureVersionBehavior: 'reject', migration: 'exact_current_only', writeSemantics: 'non_destructive_merge',
356
+ futureVersionBehavior: 'reject', migration: 'normalize_to_current', writeSemantics: 'non_destructive_merge',
355
357
  byteBudget: MAX_BULK_SESSION_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
356
- note: 'The unchanged public schema 4 portable sessions retain explicit source states, comparison limitations, and bounded profile-context provenance.',
358
+ note: 'Public schema 4 sessions migrate directly to schema 5 with unknown historical row and per-source observation times and source provenance. Current exports retain source-qualified relationship evidence and bounded profile context.',
357
359
  });
358
360
  export const INVESTIGATION_TEMPLATE_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
359
361
  id: 'export.investigation-templates', kind: 'export', schema: INVESTIGATION_TEMPLATE_SCHEMA,
@@ -365,7 +367,7 @@ export const INVESTIGATION_TEMPLATE_EXPORT_COMPATIBILITY = defineSchemaCompatibi
365
367
  export const BULK_REVIEW_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
366
368
  id: 'export.bulk-review', kind: 'export', schema: BULK_REVIEW_SCHEMA, currentVersion: BULK_REVIEW_SCHEMA_VERSION,
367
369
  supportedVersions: BULK_REVIEW_EXPORT_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
368
- futureVersionBehavior: 'reject', migration: 'exact_current_only', writeSemantics: 'non_destructive_merge',
370
+ futureVersionBehavior: 'reject', migration: 'normalize_to_current', writeSemantics: 'non_destructive_merge',
369
371
  byteBudget: MAX_BULK_REVIEW_STORE_BYTES, owner: WORKSPACE_CONTRACT_OWNER,
370
372
  note: 'Workspace-archive section for saved Bulk views and explicit review states; network collection and case disposition are never changed by import.',
371
373
  });
@@ -652,378 +654,296 @@ const WORKSPACE_LIFECYCLE_FIXTURE_SOURCE = Object.freeze([
652
654
  "path": "test/fixtures/workspace-lifecycle/browser-brand-v6.json",
653
655
  "bytes": 37,
654
656
  "sha256": "624d0b889df19b0f1abfd4026a4dd868003188ed4f4294f6e912589255847df9",
655
- "contentDigestSha256": null,
656
657
  "schema": "whoisleuth.browser.brand-profile-store",
657
- "version": 6,
658
- "role": "historical",
659
- "expectation": "normalises_to_current_output",
660
- "expectedOutputFixtureId": "workspace.browser.brand.v8",
661
- "scope": "repository",
662
- "shapeId": "workspace.browser.brand.shape"
658
+ "version": 6
663
659
  },
664
660
  {
665
661
  "id": "workspace.browser.brand.v7",
666
662
  "path": "test/fixtures/workspace-lifecycle/browser-brand-v7.json",
667
663
  "bytes": 37,
668
664
  "sha256": "c866b2aff3dad05236114e96d3997a1c6af571658b38faa6aeb3c224d125a46b",
669
- "contentDigestSha256": null,
670
665
  "schema": "whoisleuth.browser.brand-profile-store",
671
- "version": 7,
672
- "role": "historical",
673
- "expectation": "normalises_to_current_output",
674
- "expectedOutputFixtureId": "workspace.browser.brand.v8",
675
- "scope": "repository",
676
- "shapeId": "workspace.browser.brand.shape"
666
+ "version": 7
677
667
  },
678
668
  {
679
669
  "id": "workspace.browser.brand.v8",
680
670
  "path": "test/fixtures/workspace-lifecycle/browser-brand-v8.json",
681
671
  "bytes": 37,
682
672
  "sha256": "6c2310d9da626cb8acd88a47ccb60fa46051a63b23675cdc4801794ec5cc52e3",
683
- "contentDigestSha256": null,
684
673
  "schema": "whoisleuth.browser.brand-profile-store",
685
- "version": 8,
686
- "role": "current",
687
- "expectation": "accepted_exact",
688
- "expectedOutputFixtureId": null,
689
- "scope": "repository",
690
- "shapeId": "workspace.browser.brand.shape"
674
+ "version": 8
691
675
  },
692
676
  {
693
677
  "id": "workspace.browser.bulk.v4",
694
678
  "path": "test/fixtures/workspace-lifecycle/browser-bulk-v4.json",
695
679
  "bytes": 77,
696
680
  "sha256": "605442d181a35a1809edb2ff0b5be17920d52f04276cacaf1786dcc41d3c96ec",
697
- "contentDigestSha256": null,
698
681
  "schema": "whoisleuth.browser.bulk-session-store",
699
- "version": 4,
700
- "role": "current",
701
- "expectation": "accepted_exact",
702
- "expectedOutputFixtureId": null,
703
- "scope": "repository",
704
- "shapeId": "workspace.browser.bulk.shape"
682
+ "version": 4
683
+ },
684
+ {
685
+ "id": "workspace.browser.bulk.v5",
686
+ "path": "test/fixtures/workspace-lifecycle/browser-bulk-v5.json",
687
+ "bytes": 77,
688
+ "sha256": "fca31e3190ff54669c06e14e9785f6d2e504ccbf837eb57caa881b5a8874ebfe",
689
+ "schema": "whoisleuth.browser.bulk-session-store",
690
+ "version": 5
705
691
  },
706
692
  {
707
693
  "id": "workspace.browser.campaign.v1",
708
694
  "path": "test/fixtures/workspace-lifecycle/browser-campaign-v1.json",
709
695
  "bytes": 38,
710
696
  "sha256": "c9f2a2f619c6237230e609a68c895c819472eba644ac4185b3eac3af256c7709",
711
- "contentDigestSha256": null,
712
697
  "schema": "whoisleuth.browser.campaign-store",
713
- "version": 1,
714
- "role": "current",
715
- "expectation": "accepted_exact",
716
- "expectedOutputFixtureId": null,
717
- "scope": "repository",
718
- "shapeId": "workspace.browser.campaign.shape"
698
+ "version": 1
719
699
  },
720
700
  {
721
701
  "id": "workspace.browser.ct.v3",
722
702
  "path": "test/fixtures/workspace-lifecycle/browser-ct-v3.json",
723
703
  "bytes": 36,
724
704
  "sha256": "48e7de97a1b64878f450f12238f7c9f5a41b85e07db3b86b0bc95ca5a5630685",
725
- "contentDigestSha256": null,
726
705
  "schema": "whoisleuth.browser.ct-history-store",
727
- "version": 3,
728
- "role": "current",
729
- "expectation": "accepted_exact",
730
- "expectedOutputFixtureId": null,
731
- "scope": "repository",
732
- "shapeId": "workspace.browser.ct.shape"
706
+ "version": 3
733
707
  },
734
708
  {
735
709
  "id": "workspace.browser.detection.v1",
736
710
  "path": "test/fixtures/workspace-lifecycle/browser-detection-v1.json",
737
711
  "bytes": 34,
738
712
  "sha256": "8772df49af2416bdf2f4881dd6f70452b3a0a8c31cf93f4aa1d7cb40ba0537a4",
739
- "contentDigestSha256": null,
740
713
  "schema": "whoisleuth.browser.detection-rule-store",
741
- "version": 1,
742
- "role": "current",
743
- "expectation": "accepted_exact",
744
- "expectedOutputFixtureId": null,
745
- "scope": "repository",
746
- "shapeId": "workspace.browser.detection.shape"
714
+ "version": 1
747
715
  },
748
716
  {
749
717
  "id": "workspace.browser.relationship.v1",
750
718
  "path": "test/fixtures/workspace-lifecycle/browser-relationship-v1.json",
751
719
  "bytes": 41,
752
720
  "sha256": "d26f28d5d0a654c2b2eab004f2e1a72c05845f433e8b47716f416b7271142668",
753
- "contentDigestSha256": null,
754
721
  "schema": "whoisleuth.browser.relationship-observation-store",
755
- "version": 1,
756
- "role": "current",
757
- "expectation": "accepted_exact",
758
- "expectedOutputFixtureId": null,
759
- "scope": "repository",
760
- "shapeId": "workspace.browser.relationship.shape"
722
+ "version": 1
723
+ },
724
+ {
725
+ "id": "workspace.browser.relationship.v2",
726
+ "path": "test/fixtures/workspace-lifecycle/browser-relationship-v2.json",
727
+ "bytes": 41,
728
+ "sha256": "951dbf0861636b4cf1389e954eb55e81e19dd94a6bf83236ed127f288788f564",
729
+ "schema": "whoisleuth.browser.relationship-observation-store",
730
+ "version": 2
761
731
  },
762
732
  {
763
733
  "id": "workspace.browser.review.v1",
764
734
  "path": "test/fixtures/workspace-lifecycle/browser-review-v1.json",
765
735
  "bytes": 88,
766
736
  "sha256": "9da86165eee183509ad3770b4f7bcf5296040c16ac108024e10b97c54c653609",
767
- "contentDigestSha256": null,
768
737
  "schema": "whoisleuth.browser.bulk-review-store",
769
- "version": 1,
770
- "role": "current",
771
- "expectation": "accepted_exact",
772
- "expectedOutputFixtureId": null,
773
- "scope": "repository",
774
- "shapeId": "workspace.browser.review.shape"
738
+ "version": 1
739
+ },
740
+ {
741
+ "id": "workspace.browser.review.v2",
742
+ "path": "test/fixtures/workspace-lifecycle/browser-review-v2.json",
743
+ "bytes": 88,
744
+ "sha256": "2e22d70101443016b12803835d29ce35cd61a513c70976af1fca1c5de18e222d",
745
+ "schema": "whoisleuth.browser.bulk-review-store",
746
+ "version": 2
775
747
  },
776
748
  {
777
749
  "id": "workspace.browser.shortlist.v3",
778
750
  "path": "test/fixtures/workspace-lifecycle/browser-shortlist-v3.json",
779
751
  "bytes": 72,
780
752
  "sha256": "8ee7f00e2cdd899893549823df9699cf800a392e8a74cbe2907d2a8b2340ebca",
781
- "contentDigestSha256": null,
782
753
  "schema": "whoisleuth.browser.shortlist-store",
783
- "version": 3,
784
- "role": "current",
785
- "expectation": "accepted_exact",
786
- "expectedOutputFixtureId": null,
787
- "scope": "repository",
788
- "shapeId": "workspace.browser.shortlist.shape"
754
+ "version": 3
789
755
  },
790
756
  {
791
757
  "id": "workspace.browser.template.v2",
792
758
  "path": "test/fixtures/workspace-lifecycle/browser-template-v2.json",
793
759
  "bytes": 88,
794
760
  "sha256": "52c1a5a671bf3bd0d052fb7c75d746ed3527898b18ba6532fa9103bd240920aa",
795
- "contentDigestSha256": null,
796
761
  "schema": "whoisleuth.browser.investigation-template-store",
797
- "version": 2,
798
- "role": "current",
799
- "expectation": "accepted_exact",
800
- "expectedOutputFixtureId": null,
801
- "scope": "repository",
802
- "shapeId": "workspace.browser.template.shape"
762
+ "version": 2
803
763
  },
804
764
  {
805
765
  "id": "workspace.browser.watchlist.v2",
806
766
  "path": "test/fixtures/workspace-lifecycle/browser-watchlist-v2.json",
807
767
  "bytes": 76,
808
768
  "sha256": "230e7b6c83659a096e2ccd0da251558088f38b24e438c713bba1012fea788b2c",
809
- "contentDigestSha256": null,
810
769
  "schema": "whoisleuth.browser.watchlist-store",
811
- "version": 2,
812
- "role": "current",
813
- "expectation": "accepted_exact",
814
- "expectedOutputFixtureId": null,
815
- "scope": "repository",
816
- "shapeId": "workspace.browser.watchlist.shape"
770
+ "version": 2
817
771
  },
818
772
  {
819
773
  "id": "workspace.browser.website.v4",
820
774
  "path": "test/fixtures/workspace-lifecycle/browser-website-v4.json",
821
775
  "bytes": 90,
822
776
  "sha256": "b6f1aa0f80fa902c085d831c7bb3334b7c95db49906ffc8dd452173f423b85a4",
823
- "contentDigestSha256": null,
824
777
  "schema": "whoisleuth.browser.website-snapshot-store",
825
- "version": 4,
826
- "role": "historical",
827
- "expectation": "normalises_to_current_output",
828
- "expectedOutputFixtureId": "workspace.browser.website.v5",
829
- "scope": "repository",
830
- "shapeId": "workspace.browser.website.shape"
778
+ "version": 4
831
779
  },
832
780
  {
833
781
  "id": "workspace.browser.website.v5",
834
782
  "path": "test/fixtures/workspace-lifecycle/browser-website-v5.json",
835
783
  "bytes": 90,
836
784
  "sha256": "16550a8c24d5c7bcfe271086071fee7f3392279990cc6fe91e3b9c95f07d0759",
837
- "contentDigestSha256": null,
838
785
  "schema": "whoisleuth.browser.website-snapshot-store",
839
- "version": 5,
840
- "role": "current",
841
- "expectation": "accepted_exact",
842
- "expectedOutputFixtureId": null,
843
- "scope": "repository",
844
- "shapeId": "workspace.browser.website.shape"
786
+ "version": 5
845
787
  },
846
788
  {
847
789
  "id": "workspace.portable.brand.v6",
848
790
  "path": "test/fixtures/workspace-lifecycle/portable-brand-v6.json",
849
791
  "bytes": 122,
850
792
  "sha256": "19e8ab9772bcb07d989249576feab1d71a1a62d401a1a1a7cecf1c65cd7d5f80",
851
- "contentDigestSha256": null,
852
793
  "schema": "whoisleuth.brand-profiles",
853
- "version": 6,
854
- "role": "historical",
855
- "expectation": "normalises_to_current_output",
856
- "expectedOutputFixtureId": "workspace.portable.brand.v8",
857
- "scope": "repository",
858
- "shapeId": "workspace.portable.brand.shape"
794
+ "version": 6
859
795
  },
860
796
  {
861
797
  "id": "workspace.portable.brand.v7",
862
798
  "path": "test/fixtures/workspace-lifecycle/portable-brand-v7.json",
863
799
  "bytes": 122,
864
800
  "sha256": "49db0b0a2d5c00da9dc55fd5b03702d7412550e17329b917567acf6c4ec3ebb9",
865
- "contentDigestSha256": null,
866
801
  "schema": "whoisleuth.brand-profiles",
867
- "version": 7,
868
- "role": "historical",
869
- "expectation": "normalises_to_current_output",
870
- "expectedOutputFixtureId": "workspace.portable.brand.v8",
871
- "scope": "repository",
872
- "shapeId": "workspace.portable.brand.shape"
802
+ "version": 7
873
803
  },
874
804
  {
875
805
  "id": "workspace.portable.brand.v8",
876
806
  "path": "test/fixtures/workspace-lifecycle/portable-brand-v8.json",
877
807
  "bytes": 122,
878
808
  "sha256": "4e4af17a0e19cb841ea50fe150caa3de79b978311b51a334ed7f6f3d2e1c6a1c",
879
- "contentDigestSha256": null,
880
809
  "schema": "whoisleuth.brand-profiles",
881
- "version": 8,
882
- "role": "current",
883
- "expectation": "accepted_exact",
884
- "expectedOutputFixtureId": null,
885
- "scope": "repository",
886
- "shapeId": "workspace.portable.brand.shape"
810
+ "version": 8
887
811
  },
888
812
  {
889
813
  "id": "workspace.portable.bulk.v4",
890
814
  "path": "test/fixtures/workspace-lifecycle/portable-bulk-v4.json",
891
815
  "bytes": 338,
892
816
  "sha256": "5b3adc2e58c2e47399cf8c0634225dd6cad18d6189d55520603b914a6e719785",
893
- "contentDigestSha256": null,
894
817
  "schema": "whoisleuth.bulk-sessions",
895
- "version": 4,
896
- "role": "current",
897
- "expectation": "accepted_exact",
898
- "expectedOutputFixtureId": null,
899
- "scope": "repository",
900
- "shapeId": "workspace.portable.bulk.shape"
818
+ "version": 4
819
+ },
820
+ {
821
+ "id": "workspace.portable.bulk.v5",
822
+ "path": "test/fixtures/workspace-lifecycle/portable-bulk-v5.json",
823
+ "bytes": 338,
824
+ "sha256": "c58b685ef707b713a08c5ae5986128d2ed1162646f84d926316de194b2b67232",
825
+ "schema": "whoisleuth.bulk-sessions",
826
+ "version": 5
901
827
  },
902
828
  {
903
829
  "id": "workspace.portable.campaign.v1",
904
830
  "path": "test/fixtures/workspace-lifecycle/portable-campaign-v1.json",
905
831
  "bytes": 281,
906
832
  "sha256": "2a0618a9f639cd8375eb46f9bdd5fc9fbb5d8f9ce347f1b93c2d760a91162ccd",
907
- "contentDigestSha256": null,
908
833
  "schema": "whoisleuth.campaigns",
909
- "version": 1,
910
- "role": "current",
911
- "expectation": "accepted_exact",
912
- "expectedOutputFixtureId": null,
913
- "scope": "repository",
914
- "shapeId": "workspace.portable.campaign.shape"
834
+ "version": 1
915
835
  },
916
836
  {
917
837
  "id": "workspace.portable.detection.v1",
918
838
  "path": "test/fixtures/workspace-lifecycle/portable-detection-v1.json",
919
839
  "bytes": 293,
920
840
  "sha256": "0c1306057547a5bf4a5c8c37f5bfaf9195a15a4971adc8e5c9085e8e87946508",
921
- "contentDigestSha256": null,
922
841
  "schema": "whoisleuth.detection-rules",
923
- "version": 1,
924
- "role": "current",
925
- "expectation": "accepted_exact",
926
- "expectedOutputFixtureId": null,
927
- "scope": "repository",
928
- "shapeId": "workspace.portable.detection.shape"
842
+ "version": 1
929
843
  },
930
844
  {
931
845
  "id": "workspace.portable.relationship.v1",
932
846
  "path": "test/fixtures/workspace-lifecycle/portable-relationship-v1.json",
933
847
  "bytes": 363,
934
848
  "sha256": "d16411297ec5be4eb0c87dbc36e1ed34e55ef3699084af2d5f18d76b042d398a",
935
- "contentDigestSha256": null,
936
849
  "schema": "whoisleuth.relationship-observations",
937
- "version": 1,
938
- "role": "current",
939
- "expectation": "accepted_exact",
940
- "expectedOutputFixtureId": null,
941
- "scope": "repository",
942
- "shapeId": "workspace.portable.relationship.shape"
850
+ "version": 1
851
+ },
852
+ {
853
+ "id": "workspace.portable.relationship.v2",
854
+ "path": "test/fixtures/workspace-lifecycle/portable-relationship-v2.json",
855
+ "bytes": 363,
856
+ "sha256": "28322f41c059a060e77673e4aad0d9d5b7b1f6021eefcf5bfed9426588345fdf",
857
+ "schema": "whoisleuth.relationship-observations",
858
+ "version": 2
943
859
  },
944
860
  {
945
861
  "id": "workspace.portable.review.v1",
946
862
  "path": "test/fixtures/workspace-lifecycle/portable-review-v1.json",
947
863
  "bytes": 88,
948
864
  "sha256": "9da86165eee183509ad3770b4f7bcf5296040c16ac108024e10b97c54c653609",
949
- "contentDigestSha256": null,
950
865
  "schema": "whoisleuth.bulk-review",
951
- "version": 1,
952
- "role": "current",
953
- "expectation": "accepted_exact",
954
- "expectedOutputFixtureId": null,
955
- "scope": "repository",
956
- "shapeId": "workspace.portable.review.shape"
866
+ "version": 1
867
+ },
868
+ {
869
+ "id": "workspace.portable.review.v2",
870
+ "path": "test/fixtures/workspace-lifecycle/portable-review-v2.json",
871
+ "bytes": 88,
872
+ "sha256": "2e22d70101443016b12803835d29ce35cd61a513c70976af1fca1c5de18e222d",
873
+ "schema": "whoisleuth.bulk-review",
874
+ "version": 2
957
875
  },
958
876
  {
959
877
  "id": "workspace.portable.shortlist.v3",
960
878
  "path": "test/fixtures/workspace-lifecycle/portable-shortlist-v3.json",
961
879
  "bytes": 116,
962
880
  "sha256": "282569f92d9dc0cd0ef3fdca985ba066e7003477398c2aea64149a036e00e627",
963
- "contentDigestSha256": null,
964
881
  "schema": "whoisleuth.shortlist",
965
- "version": 3,
966
- "role": "current",
967
- "expectation": "accepted_exact",
968
- "expectedOutputFixtureId": null,
969
- "scope": "repository",
970
- "shapeId": "workspace.portable.shortlist.shape"
882
+ "version": 3
971
883
  },
972
884
  {
973
885
  "id": "workspace.portable.template.v2",
974
886
  "path": "test/fixtures/workspace-lifecycle/portable-template-v2.json",
975
887
  "bytes": 471,
976
888
  "sha256": "827aaa27e86cc2e22247f5709fb0df8fa92226ae0691750d7bfdb9a85ca6e6b5",
977
- "contentDigestSha256": null,
978
889
  "schema": "whoisleuth.investigation-templates",
979
- "version": 2,
980
- "role": "current",
981
- "expectation": "accepted_exact",
982
- "expectedOutputFixtureId": null,
983
- "scope": "repository",
984
- "shapeId": "workspace.portable.template.shape"
890
+ "version": 2
985
891
  },
986
892
  {
987
893
  "id": "workspace.portable.watchlist.v2",
988
894
  "path": "test/fixtures/workspace-lifecycle/portable-watchlist-v2.json",
989
895
  "bytes": 120,
990
896
  "sha256": "1176a691294968af187a383839212a75dedbc0089b7a23bbb3d29a079f91e29b",
991
- "contentDigestSha256": null,
992
897
  "schema": "whoisleuth.watchlists",
993
- "version": 2,
994
- "role": "current",
995
- "expectation": "accepted_exact",
996
- "expectedOutputFixtureId": null,
997
- "scope": "repository",
998
- "shapeId": "workspace.portable.watchlist.shape"
898
+ "version": 2
999
899
  },
1000
900
  {
1001
901
  "id": "workspace.portable.website.v4",
1002
902
  "path": "test/fixtures/workspace-lifecycle/portable-website-v4.json",
1003
903
  "bytes": 135,
1004
904
  "sha256": "08eef9ee031d5226464a0da5624c444c69eb48ca9dc2cb2fd2f1551018c4b11c",
1005
- "contentDigestSha256": null,
1006
905
  "schema": "whoisleuth.website-profile-snapshots",
1007
- "version": 4,
1008
- "role": "historical",
1009
- "expectation": "normalises_to_current_output",
1010
- "expectedOutputFixtureId": "workspace.portable.website.v5",
1011
- "scope": "repository",
1012
- "shapeId": "workspace.portable.website.shape"
906
+ "version": 4
1013
907
  },
1014
908
  {
1015
909
  "id": "workspace.portable.website.v5",
1016
910
  "path": "test/fixtures/workspace-lifecycle/portable-website-v5.json",
1017
911
  "bytes": 135,
1018
912
  "sha256": "0cab1675a632793141107fac330a130e7d300a7726e9963f8409199902bf0d23",
1019
- "contentDigestSha256": null,
1020
913
  "schema": "whoisleuth.website-profile-snapshots",
1021
- "version": 5,
1022
- "role": "current",
1023
- "expectation": "accepted_exact",
1024
- "expectedOutputFixtureId": null,
1025
- "scope": "repository",
1026
- "shapeId": "workspace.portable.website.shape"
914
+ "version": 5
915
+ },
916
+ {
917
+ "id": "workspace.browser.brand.v9",
918
+ "path": "test/fixtures/workspace-lifecycle/browser-brand-v9.json",
919
+ "bytes": 37,
920
+ "sha256": "d03cf3bad923ffa3411d218c31a910933c5bb314cc99b9df77f8ae22324c68dc",
921
+ "schema": "whoisleuth.browser.brand-profile-store",
922
+ "version": 9
923
+ },
924
+ {
925
+ "id": "workspace.browser.website.v6",
926
+ "path": "test/fixtures/workspace-lifecycle/browser-website-v6.json",
927
+ "bytes": 90,
928
+ "sha256": "89a4ae5103d848798d77c29b293316284dcf5c2b38417471ad4d55404a8a2110",
929
+ "schema": "whoisleuth.browser.website-snapshot-store",
930
+ "version": 6
931
+ },
932
+ {
933
+ "id": "workspace.portable.brand.v9",
934
+ "path": "test/fixtures/workspace-lifecycle/portable-brand-v9.json",
935
+ "bytes": 122,
936
+ "sha256": "d1ad5a1c56613b31f3bf6195bbda01f4ee9efd528c5fe0cfa5011a6549324853",
937
+ "schema": "whoisleuth.brand-profiles",
938
+ "version": 9
939
+ },
940
+ {
941
+ "id": "workspace.portable.website.v6",
942
+ "path": "test/fixtures/workspace-lifecycle/portable-website-v6.json",
943
+ "bytes": 135,
944
+ "sha256": "a9013e750b7feadbf5dc5d3de5794c27e03d73ba886ad752600df4f0c0cfed35",
945
+ "schema": "whoisleuth.website-profile-snapshots",
946
+ "version": 6
1027
947
  }
1028
948
  ]);
1029
949
  function workspaceByteBudget(descriptor) {
@@ -1038,10 +958,26 @@ function workspaceByteBoundId(descriptor) {
1038
958
  function workspaceShapeId(plane, slug, lifecycle) {
1039
959
  return 'workspace.' + plane + '.' + slug + '.' + lifecycle + '.shape';
1040
960
  }
1041
- const WORKSPACE_LIFECYCLE_FIXTURES = Object.freeze(WORKSPACE_LIFECYCLE_FIXTURE_SOURCE.map((fixture) => ({
1042
- ...fixture,
1043
- shapeId: workspaceShapeId(fixture.id.includes('.browser.') ? 'browser' : 'portable', fixture.id.split('.')[2] ?? '', fixture.role === 'historical' ? 'historical' : 'current'),
1044
- })));
961
+ const WORKSPACE_LIFECYCLE_FIXTURES = Object.freeze(WORKSPACE_LIFECYCLE_FIXTURE_SOURCE.map((fixture) => {
962
+ const definition = WORKSPACE_LIFECYCLE_DEFINITIONS.find((definition) => definition.browserSchema === fixture.schema || definition.portable?.schema === fixture.schema);
963
+ if (!definition)
964
+ throw new TypeError(`Workspace fixture ${fixture.id} has no collection owner.`);
965
+ const browser = definition.browserSchema === fixture.schema;
966
+ const descriptor = browser ? definition.browserDescriptor : definition.portable.descriptor;
967
+ const current = fixture.version === descriptor.currentVersion;
968
+ const target = WORKSPACE_LIFECYCLE_FIXTURE_SOURCE.find((candidate) => candidate.schema === fixture.schema && candidate.version === descriptor.currentVersion);
969
+ if (!target)
970
+ throw new TypeError(`Workspace schema ${fixture.schema} needs an immutable version ${descriptor.currentVersion} fixture.`);
971
+ return {
972
+ ...fixture,
973
+ contentDigestSha256: null,
974
+ role: current ? 'current' : 'historical',
975
+ expectation: current ? 'accepted_exact' : 'normalises_to_current_output',
976
+ expectedOutputFixtureId: current ? null : target.id,
977
+ scope: 'repository',
978
+ shapeId: workspaceShapeId(browser ? 'browser' : 'portable', definition.slug, current ? 'current' : 'historical'),
979
+ };
980
+ }));
1045
981
  function workspaceFixtureIds(schema, version) {
1046
982
  return WORKSPACE_LIFECYCLE_FIXTURES
1047
983
  .filter((fixture) => fixture.schema === schema && fixture.version === version)
@@ -1398,7 +1334,6 @@ export const WORKSPACE_PORTABILITY_LIFECYCLE_FAMILY = defineSchemaLifecycleFamil
1398
1334
  contracts: WORKSPACE_LIFECYCLE_CONTRACTS,
1399
1335
  fixtures: WORKSPACE_LIFECYCLE_FIXTURES,
1400
1336
  metadata: {
1401
- metadataVersion: 4,
1402
1337
  enforcement: 'declarative_only',
1403
1338
  shapes: WORKSPACE_LIFECYCLE_SHAPES,
1404
1339
  boundProfiles: WORKSPACE_LIFECYCLE_BOUNDS,