@adcp/sdk 17.0.0-beta.0 → 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.
- package/dist/lib/adapters/keyed-account-store.js +1 -1
- package/dist/lib/adapters/keyed-account-store.mjs +1 -1
- package/dist/lib/adapters/version/3.0/brand-fields.d.mts +7 -1
- package/dist/lib/adapters/version/3.0/brand-fields.d.ts +7 -1
- package/dist/lib/adapters/version/3.0/brand-fields.js +139 -3
- package/dist/lib/adapters/version/3.0/brand-fields.mjs +136 -2
- package/dist/lib/adapters/version/3.0/index.js +6 -0
- package/dist/lib/adapters/version/3.0/index.mjs +7 -1
- package/dist/lib/adapters/version/3.1/bidding.js +1 -1
- package/dist/lib/adapters/version/3.1/bidding.mjs +1 -1
- package/dist/lib/adapters/version/types.d.mts +5 -0
- package/dist/lib/adapters/version/types.d.ts +5 -0
- package/dist/lib/canonical-references/index.js +2 -0
- package/dist/lib/canonical-references/index.mjs +2 -0
- package/dist/lib/client/account-changes.d.mts +3 -1
- package/dist/lib/client/account-changes.d.ts +3 -1
- package/dist/lib/client/account-changes.js +22 -2
- package/dist/lib/client/account-changes.mjs +22 -2
- package/dist/lib/conformance/oracle.js +3 -2
- package/dist/lib/conformance/oracle.mjs +3 -2
- package/dist/lib/core/AgentClient.d.mts +165 -1
- package/dist/lib/core/AgentClient.d.ts +165 -1
- package/dist/lib/core/AgentClient.js +446 -8
- package/dist/lib/core/AgentClient.mjs +446 -8
- package/dist/lib/core/ConversationTypes.d.mts +8 -0
- package/dist/lib/core/ConversationTypes.d.ts +8 -0
- package/dist/lib/core/ProtocolResponseParser.d.mts +1 -1
- package/dist/lib/core/ProtocolResponseParser.d.ts +1 -1
- package/dist/lib/core/ProtocolResponseParser.js +28 -18
- package/dist/lib/core/ProtocolResponseParser.mjs +11 -1
- package/dist/lib/core/ResponseValidator.js +1 -1
- package/dist/lib/core/ResponseValidator.mjs +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +122 -7
- package/dist/lib/core/SingleAgentClient.d.ts +122 -7
- package/dist/lib/core/SingleAgentClient.js +601 -96
- package/dist/lib/core/SingleAgentClient.mjs +590 -84
- package/dist/lib/core/TaskExecutor.d.mts +7 -0
- package/dist/lib/core/TaskExecutor.d.ts +7 -0
- package/dist/lib/core/TaskExecutor.js +23 -8
- package/dist/lib/core/TaskExecutor.mjs +23 -8
- package/dist/lib/core/task-status.d.mts +2 -0
- package/dist/lib/core/task-status.d.ts +2 -0
- package/dist/lib/core/task-status.js +11 -2
- package/dist/lib/core/task-status.mjs +9 -1
- package/dist/lib/errors/index.d.mts +22 -0
- package/dist/lib/errors/index.d.ts +22 -0
- package/dist/lib/errors/index.js +43 -0
- package/dist/lib/errors/index.mjs +42 -0
- package/dist/lib/identity-3.3/trust.d.mts +1 -4
- package/dist/lib/identity-3.3/trust.d.ts +1 -4
- package/dist/lib/identity-3.3/trust.js +34 -21
- package/dist/lib/identity-3.3/trust.mjs +34 -21
- package/dist/lib/identity-3.3/validation.js +5 -3
- package/dist/lib/identity-3.3/validation.mjs +6 -4
- package/dist/lib/index.d.mts +7 -53
- package/dist/lib/index.d.ts +7 -53
- package/dist/lib/index.js +13 -0
- package/dist/lib/index.mjs +9 -0
- package/dist/lib/media-buy/compatibility.js +6 -1
- package/dist/lib/media-buy/compatibility.mjs +6 -1
- package/dist/lib/protocols/mcp-modern.d.mts +2 -0
- package/dist/lib/protocols/mcp-modern.d.ts +2 -0
- package/dist/lib/protocols/mcp-modern.js +31 -5
- package/dist/lib/protocols/mcp-modern.mjs +31 -5
- package/dist/lib/protocols/mcp-scope.d.mts +6 -1
- package/dist/lib/protocols/mcp-scope.d.ts +6 -1
- package/dist/lib/protocols/mcp-scope.js +25 -3
- package/dist/lib/protocols/mcp-scope.mjs +24 -3
- package/dist/lib/protocols/mcp.js +12 -6
- package/dist/lib/protocols/mcp.mjs +13 -6
- package/dist/lib/reporting/inspection.js +1 -0
- package/dist/lib/reporting/inspection.mjs +1 -0
- package/dist/lib/reporting/ledger/coverage.d.mts +5 -0
- package/dist/lib/reporting/ledger/coverage.d.ts +5 -0
- package/dist/lib/reporting/ledger/coverage.js +37 -3
- package/dist/lib/reporting/ledger/coverage.mjs +36 -3
- package/dist/lib/reporting/ledger/handler.js +7 -4
- package/dist/lib/reporting/ledger/handler.mjs +7 -4
- package/dist/lib/reporting/ledger/producer.js +215 -69
- package/dist/lib/reporting/ledger/producer.mjs +217 -70
- package/dist/lib/reporting/ledger/schedule.d.mts +2 -2
- package/dist/lib/reporting/ledger/schedule.d.ts +2 -2
- package/dist/lib/reporting/ledger/schedule.js +8 -5
- package/dist/lib/reporting/ledger/schedule.mjs +8 -5
- package/dist/lib/reporting/ledger/status-ingest.d.mts +1 -1
- package/dist/lib/reporting/ledger/status-ingest.d.ts +1 -1
- package/dist/lib/reporting/ledger/status-ingest.js +4 -3
- package/dist/lib/reporting/ledger/status-ingest.mjs +4 -3
- package/dist/lib/reporting/ledger/types.d.mts +36 -1
- package/dist/lib/reporting/ledger/types.d.ts +36 -1
- package/dist/lib/reporting/service/index.d.mts +14 -1
- package/dist/lib/reporting/service/index.d.ts +14 -1
- package/dist/lib/reporting/service/index.js +24 -12
- package/dist/lib/reporting/service/index.mjs +24 -12
- package/dist/lib/schemas/browser.d.mts +2 -0
- package/dist/lib/schemas/browser.d.ts +2 -0
- package/dist/lib/schemas/browser.js +3 -0
- package/dist/lib/schemas/browser.mjs +2 -0
- package/dist/lib/schemas/index.d.mts +5 -3
- package/dist/lib/schemas/index.d.ts +5 -3
- package/dist/lib/schemas/index.js +5 -0
- package/dist/lib/schemas/index.mjs +4 -0
- package/dist/lib/server/account-response-identity.d.mts +17 -0
- package/dist/lib/server/account-response-identity.d.ts +17 -0
- package/dist/lib/server/account-response-identity.js +52 -0
- package/dist/lib/server/account-response-identity.mjs +28 -0
- package/dist/lib/server/account-sync-contract.js +2 -2
- package/dist/lib/server/account-sync-contract.mjs +2 -2
- package/dist/lib/server/create-adcp-server.js +33 -20
- package/dist/lib/server/create-adcp-server.mjs +36 -12
- package/dist/lib/server/decisioning/account.d.mts +36 -10
- package/dist/lib/server/decisioning/account.d.ts +36 -10
- package/dist/lib/server/decisioning/account.js +33 -14
- package/dist/lib/server/decisioning/account.mjs +33 -14
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +5 -5
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +5 -5
- package/dist/lib/server/decisioning/runtime/from-platform.js +4 -4
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +4 -4
- package/dist/lib/server/media-buy-store.d.mts +26 -12
- package/dist/lib/server/media-buy-store.d.ts +26 -12
- package/dist/lib/server/media-buy-store.js +69 -21
- package/dist/lib/server/media-buy-store.mjs +69 -21
- package/dist/lib/server/notification-subscriptions/runtime.d.mts +1 -1
- package/dist/lib/server/notification-subscriptions/runtime.d.ts +1 -1
- package/dist/lib/server/notification-subscriptions/runtime.js +4 -0
- package/dist/lib/server/notification-subscriptions/runtime.mjs +4 -0
- package/dist/lib/server/responses.js +9 -1
- package/dist/lib/server/responses.mjs +9 -1
- package/dist/lib/signing/agent-fetch.d.mts +1 -1
- package/dist/lib/signing/agent-fetch.d.ts +1 -1
- package/dist/lib/signing/agent-fetch.js +3 -3
- package/dist/lib/signing/agent-fetch.mjs +3 -3
- package/dist/lib/testing/client.d.mts +1 -0
- package/dist/lib/testing/client.d.ts +1 -0
- package/dist/lib/testing/client.js +6 -5
- package/dist/lib/testing/client.mjs +6 -5
- package/dist/lib/testing/storyboard/agent-routing.d.mts +5 -2
- package/dist/lib/testing/storyboard/agent-routing.d.ts +5 -2
- package/dist/lib/testing/storyboard/agent-routing.js +11 -4
- package/dist/lib/testing/storyboard/agent-routing.mjs +11 -4
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.js +60 -14
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs +60 -14
- package/dist/lib/testing/storyboard/compliance.d.mts +2 -5
- package/dist/lib/testing/storyboard/compliance.d.ts +2 -5
- package/dist/lib/testing/storyboard/compliance.js +62 -13
- package/dist/lib/testing/storyboard/compliance.mjs +63 -14
- package/dist/lib/testing/storyboard/context.d.mts +13 -3
- package/dist/lib/testing/storyboard/context.d.ts +13 -3
- package/dist/lib/testing/storyboard/context.js +15 -10
- package/dist/lib/testing/storyboard/context.mjs +15 -10
- package/dist/lib/testing/storyboard/request-signing/vector-loader.d.mts +2 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts +2 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.js +11 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +10 -0
- package/dist/lib/testing/storyboard/runner.js +141 -13
- package/dist/lib/testing/storyboard/runner.mjs +141 -13
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.js +4 -1
- package/dist/lib/testing/storyboard/validations.mjs +4 -1
- package/dist/lib/testing/test-controller.js +3 -0
- package/dist/lib/testing/test-controller.mjs +3 -0
- package/dist/lib/types/accept-proposal.d.ts +20 -28
- package/dist/lib/types/asset-instances.d.mts +1 -1
- package/dist/lib/types/asset-instances.d.ts +1 -1
- package/dist/lib/types/buy-products.d.ts +20 -28
- package/dist/lib/types/check-governance.d.ts +20 -28
- package/dist/lib/types/comply-test-controller.d.ts +20 -28
- package/dist/lib/types/control-media-buy.d.ts +20 -28
- package/dist/lib/types/core.generated.d.mts +273 -184
- package/dist/lib/types/core.generated.d.ts +273 -184
- package/dist/lib/types/create-media-buy.d.ts +20 -28
- package/dist/lib/types/get-media-buys.d.ts +20 -28
- package/dist/lib/types/get-products.d.ts +20 -28
- package/dist/lib/types/index.d.mts +6 -1
- package/dist/lib/types/index.d.ts +6 -1
- package/dist/lib/types/index.js +6 -1
- package/dist/lib/types/index.mjs +4 -1
- package/dist/lib/types/inline-enums.generated.d.mts +30 -4
- package/dist/lib/types/inline-enums.generated.d.ts +30 -4
- package/dist/lib/types/inline-enums.generated.js +41 -2
- package/dist/lib/types/inline-enums.generated.mjs +28 -2
- package/dist/lib/types/list-products.d.ts +20 -28
- package/dist/lib/types/list-proposals.d.ts +20 -28
- package/dist/lib/types/media-buy-delivery-report.d.mts +6 -0
- package/dist/lib/types/media-buy-delivery-report.d.ts +6 -0
- package/dist/lib/types/media-buy-delivery-report.js +16 -0
- package/dist/lib/types/media-buy-delivery-report.mjs +0 -0
- package/dist/lib/types/refine-proposals.d.ts +20 -28
- package/dist/lib/types/request-proposals.d.ts +20 -28
- package/dist/lib/types/schemas.generated.d.ts +63113 -127273
- package/dist/lib/types/schemas.generated.js +759 -770
- package/dist/lib/types/schemas.generated.mjs +759 -770
- package/dist/lib/types/targeting-keys.generated.d.mts +4 -0
- package/dist/lib/types/targeting-keys.generated.d.ts +4 -0
- package/dist/lib/types/targeting-keys.generated.js +109 -0
- package/dist/lib/types/targeting-keys.generated.mjs +84 -0
- package/dist/lib/types/tools.generated.d.mts +2 -68
- package/dist/lib/types/tools.generated.d.ts +2 -68
- package/dist/lib/types/update-media-buy.d.ts +20 -28
- package/dist/lib/utils/adcp-version-config.js +5 -2
- package/dist/lib/utils/adcp-version-config.mjs +5 -2
- package/dist/lib/utils/envelope-status-compat.d.mts +18 -1
- package/dist/lib/utils/envelope-status-compat.d.ts +18 -1
- package/dist/lib/utils/envelope-status-compat.js +11 -5
- package/dist/lib/utils/envelope-status-compat.mjs +11 -5
- package/dist/lib/utils/response-unwrapper.js +18 -10
- package/dist/lib/utils/response-unwrapper.mjs +18 -10
- package/dist/lib/v2/format-schema/resolver.js +1 -1
- package/dist/lib/v2/format-schema/resolver.mjs +1 -1
- package/dist/lib/v2/projection/creative-delivery.js +159 -39
- package/dist/lib/v2/projection/creative-delivery.mjs +163 -39
- package/dist/lib/v2/projection/index.d.mts +1 -0
- package/dist/lib/v2/projection/index.d.ts +1 -0
- package/dist/lib/v2/projection/index.js +3 -0
- package/dist/lib/v2/projection/index.mjs +2 -0
- package/dist/lib/v2/projection/legacy-metadata.d.mts +19 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts +19 -0
- package/dist/lib/v2/projection/legacy-metadata.js +91 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs +86 -0
- package/dist/lib/v2/projection/v1-to-v2.d.mts +1 -1
- package/dist/lib/v2/projection/v1-to-v2.d.ts +1 -1
- package/dist/lib/v2/projection/v1-to-v2.js +12 -9
- package/dist/lib/v2/projection/v1-to-v2.mjs +12 -9
- package/dist/lib/validation/budget-allocation.d.mts +3 -3
- package/dist/lib/validation/budget-allocation.d.ts +3 -3
- package/dist/lib/validation/compact-schema.d.mts +27 -0
- package/dist/lib/validation/compact-schema.d.ts +27 -0
- package/dist/lib/validation/compact-schema.js +276 -0
- package/dist/lib/validation/compact-schema.mjs +250 -0
- package/dist/lib/validation/media-buy-delivery-report.d.mts +8 -0
- package/dist/lib/validation/media-buy-delivery-report.d.ts +8 -0
- package/dist/lib/validation/media-buy-delivery-report.js +31 -0
- package/dist/lib/validation/media-buy-delivery-report.mjs +7 -0
- package/dist/lib/validation/nonempty-array.d.mts +15 -0
- package/dist/lib/validation/nonempty-array.d.ts +15 -0
- package/dist/lib/validation/nonempty-array.js +41 -0
- package/dist/lib/validation/nonempty-array.mjs +7 -0
- package/dist/lib/validation/schema-diagnostic-origins.d.mts +21 -0
- package/dist/lib/validation/schema-diagnostic-origins.d.ts +21 -0
- package/dist/lib/validation/schema-diagnostic-origins.js +193 -0
- package/dist/lib/validation/schema-diagnostic-origins.mjs +169 -0
- package/dist/lib/validation/schema-loader.js +86 -16
- package/dist/lib/validation/schema-loader.mjs +82 -16
- package/dist/lib/validation/schema-validator.d.mts +10 -19
- package/dist/lib/validation/schema-validator.d.ts +10 -19
- package/dist/lib/validation/schema-validator.js +136 -43
- package/dist/lib/validation/schema-validator.mjs +140 -44
- package/dist/lib/validation/sync-creatives.d.mts +22 -22
- package/dist/lib/validation/sync-creatives.d.ts +22 -22
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/webhooks/index.d.mts +3 -0
- package/dist/lib/webhooks/index.d.ts +3 -0
- package/dist/lib/webhooks/index.js +3 -0
- package/dist/lib/webhooks/index.mjs +2 -0
- package/dist/lib/webhooks/media-buy-delivery.d.mts +43 -0
- package/dist/lib/webhooks/media-buy-delivery.d.ts +43 -0
- package/dist/lib/webhooks/media-buy-delivery.js +119 -0
- package/dist/lib/webhooks/media-buy-delivery.mjs +94 -0
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/COLLECTION-FIXTURE-FOUNDATION.md +11 -0
- package/docs/guides/DELIVERY-WEBHOOK-PARSING.md +142 -0
- package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +27 -1
- package/docs/guides/REPORTING-LEDGER.md +93 -0
- package/docs/guides/REPORTING-STABILITY.md +1 -1
- package/docs/guides/TYPED-CLIENT-TOOLS.md +49 -0
- package/docs/llms.txt +2 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-16-to-17.md +202 -2
- package/docs/migration-6.6-to-6.7.md +14 -0
- package/docs/migration-account-change-feed.md +15 -0
- package/docs/migration-reporting-storage-package.md +1 -1
- package/docs/recipes/verifying-inbound-webhooks.md +18 -2
- 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
|
-
|
|
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
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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: {
|
|
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<
|
|
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<
|
|
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
|
|
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)(
|
|
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
|
|
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(
|
|
321
|
+
firstOwnedOrdinal = reportingDurationCeilOrdinal(eligibilityAnchor, effectiveFrom, duration);
|
|
321
322
|
}
|
|
322
323
|
return ordinal >= firstOwnedOrdinal;
|
|
323
324
|
}
|