@adcp/sdk 17.0.0-beta.1 → 17.0.0-beta.2

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 (239) hide show
  1. package/dist/lib/adapters/keyed-account-store.js +1 -1
  2. package/dist/lib/adapters/keyed-account-store.mjs +1 -1
  3. package/dist/lib/adapters/version/3.0/brand-fields.d.mts +7 -1
  4. package/dist/lib/adapters/version/3.0/brand-fields.d.ts +7 -1
  5. package/dist/lib/adapters/version/3.0/brand-fields.js +139 -3
  6. package/dist/lib/adapters/version/3.0/brand-fields.mjs +136 -2
  7. package/dist/lib/adapters/version/3.0/index.js +6 -0
  8. package/dist/lib/adapters/version/3.0/index.mjs +7 -1
  9. package/dist/lib/adapters/version/types.d.mts +5 -0
  10. package/dist/lib/adapters/version/types.d.ts +5 -0
  11. package/dist/lib/canonical-references/index.js +2 -0
  12. package/dist/lib/canonical-references/index.mjs +2 -0
  13. package/dist/lib/client/account-changes.d.mts +3 -1
  14. package/dist/lib/client/account-changes.d.ts +3 -1
  15. package/dist/lib/client/account-changes.js +22 -2
  16. package/dist/lib/client/account-changes.mjs +22 -2
  17. package/dist/lib/conformance/oracle.js +3 -2
  18. package/dist/lib/conformance/oracle.mjs +3 -2
  19. package/dist/lib/core/AgentClient.d.mts +165 -1
  20. package/dist/lib/core/AgentClient.d.ts +165 -1
  21. package/dist/lib/core/AgentClient.js +446 -8
  22. package/dist/lib/core/AgentClient.mjs +446 -8
  23. package/dist/lib/core/ConversationTypes.d.mts +8 -0
  24. package/dist/lib/core/ConversationTypes.d.ts +8 -0
  25. package/dist/lib/core/ProtocolResponseParser.d.mts +1 -1
  26. package/dist/lib/core/ProtocolResponseParser.d.ts +1 -1
  27. package/dist/lib/core/ProtocolResponseParser.js +28 -18
  28. package/dist/lib/core/ProtocolResponseParser.mjs +11 -1
  29. package/dist/lib/core/SingleAgentClient.d.mts +115 -2
  30. package/dist/lib/core/SingleAgentClient.d.ts +115 -2
  31. package/dist/lib/core/SingleAgentClient.js +547 -67
  32. package/dist/lib/core/SingleAgentClient.mjs +534 -54
  33. package/dist/lib/core/TaskExecutor.d.mts +7 -0
  34. package/dist/lib/core/TaskExecutor.d.ts +7 -0
  35. package/dist/lib/core/TaskExecutor.js +18 -5
  36. package/dist/lib/core/TaskExecutor.mjs +18 -5
  37. package/dist/lib/core/task-status.d.mts +2 -0
  38. package/dist/lib/core/task-status.d.ts +2 -0
  39. package/dist/lib/core/task-status.js +11 -2
  40. package/dist/lib/core/task-status.mjs +9 -1
  41. package/dist/lib/identity-3.3/trust.d.mts +1 -4
  42. package/dist/lib/identity-3.3/trust.d.ts +1 -4
  43. package/dist/lib/identity-3.3/trust.js +34 -21
  44. package/dist/lib/identity-3.3/trust.mjs +34 -21
  45. package/dist/lib/identity-3.3/validation.js +5 -3
  46. package/dist/lib/identity-3.3/validation.mjs +6 -4
  47. package/dist/lib/index.d.mts +6 -52
  48. package/dist/lib/index.d.ts +6 -52
  49. package/dist/lib/index.js +11 -0
  50. package/dist/lib/index.mjs +7 -0
  51. package/dist/lib/media-buy/compatibility.js +6 -1
  52. package/dist/lib/media-buy/compatibility.mjs +6 -1
  53. package/dist/lib/protocols/mcp-scope.d.mts +3 -0
  54. package/dist/lib/protocols/mcp-scope.d.ts +3 -0
  55. package/dist/lib/protocols/mcp-scope.js +8 -3
  56. package/dist/lib/protocols/mcp-scope.mjs +8 -3
  57. package/dist/lib/reporting/inspection.js +1 -0
  58. package/dist/lib/reporting/inspection.mjs +1 -0
  59. package/dist/lib/reporting/ledger/coverage.d.mts +5 -0
  60. package/dist/lib/reporting/ledger/coverage.d.ts +5 -0
  61. package/dist/lib/reporting/ledger/coverage.js +37 -3
  62. package/dist/lib/reporting/ledger/coverage.mjs +36 -3
  63. package/dist/lib/reporting/ledger/handler.js +7 -4
  64. package/dist/lib/reporting/ledger/handler.mjs +7 -4
  65. package/dist/lib/reporting/ledger/producer.js +215 -69
  66. package/dist/lib/reporting/ledger/producer.mjs +217 -70
  67. package/dist/lib/reporting/ledger/schedule.d.mts +2 -2
  68. package/dist/lib/reporting/ledger/schedule.d.ts +2 -2
  69. package/dist/lib/reporting/ledger/schedule.js +8 -5
  70. package/dist/lib/reporting/ledger/schedule.mjs +8 -5
  71. package/dist/lib/reporting/ledger/status-ingest.d.mts +1 -1
  72. package/dist/lib/reporting/ledger/status-ingest.d.ts +1 -1
  73. package/dist/lib/reporting/ledger/status-ingest.js +4 -3
  74. package/dist/lib/reporting/ledger/status-ingest.mjs +4 -3
  75. package/dist/lib/reporting/ledger/types.d.mts +36 -1
  76. package/dist/lib/reporting/ledger/types.d.ts +36 -1
  77. package/dist/lib/reporting/service/index.d.mts +14 -1
  78. package/dist/lib/reporting/service/index.d.ts +14 -1
  79. package/dist/lib/reporting/service/index.js +24 -12
  80. package/dist/lib/reporting/service/index.mjs +24 -12
  81. package/dist/lib/schemas/browser.d.mts +2 -0
  82. package/dist/lib/schemas/browser.d.ts +2 -0
  83. package/dist/lib/schemas/browser.js +3 -0
  84. package/dist/lib/schemas/browser.mjs +2 -0
  85. package/dist/lib/schemas/index.d.mts +5 -3
  86. package/dist/lib/schemas/index.d.ts +5 -3
  87. package/dist/lib/schemas/index.js +5 -0
  88. package/dist/lib/schemas/index.mjs +4 -0
  89. package/dist/lib/server/account-response-identity.d.mts +17 -0
  90. package/dist/lib/server/account-response-identity.d.ts +17 -0
  91. package/dist/lib/server/account-response-identity.js +52 -0
  92. package/dist/lib/server/account-response-identity.mjs +28 -0
  93. package/dist/lib/server/account-sync-contract.js +2 -2
  94. package/dist/lib/server/account-sync-contract.mjs +2 -2
  95. package/dist/lib/server/create-adcp-server.js +33 -20
  96. package/dist/lib/server/create-adcp-server.mjs +36 -12
  97. package/dist/lib/server/decisioning/account.d.mts +36 -10
  98. package/dist/lib/server/decisioning/account.d.ts +36 -10
  99. package/dist/lib/server/decisioning/account.js +33 -14
  100. package/dist/lib/server/decisioning/account.mjs +33 -14
  101. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +5 -5
  102. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +5 -5
  103. package/dist/lib/server/decisioning/runtime/from-platform.js +4 -4
  104. package/dist/lib/server/decisioning/runtime/from-platform.mjs +4 -4
  105. package/dist/lib/server/media-buy-store.d.mts +26 -12
  106. package/dist/lib/server/media-buy-store.d.ts +26 -12
  107. package/dist/lib/server/media-buy-store.js +69 -21
  108. package/dist/lib/server/media-buy-store.mjs +69 -21
  109. package/dist/lib/server/notification-subscriptions/runtime.d.mts +1 -1
  110. package/dist/lib/server/notification-subscriptions/runtime.d.ts +1 -1
  111. package/dist/lib/server/notification-subscriptions/runtime.js +4 -0
  112. package/dist/lib/server/notification-subscriptions/runtime.mjs +4 -0
  113. package/dist/lib/server/responses.js +9 -1
  114. package/dist/lib/server/responses.mjs +9 -1
  115. package/dist/lib/signing/agent-fetch.d.mts +1 -1
  116. package/dist/lib/signing/agent-fetch.d.ts +1 -1
  117. package/dist/lib/signing/agent-fetch.js +3 -3
  118. package/dist/lib/signing/agent-fetch.mjs +3 -3
  119. package/dist/lib/testing/storyboard/canonical-format-satisfaction.js +60 -14
  120. package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs +60 -14
  121. package/dist/lib/testing/storyboard/compliance.d.mts +2 -5
  122. package/dist/lib/testing/storyboard/compliance.d.ts +2 -5
  123. package/dist/lib/testing/storyboard/compliance.js +62 -13
  124. package/dist/lib/testing/storyboard/compliance.mjs +63 -14
  125. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.mts +2 -0
  126. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts +2 -0
  127. package/dist/lib/testing/storyboard/request-signing/vector-loader.js +11 -0
  128. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +10 -0
  129. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  130. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  131. package/dist/lib/types/accept-proposal.d.ts +20 -28
  132. package/dist/lib/types/asset-instances.d.mts +1 -1
  133. package/dist/lib/types/asset-instances.d.ts +1 -1
  134. package/dist/lib/types/buy-products.d.ts +20 -28
  135. package/dist/lib/types/check-governance.d.ts +20 -28
  136. package/dist/lib/types/comply-test-controller.d.ts +20 -28
  137. package/dist/lib/types/control-media-buy.d.ts +20 -28
  138. package/dist/lib/types/core.generated.d.mts +273 -184
  139. package/dist/lib/types/core.generated.d.ts +273 -184
  140. package/dist/lib/types/create-media-buy.d.ts +20 -28
  141. package/dist/lib/types/get-media-buys.d.ts +20 -28
  142. package/dist/lib/types/get-products.d.ts +20 -28
  143. package/dist/lib/types/index.d.mts +6 -1
  144. package/dist/lib/types/index.d.ts +6 -1
  145. package/dist/lib/types/index.js +6 -1
  146. package/dist/lib/types/index.mjs +4 -1
  147. package/dist/lib/types/inline-enums.generated.d.mts +30 -4
  148. package/dist/lib/types/inline-enums.generated.d.ts +30 -4
  149. package/dist/lib/types/inline-enums.generated.js +41 -2
  150. package/dist/lib/types/inline-enums.generated.mjs +28 -2
  151. package/dist/lib/types/list-products.d.ts +20 -28
  152. package/dist/lib/types/list-proposals.d.ts +20 -28
  153. package/dist/lib/types/media-buy-delivery-report.d.mts +6 -0
  154. package/dist/lib/types/media-buy-delivery-report.d.ts +6 -0
  155. package/dist/lib/types/media-buy-delivery-report.js +16 -0
  156. package/dist/lib/types/media-buy-delivery-report.mjs +0 -0
  157. package/dist/lib/types/refine-proposals.d.ts +20 -28
  158. package/dist/lib/types/request-proposals.d.ts +20 -28
  159. package/dist/lib/types/schemas.generated.d.ts +63113 -127273
  160. package/dist/lib/types/schemas.generated.js +759 -770
  161. package/dist/lib/types/schemas.generated.mjs +759 -770
  162. package/dist/lib/types/targeting-keys.generated.d.mts +4 -0
  163. package/dist/lib/types/targeting-keys.generated.d.ts +4 -0
  164. package/dist/lib/types/targeting-keys.generated.js +109 -0
  165. package/dist/lib/types/targeting-keys.generated.mjs +84 -0
  166. package/dist/lib/types/tools.generated.d.mts +2 -68
  167. package/dist/lib/types/tools.generated.d.ts +2 -68
  168. package/dist/lib/types/update-media-buy.d.ts +20 -28
  169. package/dist/lib/utils/adcp-version-config.js +5 -2
  170. package/dist/lib/utils/adcp-version-config.mjs +5 -2
  171. package/dist/lib/utils/response-unwrapper.js +6 -1
  172. package/dist/lib/utils/response-unwrapper.mjs +6 -1
  173. package/dist/lib/v2/format-schema/resolver.js +1 -1
  174. package/dist/lib/v2/format-schema/resolver.mjs +1 -1
  175. package/dist/lib/v2/projection/creative-delivery.js +104 -29
  176. package/dist/lib/v2/projection/creative-delivery.mjs +108 -29
  177. package/dist/lib/v2/projection/index.d.mts +1 -0
  178. package/dist/lib/v2/projection/index.d.ts +1 -0
  179. package/dist/lib/v2/projection/index.js +3 -0
  180. package/dist/lib/v2/projection/index.mjs +2 -0
  181. package/dist/lib/v2/projection/legacy-metadata.d.mts +19 -0
  182. package/dist/lib/v2/projection/legacy-metadata.d.ts +19 -0
  183. package/dist/lib/v2/projection/legacy-metadata.js +91 -0
  184. package/dist/lib/v2/projection/legacy-metadata.mjs +86 -0
  185. package/dist/lib/v2/projection/v1-to-v2.d.mts +1 -1
  186. package/dist/lib/v2/projection/v1-to-v2.d.ts +1 -1
  187. package/dist/lib/v2/projection/v1-to-v2.js +12 -9
  188. package/dist/lib/v2/projection/v1-to-v2.mjs +12 -9
  189. package/dist/lib/validation/budget-allocation.d.mts +3 -3
  190. package/dist/lib/validation/budget-allocation.d.ts +3 -3
  191. package/dist/lib/validation/compact-schema.d.mts +27 -0
  192. package/dist/lib/validation/compact-schema.d.ts +27 -0
  193. package/dist/lib/validation/compact-schema.js +276 -0
  194. package/dist/lib/validation/compact-schema.mjs +250 -0
  195. package/dist/lib/validation/media-buy-delivery-report.d.mts +8 -0
  196. package/dist/lib/validation/media-buy-delivery-report.d.ts +8 -0
  197. package/dist/lib/validation/media-buy-delivery-report.js +31 -0
  198. package/dist/lib/validation/media-buy-delivery-report.mjs +7 -0
  199. package/dist/lib/validation/nonempty-array.d.mts +15 -0
  200. package/dist/lib/validation/nonempty-array.d.ts +15 -0
  201. package/dist/lib/validation/nonempty-array.js +41 -0
  202. package/dist/lib/validation/nonempty-array.mjs +7 -0
  203. package/dist/lib/validation/schema-diagnostic-origins.d.mts +21 -0
  204. package/dist/lib/validation/schema-diagnostic-origins.d.ts +21 -0
  205. package/dist/lib/validation/schema-diagnostic-origins.js +193 -0
  206. package/dist/lib/validation/schema-diagnostic-origins.mjs +169 -0
  207. package/dist/lib/validation/schema-loader.js +86 -16
  208. package/dist/lib/validation/schema-loader.mjs +82 -16
  209. package/dist/lib/validation/schema-validator.d.mts +10 -19
  210. package/dist/lib/validation/schema-validator.d.ts +10 -19
  211. package/dist/lib/validation/schema-validator.js +135 -42
  212. package/dist/lib/validation/schema-validator.mjs +139 -43
  213. package/dist/lib/validation/sync-creatives.d.mts +22 -22
  214. package/dist/lib/validation/sync-creatives.d.ts +22 -22
  215. package/dist/lib/version.d.mts +3 -3
  216. package/dist/lib/version.d.ts +3 -3
  217. package/dist/lib/version.js +3 -3
  218. package/dist/lib/version.mjs +3 -3
  219. package/dist/lib/webhooks/index.d.mts +3 -0
  220. package/dist/lib/webhooks/index.d.ts +3 -0
  221. package/dist/lib/webhooks/index.js +3 -0
  222. package/dist/lib/webhooks/index.mjs +2 -0
  223. package/dist/lib/webhooks/media-buy-delivery.d.mts +43 -0
  224. package/dist/lib/webhooks/media-buy-delivery.d.ts +43 -0
  225. package/dist/lib/webhooks/media-buy-delivery.js +119 -0
  226. package/dist/lib/webhooks/media-buy-delivery.mjs +94 -0
  227. package/docs/TYPE-SUMMARY.md +2 -2
  228. package/docs/guides/DELIVERY-WEBHOOK-PARSING.md +142 -0
  229. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +15 -0
  230. package/docs/guides/REPORTING-LEDGER.md +93 -0
  231. package/docs/guides/REPORTING-STABILITY.md +1 -1
  232. package/docs/guides/TYPED-CLIENT-TOOLS.md +49 -0
  233. package/docs/llms.txt +2 -2
  234. package/docs/migration-14.x-rc-worksheet.md +4 -4
  235. package/docs/migration-16-to-17.md +191 -14
  236. package/docs/migration-6.6-to-6.7.md +14 -0
  237. package/docs/migration-account-change-feed.md +15 -0
  238. package/docs/recipes/verifying-inbound-webhooks.md +18 -2
  239. package/package.json +2 -2
