@adcp/sdk 15.9.0 → 16.0.0-beta.1

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 (295) hide show
  1. package/bin/adcp-identity.js +74 -0
  2. package/bin/adcp.js +2 -0
  3. package/dist/lib/adapters/keyed-account-backend.d.mts +37 -0
  4. package/dist/lib/adapters/keyed-account-backend.d.ts +37 -0
  5. package/dist/lib/adapters/keyed-account-backend.js +121 -0
  6. package/dist/lib/adapters/keyed-account-backend.mjs +94 -0
  7. package/dist/lib/adapters/keyed-account-store.d.mts +28 -0
  8. package/dist/lib/adapters/keyed-account-store.d.ts +28 -0
  9. package/dist/lib/adapters/keyed-account-store.js +227 -0
  10. package/dist/lib/adapters/keyed-account-store.mjs +203 -0
  11. package/dist/lib/core/SingleAgentClient.d.mts +16 -2
  12. package/dist/lib/core/SingleAgentClient.d.ts +16 -2
  13. package/dist/lib/core/SingleAgentClient.js +259 -62
  14. package/dist/lib/core/SingleAgentClient.mjs +261 -57
  15. package/dist/lib/core/TaskExecutor.d.mts +6 -0
  16. package/dist/lib/core/TaskExecutor.d.ts +6 -0
  17. package/dist/lib/core/TaskExecutor.js +66 -5
  18. package/dist/lib/core/TaskExecutor.mjs +71 -5
  19. package/dist/lib/core/postgres-webhook-registration-store.js +32 -5
  20. package/dist/lib/core/postgres-webhook-registration-store.mjs +32 -5
  21. package/dist/lib/core/redis-webhook-registration-store.js +23 -1
  22. package/dist/lib/core/redis-webhook-registration-store.mjs +23 -1
  23. package/dist/lib/core/webhook-registration.d.mts +7 -1
  24. package/dist/lib/core/webhook-registration.d.ts +7 -1
  25. package/dist/lib/core/webhook-registration.js +24 -2
  26. package/dist/lib/core/webhook-registration.mjs +23 -2
  27. package/dist/lib/identity-3.3/index.d.mts +10 -0
  28. package/dist/lib/identity-3.3/index.d.ts +10 -0
  29. package/dist/lib/identity-3.3/index.js +75 -0
  30. package/dist/lib/identity-3.3/index.mjs +46 -0
  31. package/dist/lib/identity-3.3/lifecycle.d.mts +113 -0
  32. package/dist/lib/identity-3.3/lifecycle.d.ts +113 -0
  33. package/dist/lib/identity-3.3/lifecycle.js +267 -0
  34. package/dist/lib/identity-3.3/lifecycle.mjs +240 -0
  35. package/dist/lib/identity-3.3/revocation.d.mts +50 -0
  36. package/dist/lib/identity-3.3/revocation.d.ts +50 -0
  37. package/dist/lib/identity-3.3/revocation.js +238 -0
  38. package/dist/lib/identity-3.3/revocation.mjs +216 -0
  39. package/dist/lib/identity-3.3/schemas.draft.d.mts +2472 -0
  40. package/dist/lib/identity-3.3/schemas.draft.d.ts +2472 -0
  41. package/dist/lib/identity-3.3/schemas.draft.js +1448 -0
  42. package/dist/lib/identity-3.3/schemas.draft.mjs +1428 -0
  43. package/dist/lib/identity-3.3/server.d.mts +34 -0
  44. package/dist/lib/identity-3.3/server.d.ts +34 -0
  45. package/dist/lib/identity-3.3/server.js +219 -0
  46. package/dist/lib/identity-3.3/server.mjs +194 -0
  47. package/dist/lib/identity-3.3/store.d.mts +24 -0
  48. package/dist/lib/identity-3.3/store.d.ts +24 -0
  49. package/dist/lib/identity-3.3/store.js +191 -0
  50. package/dist/lib/identity-3.3/store.mjs +169 -0
  51. package/dist/lib/identity-3.3/trust.d.mts +94 -0
  52. package/dist/lib/identity-3.3/trust.d.ts +94 -0
  53. package/dist/lib/identity-3.3/trust.js +278 -0
  54. package/dist/lib/identity-3.3/trust.mjs +239 -0
  55. package/dist/lib/identity-3.3/types.d.mts +127 -0
  56. package/dist/lib/identity-3.3/types.d.ts +127 -0
  57. package/dist/lib/identity-3.3/types.js +16 -0
  58. package/dist/lib/identity-3.3/types.mjs +0 -0
  59. package/dist/lib/identity-3.3/validation.d.mts +2 -0
  60. package/dist/lib/identity-3.3/validation.d.ts +2 -0
  61. package/dist/lib/identity-3.3/validation.js +125 -0
  62. package/dist/lib/identity-3.3/validation.mjs +91 -0
  63. package/dist/lib/index.d.mts +1 -1
  64. package/dist/lib/index.d.ts +1 -1
  65. package/dist/lib/reporting/azure/index.d.mts +5 -0
  66. package/dist/lib/reporting/azure/index.d.ts +5 -0
  67. package/dist/lib/reporting/azure/index.js +26 -7
  68. package/dist/lib/reporting/azure/index.mjs +26 -7
  69. package/dist/lib/reporting/bigquery/index.d.mts +80 -25
  70. package/dist/lib/reporting/bigquery/index.d.ts +80 -25
  71. package/dist/lib/reporting/bigquery/index.js +184 -81
  72. package/dist/lib/reporting/bigquery/index.mjs +182 -80
  73. package/dist/lib/reporting/gcs/adapter.d.mts +8 -1
  74. package/dist/lib/reporting/gcs/adapter.d.ts +8 -1
  75. package/dist/lib/reporting/gcs/errors.d.mts +6 -0
  76. package/dist/lib/reporting/gcs/errors.d.ts +6 -0
  77. package/dist/lib/reporting/gcs/fence.d.mts +5 -0
  78. package/dist/lib/reporting/gcs/fence.d.ts +5 -0
  79. package/dist/lib/reporting/gcs/index.d.mts +9 -0
  80. package/dist/lib/reporting/gcs/index.d.ts +9 -0
  81. package/dist/lib/reporting/gcs/reader.d.mts +27 -3
  82. package/dist/lib/reporting/gcs/reader.d.ts +27 -3
  83. package/dist/lib/reporting/gcs/row-provider.d.mts +7 -0
  84. package/dist/lib/reporting/gcs/row-provider.d.ts +7 -0
  85. package/dist/lib/reporting/gcs/row-provider.js +34 -14
  86. package/dist/lib/reporting/gcs/row-provider.mjs +34 -14
  87. package/dist/lib/reporting/ledger/change-feed.d.mts +22 -1
  88. package/dist/lib/reporting/ledger/change-feed.d.ts +22 -1
  89. package/dist/lib/reporting/ledger/index.d.mts +11 -0
  90. package/dist/lib/reporting/ledger/index.d.ts +11 -0
  91. package/dist/lib/reporting/ledger/managed-postgres.d.mts +8 -0
  92. package/dist/lib/reporting/ledger/managed-postgres.d.ts +8 -0
  93. package/dist/lib/reporting/ledger/managed-router.d.mts +27 -2
  94. package/dist/lib/reporting/ledger/managed-router.d.ts +27 -2
  95. package/dist/lib/reporting/ledger/managed.d.mts +86 -6
  96. package/dist/lib/reporting/ledger/managed.d.ts +86 -6
  97. package/dist/lib/reporting/ledger/notification-activity.d.mts +65 -3
  98. package/dist/lib/reporting/ledger/notification-activity.d.ts +65 -3
  99. package/dist/lib/reporting/ledger/object-writes.d.mts +64 -5
  100. package/dist/lib/reporting/ledger/object-writes.d.ts +64 -5
  101. package/dist/lib/reporting/ledger/postgres.d.mts +43 -9
  102. package/dist/lib/reporting/ledger/postgres.d.ts +43 -9
  103. package/dist/lib/reporting/ledger/postgres.js +43 -27
  104. package/dist/lib/reporting/ledger/postgres.mjs +43 -27
  105. package/dist/lib/reporting/ledger/row-encoding.d.mts +92 -6
  106. package/dist/lib/reporting/ledger/row-encoding.d.ts +92 -6
  107. package/dist/lib/reporting/ledger/row-encoding.js +8 -4
  108. package/dist/lib/reporting/ledger/row-encoding.mjs +6 -3
  109. package/dist/lib/reporting/ledger/row-storage-errors.d.mts +11 -1
  110. package/dist/lib/reporting/ledger/row-storage-errors.d.ts +11 -1
  111. package/dist/lib/reporting/ledger/row-storage-migration.d.mts +19 -1
  112. package/dist/lib/reporting/ledger/row-storage-migration.d.ts +19 -1
  113. package/dist/lib/reporting/ledger/row-storage-migration.js +133 -18
  114. package/dist/lib/reporting/ledger/row-storage-migration.mjs +133 -18
  115. package/dist/lib/reporting/ledger/row-storage-object.d.mts +103 -12
  116. package/dist/lib/reporting/ledger/row-storage-object.d.ts +103 -12
  117. package/dist/lib/reporting/ledger/row-storage-object.js +106 -31
  118. package/dist/lib/reporting/ledger/row-storage-object.mjs +104 -31
  119. package/dist/lib/reporting/ledger/row-storage.d.mts +87 -20
  120. package/dist/lib/reporting/ledger/row-storage.d.ts +87 -20
  121. package/dist/lib/reporting/ledger/row-storage.js +126 -66
  122. package/dist/lib/reporting/ledger/row-storage.mjs +129 -67
  123. package/dist/lib/reporting/ledger/schedule.d.mts +13 -0
  124. package/dist/lib/reporting/ledger/schedule.d.ts +13 -0
  125. package/dist/lib/reporting/ledger/schedule.js +24 -0
  126. package/dist/lib/reporting/ledger/schedule.mjs +23 -0
  127. package/dist/lib/reporting/ledger/types.d.mts +23 -1
  128. package/dist/lib/reporting/ledger/types.d.ts +23 -1
  129. package/dist/lib/reporting/s3/index.d.mts +5 -0
  130. package/dist/lib/reporting/s3/index.d.ts +5 -0
  131. package/dist/lib/reporting/s3/index.js +37 -14
  132. package/dist/lib/reporting/s3/index.mjs +37 -14
  133. package/dist/lib/reporting/service/index.d.mts +10 -0
  134. package/dist/lib/reporting/service/index.d.ts +10 -0
  135. package/dist/lib/reporting/service/maintenance.d.mts +28 -2
  136. package/dist/lib/reporting/service/maintenance.d.ts +28 -2
  137. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  138. package/dist/lib/server/account-sync-contract.d.mts +14 -0
  139. package/dist/lib/server/account-sync-contract.d.ts +14 -0
  140. package/dist/lib/server/account-sync-contract.js +225 -0
  141. package/dist/lib/server/account-sync-contract.mjs +197 -0
  142. package/dist/lib/server/adcp-server.js +5 -0
  143. package/dist/lib/server/adcp-server.mjs +10 -0
  144. package/dist/lib/server/auth-signature.d.mts +10 -0
  145. package/dist/lib/server/auth-signature.d.ts +10 -0
  146. package/dist/lib/server/auth-signature.js +61 -2
  147. package/dist/lib/server/auth-signature.mjs +65 -2
  148. package/dist/lib/server/auth.d.mts +6 -0
  149. package/dist/lib/server/auth.d.ts +6 -0
  150. package/dist/lib/server/auth.js +6 -0
  151. package/dist/lib/server/auth.mjs +6 -0
  152. package/dist/lib/server/create-adcp-server.d.mts +19 -5
  153. package/dist/lib/server/create-adcp-server.d.ts +19 -5
  154. package/dist/lib/server/create-adcp-server.js +238 -19
  155. package/dist/lib/server/create-adcp-server.mjs +240 -10
  156. package/dist/lib/server/decisioning/account.d.mts +24 -1
  157. package/dist/lib/server/decisioning/account.d.ts +24 -1
  158. package/dist/lib/server/decisioning/index.d.mts +1 -1
  159. package/dist/lib/server/decisioning/index.d.ts +1 -1
  160. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +0 -50
  161. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +0 -50
  162. package/dist/lib/server/decisioning/runtime/from-platform.js +61 -11
  163. package/dist/lib/server/decisioning/runtime/from-platform.mjs +65 -12
  164. package/dist/lib/server/index.d.mts +2 -0
  165. package/dist/lib/server/index.d.ts +2 -0
  166. package/dist/lib/server/index.js +12 -0
  167. package/dist/lib/server/index.mjs +12 -0
  168. package/dist/lib/server/notification-subscriptions/runtime.js +9 -1
  169. package/dist/lib/server/notification-subscriptions/runtime.mjs +9 -1
  170. package/dist/lib/server/notification-subscriptions/types.d.mts +5 -0
  171. package/dist/lib/server/notification-subscriptions/types.d.ts +5 -0
  172. package/dist/lib/server/principal/types.d.mts +1 -1
  173. package/dist/lib/server/principal/types.d.ts +1 -1
  174. package/dist/lib/server/serve.js +5 -0
  175. package/dist/lib/server/serve.mjs +5 -0
  176. package/dist/lib/server/webhook-delivery/recovery.d.mts +1 -0
  177. package/dist/lib/server/webhook-delivery/recovery.d.ts +1 -0
  178. package/dist/lib/server/webhook-delivery/recovery.js +4 -0
  179. package/dist/lib/server/webhook-delivery/recovery.mjs +4 -0
  180. package/dist/lib/server/webhook-emitter.d.mts +10 -0
  181. package/dist/lib/server/webhook-emitter.d.ts +10 -0
  182. package/dist/lib/server/webhook-emitter.js +22 -0
  183. package/dist/lib/server/webhook-emitter.mjs +22 -0
  184. package/dist/lib/signing/agent-context.d.mts +0 -5
  185. package/dist/lib/signing/agent-context.d.ts +0 -5
  186. package/dist/lib/signing/agent-context.js +9 -1
  187. package/dist/lib/signing/agent-context.mjs +9 -1
  188. package/dist/lib/signing/agent-fetch.js +27 -2
  189. package/dist/lib/signing/agent-fetch.mjs +32 -2
  190. package/dist/lib/signing/agent-resolver/canonicalize.d.mts +10 -15
  191. package/dist/lib/signing/agent-resolver/canonicalize.d.ts +10 -15
  192. package/dist/lib/signing/agent-resolver/canonicalize.js +17 -5
  193. package/dist/lib/signing/agent-resolver/canonicalize.mjs +16 -5
  194. package/dist/lib/signing/agent-resolver/index.d.mts +1 -0
  195. package/dist/lib/signing/agent-resolver/index.d.ts +1 -0
  196. package/dist/lib/signing/agent-resolver/index.js +5 -0
  197. package/dist/lib/signing/agent-resolver/index.mjs +3 -0
  198. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +3 -2
  199. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +3 -2
  200. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +30 -3
  201. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +30 -3
  202. package/dist/lib/signing/agent-resolver/strict-json.d.mts +2 -2
  203. package/dist/lib/signing/agent-resolver/strict-json.d.ts +2 -2
  204. package/dist/lib/signing/agent-resolver/strict-json.js +5 -3
  205. package/dist/lib/signing/agent-resolver/strict-json.mjs +5 -3
  206. package/dist/lib/signing/brand-jwks.d.mts +2 -2
  207. package/dist/lib/signing/brand-jwks.d.ts +2 -2
  208. package/dist/lib/signing/brand-jwks.js +4 -4
  209. package/dist/lib/signing/brand-jwks.mjs +4 -4
  210. package/dist/lib/signing/client.d.mts +1 -0
  211. package/dist/lib/signing/client.d.ts +1 -0
  212. package/dist/lib/signing/client.js +5 -0
  213. package/dist/lib/signing/client.mjs +3 -0
  214. package/dist/lib/signing/jwks-https.d.mts +1 -1
  215. package/dist/lib/signing/jwks-https.d.ts +1 -1
  216. package/dist/lib/signing/jwks-https.js +1 -1
  217. package/dist/lib/signing/jwks-https.mjs +1 -1
  218. package/dist/lib/signing/jwks.d.mts +11 -3
  219. package/dist/lib/signing/jwks.d.ts +11 -3
  220. package/dist/lib/signing/jwks.js +1 -1
  221. package/dist/lib/signing/jwks.mjs +1 -1
  222. package/dist/lib/signing/parser.d.mts +1 -1
  223. package/dist/lib/signing/parser.d.ts +1 -1
  224. package/dist/lib/signing/parser.js +5 -3
  225. package/dist/lib/signing/parser.mjs +6 -4
  226. package/dist/lib/signing/revocation.d.mts +4 -1
  227. package/dist/lib/signing/revocation.d.ts +4 -1
  228. package/dist/lib/signing/revocation.js +33 -3
  229. package/dist/lib/signing/revocation.mjs +31 -2
  230. package/dist/lib/signing/server.d.mts +1 -1
  231. package/dist/lib/signing/server.d.ts +1 -1
  232. package/dist/lib/signing/server.js +4 -0
  233. package/dist/lib/signing/server.mjs +4 -0
  234. package/dist/lib/signing/types.d.mts +3 -0
  235. package/dist/lib/signing/types.d.ts +3 -0
  236. package/dist/lib/signing/web-bot-auth.d.mts +117 -0
  237. package/dist/lib/signing/web-bot-auth.d.ts +117 -0
  238. package/dist/lib/signing/web-bot-auth.js +660 -0
  239. package/dist/lib/signing/web-bot-auth.mjs +624 -0
  240. package/dist/lib/testing/client.d.mts +1 -4
  241. package/dist/lib/testing/client.d.ts +1 -4
  242. package/dist/lib/testing/client.js +141 -20
  243. package/dist/lib/testing/client.mjs +137 -19
  244. package/dist/lib/testing/compliance/comply.js +29 -10
  245. package/dist/lib/testing/compliance/comply.mjs +37 -11
  246. package/dist/lib/testing/storyboard/account-sync-contract.d.mts +3 -0
  247. package/dist/lib/testing/storyboard/account-sync-contract.d.ts +3 -0
  248. package/dist/lib/testing/storyboard/account-sync-contract.js +79 -0
  249. package/dist/lib/testing/storyboard/account-sync-contract.mjs +55 -0
  250. package/dist/lib/testing/storyboard/default-invariants.js +2 -0
  251. package/dist/lib/testing/storyboard/default-invariants.mjs +2 -0
  252. package/dist/lib/testing/storyboard/runner.js +23 -8
  253. package/dist/lib/testing/storyboard/runner.mjs +24 -8
  254. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  255. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  256. package/dist/lib/types/adcp.d.mts +4 -0
  257. package/dist/lib/types/adcp.d.ts +4 -0
  258. package/dist/lib/types/schemas.generated.d.ts +6 -6
  259. package/dist/lib/utils/capabilities.d.mts +1 -0
  260. package/dist/lib/utils/capabilities.d.ts +1 -0
  261. package/dist/lib/utils/capabilities.js +5 -0
  262. package/dist/lib/utils/capabilities.mjs +5 -0
  263. package/dist/lib/v2/projection/creative-delivery.d.mts +3 -1
  264. package/dist/lib/v2/projection/creative-delivery.d.ts +3 -1
  265. package/dist/lib/v2/projection/creative-delivery.js +26 -10
  266. package/dist/lib/v2/projection/creative-delivery.mjs +26 -10
  267. package/dist/lib/v2/projection/index.d.mts +1 -1
  268. package/dist/lib/v2/projection/index.d.ts +1 -1
  269. package/dist/lib/v2/projection/legacy-routes.d.mts +24 -2
  270. package/dist/lib/v2/projection/legacy-routes.d.ts +24 -2
  271. package/dist/lib/v2/projection/legacy-routes.js +28 -2
  272. package/dist/lib/v2/projection/legacy-routes.mjs +26 -2
  273. package/dist/lib/validation/schema-loader.d.mts +2 -0
  274. package/dist/lib/validation/schema-loader.d.ts +2 -0
  275. package/dist/lib/validation/schema-loader.js +43 -4
  276. package/dist/lib/validation/schema-loader.mjs +39 -3
  277. package/dist/lib/version.d.mts +3 -3
  278. package/dist/lib/version.d.ts +3 -3
  279. package/dist/lib/version.js +3 -3
  280. package/dist/lib/version.mjs +3 -3
  281. package/docs/TYPE-SUMMARY.md +2 -2
  282. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +4 -0
  283. package/docs/guides/REPORTING-LEDGER-CUTOVER.md +1 -1
  284. package/docs/guides/REPORTING-LEDGER.md +132 -46
  285. package/docs/guides/REPORTING-OPERATIONS.md +25 -4
  286. package/docs/guides/REPORTING-STABILITY.md +76 -0
  287. package/docs/guides/account-resolution.md +101 -0
  288. package/docs/llms.txt +3 -2
  289. package/docs/migration-12-to-13.md +7 -0
  290. package/docs/migration-14.x-rc-worksheet.md +4 -4
  291. package/docs/migration-agent-identity-3.3.md +355 -0
  292. package/docs/migration-agent-resolution-3.3.md +5 -0
  293. package/docs/migration-reporting-row-storage-alignment.md +137 -0
  294. package/examples/storyboards/account-sync-contract.yaml +57 -0
  295. package/package.json +15 -1
@@ -24,6 +24,53 @@ __export(row_storage_migration_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(row_storage_migration_exports);
26
26
  const REPORTING_ROW_STORAGE_MIGRATION = `
27
+ -- Serialize concurrent appliers (several instances may run migrations at startup) so the guarded
28
+ -- renames below cannot race each other. Released at the end of this implicit transaction.
29
+ SELECT pg_advisory_xact_lock(hashtextextended('adcp-reporting-row-storage-migration', 0));
30
+
31
+ -- In-place upgrade from the pre-AdCP-alignment column names. Every step is guarded, so a fresh
32
+ -- install (no tables yet) and a reapplied migration are both no-ops here.
33
+ DO $align$
34
+ DECLARE
35
+ renames TEXT[][] := ARRAY[
36
+ ARRAY['adcp_reporting_row_chunks', 'object_key', 'object_ref'],
37
+ ARRAY['adcp_reporting_row_chunks', 'native_version', 'native_version_ref'],
38
+ ARRAY['adcp_reporting_row_chunks', 'byte_count', 'canonical_size_bytes'],
39
+ ARRAY['adcp_reporting_row_chunks', 'physical_byte_count', 'size_bytes'],
40
+ ARRAY['adcp_reporting_row_sets', 'canonical_byte_count', 'content_size_bytes']
41
+ ];
42
+ item TEXT[];
43
+ BEGIN
44
+ FOREACH item SLICE 1 IN ARRAY renames LOOP
45
+ IF EXISTS (
46
+ SELECT 1 FROM information_schema.columns
47
+ WHERE table_schema = current_schema() AND table_name = item[1] AND column_name = item[2]
48
+ ) AND NOT EXISTS (
49
+ SELECT 1 FROM information_schema.columns
50
+ WHERE table_schema = current_schema() AND table_name = item[1] AND column_name = item[3]
51
+ ) THEN
52
+ EXECUTE format('ALTER TABLE %I RENAME COLUMN %I TO %I', item[1], item[2], item[3]);
53
+ END IF;
54
+ END LOOP;
55
+ -- encoding = 'adcp_canonical_jsonl_v1' becomes AdCP's format = 'jsonl' plus
56
+ -- algorithm = 'adcp_jcs_rows_v1'. The old CHECK admitted one value, so the defaults backfill
57
+ -- every row without an UPDATE (the immutability trigger forbids one).
58
+ IF EXISTS (
59
+ SELECT 1 FROM information_schema.columns
60
+ WHERE table_schema = current_schema() AND table_name = 'adcp_reporting_row_sets' AND column_name = 'encoding'
61
+ ) AND NOT EXISTS (
62
+ SELECT 1 FROM information_schema.columns
63
+ WHERE table_schema = current_schema() AND table_name = 'adcp_reporting_row_sets' AND column_name = 'format'
64
+ ) THEN
65
+ ALTER TABLE adcp_reporting_row_sets
66
+ ADD COLUMN format TEXT NOT NULL DEFAULT 'jsonl' CHECK (format = 'jsonl'),
67
+ ADD COLUMN algorithm TEXT NOT NULL DEFAULT 'adcp_jcs_rows_v1' CHECK (algorithm = 'adcp_jcs_rows_v1');
68
+ ALTER TABLE adcp_reporting_row_sets ALTER COLUMN format DROP DEFAULT, ALTER COLUMN algorithm DROP DEFAULT;
69
+ ALTER TABLE adcp_reporting_row_sets DROP COLUMN encoding;
70
+ END IF;
71
+ END
72
+ $align$;
73
+
27
74
  CREATE TABLE IF NOT EXISTS adcp_persistence_installation (
28
75
  singleton BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (singleton),
29
76
  installation_id UUID NOT NULL DEFAULT gen_random_uuid(),
@@ -50,10 +97,11 @@ CREATE TABLE IF NOT EXISTS adcp_reporting_row_sets (
50
97
  row_set_kind TEXT NOT NULL CHECK (row_set_kind IN ('revision', 'adjustment')),
51
98
  account_id TEXT COLLATE "C" NOT NULL,
52
99
  obligation_id TEXT COLLATE "C" NOT NULL REFERENCES adcp_reporting_obligations(obligation_id),
53
- encoding TEXT NOT NULL CHECK (encoding = 'adcp_canonical_jsonl_v1'),
100
+ format TEXT NOT NULL CHECK (format = 'jsonl'),
101
+ algorithm TEXT NOT NULL CHECK (algorithm = 'adcp_jcs_rows_v1'),
54
102
  digest_profile TEXT NOT NULL CHECK (digest_profile IN ('revision_envelope_v1', 'rows_v1')),
55
103
  content_sha256 TEXT COLLATE "C" NOT NULL CHECK (content_sha256 ~ '^[0-9a-f]{64}$'),
56
- canonical_byte_count BIGINT NOT NULL CHECK (canonical_byte_count > 0),
104
+ content_size_bytes BIGINT NOT NULL CHECK (content_size_bytes > 0),
57
105
  row_count BIGINT NOT NULL CHECK (row_count >= 0),
58
106
  row_manifest_sha256 TEXT COLLATE "C" NOT NULL CHECK (row_manifest_sha256 ~ '^[0-9a-f]{64}$'),
59
107
  chunk_count INTEGER NOT NULL CHECK (chunk_count >= 0),
@@ -87,27 +135,29 @@ CREATE TABLE IF NOT EXISTS adcp_reporting_row_chunks (
87
135
  obligation_id TEXT COLLATE "C" NOT NULL,
88
136
  first_ordinal BIGINT NOT NULL CHECK (first_ordinal >= 0),
89
137
  row_count INTEGER NOT NULL CHECK (row_count > 0),
90
- byte_count BIGINT NOT NULL CHECK (byte_count > 0),
138
+ canonical_size_bytes BIGINT NOT NULL CHECK (canonical_size_bytes > 0),
91
139
  sha256 TEXT COLLATE "C" NOT NULL CHECK (sha256 ~ '^[0-9a-f]{64}$'),
92
140
  segments JSONB NOT NULL CHECK (jsonb_typeof(segments) = 'array'),
93
- object_key TEXT COLLATE "C",
94
- native_version TEXT,
141
+ object_ref TEXT COLLATE "C" CONSTRAINT adcp_reporting_row_chunks_object_ref_bounds
142
+ CHECK (object_ref IS NULL OR char_length(object_ref) BETWEEN 1 AND 1024),
143
+ native_version_ref TEXT CONSTRAINT adcp_reporting_row_chunks_native_version_ref_bounds
144
+ CHECK (native_version_ref IS NULL OR char_length(native_version_ref) BETWEEN 1 AND 1024),
95
145
  compression TEXT NOT NULL DEFAULT 'none' CHECK (compression IN ('none', 'gzip')),
96
146
  physical_sha256 TEXT COLLATE "C" CHECK (physical_sha256 IS NULL OR physical_sha256 ~ '^[0-9a-f]{64}$'),
97
- physical_byte_count BIGINT CHECK (physical_byte_count IS NULL OR physical_byte_count > 0),
147
+ size_bytes BIGINT CHECK (size_bytes IS NULL OR size_bytes > 0),
98
148
  PRIMARY KEY (row_set_id, chunk_index),
99
149
  FOREIGN KEY (row_set_id, account_id, obligation_id)
100
150
  REFERENCES adcp_reporting_row_sets (row_set_id, account_id, obligation_id),
101
- CHECK ((object_key IS NULL) = (native_version IS NULL)),
102
- CHECK ((object_key IS NULL) = (physical_sha256 IS NULL)),
103
- CHECK ((object_key IS NULL) = (physical_byte_count IS NULL))
151
+ CHECK ((object_ref IS NULL) = (native_version_ref IS NULL)),
152
+ CHECK ((object_ref IS NULL) = (physical_sha256 IS NULL)),
153
+ CHECK ((object_ref IS NULL) = (size_bytes IS NULL))
104
154
  );
105
155
  CREATE INDEX IF NOT EXISTS adcp_reporting_row_chunks_object
106
- ON adcp_reporting_row_chunks (object_key, native_version)
107
- WHERE object_key IS NOT NULL;
156
+ ON adcp_reporting_row_chunks (object_ref, native_version_ref)
157
+ WHERE object_ref IS NOT NULL;
108
158
  CREATE INDEX IF NOT EXISTS adcp_reporting_row_chunks_body
109
159
  ON adcp_reporting_row_chunks (account_id, obligation_id, sha256)
110
- WHERE object_key IS NULL;
160
+ WHERE object_ref IS NULL;
111
161
 
112
162
  CREATE TABLE IF NOT EXISTS adcp_reporting_chunk_bodies (
113
163
  account_id TEXT COLLATE "C" NOT NULL,
@@ -161,12 +211,12 @@ BEGIN
161
211
  RAISE EXCEPTION 'reporting row binding identity is immutable' USING ERRCODE = '23514';
162
212
  END IF;
163
213
  ELSIF TG_TABLE_NAME = 'adcp_reporting_row_sets' THEN
164
- IF (NEW.row_set_id, NEW.row_set_kind, NEW.account_id, NEW.obligation_id, NEW.encoding, NEW.digest_profile,
165
- NEW.content_sha256, NEW.canonical_byte_count, NEW.row_count, NEW.row_manifest_sha256, NEW.chunk_count,
214
+ IF (NEW.row_set_id, NEW.row_set_kind, NEW.account_id, NEW.obligation_id, NEW.format, NEW.algorithm,
215
+ NEW.digest_profile, NEW.content_sha256, NEW.content_size_bytes, NEW.row_count, NEW.row_manifest_sha256, NEW.chunk_count,
166
216
  NEW.rows_shared_from_row_set_id, NEW.recorded_at)
167
217
  IS DISTINCT FROM
168
- (OLD.row_set_id, OLD.row_set_kind, OLD.account_id, OLD.obligation_id, OLD.encoding, OLD.digest_profile,
169
- OLD.content_sha256, OLD.canonical_byte_count, OLD.row_count, OLD.row_manifest_sha256, OLD.chunk_count,
218
+ (OLD.row_set_id, OLD.row_set_kind, OLD.account_id, OLD.obligation_id, OLD.format, OLD.algorithm,
219
+ OLD.digest_profile, OLD.content_sha256, OLD.content_size_bytes, OLD.row_count, OLD.row_manifest_sha256, OLD.chunk_count,
170
220
  OLD.rows_shared_from_row_set_id, OLD.recorded_at) THEN
171
221
  RAISE EXCEPTION 'reporting row set content is immutable' USING ERRCODE = '23514';
172
222
  END IF;
@@ -176,10 +226,10 @@ BEGIN
176
226
  END IF;
177
227
  ELSIF TG_TABLE_NAME = 'adcp_reporting_row_chunks' THEN
178
228
  IF (NEW.row_set_id, NEW.chunk_index, NEW.account_id, NEW.obligation_id, NEW.first_ordinal, NEW.row_count,
179
- NEW.byte_count, NEW.sha256, NEW.segments)
229
+ NEW.canonical_size_bytes, NEW.sha256, NEW.segments)
180
230
  IS DISTINCT FROM
181
231
  (OLD.row_set_id, OLD.chunk_index, OLD.account_id, OLD.obligation_id, OLD.first_ordinal, OLD.row_count,
182
- OLD.byte_count, OLD.sha256, OLD.segments) THEN
232
+ OLD.canonical_size_bytes, OLD.sha256, OLD.segments) THEN
183
233
  RAISE EXCEPTION 'reporting row chunk content is immutable' USING ERRCODE = '23514';
184
234
  END IF;
185
235
  ELSIF TG_TABLE_NAME = 'adcp_reporting_chunk_bodies' THEN
@@ -213,6 +263,50 @@ BEGIN
213
263
  END LOOP;
214
264
  END
215
265
  $triggers$;
266
+
267
+ -- AdCP bounds on installs created before they existed (a fresh install has them inline).
268
+ DO $bounds$
269
+ DECLARE
270
+ offending BIGINT;
271
+ BEGIN
272
+ SELECT count(*) INTO offending FROM adcp_reporting_row_chunks
273
+ WHERE object_ref IS NOT NULL
274
+ AND (char_length(object_ref) NOT BETWEEN 1 AND 1024 OR char_length(native_version_ref) NOT BETWEEN 1 AND 1024);
275
+ IF offending > 0 THEN
276
+ RAISE EXCEPTION 'reporting row storage: % chunk(s) hold an object_ref or native_version_ref outside AdCP 1 to 1024 characters; re-sync them before applying this migration', offending
277
+ USING ERRCODE = '23514';
278
+ END IF;
279
+ IF NOT EXISTS (
280
+ SELECT 1 FROM pg_constraint
281
+ WHERE conname = 'adcp_reporting_row_chunks_object_ref_bounds'
282
+ AND conrelid = to_regclass(quote_ident(current_schema()) || '.adcp_reporting_row_chunks')
283
+ ) THEN
284
+ ALTER TABLE adcp_reporting_row_chunks ADD CONSTRAINT adcp_reporting_row_chunks_object_ref_bounds
285
+ CHECK (object_ref IS NULL OR char_length(object_ref) BETWEEN 1 AND 1024);
286
+ END IF;
287
+ IF NOT EXISTS (
288
+ SELECT 1 FROM pg_constraint
289
+ WHERE conname = 'adcp_reporting_row_chunks_native_version_ref_bounds'
290
+ AND conrelid = to_regclass(quote_ident(current_schema()) || '.adcp_reporting_row_chunks')
291
+ ) THEN
292
+ ALTER TABLE adcp_reporting_row_chunks ADD CONSTRAINT adcp_reporting_row_chunks_native_version_ref_bounds
293
+ CHECK (native_version_ref IS NULL OR char_length(native_version_ref) BETWEEN 1 AND 1024);
294
+ END IF;
295
+ END
296
+ $bounds$;
297
+
298
+ -- Open write intents written before the rename name objects objectKey / nativeVersion.
299
+ UPDATE adcp_reporting_row_write_intents
300
+ SET created_objects = (
301
+ SELECT COALESCE(jsonb_agg(
302
+ CASE WHEN jsonb_typeof(created) = 'object' AND created ? 'objectKey'
303
+ THEN jsonb_build_object('object_ref', created -> 'objectKey', 'native_version_ref', created -> 'nativeVersion')
304
+ ELSE created END
305
+ ORDER BY ordinality
306
+ ), '[]'::jsonb)
307
+ FROM jsonb_array_elements(created_objects) WITH ORDINALITY AS element(created, ordinality)
308
+ )
309
+ WHERE EXISTS (SELECT 1 FROM jsonb_array_elements(created_objects) AS element(created) WHERE jsonb_typeof(created) = 'object' AND created ? 'objectKey');
216
310
  `.trim();
217
311
  const REPORTING_ROW_STORAGE_TABLES = Object.freeze([
218
312
  "adcp_persistence_installation",
@@ -222,6 +316,15 @@ const REPORTING_ROW_STORAGE_TABLES = Object.freeze([
222
316
  "adcp_reporting_chunk_bodies",
223
317
  "adcp_reporting_row_write_intents"
224
318
  ]);
319
+ const REPORTING_ROW_STORAGE_REQUIRED_COLUMNS = Object.freeze([
320
+ "adcp_reporting_row_sets.format",
321
+ "adcp_reporting_row_sets.algorithm",
322
+ "adcp_reporting_row_sets.content_size_bytes",
323
+ "adcp_reporting_row_chunks.canonical_size_bytes",
324
+ "adcp_reporting_row_chunks.object_ref",
325
+ "adcp_reporting_row_chunks.native_version_ref",
326
+ "adcp_reporting_row_chunks.size_bytes"
327
+ ]);
225
328
  async function probeReportingRowStorageSchemaV1(pool) {
226
329
  const present = await pool.query(
227
330
  `SELECT table_name AS name FROM information_schema.tables
@@ -235,6 +338,18 @@ async function probeReportingRowStorageSchemaV1(pool) {
235
338
  `Reporting row storage requires REPORTING_ROW_STORAGE_MIGRATION; missing tables: ${missing.join(", ")}`
236
339
  );
237
340
  }
341
+ const columns = await pool.query(
342
+ `SELECT table_name || '.' || column_name AS name FROM information_schema.columns
343
+ WHERE table_schema = current_schema() AND table_name = ANY($1::text[])`,
344
+ [["adcp_reporting_row_chunks", "adcp_reporting_row_sets"]]
345
+ );
346
+ const columnNames = new Set(columns.rows.map((row) => row.name));
347
+ const absent = REPORTING_ROW_STORAGE_REQUIRED_COLUMNS.filter((name) => !columnNames.has(name));
348
+ if (absent.length > 0) {
349
+ throw new Error(
350
+ `Reporting row storage requires the current REPORTING_ROW_STORAGE_MIGRATION; missing columns: ${absent.join(", ")}`
351
+ );
352
+ }
238
353
  const installation = await pool.query(
239
354
  "SELECT installation_id::text AS installation_id FROM adcp_persistence_installation WHERE singleton"
240
355
  );
@@ -1,4 +1,51 @@
1
1
  const REPORTING_ROW_STORAGE_MIGRATION = `
2
+ -- Serialize concurrent appliers (several instances may run migrations at startup) so the guarded
3
+ -- renames below cannot race each other. Released at the end of this implicit transaction.
4
+ SELECT pg_advisory_xact_lock(hashtextextended('adcp-reporting-row-storage-migration', 0));
5
+
6
+ -- In-place upgrade from the pre-AdCP-alignment column names. Every step is guarded, so a fresh
7
+ -- install (no tables yet) and a reapplied migration are both no-ops here.
8
+ DO $align$
9
+ DECLARE
10
+ renames TEXT[][] := ARRAY[
11
+ ARRAY['adcp_reporting_row_chunks', 'object_key', 'object_ref'],
12
+ ARRAY['adcp_reporting_row_chunks', 'native_version', 'native_version_ref'],
13
+ ARRAY['adcp_reporting_row_chunks', 'byte_count', 'canonical_size_bytes'],
14
+ ARRAY['adcp_reporting_row_chunks', 'physical_byte_count', 'size_bytes'],
15
+ ARRAY['adcp_reporting_row_sets', 'canonical_byte_count', 'content_size_bytes']
16
+ ];
17
+ item TEXT[];
18
+ BEGIN
19
+ FOREACH item SLICE 1 IN ARRAY renames LOOP
20
+ IF EXISTS (
21
+ SELECT 1 FROM information_schema.columns
22
+ WHERE table_schema = current_schema() AND table_name = item[1] AND column_name = item[2]
23
+ ) AND NOT EXISTS (
24
+ SELECT 1 FROM information_schema.columns
25
+ WHERE table_schema = current_schema() AND table_name = item[1] AND column_name = item[3]
26
+ ) THEN
27
+ EXECUTE format('ALTER TABLE %I RENAME COLUMN %I TO %I', item[1], item[2], item[3]);
28
+ END IF;
29
+ END LOOP;
30
+ -- encoding = 'adcp_canonical_jsonl_v1' becomes AdCP's format = 'jsonl' plus
31
+ -- algorithm = 'adcp_jcs_rows_v1'. The old CHECK admitted one value, so the defaults backfill
32
+ -- every row without an UPDATE (the immutability trigger forbids one).
33
+ IF EXISTS (
34
+ SELECT 1 FROM information_schema.columns
35
+ WHERE table_schema = current_schema() AND table_name = 'adcp_reporting_row_sets' AND column_name = 'encoding'
36
+ ) AND NOT EXISTS (
37
+ SELECT 1 FROM information_schema.columns
38
+ WHERE table_schema = current_schema() AND table_name = 'adcp_reporting_row_sets' AND column_name = 'format'
39
+ ) THEN
40
+ ALTER TABLE adcp_reporting_row_sets
41
+ ADD COLUMN format TEXT NOT NULL DEFAULT 'jsonl' CHECK (format = 'jsonl'),
42
+ ADD COLUMN algorithm TEXT NOT NULL DEFAULT 'adcp_jcs_rows_v1' CHECK (algorithm = 'adcp_jcs_rows_v1');
43
+ ALTER TABLE adcp_reporting_row_sets ALTER COLUMN format DROP DEFAULT, ALTER COLUMN algorithm DROP DEFAULT;
44
+ ALTER TABLE adcp_reporting_row_sets DROP COLUMN encoding;
45
+ END IF;
46
+ END
47
+ $align$;
48
+
2
49
  CREATE TABLE IF NOT EXISTS adcp_persistence_installation (
3
50
  singleton BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (singleton),
4
51
  installation_id UUID NOT NULL DEFAULT gen_random_uuid(),
@@ -25,10 +72,11 @@ CREATE TABLE IF NOT EXISTS adcp_reporting_row_sets (
25
72
  row_set_kind TEXT NOT NULL CHECK (row_set_kind IN ('revision', 'adjustment')),
26
73
  account_id TEXT COLLATE "C" NOT NULL,
27
74
  obligation_id TEXT COLLATE "C" NOT NULL REFERENCES adcp_reporting_obligations(obligation_id),
28
- encoding TEXT NOT NULL CHECK (encoding = 'adcp_canonical_jsonl_v1'),
75
+ format TEXT NOT NULL CHECK (format = 'jsonl'),
76
+ algorithm TEXT NOT NULL CHECK (algorithm = 'adcp_jcs_rows_v1'),
29
77
  digest_profile TEXT NOT NULL CHECK (digest_profile IN ('revision_envelope_v1', 'rows_v1')),
30
78
  content_sha256 TEXT COLLATE "C" NOT NULL CHECK (content_sha256 ~ '^[0-9a-f]{64}$'),
31
- canonical_byte_count BIGINT NOT NULL CHECK (canonical_byte_count > 0),
79
+ content_size_bytes BIGINT NOT NULL CHECK (content_size_bytes > 0),
32
80
  row_count BIGINT NOT NULL CHECK (row_count >= 0),
33
81
  row_manifest_sha256 TEXT COLLATE "C" NOT NULL CHECK (row_manifest_sha256 ~ '^[0-9a-f]{64}$'),
34
82
  chunk_count INTEGER NOT NULL CHECK (chunk_count >= 0),
@@ -62,27 +110,29 @@ CREATE TABLE IF NOT EXISTS adcp_reporting_row_chunks (
62
110
  obligation_id TEXT COLLATE "C" NOT NULL,
63
111
  first_ordinal BIGINT NOT NULL CHECK (first_ordinal >= 0),
64
112
  row_count INTEGER NOT NULL CHECK (row_count > 0),
65
- byte_count BIGINT NOT NULL CHECK (byte_count > 0),
113
+ canonical_size_bytes BIGINT NOT NULL CHECK (canonical_size_bytes > 0),
66
114
  sha256 TEXT COLLATE "C" NOT NULL CHECK (sha256 ~ '^[0-9a-f]{64}$'),
67
115
  segments JSONB NOT NULL CHECK (jsonb_typeof(segments) = 'array'),
68
- object_key TEXT COLLATE "C",
69
- native_version TEXT,
116
+ object_ref TEXT COLLATE "C" CONSTRAINT adcp_reporting_row_chunks_object_ref_bounds
117
+ CHECK (object_ref IS NULL OR char_length(object_ref) BETWEEN 1 AND 1024),
118
+ native_version_ref TEXT CONSTRAINT adcp_reporting_row_chunks_native_version_ref_bounds
119
+ CHECK (native_version_ref IS NULL OR char_length(native_version_ref) BETWEEN 1 AND 1024),
70
120
  compression TEXT NOT NULL DEFAULT 'none' CHECK (compression IN ('none', 'gzip')),
71
121
  physical_sha256 TEXT COLLATE "C" CHECK (physical_sha256 IS NULL OR physical_sha256 ~ '^[0-9a-f]{64}$'),
72
- physical_byte_count BIGINT CHECK (physical_byte_count IS NULL OR physical_byte_count > 0),
122
+ size_bytes BIGINT CHECK (size_bytes IS NULL OR size_bytes > 0),
73
123
  PRIMARY KEY (row_set_id, chunk_index),
74
124
  FOREIGN KEY (row_set_id, account_id, obligation_id)
75
125
  REFERENCES adcp_reporting_row_sets (row_set_id, account_id, obligation_id),
76
- CHECK ((object_key IS NULL) = (native_version IS NULL)),
77
- CHECK ((object_key IS NULL) = (physical_sha256 IS NULL)),
78
- CHECK ((object_key IS NULL) = (physical_byte_count IS NULL))
126
+ CHECK ((object_ref IS NULL) = (native_version_ref IS NULL)),
127
+ CHECK ((object_ref IS NULL) = (physical_sha256 IS NULL)),
128
+ CHECK ((object_ref IS NULL) = (size_bytes IS NULL))
79
129
  );
80
130
  CREATE INDEX IF NOT EXISTS adcp_reporting_row_chunks_object
81
- ON adcp_reporting_row_chunks (object_key, native_version)
82
- WHERE object_key IS NOT NULL;
131
+ ON adcp_reporting_row_chunks (object_ref, native_version_ref)
132
+ WHERE object_ref IS NOT NULL;
83
133
  CREATE INDEX IF NOT EXISTS adcp_reporting_row_chunks_body
84
134
  ON adcp_reporting_row_chunks (account_id, obligation_id, sha256)
85
- WHERE object_key IS NULL;
135
+ WHERE object_ref IS NULL;
86
136
 
87
137
  CREATE TABLE IF NOT EXISTS adcp_reporting_chunk_bodies (
88
138
  account_id TEXT COLLATE "C" NOT NULL,
@@ -136,12 +186,12 @@ BEGIN
136
186
  RAISE EXCEPTION 'reporting row binding identity is immutable' USING ERRCODE = '23514';
137
187
  END IF;
138
188
  ELSIF TG_TABLE_NAME = 'adcp_reporting_row_sets' THEN
139
- IF (NEW.row_set_id, NEW.row_set_kind, NEW.account_id, NEW.obligation_id, NEW.encoding, NEW.digest_profile,
140
- NEW.content_sha256, NEW.canonical_byte_count, NEW.row_count, NEW.row_manifest_sha256, NEW.chunk_count,
189
+ IF (NEW.row_set_id, NEW.row_set_kind, NEW.account_id, NEW.obligation_id, NEW.format, NEW.algorithm,
190
+ NEW.digest_profile, NEW.content_sha256, NEW.content_size_bytes, NEW.row_count, NEW.row_manifest_sha256, NEW.chunk_count,
141
191
  NEW.rows_shared_from_row_set_id, NEW.recorded_at)
142
192
  IS DISTINCT FROM
143
- (OLD.row_set_id, OLD.row_set_kind, OLD.account_id, OLD.obligation_id, OLD.encoding, OLD.digest_profile,
144
- OLD.content_sha256, OLD.canonical_byte_count, OLD.row_count, OLD.row_manifest_sha256, OLD.chunk_count,
193
+ (OLD.row_set_id, OLD.row_set_kind, OLD.account_id, OLD.obligation_id, OLD.format, OLD.algorithm,
194
+ OLD.digest_profile, OLD.content_sha256, OLD.content_size_bytes, OLD.row_count, OLD.row_manifest_sha256, OLD.chunk_count,
145
195
  OLD.rows_shared_from_row_set_id, OLD.recorded_at) THEN
146
196
  RAISE EXCEPTION 'reporting row set content is immutable' USING ERRCODE = '23514';
147
197
  END IF;
@@ -151,10 +201,10 @@ BEGIN
151
201
  END IF;
152
202
  ELSIF TG_TABLE_NAME = 'adcp_reporting_row_chunks' THEN
153
203
  IF (NEW.row_set_id, NEW.chunk_index, NEW.account_id, NEW.obligation_id, NEW.first_ordinal, NEW.row_count,
154
- NEW.byte_count, NEW.sha256, NEW.segments)
204
+ NEW.canonical_size_bytes, NEW.sha256, NEW.segments)
155
205
  IS DISTINCT FROM
156
206
  (OLD.row_set_id, OLD.chunk_index, OLD.account_id, OLD.obligation_id, OLD.first_ordinal, OLD.row_count,
157
- OLD.byte_count, OLD.sha256, OLD.segments) THEN
207
+ OLD.canonical_size_bytes, OLD.sha256, OLD.segments) THEN
158
208
  RAISE EXCEPTION 'reporting row chunk content is immutable' USING ERRCODE = '23514';
159
209
  END IF;
160
210
  ELSIF TG_TABLE_NAME = 'adcp_reporting_chunk_bodies' THEN
@@ -188,6 +238,50 @@ BEGIN
188
238
  END LOOP;
189
239
  END
190
240
  $triggers$;
241
+
242
+ -- AdCP bounds on installs created before they existed (a fresh install has them inline).
243
+ DO $bounds$
244
+ DECLARE
245
+ offending BIGINT;
246
+ BEGIN
247
+ SELECT count(*) INTO offending FROM adcp_reporting_row_chunks
248
+ WHERE object_ref IS NOT NULL
249
+ AND (char_length(object_ref) NOT BETWEEN 1 AND 1024 OR char_length(native_version_ref) NOT BETWEEN 1 AND 1024);
250
+ IF offending > 0 THEN
251
+ RAISE EXCEPTION 'reporting row storage: % chunk(s) hold an object_ref or native_version_ref outside AdCP 1 to 1024 characters; re-sync them before applying this migration', offending
252
+ USING ERRCODE = '23514';
253
+ END IF;
254
+ IF NOT EXISTS (
255
+ SELECT 1 FROM pg_constraint
256
+ WHERE conname = 'adcp_reporting_row_chunks_object_ref_bounds'
257
+ AND conrelid = to_regclass(quote_ident(current_schema()) || '.adcp_reporting_row_chunks')
258
+ ) THEN
259
+ ALTER TABLE adcp_reporting_row_chunks ADD CONSTRAINT adcp_reporting_row_chunks_object_ref_bounds
260
+ CHECK (object_ref IS NULL OR char_length(object_ref) BETWEEN 1 AND 1024);
261
+ END IF;
262
+ IF NOT EXISTS (
263
+ SELECT 1 FROM pg_constraint
264
+ WHERE conname = 'adcp_reporting_row_chunks_native_version_ref_bounds'
265
+ AND conrelid = to_regclass(quote_ident(current_schema()) || '.adcp_reporting_row_chunks')
266
+ ) THEN
267
+ ALTER TABLE adcp_reporting_row_chunks ADD CONSTRAINT adcp_reporting_row_chunks_native_version_ref_bounds
268
+ CHECK (native_version_ref IS NULL OR char_length(native_version_ref) BETWEEN 1 AND 1024);
269
+ END IF;
270
+ END
271
+ $bounds$;
272
+
273
+ -- Open write intents written before the rename name objects objectKey / nativeVersion.
274
+ UPDATE adcp_reporting_row_write_intents
275
+ SET created_objects = (
276
+ SELECT COALESCE(jsonb_agg(
277
+ CASE WHEN jsonb_typeof(created) = 'object' AND created ? 'objectKey'
278
+ THEN jsonb_build_object('object_ref', created -> 'objectKey', 'native_version_ref', created -> 'nativeVersion')
279
+ ELSE created END
280
+ ORDER BY ordinality
281
+ ), '[]'::jsonb)
282
+ FROM jsonb_array_elements(created_objects) WITH ORDINALITY AS element(created, ordinality)
283
+ )
284
+ WHERE EXISTS (SELECT 1 FROM jsonb_array_elements(created_objects) AS element(created) WHERE jsonb_typeof(created) = 'object' AND created ? 'objectKey');
191
285
  `.trim();
192
286
  const REPORTING_ROW_STORAGE_TABLES = Object.freeze([
193
287
  "adcp_persistence_installation",
@@ -197,6 +291,15 @@ const REPORTING_ROW_STORAGE_TABLES = Object.freeze([
197
291
  "adcp_reporting_chunk_bodies",
198
292
  "adcp_reporting_row_write_intents"
199
293
  ]);
294
+ const REPORTING_ROW_STORAGE_REQUIRED_COLUMNS = Object.freeze([
295
+ "adcp_reporting_row_sets.format",
296
+ "adcp_reporting_row_sets.algorithm",
297
+ "adcp_reporting_row_sets.content_size_bytes",
298
+ "adcp_reporting_row_chunks.canonical_size_bytes",
299
+ "adcp_reporting_row_chunks.object_ref",
300
+ "adcp_reporting_row_chunks.native_version_ref",
301
+ "adcp_reporting_row_chunks.size_bytes"
302
+ ]);
200
303
  async function probeReportingRowStorageSchemaV1(pool) {
201
304
  const present = await pool.query(
202
305
  `SELECT table_name AS name FROM information_schema.tables
@@ -210,6 +313,18 @@ async function probeReportingRowStorageSchemaV1(pool) {
210
313
  `Reporting row storage requires REPORTING_ROW_STORAGE_MIGRATION; missing tables: ${missing.join(", ")}`
211
314
  );
212
315
  }
316
+ const columns = await pool.query(
317
+ `SELECT table_name || '.' || column_name AS name FROM information_schema.columns
318
+ WHERE table_schema = current_schema() AND table_name = ANY($1::text[])`,
319
+ [["adcp_reporting_row_chunks", "adcp_reporting_row_sets"]]
320
+ );
321
+ const columnNames = new Set(columns.rows.map((row) => row.name));
322
+ const absent = REPORTING_ROW_STORAGE_REQUIRED_COLUMNS.filter((name) => !columnNames.has(name));
323
+ if (absent.length > 0) {
324
+ throw new Error(
325
+ `Reporting row storage requires the current REPORTING_ROW_STORAGE_MIGRATION; missing columns: ${absent.join(", ")}`
326
+ );
327
+ }
213
328
  const installation = await pool.query(
214
329
  "SELECT installation_id::text AS installation_id FROM adcp_persistence_installation WHERE singleton"
215
330
  );
@@ -7,26 +7,51 @@
7
7
  * binding, and every byte a provider returns is verified against digests
8
8
  * committed in PostgreSQL before rows are released.
9
9
  */
10
+ /**
11
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
12
+ */
10
13
  export interface ReportingRowObjectContextV1 {
11
14
  signal: AbortSignal;
12
15
  }
16
+ /**
17
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
18
+ */
13
19
  export interface ReportingRowObjectLocationV1 {
14
20
  /** Non-secret provider coordinates (bucket, container, filesystem root name, endpoint host). */
15
21
  readonly location: Readonly<Record<string, string>>;
16
22
  /** Host-mapped client reference; never a secret. */
17
23
  readonly credentialRef?: string;
18
24
  }
25
+ /**
26
+ * Bounds AdCP puts on `object_ref` and `native_version_ref`
27
+ * (`reporting-file-object-ref.json`, `reporting-native-version-ref.json`):
28
+ * 1 to 1024 characters. The row-storage `CHECK` constraints enforce the same.
29
+ *
30
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
31
+ */
32
+ export declare const REPORTING_ROW_OBJECT_REF_MAX_LENGTH = 1024;
33
+ /**
34
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
35
+ */
36
+ export declare const REPORTING_ROW_NATIVE_VERSION_REF_MAX_LENGTH = 1024;
37
+ /**
38
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
39
+ */
19
40
  export interface ReportingRowObjectPutInputV1 extends ReportingRowObjectLocationV1 {
20
- key: string;
41
+ /** AdCP `object_ref`: decoded, destination-relative object key. */
42
+ objectRef: string;
21
43
  bytes: Uint8Array;
22
44
  contentType: 'application/x-ndjson' | 'application/gzip';
23
45
  /** Non-secret object metadata (installation and intent identifiers). */
24
46
  metadata: Readonly<Record<string, string>>;
25
47
  }
48
+ /**
49
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
50
+ */
26
51
  export interface ReportingRowObjectGetInputV1 extends ReportingRowObjectLocationV1 {
27
- key: string;
28
- /** Exact native version recorded at write time. */
29
- nativeVersion: string;
52
+ objectRef: string;
53
+ /** AdCP `native_version_ref`: the exact native version recorded at write time. */
54
+ nativeVersionRef: string;
30
55
  /** Byte range within the stored object; omit for the whole object. */
31
56
  range?: {
32
57
  offset: number;
@@ -35,10 +60,23 @@ export interface ReportingRowObjectGetInputV1 extends ReportingRowObjectLocation
35
60
  /** Hard cap on bytes read; providers abort beyond it. */
36
61
  maxBytes: number;
37
62
  }
63
+ /**
64
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
65
+ */
38
66
  export interface ReportingRowObjectDeleteInputV1 extends ReportingRowObjectLocationV1 {
39
- key: string;
40
- nativeVersion: string;
67
+ objectRef: string;
68
+ nativeVersionRef: string;
41
69
  }
70
+ /**
71
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
72
+ */
73
+ export interface ReportingRowObjectStatInputV1 extends ReportingRowObjectLocationV1 {
74
+ objectRef: string;
75
+ nativeVersionRef: string;
76
+ }
77
+ /**
78
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
79
+ */
42
80
  export interface ReportingRowObjectProviderV1 {
43
81
  /** Registry name, e.g. `filesystem`, `gcs`, `s3`, `azure_blob`. */
44
82
  readonly name: string;
@@ -60,35 +98,84 @@ export interface ReportingRowObjectProviderV1 {
60
98
  */
61
99
  putIfAbsent(input: ReportingRowObjectPutInputV1, context: ReportingRowObjectContextV1): Promise<{
62
100
  created: boolean;
63
- nativeVersion: string;
101
+ nativeVersionRef: string;
64
102
  }>;
65
103
  /** Bytes at the exact native version, or `null` when that version is absent. */
66
104
  get(input: ReportingRowObjectGetInputV1, context: ReportingRowObjectContextV1): Promise<Uint8Array | null>;
105
+ /**
106
+ * Metadata of the exact native version without reading its bytes: the version it resolved (which must equal the
107
+ * requested `nativeVersionRef`) and its stored size (`size_bytes`), or `null` when that version is absent. Optional. The commit transaction uses it to
108
+ * prove an adopted object is unchanged (native versions are write-unique, so the same version at
109
+ * the recorded size is the same bytes) without downloading it under the account lock. A provider
110
+ * without `stat` falls back to a full read there. Never return a size for a different version.
111
+ */
112
+ stat?(input: ReportingRowObjectStatInputV1, context: ReportingRowObjectContextV1): Promise<{
113
+ nativeVersionRef: string;
114
+ sizeBytes: number;
115
+ } | null>;
67
116
  /** Delete the exact native version. Idempotent: `absent` is success. */
68
117
  delete(input: ReportingRowObjectDeleteInputV1, context: ReportingRowObjectContextV1): Promise<'deleted' | 'absent'>;
69
118
  }
70
- /** Placeholders allowed in object key templates. */
119
+ /**
120
+ * Placeholders allowed in object key templates. `{period_date}` renders AdCP's `YYYY-MM-DD`: the calendar
121
+ * date of the period start in the period's `source_timezone` (never UTC, unless that is the source
122
+ * timezone). It is one path segment, so the default layout is
123
+ * `.../{account_key}/2026-10-02/{finality}/...`.
124
+ *
125
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
126
+ */
71
127
  export declare const REPORTING_ROW_KEY_PLACEHOLDERS: readonly ["prefix", "namespace_key", "account_key", "period_date", "finality", "revision_key", "content_sha256", "chunk_index"];
128
+ /**
129
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
130
+ */
72
131
  export declare const REPORTING_ROW_DEFAULT_KEY_TEMPLATE = "{prefix}/{namespace_key}/{account_key}/{period_date}/{finality}/{revision_key}/{content_sha256}/{chunk_index}";
73
- /** Validate an object key template; throws `INVALID_INPUT`. */
74
- export declare function assertReportingRowKeyTemplateV1(template: string): void;
132
+ /**
133
+ * Validate an object key template; throws `INVALID_INPUT`. Every template that writes must include
134
+ * `{revision_key}`, so no two row sets can ever name the same object key; `requireRevisionKey: false`
135
+ * is for a read-only binding that only reads rows written under an older template.
136
+ *
137
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
138
+ */
139
+ export declare function assertReportingRowKeyTemplateV1(template: string, options?: {
140
+ requireRevisionKey?: boolean;
141
+ }): void;
142
+ /**
143
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
144
+ */
75
145
  export declare function assertReportingRowPrefixV1(prefix: string): void;
76
- /** Pseudonymous account segment; never a raw account ID. */
146
+ /**
147
+ * Pseudonymous account segment; never a raw account ID.
148
+ *
149
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
150
+ */
77
151
  export declare function reportingRowAccountKeyV1(namespaceKey: string, accountId: string): string;
152
+ /**
153
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
154
+ */
78
155
  export declare function reportingRowRevisionKeyV1(rowSetId: string): string;
79
- /** Render one chunk's object key and confine it to the binding's owned prefix. */
156
+ /**
157
+ * Render one chunk's object key and confine it to the binding's owned prefix.
158
+ *
159
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
160
+ */
80
161
  export declare function renderReportingRowObjectKeyV1(input: {
81
162
  template: string;
82
163
  prefix: string;
83
164
  namespaceKey: string;
84
165
  accountId: string;
85
166
  rowSetId: string;
167
+ /** Period start instant; with `sourceTimezone`, renders `{period_date}`. Absent renders `undated`. */
86
168
  periodStart?: string;
169
+ /** The period's `source_timezone`; required whenever `periodStart` is set. Invalid values fail closed. */
170
+ sourceTimezone?: string;
87
171
  finality?: 'snapshot' | 'official';
88
172
  contentSha256: string;
89
173
  chunkIndex: number;
90
174
  compression: 'none' | 'gzip';
91
175
  }): string;
176
+ /**
177
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
178
+ */
92
179
  export interface FilesystemReportingRowObjectProviderOptionsV1 {
93
180
  /**
94
181
  * Root name → absolute directory. Bindings name a root (`location.root`);
@@ -100,12 +187,16 @@ export interface FilesystemReportingRowObjectProviderOptionsV1 {
100
187
  * Local filesystem provider for tests and single-host deployments. Create-only
101
188
  * writes use a temporary file, `fsync`, and `link()` to the final name, which
102
189
  * fails rather than replacing an existing object.
190
+ *
191
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
103
192
  */
104
193
  export declare function createFilesystemReportingRowObjectProviderV1(options: FilesystemReportingRowObjectProviderOptionsV1): ReportingRowObjectProviderV1;
105
194
  /**
106
195
  * Behavioural conformance for a custom object provider. Writes and deletes
107
196
  * only under `prefix`; run it against a dedicated test bucket or prefix.
108
197
  * Resolves with the checks that passed; rejects on the first violation.
198
+ *
199
+ * @experimental Reliable Reporting storage surface: breaking changes may ship in minor releases. See https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/REPORTING-STABILITY.md
109
200
  */
110
201
  export declare function runReportingRowObjectProviderConformanceV1(provider: ReportingRowObjectProviderV1, input: ReportingRowObjectLocationV1 & {
111
202
  prefix: string;