@@ -10,9 +10,10 @@ import {
10
10
  reportingScheduleOriginV1,
11
11
  reportingUtcOffsetChangesV1,
12
12
  REPORTING_SOURCE_CONTRACT_VERSION_V1,
13
+ ReportingInstantV1Schema,
13
14
  validateReportingSourceExecutionV1
14
15
  } from "../source/index.mjs";
15
- import { reportingLedgerSuccessor } from "./coverage.mjs";
16
+ import { assertReportingLedgerHistoryDoesNotOverlap, reportingLedgerSuccessor } from "./coverage.mjs";
16
17
  import {
17
18
  assertReportingCalendarDayPeriod,
18
19
  assertReportingCalendarDaySource,
@@ -50,7 +51,10 @@ function createReportingProducer(options) {
50
51
  subscribersByAccount.set(subscriber.account_id, count);
51
52
  }
52
53
  return {
53
- async installConfiguration(input) {
54
+ async installConfiguration(input, installOptions = {}) {
55
+ if (Object.hasOwn(input, "historyStart") || Object.hasOwn(input, "historyStartOrdinal")) {
56
+ throw new TypeError("Reporting history must be supplied through trusted install options");
57
+ }
54
58
  const normalizedInput = {
55
59
  ...structuredClone(input),
56
60
  schedule: {
@@ -77,6 +81,19 @@ function createReportingProducer(options) {
77
81
  if (!replayFingerprints.includes(replay.semanticFingerprint)) {
78
82
  throw new Error("Reporting configuration generation is immutable");
79
83
  }
84
+ if (installOptions.historyStart !== void 0) {
85
+ const requested = historyInstant(installOptions.historyStart);
86
+ const ownedStart = replay.historyStart !== void 0 ? Date.parse(replay.historyStart) : void 0;
87
+ if (ownedStart !== void 0 && requested === ownedStart) return replay;
88
+ if (requested > Date.now()) throw new RangeError("Reporting historyStart must not be in the future");
89
+ if (ownedStart !== void 0 && requested >= ownedStart) return replay;
90
+ const schedule = reportingPeriodSchedule(replay);
91
+ const start = schedule.boundary(schedule.ceil(requested));
92
+ const previous = ownedStart ?? schedule.boundary(Math.max(0, schedule.ceil(Date.parse(replay.installedAt))));
93
+ if (start < previous) {
94
+ throw new Error("Use extendConfigurationHistory to change an installed history boundary");
95
+ }
96
+ }
80
97
  return replay;
81
98
  }
82
99
  validateConfigurationAgainstOffering(normalizedInput, requiredOffering(offeringById, normalizedInput.offeringId));
@@ -109,12 +126,59 @@ function createReportingProducer(options) {
109
126
  installedAt,
110
127
  semanticFingerprint: calendarRules ? reportingCalendarDayFingerprint(normalizedInput, calendarRules) : semanticFingerprint
111
128
  };
112
- return (await options.store.putConfiguration(configuration)).value;
129
+ if (installOptions.historyStart !== void 0) {
130
+ Object.assign(
131
+ configuration,
132
+ normalizeConfigurationHistory(
133
+ configuration,
134
+ installOptions.historyStart,
135
+ requiredOffering(offeringById, configuration.offeringId),
136
+ Date.parse(installedAt)
137
+ )
138
+ );
139
+ }
140
+ assertReportingLedgerHistoryDoesNotOverlap(configuration, existing);
141
+ const stored = (await options.store.putConfiguration(configuration)).value;
142
+ if (configuration.historyStart !== void 0) {
143
+ const storedSchedule = stored.historyStart === void 0 ? reportingPeriodSchedule(stored) : void 0;
144
+ const storedStart = stored.historyStart !== void 0 ? Date.parse(stored.historyStart) : storedSchedule.boundary(Math.max(0, storedSchedule.ceil(Date.parse(stored.installedAt))));
145
+ if (storedStart > Date.parse(configuration.historyStart)) {
146
+ throw new Error("Use extendConfigurationHistory to change an installed history boundary");
147
+ }
148
+ }
149
+ return stored;
150
+ },
151
+ async extendConfigurationHistory(input) {
152
+ if (!options.store.extendConfigurationHistory) {
153
+ throw new TypeError("Reporting ledger store does not support history extension");
154
+ }
155
+ const configuration = (await options.store.listConfigurations()).find(
156
+ (value) => value.configurationId === input.configurationId
157
+ );
158
+ if (!configuration) throw new Error("Unknown reporting configuration");
159
+ if (configuration.historyStart !== void 0) {
160
+ const requested = historyInstant(input.historyStart);
161
+ if (requested === Date.parse(configuration.historyStart)) return configuration;
162
+ const schedule = reportingPeriodSchedule(configuration);
163
+ if (schedule.boundary(schedule.ceil(requested)) === Date.parse(configuration.historyStart)) {
164
+ return configuration;
165
+ }
166
+ }
167
+ const history = normalizeConfigurationHistory(
168
+ configuration,
169
+ input.historyStart,
170
+ requiredOffering(offeringById, configuration.offeringId),
171
+ Date.now()
172
+ );
173
+ return options.store.extendConfigurationHistory({ configurationId: input.configurationId, ...history });
113
174
  },
114
175
  async planObligations(now = (/* @__PURE__ */ new Date()).toISOString(), planOptions = {}) {
115
176
  const nowMs = instant(now, "now");
116
177
  const maxObligations = planOptions.maxObligations ?? 1e3;
117
178
  positiveInteger(maxObligations, "maxObligations");
179
+ const maxHistory = planOptions.maxHistoryObligationsPerSource ?? maxObligations;
180
+ nonnegativeInteger(maxHistory, "maxHistoryObligationsPerSource");
181
+ const historyBySource = /* @__PURE__ */ new Map();
118
182
  const created = [];
119
183
  let attempted = 0;
120
184
  const configurations = (await options.store.listConfigurations(planOptions.account_id)).sort(
@@ -122,74 +186,86 @@ function createReportingProducer(options) {
122
186
  );
123
187
  const obligationsByAccount = /* @__PURE__ */ new Map();
124
188
  const retiredByAccount = /* @__PURE__ */ new Map();
125
- for (const configuration of configurations) {
126
- const successor = reportingLedgerSuccessor(configuration, configurations);
127
- const anchor = instant(configuration.schedule.anchor, "schedule.anchor");
128
- const effectiveFrom = Math.max(anchor, instant(configuration.installedAt, "installedAt"));
129
- let schedule;
130
- try {
131
- schedule = reportingPeriodSchedule(configuration);
132
- } catch (error) {
133
- const predecessor = configurations.find(
134
- (candidate) => reportingLedgerSuccessor(candidate, configurations)?.configurationId === configuration.configurationId
135
- );
136
- if (isFrozenCalendarRulesMismatch(error) && !successor && predecessor && runtimeCanResolveCalendarGeneration(predecessor)) {
189
+ for (const historyPass of [false, true]) {
190
+ for (const configuration of configurations) {
191
+ const successor = reportingLedgerSuccessor(configuration, configurations);
192
+ const anchor = instant(configuration.schedule.anchor, "schedule.anchor");
193
+ const effectiveFrom = configuration.historyStart ? instant(configuration.historyStart, "historyStart") : Math.max(anchor, instant(configuration.installedAt, "installedAt"));
194
+ let schedule;
195
+ try {
196
+ schedule = reportingPeriodSchedule(configuration);
197
+ } catch (error) {
198
+ const predecessor = configurations.find(
199
+ (candidate) => reportingLedgerSuccessor(candidate, configurations)?.configurationId === configuration.configurationId
200
+ );
201
+ if (isFrozenCalendarRulesMismatch(error) && !successor && predecessor && runtimeCanResolveCalendarGeneration(predecessor)) {
202
+ continue;
203
+ }
204
+ if (!isFrozenCalendarRulesMismatch(error) || !await supersededCalendarGenerationIsDurablyCovered(options.store, configuration, successor)) {
205
+ throw error;
206
+ }
137
207
  continue;
138
208
  }
139
- if (!isFrozenCalendarRulesMismatch(error) || !await supersededCalendarGenerationIsDurablyCovered(options.store, configuration, successor)) {
140
- throw error;
209
+ const generationEndValue = Math.min(
210
+ successor ? instant(successor.installedAt, "successor.installedAt") : Number.POSITIVE_INFINITY,
211
+ configuration.supersededAt ? instant(configuration.supersededAt, "supersededAt") : Number.POSITIVE_INFINITY
212
+ );
213
+ const generationEnd = Number.isFinite(generationEndValue) ? generationEndValue : void 0;
214
+ const effectiveUntil = Math.min(nowMs, generationEnd ?? nowMs);
215
+ const ownedFirst = configuration.historyStartOrdinal ?? Math.max(0, schedule.ceil(effectiveFrom));
216
+ const liveFirst = Math.max(0, schedule.ceil(instant(configuration.installedAt, "installedAt")));
217
+ const first = historyPass ? ownedFirst : Math.max(ownedFirst, liveFirst);
218
+ const ownershipLast = generationEnd ? schedule.ceil(effectiveUntil) - 1 : Number.POSITIVE_INFINITY;
219
+ if (ownershipLast < first) continue;
220
+ const latestClosed = schedule.floor(
221
+ Number.isFinite(ownershipLast) ? Math.min(nowMs, schedule.boundary(ownershipLast + 1)) : nowMs
222
+ ) - 1;
223
+ const last = Math.min(ownershipLast, latestClosed, historyPass ? liveFirst - 1 : Number.POSITIVE_INFINITY);
224
+ let accountObligations = obligationsByAccount.get(configuration.account.account_id);
225
+ if (!accountObligations) {
226
+ accountObligations = await options.store.listObligations(configuration.account.account_id);
227
+ obligationsByAccount.set(configuration.account.account_id, accountObligations);
141
228
  }
142
- continue;
143
- }
144
- const generationEndValue = Math.min(
145
- successor ? instant(successor.installedAt, "successor.installedAt") : Number.POSITIVE_INFINITY,
146
- configuration.supersededAt ? instant(configuration.supersededAt, "supersededAt") : Number.POSITIVE_INFINITY
147
- );
148
- const generationEnd = Number.isFinite(generationEndValue) ? generationEndValue : void 0;
149
- const effectiveUntil = Math.min(nowMs, generationEnd ?? nowMs);
150
- const first = Math.max(0, schedule.ceil(effectiveFrom));
151
- const ownershipLast = generationEnd ? schedule.ceil(effectiveUntil) - 1 : Number.POSITIVE_INFINITY;
152
- if (ownershipLast < first) continue;
153
- const latestClosed = schedule.floor(
154
- Number.isFinite(ownershipLast) ? Math.min(nowMs, schedule.boundary(ownershipLast + 1)) : nowMs
155
- ) - 1;
156
- const last = Math.min(ownershipLast, latestClosed);
157
- let accountObligations = obligationsByAccount.get(configuration.account.account_id);
158
- if (!accountObligations) {
159
- accountObligations = await options.store.listObligations(configuration.account.account_id);
160
- obligationsByAccount.set(configuration.account.account_id, accountObligations);
161
- }
162
- let accountRetired = retiredByAccount.get(configuration.account.account_id);
163
- if (!accountRetired) {
164
- accountRetired = await options.store.listRetiredObligationOrdinals?.(configuration.account.account_id) ?? [];
165
- retiredByAccount.set(configuration.account.account_id, accountRetired);
166
- }
167
- const existingOrdinals = [
168
- ...accountObligations.filter((value) => value.configurationId === configuration.configurationId).map((value) => value.periodOrdinal),
169
- ...accountRetired.filter((value) => value.configurationId === configuration.configurationId).map((value) => value.periodOrdinal)
170
- ];
171
- const candidates = missingOrdinals(first, last, existingOrdinals, maxObligations - attempted);
172
- for (const ordinal of candidates) {
173
- attempted += 1;
174
- const obligation = planObligation(configuration, ordinal, now);
175
- const written = await options.store.putObligation(obligation);
176
- if (written.inserted) {
177
- created.push(written.value);
178
- await reconcileReportingStatusLifecycleV1({
179
- store: options.store,
180
- reporting_obligation_id: written.value.reporting_obligation_id,
181
- // A fallback clock, never a pinned cutoff. `now` is this host's
182
- // instant: a host running fast pinned a cutoff ahead of the
183
- // database, the watermark was stamped with it, and every
184
- // database-timestamped change inside that skew — a revocation,
185
- // a receipt — landed behind the watermark and never made the
186
- // obligation due again, so a `complete` transition and its
187
- // webhook stood over state that had already contradicted it.
188
- now: () => new Date(now),
189
- subscribers: options.subscribers
190
- });
229
+ let accountRetired = retiredByAccount.get(configuration.account.account_id);
230
+ if (!accountRetired) {
231
+ accountRetired = await options.store.listRetiredObligationOrdinals?.(configuration.account.account_id) ?? [];
232
+ retiredByAccount.set(configuration.account.account_id, accountRetired);
233
+ }
234
+ const existingOrdinals = [
235
+ ...accountObligations.filter((value) => value.configurationId === configuration.configurationId).map((value) => value.periodOrdinal),
236
+ ...accountRetired.filter((value) => value.configurationId === configuration.configurationId).map((value) => value.periodOrdinal)
237
+ ];
238
+ const historyRemaining = maxHistory - (historyBySource.get(configuration.offeringId) ?? 0);
239
+ const candidates = missingOrdinals(
240
+ first,
241
+ last,
242
+ existingOrdinals,
243
+ Math.min(maxObligations - attempted, historyPass ? historyRemaining : maxObligations)
244
+ );
245
+ for (const ordinal of candidates) {
246
+ attempted += 1;
247
+ if (historyPass)
248
+ historyBySource.set(configuration.offeringId, (historyBySource.get(configuration.offeringId) ?? 0) + 1);
249
+ const obligation = planObligation(configuration, ordinal, now);
250
+ const written = await options.store.putObligation(obligation);
251
+ if (written.inserted) {
252
+ created.push(written.value);
253
+ await reconcileReportingStatusLifecycleV1({
254
+ store: options.store,
255
+ reporting_obligation_id: written.value.reporting_obligation_id,
256
+ // A fallback clock, never a pinned cutoff. `now` is this host's
257
+ // instant: a host running fast pinned a cutoff ahead of the
258
+ // database, the watermark was stamped with it, and every
259
+ // database-timestamped change inside that skew — a revocation,
260
+ // a receipt — landed behind the watermark and never made the
261
+ // obligation due again, so a `complete` transition and its
262
+ // webhook stood over state that had already contradicted it.
263
+ now: () => new Date(now),
264
+ subscribers: options.subscribers
265
+ });
266
+ }
267
+ if (attempted >= maxObligations) return created;
191
268
  }
192
- if (attempted >= maxObligations) return created;
193
269
  }
194
270
  }
195
271
  return created;
@@ -197,6 +273,9 @@ function createReportingProducer(options) {
197
273
  async runWorker(workerOptions = {}) {
198
274
  const now = workerOptions.now ?? (() => /* @__PURE__ */ new Date());
199
275
  const maxIterations = workerOptions.maxIterations ?? 25;
276
+ const maxHistory = workerOptions.maxHistorySlicesPerSource ?? 10;
277
+ nonnegativeInteger(maxHistory, "maxHistorySlicesPerSource");
278
+ const historyBySource = /* @__PURE__ */ new Map();
200
279
  const retryDelay = workerOptions.retryDelayMilliseconds ?? 3e4;
201
280
  const executionDeadline = workerOptions.executionDeadlineMilliseconds ?? EXECUTION_DEADLINE_MS;
202
281
  const settlementGrace = workerOptions.settlementGraceMilliseconds ?? DEFAULT_SETTLEMENT_GRACE_MS;
@@ -210,6 +289,10 @@ function createReportingProducer(options) {
210
289
  if (settlementGrace > MAX_SETTLEMENT_GRACE_MS) {
211
290
  throw new RangeError(`settlementGraceMilliseconds must not exceed ${MAX_SETTLEMENT_GRACE_MS}`);
212
291
  }
292
+ const historyOfferingIds = /* @__PURE__ */ new Set([
293
+ ...offeringById.keys(),
294
+ ...(await options.store.listConfigurations(workerOptions.account_id)).map((value) => value.offeringId)
295
+ ]);
213
296
  const owner = `reporting-worker-${randomUUID()}`;
214
297
  const counts = { claimed: 0, revisionsCommitted: 0, notReady: 0, failed: 0, reconcilesDeferred: 0 };
215
298
  const reconcileQuietly = async (reporting_obligation_id, nowAt) => {
@@ -250,14 +333,35 @@ function createReportingProducer(options) {
250
333
  owner,
251
334
  now: nowValue.toISOString(),
252
335
  leaseMilliseconds: DEFAULT_LEASE_MS,
336
+ excludeHistoryOfferingIds: [.../* @__PURE__ */ new Set([...historyOfferingIds, ...historyBySource.keys()])].filter(
337
+ (id) => (historyBySource.get(id) ?? 0) >= maxHistory
338
+ ),
253
339
  ...workerOptions.account_id ? { account_id: workerOptions.account_id } : {}
254
340
  });
255
341
  if (!lease) break;
342
+ if (lease.obligation.history && (historyBySource.get(lease.obligation.offeringId) ?? 0) >= maxHistory) {
343
+ await options.store.releaseObligationLease(lease);
344
+ throw new TypeError("Reporting ledger store must honor excludeHistoryOfferingIds");
345
+ }
256
346
  counts.claimed += 1;
347
+ if (lease.obligation.history) {
348
+ const sourceId = lease.obligation.offeringId;
349
+ historyBySource.set(sourceId, (historyBySource.get(sourceId) ?? 0) + 1);
350
+ }
257
351
  let executionDeadlineTimer;
258
- let activeRecoveryDeadlineAt = lease.obligation.recoveryDeadlineAt;
352
+ let activeRecoveryDeadlineAt = lease.obligation.historyRecoveryDeadlineAt ?? lease.obligation.recoveryDeadlineAt;
259
353
  try {
260
354
  const obligation = lease.obligation;
355
+ if (obligation.history && !obligation.historyRecoveryDeadlineAt) {
356
+ obligation.historyRecoveryDeadlineAt = new Date(
357
+ Math.max(
358
+ Date.parse(obligation.recoveryDeadlineAt),
359
+ nowValue.getTime() + obligation.schedule.recoveryWindowMilliseconds
360
+ )
361
+ ).toISOString();
362
+ activeRecoveryDeadlineAt = obligation.historyRecoveryDeadlineAt;
363
+ await options.store.updateObligation(obligation, lease);
364
+ }
261
365
  const offering = requiredOffering(offeringById, obligation.offeringId);
262
366
  if (isReportingCalendarDay(obligation.schedule, obligation.period.sourceTimezone)) {
263
367
  try {
@@ -283,7 +387,10 @@ function createReportingProducer(options) {
283
387
  if (scheduledAt) {
284
388
  const recoveryWindow = instant(obligation.recoveryDeadlineAt, "recoveryDeadlineAt") - instant(obligation.expectedAt, "expectedAt");
285
389
  activeRecoveryDeadlineAt = new Date(
286
- instant(scheduledAt, "nextPublicationAt") + recoveryWindow
390
+ Math.max(
391
+ obligation.history ? Date.parse(activeRecoveryDeadlineAt) : Number.NEGATIVE_INFINITY,
392
+ instant(scheduledAt, "nextPublicationAt") + recoveryWindow
393
+ )
287
394
  ).toISOString();
288
395
  }
289
396
  }
@@ -454,6 +561,42 @@ async function supersededCalendarGenerationIsDurablyCovered(store, configuration
454
561
  await store.listObligations(configuration.account.account_id)
455
562
  );
456
563
  }
564
+ function historyInstant(value) {
565
+ const parsed = ReportingInstantV1Schema.safeParse(value);
566
+ if (!parsed.success) {
567
+ throw new TypeError("historyStart must be a valid RFC 3339 instant with at most millisecond precision");
568
+ }
569
+ return instant(parsed.data, "historyStart");
570
+ }
571
+ function normalizeConfigurationHistory(configuration, requested, offering, now) {
572
+ const requestedMs = historyInstant(requested);
573
+ if (requestedMs < now - offering.retentionDays * 864e5) {
574
+ throw new RangeError("Reporting historyStart is outside source retentionDays");
575
+ }
576
+ if (requestedMs > now) throw new RangeError("Reporting historyStart must not be in the future");
577
+ const schedule = reportingPeriodSchedule(configuration);
578
+ const ordinal = schedule.ceil(requestedMs);
579
+ const start = schedule.boundary(ordinal);
580
+ const previousStart = configuration.historyStart ? Date.parse(configuration.historyStart) : schedule.boundary(
581
+ Math.max(
582
+ 0,
583
+ schedule.ceil(Math.max(Date.parse(configuration.schedule.anchor), Date.parse(configuration.installedAt)))
584
+ )
585
+ );
586
+ if (requestedMs > previousStart || start > previousStart) {
587
+ throw new RangeError("Reporting historyStart may only move earlier");
588
+ }
589
+ if (isReportingCalendarDay(configuration.schedule, configuration.sourceTimezone)) {
590
+ assertReportingCalendarDaySource(configuration.schedule, configuration.sourceTimezone, offering, now, start);
591
+ const previousFirst = configuration.historyStartOrdinal ?? Math.max(0, schedule.ceil(previousStart));
592
+ for (let selected = ordinal; selected < previousFirst; selected += 1) schedule.period(selected);
593
+ } else if (configuration.schedule.alignment && configuration.schedule.alignment !== "billing_cycle") {
594
+ if (reportingUtcOffsetChangesV1(configuration.sourceTimezone, start, Math.max(now, start))) {
595
+ throw new Error("Reporting history crosses a timezone offset change; install a calendar-day generation");
596
+ }
597
+ }
598
+ return { historyStart: new Date(start).toISOString(), historyStartOrdinal: ordinal };
599
+ }
457
600
  function planObligation(configuration, periodOrdinal, createdAt) {
458
601
  const schedule = reportingPeriodSchedule(configuration);
459
602
  const { start, end } = schedule.period(periodOrdinal);
@@ -482,6 +625,7 @@ function planObligation(configuration, periodOrdinal, createdAt) {
482
625
  ...configuration.finalityPolicy ? { finalityPolicy: structuredClone(configuration.finalityPolicy) } : {},
483
626
  ...configuration.canonicalization ? { canonicalization: structuredClone(configuration.canonicalization) } : {},
484
627
  periodOrdinal,
628
+ ...configuration.historyStart !== void 0 && periodOrdinal < Math.max(0, schedule.ceil(Date.parse(configuration.installedAt))) ? { history: true } : {},
485
629
  period: {
486
630
  start: new Date(start).toISOString(),
487
631
  end: new Date(end).toISOString(),
@@ -548,7 +692,10 @@ function sourceRequest(obligation, offering, previous, adjustments, now) {
548
692
  windowing: structuredClone(offering.windowing)
549
693
  },
550
694
  finality: revisionKind === "correction" ? { revisionKind, supersedesPublicationId: latest.sourcePublicationId } : { revisionKind },
551
- trigger: { kind: obligation.attemptCount ? "retry" : "scheduled_poll", id: `trigger-${obligation.attemptCount}` },
695
+ trigger: {
696
+ kind: obligation.attemptCount ? "retry" : obligation.history ? "backfill" : "scheduled_poll",
697
+ id: `trigger-${obligation.attemptCount}`
698
+ },
552
699
  sourceRequest: { groupIds: [] },
553
700
  coverage: {
554
701
  expected: "full",
@@ -1,7 +1,7 @@
1
1
  import { type ReportingSourceOfferingV1 } from '../source/index.mjs';
2
2
  import type { ReportingCalendarRulesV1, ReportingLedgerConfigurationV1 } from './types.mjs';
3
3
  type Schedule = ReportingLedgerConfigurationV1['schedule'];
4
- type ConfigurationInput = Omit<ReportingLedgerConfigurationV1, 'configurationId' | 'installedAt' | 'semanticFingerprint' | 'calendarRules'>;
4
+ type ConfigurationInput = Omit<ReportingLedgerConfigurationV1, 'configurationId' | 'installedAt' | 'semanticFingerprint' | 'calendarRules' | 'historyStart' | 'historyStartOrdinal'>;
5
5
  interface PeriodSchedule {
6
6
  boundary(ordinal: number): number;
7
7
  floor(instant: number): number;
@@ -49,7 +49,7 @@ export declare function reportingCalendarDayNeedsNewIdentity(input: Configuratio
49
49
  /** Shared by planning, coverage, projections and consumer status eligibility. */
50
50
  export declare function reportingPeriodSchedule(configuration: ReportingLedgerConfigurationV1): PeriodSchedule;
51
51
  /** Refuse a day the declared source cannot produce losslessly before accepting it. */
52
- export declare function assertReportingCalendarDaySource(schedule: Schedule, sourceTimezone: string, source: ReportingSourceOfferingV1, now: number): void;
52
+ export declare function assertReportingCalendarDaySource(schedule: Schedule, sourceTimezone: string, source: ReportingSourceOfferingV1, now: number, historyStart?: number): void;
53
53
  /** Recheck the actual period before writing, including periods beyond the install horizon. */
54
54
  export declare function assertReportingCalendarDayPeriod(schedule: Schedule, sourceTimezone: string, source: ReportingSourceOfferingV1, start: number, end: number): void;
55
55
  export {};
@@ -1,7 +1,7 @@
1
1
  import { type ReportingSourceOfferingV1 } from '../source';
2
2
  import type { ReportingCalendarRulesV1, ReportingLedgerConfigurationV1 } from './types';
3
3
  type Schedule = ReportingLedgerConfigurationV1['schedule'];
4
- type ConfigurationInput = Omit<ReportingLedgerConfigurationV1, 'configurationId' | 'installedAt' | 'semanticFingerprint' | 'calendarRules'>;
4
+ type ConfigurationInput = Omit<ReportingLedgerConfigurationV1, 'configurationId' | 'installedAt' | 'semanticFingerprint' | 'calendarRules' | 'historyStart' | 'historyStartOrdinal'>;
5
5
  interface PeriodSchedule {
6
6
  boundary(ordinal: number): number;
7
7
  floor(instant: number): number;
@@ -49,7 +49,7 @@ export declare function reportingCalendarDayNeedsNewIdentity(input: Configuratio
49
49
  /** Shared by planning, coverage, projections and consumer status eligibility. */
50
50
  export declare function reportingPeriodSchedule(configuration: ReportingLedgerConfigurationV1): PeriodSchedule;
51
51
  /** Refuse a day the declared source cannot produce losslessly before accepting it. */
52
- export declare function assertReportingCalendarDaySource(schedule: Schedule, sourceTimezone: string, source: ReportingSourceOfferingV1, now: number): void;
52
+ export declare function assertReportingCalendarDaySource(schedule: Schedule, sourceTimezone: string, source: ReportingSourceOfferingV1, now: number, historyStart?: number): void;
53
53
  /** Recheck the actual period before writing, including periods beyond the install horizon. */
54
54
  export declare function assertReportingCalendarDayPeriod(schedule: Schedule, sourceTimezone: string, source: ReportingSourceOfferingV1, start: number, end: number): void;
55
55
  export {};
@@ -105,10 +105,10 @@ function isFrozenCalendarRulesMismatch(error) {
105
105
  return error instanceof Error && /calendar timezone rules differ from this stored generation/.test(error.message);
106
106
  }
107
107
  function frozenCalendarObligationsCoverOwnership(configuration, ownershipEnd, obligations) {
108
- const first = configuration.calendarRules?.firstOwnedOrdinal;
109
- const firstBoundary = configuration.calendarRules?.firstOwnedBoundary;
108
+ const first = configuration.historyStartOrdinal ?? configuration.calendarRules?.firstOwnedOrdinal;
109
+ const firstBoundary = configuration.historyStart ?? configuration.calendarRules?.firstOwnedBoundary;
110
110
  const firstBoundaryMs = firstBoundary ? Date.parse(firstBoundary) : Number.NaN;
111
- if (!Number.isSafeInteger(first) || first < 0 || !Number.isFinite(firstBoundaryMs) || !Number.isFinite(ownershipEnd)) {
111
+ if (!Number.isSafeInteger(first) || first < 0 && configuration.historyStart === void 0 || !Number.isFinite(firstBoundaryMs) || !Number.isFinite(ownershipEnd)) {
112
112
  return false;
113
113
  }
114
114
  const owned = obligations.filter((value) => value.configurationId === configuration.configurationId).sort((left, right) => left.periodOrdinal - right.periodOrdinal);
@@ -238,6 +238,8 @@ function reportingPeriodSchedule(configuration) {
238
238
  const {
239
239
  configurationId: _id,
240
240
  installedAt: _installedAt,
241
+ historyStart: _historyStart,
242
+ historyStartOrdinal: _historyStartOrdinal,
241
243
  semanticFingerprint,
242
244
  calendarRules,
243
245
  ...input
@@ -276,8 +278,9 @@ function reportingPeriodSchedule(configuration) {
276
278
  period: (ordinal) => canonicalCalendarPeriod(assertUnchanged(ordinal), assertUnchanged(ordinal + 1))
277
279
  };
278
280
  }
279
- function assertReportingCalendarDaySource(schedule, sourceTimezone, source, now) {
280
- const { calendar, first, last } = calendarOrdinals(schedule, sourceTimezone, now);
281
+ function assertReportingCalendarDaySource(schedule, sourceTimezone, source, now, historyStart) {
282
+ const { calendar, first: liveFirst, last } = calendarOrdinals(schedule, sourceTimezone, now);
283
+ const first = historyStart === void 0 ? liveFirst : Math.min(liveFirst, calendar.ceil(historyStart));
281
284
  for (let ordinal = first; ordinal < last; ordinal += 1) {
282
285
  assertReportingCalendarDayPeriod(
283
286
  schedule,
@@ -71,10 +71,10 @@ function isFrozenCalendarRulesMismatch(error) {
71
71
  return error instanceof Error && /calendar timezone rules differ from this stored generation/.test(error.message);
72
72
  }
73
73
  function frozenCalendarObligationsCoverOwnership(configuration, ownershipEnd, obligations) {
74
- const first = configuration.calendarRules?.firstOwnedOrdinal;
75
- const firstBoundary = configuration.calendarRules?.firstOwnedBoundary;
74
+ const first = configuration.historyStartOrdinal ?? configuration.calendarRules?.firstOwnedOrdinal;
75
+ const firstBoundary = configuration.historyStart ?? configuration.calendarRules?.firstOwnedBoundary;
76
76
  const firstBoundaryMs = firstBoundary ? Date.parse(firstBoundary) : Number.NaN;
77
- if (!Number.isSafeInteger(first) || first < 0 || !Number.isFinite(firstBoundaryMs) || !Number.isFinite(ownershipEnd)) {
77
+ if (!Number.isSafeInteger(first) || first < 0 && configuration.historyStart === void 0 || !Number.isFinite(firstBoundaryMs) || !Number.isFinite(ownershipEnd)) {
78
78
  return false;
79
79
  }
80
80
  const owned = obligations.filter((value) => value.configurationId === configuration.configurationId).sort((left, right) => left.periodOrdinal - right.periodOrdinal);
@@ -204,6 +204,8 @@ function reportingPeriodSchedule(configuration) {
204
204
  const {
205
205
  configurationId: _id,
206
206
  installedAt: _installedAt,
207
+ historyStart: _historyStart,
208
+ historyStartOrdinal: _historyStartOrdinal,
207
209
  semanticFingerprint,
208
210
  calendarRules,
209
211
  ...input
@@ -242,8 +244,9 @@ function reportingPeriodSchedule(configuration) {
242
244
  period: (ordinal) => canonicalCalendarPeriod(assertUnchanged(ordinal), assertUnchanged(ordinal + 1))
243
245
  };
244
246
  }
245
- function assertReportingCalendarDaySource(schedule, sourceTimezone, source, now) {
246
- const { calendar, first, last } = calendarOrdinals(schedule, sourceTimezone, now);
247
+ function assertReportingCalendarDaySource(schedule, sourceTimezone, source, now, historyStart) {
248
+ const { calendar, first: liveFirst, last } = calendarOrdinals(schedule, sourceTimezone, now);
249
+ const first = historyStart === void 0 ? liveFirst : Math.min(liveFirst, calendar.ceil(historyStart));
247
250
  for (let ordinal = first; ordinal < last; ordinal += 1) {
248
251
  assertReportingCalendarDayPeriod(
249
252
  schedule,
@@ -195,7 +195,7 @@ export declare const SyncReportingStatusRequestV1Schema: z.ZodObject<{
195
195
  brand: z.ZodObject<{
196
196
  domain: z.ZodString;
197
197
  brand_id: z.ZodOptional<z.ZodString>;
198
- countries: z.ZodOptional<z.ZodArray<z.ZodString>>;
198
+ countries: z.ZodOptional<import("../../validation/nonempty-array.mjs").NonemptyArraySchema<z.ZodString>>;
199
199
  }, z.core.$loose>;
200
200
  operator: z.ZodString;
201
201
  operator_unit: z.ZodOptional<z.ZodObject<{
@@ -195,7 +195,7 @@ export declare const SyncReportingStatusRequestV1Schema: z.ZodObject<{
195
195
  brand: z.ZodObject<{
196
196
  domain: z.ZodString;
197
197
  brand_id: z.ZodOptional<z.ZodString>;
198
- countries: z.ZodOptional<z.ZodArray<z.ZodString>>;
198
+ countries: z.ZodOptional<import("../../validation/nonempty-array").NonemptyArraySchema<z.ZodString>>;
199
199
  }, z.core.$loose>;
200
200
  operator: z.ZodString;
201
201
  operator_unit: z.ZodOptional<z.ZodObject<{
@@ -310,7 +310,7 @@ function isExactPeriod(configuration, configurations, period) {
310
310
  const scheduleAnchor = new Date(Date.parse(configuration.schedule.anchor)).toISOString();
311
311
  let ordinal;
312
312
  let firstOwnedOrdinal;
313
- const effectiveFrom = (0, import_instant.compareReportingInstants)(scheduleAnchor, configuration.installedAt) >= 0 ? scheduleAnchor : configuration.installedAt;
313
+ const effectiveFrom = configuration.historyStart ?? ((0, import_instant.compareReportingInstants)(scheduleAnchor, configuration.installedAt) >= 0 ? scheduleAnchor : configuration.installedAt);
314
314
  if ((0, import_instant.compareReportingInstants)(period.start, effectiveFrom) < 0) return false;
315
315
  if ((0, import_schedule.isReportingCalendarDay)(configuration.schedule, configuration.sourceTimezone)) {
316
316
  const schedule = (0, import_schedule.reportingPeriodSchedule)(configuration);
@@ -324,10 +324,11 @@ function isExactPeriod(configuration, configurations, period) {
324
324
  first + ((0, import_instant.compareReportingInstants)(effectiveFrom, new Date(schedule.boundary(first)).toISOString()) > 0 ? 1 : 0)
325
325
  );
326
326
  } else {
327
- const selected = (0, import_instant.reportingPeriodOrdinal)(period.start, scheduleAnchor, duration);
327
+ const eligibilityAnchor = configuration.historyStart ?? scheduleAnchor;
328
+ const selected = (0, import_instant.reportingPeriodOrdinal)(period.start, eligibilityAnchor, duration);
328
329
  if (selected === null || !(0, import_instant.reportingInstantHasDuration)(period.start, period.end, duration)) return false;
329
330
  ordinal = selected;
330
- firstOwnedOrdinal = (0, import_instant.reportingDurationCeilOrdinal)(scheduleAnchor, effectiveFrom, duration);
331
+ firstOwnedOrdinal = (0, import_instant.reportingDurationCeilOrdinal)(eligibilityAnchor, effectiveFrom, duration);
331
332
  }
332
333
  return ordinal >= firstOwnedOrdinal;
333
334
  }
@@ -300,7 +300,7 @@ function isExactPeriod(configuration, configurations, period) {
300
300
  const scheduleAnchor = new Date(Date.parse(configuration.schedule.anchor)).toISOString();
301
301
  let ordinal;
302
302
  let firstOwnedOrdinal;
303
- const effectiveFrom = compareReportingInstants(scheduleAnchor, configuration.installedAt) >= 0 ? scheduleAnchor : configuration.installedAt;
303
+ const effectiveFrom = configuration.historyStart ?? (compareReportingInstants(scheduleAnchor, configuration.installedAt) >= 0 ? scheduleAnchor : configuration.installedAt);
304
304
  if (compareReportingInstants(period.start, effectiveFrom) < 0) return false;
305
305
  if (isReportingCalendarDay(configuration.schedule, configuration.sourceTimezone)) {
306
306
  const schedule = reportingPeriodSchedule(configuration);
@@ -314,10 +314,11 @@ function isExactPeriod(configuration, configurations, period) {
314
314
  first + (compareReportingInstants(effectiveFrom, new Date(schedule.boundary(first)).toISOString()) > 0 ? 1 : 0)
315
315
  );
316
316
  } else {
317
- const selected = reportingPeriodOrdinal(period.start, scheduleAnchor, duration);
317
+ const eligibilityAnchor = configuration.historyStart ?? scheduleAnchor;
318
+ const selected = reportingPeriodOrdinal(period.start, eligibilityAnchor, duration);
318
319
  if (selected === null || !reportingInstantHasDuration(period.start, period.end, duration)) return false;
319
320
  ordinal = selected;
320
- firstOwnedOrdinal = reportingDurationCeilOrdinal(scheduleAnchor, effectiveFrom, duration);
321
+ firstOwnedOrdinal = reportingDurationCeilOrdinal(eligibilityAnchor, effectiveFrom, duration);
321
322
  }
322
323
  return ordinal >= firstOwnedOrdinal;
323
324
  }