@greatwait/contracts 0.1.0-alpha.3 → 0.1.0-alpha.5
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/README.md +12 -10
- package/dist/authority.d.ts +33 -27
- package/dist/authority.js +12 -2
- package/dist/billing.d.ts +1231 -0
- package/dist/billing.js +833 -0
- package/dist/callback-safety.d.ts +2 -2
- package/dist/callback-safety.js +15 -5
- package/dist/compatibility.d.ts +3 -3
- package/dist/compatibility.js +1 -1
- package/dist/configuration-bounds.d.ts +13 -0
- package/dist/configuration-bounds.js +13 -0
- package/dist/data-export.d.ts +67 -0
- package/dist/data-export.js +182 -0
- package/dist/effective-limits.d.ts +40 -0
- package/dist/effective-limits.js +42 -0
- package/dist/endpoints.d.ts +186 -245
- package/dist/endpoints.js +82 -86
- package/dist/events.d.ts +17 -17
- package/dist/events.js +1 -1
- package/dist/first-value-journey.d.ts +50 -56
- package/dist/first-value-journey.js +17 -20
- package/dist/forecast.d.ts +182 -92
- package/dist/forecast.js +88 -25
- package/dist/identifiers.d.ts +20 -20
- package/dist/identifiers.js +1 -1
- package/dist/identity.d.ts +41 -37
- package/dist/identity.js +5 -4
- package/dist/index.d.ts +12 -6
- package/dist/index.js +12 -6
- package/dist/mcp.d.ts +21 -19
- package/dist/mcp.js +2 -2
- package/dist/naming.d.ts +45 -0
- package/dist/naming.js +159 -0
- package/dist/oauth.d.ts +186 -172
- package/dist/oauth.js +1 -1
- package/dist/operation-definitions.d.ts +9 -0
- package/dist/operation-definitions.js +7 -0
- package/dist/operational-config.d.ts +101 -0
- package/dist/operational-config.js +341 -0
- package/dist/operations.d.ts +4742 -2170
- package/dist/operations.js +3064 -932
- package/dist/organization-lifecycle.d.ts +34 -0
- package/dist/organization-lifecycle.js +122 -0
- package/dist/partition.d.ts +43 -9
- package/dist/partition.js +3 -3
- package/dist/policy.d.ts +4 -4
- package/dist/policy.js +9 -5
- package/dist/problems.d.ts +57 -28
- package/dist/problems.js +55 -3
- package/dist/protocol.d.ts +209 -80
- package/dist/protocol.js +110 -37
- package/dist/scheduling-records.d.ts +349 -285
- package/dist/scheduling-records.js +1 -1
- package/dist/scheduling.d.ts +707 -361
- package/dist/scheduling.js +443 -61
- package/dist/secrets.d.ts +119 -105
- package/dist/secrets.js +1 -1
- package/dist/signatures.d.ts +74 -74
- package/dist/signatures.js +1 -1
- package/dist/tenancy.d.ts +142 -68
- package/dist/tenancy.js +70 -17
- package/dist/test-sink.d.ts +127 -127
- package/dist/test-sink.js +1 -1
- package/dist/time.d.ts +4 -4
- package/dist/time.js +1 -1
- package/dist/timing.d.ts +65 -0
- package/dist/timing.js +82 -0
- package/dist/usage.d.ts +103 -47
- package/dist/usage.js +183 -32
- package/dist/workspace.d.ts +34 -34
- package/dist/workspace.js +1 -1
- package/dist/zod-values.d.ts +3 -0
- package/dist/zod-values.js +2 -0
- package/dist/zod.d.ts +2 -0
- package/dist/zod.js +2 -0
- package/generated/v1/examples/attempts.list.response.json +6 -0
- package/generated/v1/examples/attempts.read.response.json +6 -0
- package/generated/v1/examples/billing.autotopup.request.json +5 -0
- package/generated/v1/examples/billing.autotopup.response.json +120 -0
- package/generated/v1/examples/billing.autotopuppreview.request.json +19 -0
- package/generated/v1/examples/billing.autotopuppreview.response.json +45 -0
- package/generated/v1/examples/billing.budgets.request.json +13 -0
- package/generated/v1/examples/billing.budgets.response.json +22 -0
- package/generated/v1/examples/billing.ceiling.request.json +8 -0
- package/generated/v1/examples/billing.ceiling.response.json +120 -0
- package/generated/v1/examples/billing.change.request.json +5 -0
- package/generated/v1/examples/billing.change.response.json +12 -0
- package/generated/v1/examples/billing.funding.response.json +120 -0
- package/generated/v1/examples/billing.fundingconfirm.response.json +120 -0
- package/generated/v1/examples/billing.fundingquote.request.json +7 -0
- package/generated/v1/examples/billing.fundingquote.response.json +13 -0
- package/generated/v1/examples/billing.invoicehistory.response.json +6 -0
- package/generated/v1/examples/billing.invoices.response.json +15 -0
- package/generated/v1/examples/billing.ledger.response.json +23 -0
- package/generated/v1/examples/billing.notices.response.json +41 -0
- package/generated/v1/examples/billing.notificationsettings.response.json +17 -0
- package/generated/v1/examples/billing.notificationsettingsupdate.request.json +9 -0
- package/generated/v1/examples/billing.notificationsettingsupdate.response.json +17 -0
- package/generated/v1/examples/billing.paymenthistory.response.json +6 -0
- package/generated/v1/examples/billing.paymentmethod.response.json +16 -0
- package/generated/v1/examples/billing.paymentmethodconfirm.request.json +3 -0
- package/generated/v1/examples/billing.paymentmethodconfirm.response.json +16 -0
- package/generated/v1/examples/billing.paymentmethodremove.request.json +3 -0
- package/generated/v1/examples/billing.paymentmethodremove.response.json +4 -0
- package/generated/v1/examples/billing.paymentmethodsetup.request.json +1 -0
- package/generated/v1/examples/billing.paymentmethodsetup.response.json +3 -0
- package/generated/v1/examples/billing.portal.response.json +3 -0
- package/generated/v1/examples/billing.projects.response.json +12 -0
- package/generated/v1/examples/billing.purchase.request.json +5 -0
- package/generated/v1/examples/billing.purchase.response.json +120 -0
- package/generated/v1/examples/billing.quote.request.json +4 -0
- package/generated/v1/examples/billing.quote.response.json +19 -0
- package/generated/v1/examples/billing.reconcile.response.json +73 -0
- package/generated/v1/examples/billing.refundrequest.request.json +10 -0
- package/generated/v1/examples/billing.refundrequest.response.json +11 -0
- package/generated/v1/examples/billing.refundresolution.request.json +4 -0
- package/generated/v1/examples/billing.refundresolution.response.json +120 -0
- package/generated/v1/examples/billing.summary.response.json +73 -0
- package/generated/v1/examples/endpoints.acknowledgeSigningKeyRotation.response.json +18 -0
- package/generated/v1/examples/endpoints.create.response.json +1 -1
- package/generated/v1/examples/endpoints.disable.response.json +0 -1
- package/generated/v1/examples/{endpoints.verify.response.json → endpoints.enable.response.json} +4 -5
- package/generated/v1/examples/endpoints.list.response.json +1 -2
- package/generated/v1/examples/endpoints.read.response.json +2 -2
- package/generated/v1/examples/endpoints.revealSigningSecret.response.json +7 -0
- package/generated/v1/examples/endpoints.rotateSigningKey.response.json +1 -2
- package/generated/v1/examples/endpoints.update.response.json +1 -1
- package/generated/v1/examples/executions.list.response.json +20 -0
- package/generated/v1/examples/exports.cancel.response.json +11 -0
- package/generated/v1/examples/exports.create.request.json +1 -0
- package/generated/v1/examples/exports.create.response.json +8 -0
- package/generated/v1/examples/exports.part.response.json +5 -0
- package/generated/v1/examples/exports.read.response.json +8 -0
- package/generated/v1/examples/limits.read.response.json +16 -0
- package/generated/v1/examples/occurrences.read.response.json +4 -0
- package/generated/v1/examples/occurrences.redeliver.response.json +2 -0
- package/generated/v1/examples/organizations.close.request.json +5 -0
- package/generated/v1/examples/organizations.close.response.json +10 -0
- package/generated/v1/examples/organizations.lifecycle.response.json +10 -0
- package/generated/v1/examples/organizations.restore.request.json +3 -0
- package/generated/v1/examples/organizations.restore.response.json +10 -0
- package/generated/v1/examples/projects.archive.request.json +3 -0
- package/generated/v1/examples/projects.archive.response.json +14 -0
- package/generated/v1/examples/projects.create.response.json +10 -1
- package/generated/v1/examples/projects.delete.response.json +9 -0
- package/generated/v1/examples/projects.list.response.json +12 -2
- package/generated/v1/examples/projects.rename.request.json +3 -0
- package/generated/v1/examples/projects.rename.response.json +14 -0
- package/generated/v1/examples/schedules.archive.response.json +3 -0
- package/generated/v1/examples/schedules.cancel.response.json +3 -0
- package/generated/v1/examples/schedules.create.request.json +4 -0
- package/generated/v1/examples/schedules.create.response.json +3 -0
- package/generated/v1/examples/schedules.list.response.json +3 -0
- package/generated/v1/examples/schedules.pause.response.json +3 -0
- package/generated/v1/examples/schedules.read.response.json +6 -0
- package/generated/v1/examples/schedules.replace.request.json +4 -0
- package/generated/v1/examples/schedules.replace.response.json +3 -0
- package/generated/v1/examples/schedules.resume.response.json +3 -0
- package/generated/v1/examples/timing.summary.response.json +35 -0
- package/generated/v1/examples/usage.read.response.json +1 -0
- package/generated/v1/first-value-journey.json +43 -18
- package/generated/v1/manifest.json +1287 -42
- package/generated/v1/openapi.json +36033 -5982
- package/generated/v1/policy-matrix.json +341 -47
- package/generated/v1/schemas/attempts.list.response.json +39 -1
- package/generated/v1/schemas/attempts.read.response.json +39 -1
- package/generated/v1/schemas/billing.autotopup.request-headers.json +13 -0
- package/generated/v1/schemas/billing.autotopup.request.json +22 -0
- package/generated/v1/schemas/billing.autotopup.response-headers.json +10 -0
- package/generated/v1/schemas/billing.autotopup.response.json +552 -0
- package/generated/v1/schemas/billing.autotopuppreview.request.json +77 -0
- package/generated/v1/schemas/billing.autotopuppreview.response.json +277 -0
- package/generated/v1/schemas/billing.budgets.request-headers.json +13 -0
- package/generated/v1/schemas/billing.budgets.request.json +66 -0
- package/generated/v1/schemas/billing.budgets.response-headers.json +10 -0
- package/generated/v1/schemas/billing.budgets.response.json +92 -0
- package/generated/v1/schemas/billing.ceiling.request-headers.json +13 -0
- package/generated/v1/schemas/billing.ceiling.request.json +40 -0
- package/generated/v1/schemas/billing.ceiling.response-headers.json +10 -0
- package/generated/v1/schemas/billing.ceiling.response.json +552 -0
- package/generated/v1/schemas/billing.change.request-headers.json +13 -0
- package/generated/v1/schemas/billing.change.request.json +22 -0
- package/generated/v1/schemas/billing.change.response.json +119 -0
- package/generated/v1/schemas/billing.funding.response.json +552 -0
- package/generated/v1/schemas/billing.fundingconfirm.response.json +552 -0
- package/generated/v1/schemas/billing.fundingquote.request.json +28 -0
- package/generated/v1/schemas/billing.fundingquote.response.json +61 -0
- package/generated/v1/schemas/billing.invoicehistory.query.json +16 -0
- package/generated/v1/schemas/billing.invoicehistory.response.json +88 -0
- package/generated/v1/schemas/billing.invoices.response.json +51 -0
- package/generated/v1/schemas/billing.ledger.query.json +16 -0
- package/generated/v1/schemas/billing.ledger.response.json +130 -0
- package/generated/v1/schemas/billing.notices.query.json +16 -0
- package/generated/v1/schemas/billing.notices.response.json +278 -0
- package/generated/v1/schemas/billing.notificationsettings.response.json +70 -0
- package/generated/v1/schemas/billing.notificationsettingsupdate.request-headers.json +13 -0
- package/generated/v1/schemas/billing.notificationsettingsupdate.request.json +43 -0
- package/generated/v1/schemas/billing.notificationsettingsupdate.response-headers.json +10 -0
- package/generated/v1/schemas/billing.notificationsettingsupdate.response.json +70 -0
- package/generated/v1/schemas/billing.paymenthistory.query.json +16 -0
- package/generated/v1/schemas/billing.paymenthistory.response.json +182 -0
- package/generated/v1/schemas/billing.paymentmethod.response.json +137 -0
- package/generated/v1/schemas/billing.paymentmethodconfirm.request.json +14 -0
- package/generated/v1/schemas/billing.paymentmethodconfirm.response.json +137 -0
- package/generated/v1/schemas/billing.paymentmethodremove.request.json +12 -0
- package/generated/v1/schemas/billing.paymentmethodremove.response.json +137 -0
- package/generated/v1/schemas/billing.paymentmethodsetup.request.json +12 -0
- package/generated/v1/schemas/billing.paymentmethodsetup.response.json +11 -0
- package/generated/v1/schemas/billing.portal.response.json +11 -0
- package/generated/v1/schemas/billing.projects.response.json +119 -0
- package/generated/v1/schemas/billing.purchase.request-headers.json +13 -0
- package/generated/v1/schemas/billing.purchase.request.json +26 -0
- package/generated/v1/schemas/billing.purchase.response-headers.json +10 -0
- package/generated/v1/schemas/billing.purchase.response.json +552 -0
- package/generated/v1/schemas/billing.quote.request.json +20 -0
- package/generated/v1/schemas/billing.quote.response.json +86 -0
- package/generated/v1/schemas/billing.reconcile.response.json +468 -0
- package/generated/v1/schemas/billing.refundrequest.request-headers.json +13 -0
- package/generated/v1/schemas/billing.refundrequest.request.json +42 -0
- package/generated/v1/schemas/billing.refundrequest.response-headers.json +10 -0
- package/generated/v1/schemas/billing.refundrequest.response.json +45 -0
- package/generated/v1/schemas/billing.refundresolution.request-headers.json +13 -0
- package/generated/v1/schemas/billing.refundresolution.request.json +61 -0
- package/generated/v1/schemas/billing.refundresolution.response-headers.json +10 -0
- package/generated/v1/schemas/billing.refundresolution.response.json +552 -0
- package/generated/v1/schemas/billing.summary.response.json +468 -0
- package/generated/v1/schemas/context.read.response.json +2 -0
- package/generated/v1/schemas/credentials.connection.response.json +2 -0
- package/generated/v1/schemas/credentials.create.request.json +2 -0
- package/generated/v1/schemas/credentials.create.response.json +2 -0
- package/generated/v1/schemas/credentials.list.response.json +2 -0
- package/generated/v1/schemas/credentials.revoke.response.json +2 -0
- package/generated/v1/schemas/credentials.rotate.response.json +4 -0
- package/generated/v1/schemas/{endpoints.verify.request-headers.json → endpoints.acknowledgeSigningKeyRotation.request-headers.json} +2 -1
- package/generated/v1/schemas/{endpoints.verify.response.json → endpoints.acknowledgeSigningKeyRotation.response.json} +8 -21
- package/generated/v1/schemas/endpoints.create.request.json +5 -0
- package/generated/v1/schemas/endpoints.create.response.json +8 -21
- package/generated/v1/schemas/endpoints.disable.response.json +8 -21
- package/generated/v1/schemas/endpoints.enable.response-headers.json +11 -0
- package/generated/v1/schemas/endpoints.enable.response.json +87 -0
- package/generated/v1/schemas/endpoints.list.response.json +8 -21
- package/generated/v1/schemas/endpoints.read.response.json +8 -21
- package/generated/v1/schemas/endpoints.revealSigningSecret.response.json +43 -0
- package/generated/v1/schemas/endpoints.rotateSigningKey.response.json +8 -21
- package/generated/v1/schemas/endpoints.update.request.json +5 -0
- package/generated/v1/schemas/endpoints.update.response.json +8 -21
- package/generated/v1/schemas/executions.list.query.json +46 -0
- package/generated/v1/schemas/executions.list.response.json +116 -0
- package/generated/v1/schemas/exports.cancel.response.json +48 -0
- package/generated/v1/schemas/exports.create.request.json +6 -0
- package/generated/v1/schemas/exports.create.response.json +38 -0
- package/generated/v1/schemas/exports.part.query.json +11 -0
- package/generated/v1/schemas/exports.part.response.json +22 -0
- package/generated/v1/schemas/exports.read.response.json +38 -0
- package/generated/v1/schemas/forecast.read.request-headers.json +10 -0
- package/generated/v1/schemas/forecast.read.response.json +148 -0
- package/generated/v1/schemas/grants.list.response.json +2 -0
- package/generated/v1/schemas/grants.revoke.response.json +2 -0
- package/generated/v1/schemas/limits.read.response.json +79 -0
- package/generated/v1/schemas/occurrences.list.query.json +5 -0
- package/generated/v1/schemas/occurrences.list.response.json +23 -0
- package/generated/v1/schemas/occurrences.read.response.json +23 -0
- package/generated/v1/schemas/occurrences.redeliver.response.json +39 -1
- package/generated/v1/schemas/organizations.close.request.json +20 -0
- package/generated/v1/schemas/organizations.close.response.json +68 -0
- package/generated/v1/schemas/organizations.lifecycle.response.json +68 -0
- package/generated/v1/schemas/organizations.restore.request.json +13 -0
- package/generated/v1/schemas/organizations.restore.response.json +68 -0
- package/generated/v1/schemas/projects.archive.request-headers.json +11 -0
- package/generated/v1/schemas/projects.archive.request.json +10 -0
- package/generated/v1/schemas/projects.archive.response-headers.json +11 -0
- package/generated/v1/schemas/projects.archive.response.json +79 -0
- package/generated/v1/schemas/projects.create.response.json +55 -1
- package/generated/v1/schemas/projects.delete.request-headers.json +11 -0
- package/generated/v1/schemas/projects.delete.response.json +37 -0
- package/generated/v1/schemas/projects.list.response.json +61 -2
- package/generated/v1/schemas/projects.rename.request-headers.json +11 -0
- package/generated/v1/schemas/projects.rename.request.json +12 -0
- package/generated/v1/schemas/projects.rename.response-headers.json +11 -0
- package/generated/v1/schemas/projects.rename.response.json +79 -0
- package/generated/v1/schemas/schedules.archive.response.json +90 -2
- package/generated/v1/schemas/schedules.cancel.response.json +90 -2
- package/generated/v1/schemas/schedules.create.request.json +24 -3
- package/generated/v1/schemas/schedules.create.response.json +90 -2
- package/generated/v1/schemas/schedules.list.query.json +13 -0
- package/generated/v1/schemas/schedules.list.response.json +67 -2
- package/generated/v1/schemas/schedules.pause.response.json +90 -2
- package/generated/v1/schemas/schedules.read.response.json +90 -2
- package/generated/v1/schemas/schedules.replace.request.json +24 -3
- package/generated/v1/schemas/schedules.replace.response.json +90 -2
- package/generated/v1/schemas/schedules.resume.response.json +90 -2
- package/generated/v1/schemas/timing.summary.response.json +191 -0
- package/generated/v1/schemas/usage.read.query.json +7 -0
- package/generated/v1/schemas/usage.read.response.json +36 -1
- package/generated/v1/snippets/billing.autotopup.json +6 -0
- package/generated/v1/snippets/billing.autotopuppreview.json +6 -0
- package/generated/v1/snippets/billing.budgets.json +6 -0
- package/generated/v1/snippets/billing.ceiling.json +6 -0
- package/generated/v1/snippets/billing.change.json +6 -0
- package/generated/v1/snippets/billing.funding.json +6 -0
- package/generated/v1/snippets/billing.fundingconfirm.json +6 -0
- package/generated/v1/snippets/billing.fundingquote.json +6 -0
- package/generated/v1/snippets/billing.invoicehistory.json +6 -0
- package/generated/v1/snippets/billing.invoices.json +6 -0
- package/generated/v1/snippets/billing.ledger.json +6 -0
- package/generated/v1/snippets/billing.notices.json +6 -0
- package/generated/v1/snippets/billing.notificationsettings.json +6 -0
- package/generated/v1/snippets/billing.notificationsettingsupdate.json +6 -0
- package/generated/v1/snippets/billing.paymenthistory.json +6 -0
- package/generated/v1/snippets/billing.paymentmethod.json +6 -0
- package/generated/v1/snippets/billing.paymentmethodconfirm.json +6 -0
- package/generated/v1/snippets/billing.paymentmethodremove.json +6 -0
- package/generated/v1/snippets/billing.paymentmethodsetup.json +6 -0
- package/generated/v1/snippets/billing.portal.json +6 -0
- package/generated/v1/snippets/billing.projects.json +6 -0
- package/generated/v1/snippets/billing.purchase.json +6 -0
- package/generated/v1/snippets/billing.quote.json +6 -0
- package/generated/v1/snippets/billing.reconcile.json +6 -0
- package/generated/v1/snippets/billing.refundrequest.json +6 -0
- package/generated/v1/snippets/billing.refundresolution.json +6 -0
- package/generated/v1/snippets/billing.summary.json +6 -0
- package/generated/v1/snippets/endpoints.acknowledgeSigningKeyRotation.json +6 -0
- package/generated/v1/snippets/{endpoints.verify.json → endpoints.enable.json} +3 -3
- package/generated/v1/snippets/endpoints.revealSigningSecret.json +6 -0
- package/generated/v1/snippets/executions.list.json +6 -0
- package/generated/v1/snippets/exports.cancel.json +6 -0
- package/generated/v1/snippets/exports.create.json +6 -0
- package/generated/v1/snippets/exports.part.json +6 -0
- package/generated/v1/snippets/exports.read.json +6 -0
- package/generated/v1/snippets/forecast.windows.json +1 -1
- package/generated/v1/snippets/limits.read.json +6 -0
- package/generated/v1/snippets/organizations.close.json +6 -0
- package/generated/v1/snippets/organizations.lifecycle.json +6 -0
- package/generated/v1/snippets/organizations.restore.json +6 -0
- package/generated/v1/snippets/projects.archive.json +6 -0
- package/generated/v1/snippets/projects.delete.json +6 -0
- package/generated/v1/snippets/projects.rename.json +6 -0
- package/generated/v1/snippets/schedules.create.json +3 -3
- package/generated/v1/snippets/schedules.replace.json +3 -3
- package/generated/v1/snippets/timing.summary.json +6 -0
- package/package.json +7 -7
- package/dist/callback-ownership.d.ts +0 -58
- package/dist/callback-ownership.js +0 -71
- package/generated/v1/callback-endpoint-verification.json +0 -59
- /package/generated/v1/schemas/{endpoints.verify.response-headers.json → endpoints.acknowledgeSigningKeyRotation.response-headers.json} +0 -0
package/dist/scheduling.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FIXED_INTERVAL_MIN_SECONDS, FIXED_INTERVAL_MAX_SECONDS, MAX_INLINE_PAYLOAD_BYTES, } from "./configuration-bounds.js";
|
|
2
|
+
import { z } from "./zod.js";
|
|
2
3
|
import { attemptIdSchema, deliveryIdSchema, endpointIdSchema, occurrenceIdSchema, scheduleIdSchema, } from "./identifiers.js";
|
|
3
4
|
import { CALLBACK_ATTEMPT_DIAGNOSTICS } from "./delivery.js";
|
|
4
|
-
import { pageSchema, versionSchema } from "./protocol.js";
|
|
5
|
+
import { pageRequestSchema, pageSchema, versionSchema, } from "./protocol.js";
|
|
5
6
|
import { callbackTriggerSchema, } from "./signatures.js";
|
|
6
7
|
import { durationSecondsSchema, timeZoneSchema, timestampSchema } from "./time.js";
|
|
7
8
|
/**
|
|
@@ -46,6 +47,38 @@ export const SCHEDULE_STATUSES = [
|
|
|
46
47
|
"completed",
|
|
47
48
|
];
|
|
48
49
|
export const scheduleStatusSchema = z.enum(SCHEDULE_STATUSES);
|
|
50
|
+
/**
|
|
51
|
+
* The statuses a schedule never leaves, and from which it never fires again on
|
|
52
|
+
* its own.
|
|
53
|
+
*
|
|
54
|
+
* **"Never fires again on its own" rather than "never delivers again", and the
|
|
55
|
+
* distinction is real.** A member can still press Redeliver on a completed
|
|
56
|
+
* occurrence, and the repository admits that for a `completed` schedule as well
|
|
57
|
+
* as an `active` one. What is terminal here is the schedule's own timetable, not
|
|
58
|
+
* every possible future delivery.
|
|
59
|
+
*
|
|
60
|
+
* Exported for the same reason as {@link TERMINAL_OCCURRENCE_STATES}: so a
|
|
61
|
+
* caller enumerates them rather than writing the list out and quietly missing
|
|
62
|
+
* one if another is ever added. Three places had already written it out — the
|
|
63
|
+
* `archivedAt` validator in `scheduling-records.ts`, the dashboard's schedule
|
|
64
|
+
* controls, and the dashboard's pending-action rules — and the third had missed
|
|
65
|
+
* it entirely, which is the defect this list was extracted for.
|
|
66
|
+
*/
|
|
67
|
+
export const TERMINAL_SCHEDULE_STATUSES = [
|
|
68
|
+
"cancelled",
|
|
69
|
+
"completed",
|
|
70
|
+
];
|
|
71
|
+
/**
|
|
72
|
+
* Whether this schedule has finished, either on its own or because somebody
|
|
73
|
+
* stopped it.
|
|
74
|
+
*
|
|
75
|
+
* `paused` is deliberately not terminal: it is reversible, so a paused schedule
|
|
76
|
+
* pointed at a broken destination is still a problem worth telling somebody
|
|
77
|
+
* about.
|
|
78
|
+
*/
|
|
79
|
+
export function isTerminalScheduleStatus(status) {
|
|
80
|
+
return TERMINAL_SCHEDULE_STATUSES.includes(status);
|
|
81
|
+
}
|
|
49
82
|
/**
|
|
50
83
|
* How promptly an occurrence expects to be dispatched.
|
|
51
84
|
*
|
|
@@ -55,29 +88,87 @@ export const scheduleStatusSchema = z.enum(SCHEDULE_STATUSES);
|
|
|
55
88
|
*/
|
|
56
89
|
export const LATENCY_CLASSES = ["realtime", "bulk"];
|
|
57
90
|
export const latencyClassSchema = z.enum(LATENCY_CLASSES);
|
|
91
|
+
/**
|
|
92
|
+
* What happens when an occurrence reaches its first attempt after its planned
|
|
93
|
+
* instant.
|
|
94
|
+
*
|
|
95
|
+
* `skipAhead` never carries a recurring first arrival across its next planned
|
|
96
|
+
* instant (and a shorter custom window may retire it earlier). `catchUp`
|
|
97
|
+
* admits one first arrival while it is still inside the schedule's staleness
|
|
98
|
+
* window, even after later recurrence instants passed. A one-shot has no next
|
|
99
|
+
* recurrence, so either explicit policy uses its staleness window and its
|
|
100
|
+
* default is `catchUp`. Neither choice applies to an occurrence already on its
|
|
101
|
+
* retry ladder: that work arrived, failed, and keeps the retry/give-up policy
|
|
102
|
+
* it started under.
|
|
103
|
+
*/
|
|
104
|
+
export const LATENESS_POLICIES = ["skipAhead", "catchUp"];
|
|
105
|
+
export const latenessPolicySchema = z.enum(LATENESS_POLICIES);
|
|
106
|
+
/** The inherited one-shot staleness window: twenty-four hours. */
|
|
107
|
+
export const DEFAULT_ONE_SHOT_MAX_STALENESS_SECONDS = 86_400;
|
|
108
|
+
/**
|
|
109
|
+
* The per-schedule lateness choices a caller may state.
|
|
110
|
+
*
|
|
111
|
+
* Both members are optional so a caller can override only the part whose
|
|
112
|
+
* default does not fit. The policy defaults from recurrence kind. An omitted
|
|
113
|
+
* staleness bound uses the recurrence's own default: twenty-four hours for a
|
|
114
|
+
* one-shot, and one recurrence interval for a recurring schedule.
|
|
115
|
+
*/
|
|
116
|
+
export const scheduleLatenessRequestSchema = z
|
|
117
|
+
.object({
|
|
118
|
+
maxStalenessSeconds: durationSecondsSchema
|
|
119
|
+
.positive("must be at least 1 second")
|
|
120
|
+
.max(Number.MAX_SAFE_INTEGER, "must be a safely representable duration")
|
|
121
|
+
.optional(),
|
|
122
|
+
policy: latenessPolicySchema.optional(),
|
|
123
|
+
})
|
|
124
|
+
.strict();
|
|
125
|
+
/**
|
|
126
|
+
* Lateness settings as a schedule reads back.
|
|
127
|
+
*
|
|
128
|
+
* The policy is always effective and therefore required. The numeric member
|
|
129
|
+
* remains optional because it records a per-schedule override, not a duplicated
|
|
130
|
+
* copy of a default that can be derived from the recurrence.
|
|
131
|
+
*/
|
|
132
|
+
export const scheduleLatenessSchema = scheduleLatenessRequestSchema.extend({
|
|
133
|
+
policy: latenessPolicySchema,
|
|
134
|
+
});
|
|
58
135
|
/* -------------------------------------------------------------------------- */
|
|
59
136
|
/* Delivery duration */
|
|
60
137
|
/* -------------------------------------------------------------------------- */
|
|
61
138
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* **
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
139
|
+
* The durations this product used to sell, one rung at a time.
|
|
140
|
+
*
|
|
141
|
+
* **Kept, and no longer the whole of what is accepted** (FXO-2258). The ladder
|
|
142
|
+
* was the argument that 47 seconds is a question with no answer — past the
|
|
143
|
+
* point where a delivery may occupy a partition slot, short of the point where
|
|
144
|
+
* it is worth carrying separately. The operator overruled it after the walk:
|
|
145
|
+
* "This can have a completely user definable timeout, they don't cost us hardly
|
|
146
|
+
* anything, but we should cap at something anyway, 24h is still fine."
|
|
147
|
+
*
|
|
148
|
+
* What the rungs were actually carrying survives without them. The line that
|
|
149
|
+
* matters commercially is {@link FREE_DELIVERY_DURATION_SECONDS} — above thirty
|
|
150
|
+
* seconds a delivery is carried outside the partition and needs a plan that
|
|
151
|
+
* allows it — and that line is a comparison, not a membership test. The line
|
|
152
|
+
* that matters technically is {@link RESPONSE_DELIVERY_MAX_DURATION_SECONDS},
|
|
153
|
+
* which is also a comparison. Neither ever needed eight fixed values to be
|
|
154
|
+
* expressible; the ladder was how they were *sold*, and per-rung pricing is
|
|
155
|
+
* what the operator withdrew.
|
|
156
|
+
*
|
|
157
|
+
* The list stays because a schedule saved on a rung must keep working, because
|
|
158
|
+
* the delivery-class rules are still exercised against exactly these values,
|
|
159
|
+
* and because the dashboard offers them as the shortcuts most people want.
|
|
77
160
|
*/
|
|
78
161
|
export const DELIVERY_DURATION_TIERS = [
|
|
79
162
|
5, 30, 300, 900, 1_800, 3_600, 43_200, 86_400,
|
|
80
163
|
];
|
|
164
|
+
/**
|
|
165
|
+
* The longest duration any delivery may be given, in seconds.
|
|
166
|
+
*
|
|
167
|
+
* Twenty-four hours, which is the ladder's own top rung — so widening the field
|
|
168
|
+
* to a free-form integer admits nothing that was not already for sale. "24h is
|
|
169
|
+
* still fine."
|
|
170
|
+
*/
|
|
171
|
+
export const MAX_DELIVERY_DURATION_SECONDS = 86_400;
|
|
81
172
|
/**
|
|
82
173
|
* What a schedule gets without asking, in seconds.
|
|
83
174
|
*
|
|
@@ -134,8 +225,26 @@ export const FREE_DELIVERY_DURATION_SECONDS = 30;
|
|
|
134
225
|
* than thirty minutes has to do is answer immediately and call us back.
|
|
135
226
|
*/
|
|
136
227
|
export const RESPONSE_DELIVERY_MAX_DURATION_SECONDS = 1_800;
|
|
228
|
+
/**
|
|
229
|
+
* How long the receiver may take to answer, in whole seconds.
|
|
230
|
+
*
|
|
231
|
+
* **A bounded integer since FXO-2258, and every value the enum accepted is
|
|
232
|
+
* still accepted.** A widening is the one shape of contract change that cannot
|
|
233
|
+
* break a caller: a request that was valid before is valid now, and a stored
|
|
234
|
+
* schedule reads back and re-saves unchanged.
|
|
235
|
+
*
|
|
236
|
+
* The two bounds that are real are still enforced, and neither of them was ever
|
|
237
|
+
* the enum's doing: `maxDurationSeconds` above
|
|
238
|
+
* {@link RESPONSE_DELIVERY_MAX_DURATION_SECONDS} is refused unless `completion`
|
|
239
|
+
* is `callback` (checked in `scheduleRequestSchema`'s refinement, because it is
|
|
240
|
+
* a relation between two fields), and above
|
|
241
|
+
* {@link FREE_DELIVERY_DURATION_SECONDS} it needs a plan that allows it.
|
|
242
|
+
*/
|
|
137
243
|
export const deliveryDurationSecondsSchema = z
|
|
138
|
-
.
|
|
244
|
+
.number()
|
|
245
|
+
.int("must be a whole number of seconds")
|
|
246
|
+
.positive("must be at least 1 second")
|
|
247
|
+
.max(MAX_DELIVERY_DURATION_SECONDS, `must be at most ${String(MAX_DELIVERY_DURATION_SECONDS)} seconds`)
|
|
139
248
|
.describe("How long the receiver may take to answer, in seconds");
|
|
140
249
|
/** Whether a duration is past the point where it needs a plan that allows it. */
|
|
141
250
|
export function isLongRunningDuration(seconds) {
|
|
@@ -247,6 +356,89 @@ export const OCCURRENCE_STATES = [
|
|
|
247
356
|
"skipped_stale",
|
|
248
357
|
];
|
|
249
358
|
export const occurrenceStateSchema = z.enum(OCCURRENCE_STATES);
|
|
359
|
+
/**
|
|
360
|
+
* One project-wide walk over retained occurrences, newest first.
|
|
361
|
+
*
|
|
362
|
+
* There is deliberately no order parameter: the Executions page answers what
|
|
363
|
+
* happened most recently, and making that fixed lets its index and cursor say
|
|
364
|
+
* the same thing. The date range is half-open, like every other time window in
|
|
365
|
+
* this contract, so adjacent requests neither overlap nor leave a gap.
|
|
366
|
+
*/
|
|
367
|
+
export const projectOccurrenceListRequestSchema = pageRequestSchema
|
|
368
|
+
.extend({
|
|
369
|
+
endpoint: endpointIdSchema.optional(),
|
|
370
|
+
from: timestampSchema.optional(),
|
|
371
|
+
outcome: occurrenceStateSchema.optional(),
|
|
372
|
+
schedule: scheduleIdSchema.optional(),
|
|
373
|
+
to: timestampSchema.optional(),
|
|
374
|
+
})
|
|
375
|
+
.refine((request) => request.from === undefined ||
|
|
376
|
+
request.to === undefined ||
|
|
377
|
+
Date.parse(request.from) < Date.parse(request.to), {
|
|
378
|
+
message: "must be later than from",
|
|
379
|
+
path: ["to"],
|
|
380
|
+
});
|
|
381
|
+
const PROJECT_OCCURRENCE_LIST_PARAMETERS = [
|
|
382
|
+
"cursor",
|
|
383
|
+
"limit",
|
|
384
|
+
"endpoint",
|
|
385
|
+
"from",
|
|
386
|
+
"outcome",
|
|
387
|
+
"schedule",
|
|
388
|
+
"to",
|
|
389
|
+
];
|
|
390
|
+
/** Read the project occurrence filters exactly as REST and MCP send them. */
|
|
391
|
+
export function parseProjectOccurrenceListRequest(query) {
|
|
392
|
+
const errors = [];
|
|
393
|
+
const raw = {};
|
|
394
|
+
for (const field of PROJECT_OCCURRENCE_LIST_PARAMETERS) {
|
|
395
|
+
const values = query.getAll(field);
|
|
396
|
+
if (values.length > 1) {
|
|
397
|
+
errors.push({
|
|
398
|
+
pointer: `/${field}`,
|
|
399
|
+
reason: "must be given at most once",
|
|
400
|
+
});
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const value = values[0];
|
|
404
|
+
if (value === undefined)
|
|
405
|
+
continue;
|
|
406
|
+
raw[field] =
|
|
407
|
+
field === "limit" && /^-?\d+$/u.test(value) ? Number(value) : value;
|
|
408
|
+
}
|
|
409
|
+
if (errors.length > 0)
|
|
410
|
+
return { errors, ok: false };
|
|
411
|
+
const parsed = projectOccurrenceListRequestSchema.safeParse(raw);
|
|
412
|
+
if (parsed.success)
|
|
413
|
+
return { ok: true, page: parsed.data };
|
|
414
|
+
return {
|
|
415
|
+
errors: parsed.error.issues.map((issue) => ({
|
|
416
|
+
pointer: `/${issue.path.join("/")}`,
|
|
417
|
+
reason: issue.message,
|
|
418
|
+
})),
|
|
419
|
+
ok: false,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* The exact operation and filters a project occurrence cursor belongs to.
|
|
424
|
+
*
|
|
425
|
+
* Cursor and limit do not affect which ordered collection is being walked, so
|
|
426
|
+
* neither belongs in the binding. Every actual filter does. Optional values
|
|
427
|
+
* stay absent rather than becoming empty strings, which are invalid inputs and
|
|
428
|
+
* must never collide with an unfiltered request.
|
|
429
|
+
*/
|
|
430
|
+
export function projectOccurrenceCursorBinding(request) {
|
|
431
|
+
return {
|
|
432
|
+
operation: "executions.list",
|
|
433
|
+
parameters: {
|
|
434
|
+
...(request.endpoint === undefined ? {} : { endpoint: request.endpoint }),
|
|
435
|
+
...(request.from === undefined ? {} : { from: request.from }),
|
|
436
|
+
...(request.outcome === undefined ? {} : { outcome: request.outcome }),
|
|
437
|
+
...(request.schedule === undefined ? {} : { schedule: request.schedule }),
|
|
438
|
+
...(request.to === undefined ? {} : { to: request.to }),
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
}
|
|
250
442
|
/**
|
|
251
443
|
* The states an occurrence never leaves.
|
|
252
444
|
*
|
|
@@ -296,7 +488,7 @@ export const ATTEMPT_REMEDIATIONS = [
|
|
|
296
488
|
"none",
|
|
297
489
|
"review_retry_policy",
|
|
298
490
|
"inspect_receiver",
|
|
299
|
-
"
|
|
491
|
+
"review_endpoint",
|
|
300
492
|
"replace_payload",
|
|
301
493
|
"contact_support",
|
|
302
494
|
"review_before_redelivery",
|
|
@@ -320,7 +512,7 @@ export function remediationForAttempt(diagnostic) {
|
|
|
320
512
|
return "none";
|
|
321
513
|
case "endpoint_unavailable":
|
|
322
514
|
case "endpoint_unsafe":
|
|
323
|
-
return "
|
|
515
|
+
return "review_endpoint";
|
|
324
516
|
case "http_refused":
|
|
325
517
|
case "http_terminal":
|
|
326
518
|
// The same advice for two different futures, deliberately. Both mean the
|
|
@@ -409,17 +601,37 @@ export const SCHEDULING_BOUNDS = {
|
|
|
409
601
|
* Attempts made against one occurrence before it is terminal, including the
|
|
410
602
|
* first. Bounded so a permanently broken receiver cannot make one occurrence
|
|
411
603
|
* cost an unbounded amount of work.
|
|
604
|
+
*
|
|
605
|
+
* **Eight, and the number is load-bearing against a second limit rather than
|
|
606
|
+
* on its own.** `MAX_BUDGETED_DELIVERY_FAILURES` stops an occurrence after
|
|
607
|
+
* five failures the *receiver* caused; a rate limit and this product's own
|
|
608
|
+
* infrastructure faults are exempt from that count. The gap between the two
|
|
609
|
+
* is what the exemption spends — an occurrence throttled twice still has
|
|
610
|
+
* five receiver failures left. Lowering this to five was considered during
|
|
611
|
+
* the W4-10 walk and rejected once the pair was understood: it would make
|
|
612
|
+
* both limits fire on the same attempt and leave the exemption buying
|
|
613
|
+
* nothing.
|
|
412
614
|
*/
|
|
413
615
|
attemptsPerOccurrence: 8,
|
|
414
616
|
/**
|
|
415
617
|
* The longest fixed interval a recurring schedule may ask for, in seconds.
|
|
416
618
|
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
619
|
+
* One year. **The thirty days this replaces had no reason of its own** — it
|
|
620
|
+
* was the copied scheduler's `MAX_INTERVAL_SECONDS`, carried over on the
|
|
621
|
+
* belief that "a Durable Object alarm is not arbitrarily far in the future".
|
|
622
|
+
* Cloudflare documents no maximum distance for `setAlarm()`, and the belief
|
|
623
|
+
* predates the PostgreSQL authority besides: the durable fact is a row, and
|
|
624
|
+
* the alarm is only what wakes a bucket to read it.
|
|
625
|
+
*
|
|
626
|
+
* What makes a distant alarm safe is not the alarm. `TimerBucketRepairer`
|
|
627
|
+
* asks PostgreSQL for occurrences whose instant has passed and re-signals
|
|
628
|
+
* their bucket, on committed truth, never asking whether an alarm was armed
|
|
629
|
+
* — so an alarm that is somehow lost costs lateness rather than the run.
|
|
630
|
+
*
|
|
631
|
+
* Widening a bound is additive by `classifyChange` and narrowing it is
|
|
632
|
+
* breaking, so this direction is the safe one to move in.
|
|
421
633
|
*/
|
|
422
|
-
everySecondsMax:
|
|
634
|
+
everySecondsMax: FIXED_INTERVAL_MAX_SECONDS,
|
|
423
635
|
/**
|
|
424
636
|
* The shortest fixed interval a recurring schedule may ask for, in seconds.
|
|
425
637
|
*
|
|
@@ -431,7 +643,7 @@ export const SCHEDULING_BOUNDS = {
|
|
|
431
643
|
* `classifyChange` and narrowing it is breaking, which is the direction to be
|
|
432
644
|
* conservative in.
|
|
433
645
|
*/
|
|
434
|
-
everySecondsMin:
|
|
646
|
+
everySecondsMin: FIXED_INTERVAL_MIN_SECONDS,
|
|
435
647
|
/**
|
|
436
648
|
* How long an idempotency receipt is honoured. A client retrying a create for
|
|
437
649
|
* longer than a day is not retrying, it is making a new request; keeping
|
|
@@ -483,7 +695,7 @@ export const SCHEDULING_BOUNDS = {
|
|
|
483
695
|
* command-shaped payloads this product is for, and refuses the ones that are
|
|
484
696
|
* really a file transfer.
|
|
485
697
|
*/
|
|
486
|
-
payloadBytes:
|
|
698
|
+
payloadBytes: MAX_INLINE_PAYLOAD_BYTES,
|
|
487
699
|
/**
|
|
488
700
|
* How soon a one-shot has to be for `realtime` to be the class it gets by
|
|
489
701
|
* default, in seconds.
|
|
@@ -573,6 +785,10 @@ export const recurrenceSchema = z.discriminatedUnion("kind", [
|
|
|
573
785
|
timeZone: timeZoneSchema,
|
|
574
786
|
}),
|
|
575
787
|
]);
|
|
788
|
+
/** The customer-safe default for a schedule's recurrence kind. */
|
|
789
|
+
export function defaultLatenessPolicyForRecurrence(kind) {
|
|
790
|
+
return kind === "at" ? "catchUp" : "skipAhead";
|
|
791
|
+
}
|
|
576
792
|
/* -------------------------------------------------------------------------- */
|
|
577
793
|
/* Public resources */
|
|
578
794
|
/* -------------------------------------------------------------------------- */
|
|
@@ -584,6 +800,23 @@ export const recurrenceSchema = z.discriminatedUnion("kind", [
|
|
|
584
800
|
* endpoint id is useful definition metadata; the encrypted payload is content
|
|
585
801
|
* and stays only in the authoritative partition.
|
|
586
802
|
*/
|
|
803
|
+
/**
|
|
804
|
+
* A schedule's name: for people, never an identifier, and free to collide.
|
|
805
|
+
*
|
|
806
|
+
* Generated on save when nobody types one (W4-14, FXO-2255) — "Every 60s →
|
|
807
|
+
* flowxo.com" — and stored as any other name, so editing the recurrence
|
|
808
|
+
* later rewrites nothing. Two schedules may share a name: two on the same
|
|
809
|
+
* cadence against the same host both generate the same one, and the operator
|
|
810
|
+
* chose to let them collide, with the other columns and the identifier
|
|
811
|
+
* telling them apart. Endpoints get the opposite rule.
|
|
812
|
+
*/
|
|
813
|
+
/** The most a schedule's name may hold; the generated one is cut to fit it. */
|
|
814
|
+
export const SCHEDULE_NAME_MAX_LENGTH = 120;
|
|
815
|
+
export const scheduleNameSchema = z
|
|
816
|
+
.string()
|
|
817
|
+
.trim()
|
|
818
|
+
.min(1, "must not be blank")
|
|
819
|
+
.max(SCHEDULE_NAME_MAX_LENGTH, `must be at most ${String(SCHEDULE_NAME_MAX_LENGTH)} characters`);
|
|
587
820
|
export const scheduleSchema = z.object({
|
|
588
821
|
/**
|
|
589
822
|
* When the schedule was filed away, and absent until it is.
|
|
@@ -596,6 +829,20 @@ export const scheduleSchema = z.object({
|
|
|
596
829
|
* terminal status — archiving is filing, not stopping.
|
|
597
830
|
*/
|
|
598
831
|
archivedAt: timestampSchema.optional(),
|
|
832
|
+
/**
|
|
833
|
+
* The payload the schedule carries, as it was given.
|
|
834
|
+
*
|
|
835
|
+
* Present on a read when the schedule has one; left off listings, which are
|
|
836
|
+
* served from a projection that holds a summary. **Readable by anyone who
|
|
837
|
+
* can read the schedule** — the dashboard, the API, the SDK and MCP alike —
|
|
838
|
+
* since FXO-2243, chosen with the cost stated: a payload somebody put a
|
|
839
|
+
* secret in is readable by anything holding a read key, and appears in an
|
|
840
|
+
* agent's tool history. The write-only custody this replaces answered that
|
|
841
|
+
* with a blanket rule over the whole body — "I've never used a platform
|
|
842
|
+
* where my configured payload was hidden from me" — and W4-13 answers it
|
|
843
|
+
* per field instead, with a header value that is a secret marked as one.
|
|
844
|
+
*/
|
|
845
|
+
data: z.json().optional(),
|
|
599
846
|
createdAt: timestampSchema,
|
|
600
847
|
/**
|
|
601
848
|
* The delivery settings in force, with the defaults filled in.
|
|
@@ -626,9 +873,33 @@ export const scheduleSchema = z.object({
|
|
|
626
873
|
.optional(),
|
|
627
874
|
/** The verified reusable callback endpoint this schedule delivers to. */
|
|
628
875
|
endpoint: endpointIdSchema.optional(),
|
|
876
|
+
/**
|
|
877
|
+
* Where the schedule delivers, as an address (W4-14, FXO-2255).
|
|
878
|
+
*
|
|
879
|
+
* Beside the identifier rather than instead of it, on the listing and the
|
|
880
|
+
* read alike, so every caller — dashboard, SDK, agent — sees a readable
|
|
881
|
+
* destination without a second request. Present when the schedule has an
|
|
882
|
+
* endpoint the service could name; absent on a timer, and on a row whose
|
|
883
|
+
* endpoint is no longer readable.
|
|
884
|
+
*/
|
|
885
|
+
endpointUrl: z.string().url().optional(),
|
|
629
886
|
generation: generationSchema,
|
|
630
887
|
id: scheduleIdSchema,
|
|
631
888
|
latencyClass: latencyClassSchema,
|
|
889
|
+
/**
|
|
890
|
+
* What this schedule does with an occurrence that reaches its first attempt
|
|
891
|
+
* late. Present on every schedule so a caller never has to duplicate the
|
|
892
|
+
* recurrence-sensitive policy default. `maxStalenessSeconds` is present only
|
|
893
|
+
* when this schedule overrides its recurrence's window.
|
|
894
|
+
*/
|
|
895
|
+
lateness: scheduleLatenessSchema,
|
|
896
|
+
/**
|
|
897
|
+
* The schedule's name. Every schedule the service has written since W4-14
|
|
898
|
+
* has one — generated on save when none was given — and it is optional on
|
|
899
|
+
* the wire only so a row from before the column, or a reader holding a
|
|
900
|
+
* projection the migration has not reached, parses rather than fails.
|
|
901
|
+
*/
|
|
902
|
+
name: scheduleNameSchema.optional(),
|
|
632
903
|
partitionKey: partitionKeySchema.optional(),
|
|
633
904
|
/**
|
|
634
905
|
* How this schedule recurs, in the vocabulary it will keep being read in.
|
|
@@ -643,6 +914,24 @@ export const scheduleSchema = z.object({
|
|
|
643
914
|
* was converted to an instant once, at acceptance, and reporting the delay
|
|
644
915
|
* back would describe a schedule whose next fire moves every time it is read.
|
|
645
916
|
*/
|
|
917
|
+
/**
|
|
918
|
+
* When this schedule next fires, and absent when nothing is on the books.
|
|
919
|
+
*
|
|
920
|
+
* **The earliest unsettled occurrence, which is a fact rather than a
|
|
921
|
+
* projection** (FXO-2283). It is absent for a schedule that will not fire
|
|
922
|
+
* again — cancelled, archived, or a one-shot that has run — and absent is the
|
|
923
|
+
* truth about those rather than a value withheld: there is no instant to
|
|
924
|
+
* report, and reporting the last one would read as a plan.
|
|
925
|
+
*
|
|
926
|
+
* **This is the one field on a listing row that does not come from the
|
|
927
|
+
* projection.** The page comment below says a listing holds a route and a
|
|
928
|
+
* summary rather than every partition's live occurrence, and that is still
|
|
929
|
+
* true of the *detail* an occurrence carries — its state, its attempts, its
|
|
930
|
+
* identifier. This is one instant, taken by a lateral against a partial index
|
|
931
|
+
* built for it, and it is here because "when does this next run" is the
|
|
932
|
+
* question a list of schedules is read to answer.
|
|
933
|
+
*/
|
|
934
|
+
nextDueAt: timestampSchema.optional(),
|
|
646
935
|
recurrence: recurrenceSchema,
|
|
647
936
|
status: scheduleStatusSchema,
|
|
648
937
|
updatedAt: timestampSchema,
|
|
@@ -652,13 +941,39 @@ export const scheduleSchema = z.object({
|
|
|
652
941
|
* A page of schedules.
|
|
653
942
|
*
|
|
654
943
|
* Items are schedules rather than schedule *views*. A cross-partition listing is
|
|
655
|
-
* served from the
|
|
944
|
+
* served from the control-plane projection W2-06 built, and that projection holds a route
|
|
656
945
|
* and a summary rather than every partition's live occurrence — so a page that
|
|
657
946
|
* promised an occurrence would either lie about its freshness or force the
|
|
658
947
|
* listing back through every partition it spans. Detail is where the occurrence
|
|
659
948
|
* is authoritative, and detail is one call away.
|
|
660
949
|
*/
|
|
661
950
|
export const schedulePageSchema = pageSchema(scheduleSchema);
|
|
951
|
+
function hasCompleteTimingEvidence(value) {
|
|
952
|
+
return ((value.firstAttemptStartedAt === undefined) ===
|
|
953
|
+
(value.firstAttemptLatenessMs === undefined) &&
|
|
954
|
+
(value.firstAttemptStartedAt === undefined) ===
|
|
955
|
+
(value.timingPolicyVersion === undefined));
|
|
956
|
+
}
|
|
957
|
+
function hasConsistentTimingEligibility(value) {
|
|
958
|
+
return value.timingEligibility === undefined
|
|
959
|
+
? value.timingEligibilityReason === undefined
|
|
960
|
+
: value.timingEligibility === "eligible"
|
|
961
|
+
? value.timingEligibilityReason === undefined
|
|
962
|
+
: value.timingEligibilityReason !== undefined;
|
|
963
|
+
}
|
|
964
|
+
const timingEvidenceShape = {
|
|
965
|
+
/** Signed difference from `plannedFor`; a negative value is an early send. */
|
|
966
|
+
firstAttemptLatenessMs: z.int().optional(),
|
|
967
|
+
/** Earliest trusted outbound start across this occurrence's attempts. */
|
|
968
|
+
firstAttemptStartedAt: timestampSchema.optional(),
|
|
969
|
+
/** Absent on records that predate timing evidence, which means unknown. */
|
|
970
|
+
timingEligibility: z
|
|
971
|
+
.enum(["eligible", "customer_blocked", "platform_blocked"])
|
|
972
|
+
.optional(),
|
|
973
|
+
timingEligibilityReason: z.string().min(1).max(128).optional(),
|
|
974
|
+
/** Policy pinned when trusted first-attempt timing evidence is recorded. */
|
|
975
|
+
timingPolicyVersion: z.string().min(1).max(64).optional(),
|
|
976
|
+
};
|
|
662
977
|
/**
|
|
663
978
|
* One concrete occurrence of a schedule.
|
|
664
979
|
*
|
|
@@ -670,6 +985,7 @@ export const schedulePageSchema = pageSchema(scheduleSchema);
|
|
|
670
985
|
export const occurrenceSchema = z
|
|
671
986
|
.object({
|
|
672
987
|
attemptCount: z.number().int().nonnegative(),
|
|
988
|
+
...timingEvidenceShape,
|
|
673
989
|
generation: generationSchema,
|
|
674
990
|
id: occurrenceIdSchema,
|
|
675
991
|
/** When the occurrence was planned to fire. Never earlier than this. */
|
|
@@ -689,6 +1005,14 @@ export const occurrenceSchema = z
|
|
|
689
1005
|
(occurrence.settledAt !== undefined), {
|
|
690
1006
|
message: "settledAt must be present for a terminal state and absent otherwise",
|
|
691
1007
|
path: ["settledAt"],
|
|
1008
|
+
})
|
|
1009
|
+
.refine(hasCompleteTimingEvidence, {
|
|
1010
|
+
message: "firstAttemptStartedAt, firstAttemptLatenessMs and timingPolicyVersion must be present together",
|
|
1011
|
+
path: ["firstAttemptStartedAt"],
|
|
1012
|
+
})
|
|
1013
|
+
.refine(hasConsistentTimingEligibility, {
|
|
1014
|
+
message: "timingEligibilityReason is required for a blocked population and absent otherwise",
|
|
1015
|
+
path: ["timingEligibilityReason"],
|
|
692
1016
|
});
|
|
693
1017
|
/**
|
|
694
1018
|
* One delivery attempt against one occurrence.
|
|
@@ -746,6 +1070,12 @@ export const attemptSchema = z
|
|
|
746
1070
|
delivery: deliveryIdSchema,
|
|
747
1071
|
/** Safe classifier code; absent when durable evidence predates diagnostics. */
|
|
748
1072
|
diagnostic: attemptDiagnosticSchema.optional(),
|
|
1073
|
+
/**
|
|
1074
|
+
* Instrumented re-drives of the same dispatching occurrence under a new
|
|
1075
|
+
* dispatch epoch. Includes deliberate admission deferrals; this is not a
|
|
1076
|
+
* count of database failures or callback sends. Absent on older responses.
|
|
1077
|
+
*/
|
|
1078
|
+
dispatchRecoveries: z.int().nonnegative().optional(),
|
|
749
1079
|
/** Executor-observed elapsed time; absent when no durable timing survived. */
|
|
750
1080
|
durationMs: z
|
|
751
1081
|
.number()
|
|
@@ -753,8 +1083,26 @@ export const attemptSchema = z
|
|
|
753
1083
|
.nonnegative()
|
|
754
1084
|
.max(ATTEMPT_DURATION_MAX_MS)
|
|
755
1085
|
.optional(),
|
|
1086
|
+
...timingEvidenceShape,
|
|
756
1087
|
id: attemptIdSchema,
|
|
1088
|
+
/**
|
|
1089
|
+
* Instrumented send authorizations granted after a later local alarm
|
|
1090
|
+
* replayed the original wake receipt. Preserves the attempt and delivery
|
|
1091
|
+
* identity; excludes synchronous receipt hedges and repeated authorization
|
|
1092
|
+
* acknowledgements. A grant does not prove a callback left. Absent on
|
|
1093
|
+
* older responses.
|
|
1094
|
+
*/
|
|
1095
|
+
localReceiptRecoveries: z.int().nonnegative().optional(),
|
|
757
1096
|
occurrence: occurrenceIdSchema,
|
|
1097
|
+
/**
|
|
1098
|
+
* How many times the watchdog took this attempt's send back and sent it
|
|
1099
|
+
* again: the send was authorized, and its outcome was never recorded
|
|
1100
|
+
* before its lease expired. Each re-send uses this attempt's `delivery` id,
|
|
1101
|
+
* so the receiver may have got the attempt more than once. `state` is the
|
|
1102
|
+
* outcome that was recorded. The timing evidence is the earliest send
|
|
1103
|
+
* known to have left. Absent when nothing was re-sent.
|
|
1104
|
+
*/
|
|
1105
|
+
rescues: z.int().positive().optional(),
|
|
758
1106
|
/** Stable next-action code paired with a diagnostic when one is known. */
|
|
759
1107
|
remediation: attemptRemediationSchema.optional(),
|
|
760
1108
|
/**
|
|
@@ -802,11 +1150,24 @@ export const attemptSchema = z
|
|
|
802
1150
|
attempt.responseStatus === undefined), {
|
|
803
1151
|
message: "classification metadata must be absent while an attempt is in flight",
|
|
804
1152
|
path: ["state"],
|
|
1153
|
+
})
|
|
1154
|
+
.refine(hasCompleteTimingEvidence, {
|
|
1155
|
+
message: "firstAttemptStartedAt, firstAttemptLatenessMs and timingPolicyVersion must be present together",
|
|
1156
|
+
path: ["firstAttemptStartedAt"],
|
|
1157
|
+
})
|
|
1158
|
+
.refine(hasConsistentTimingEligibility, {
|
|
1159
|
+
message: "timingEligibilityReason is required for a blocked population and absent otherwise",
|
|
1160
|
+
path: ["timingEligibilityReason"],
|
|
805
1161
|
});
|
|
806
1162
|
/** An authoritative page over one schedule's retained occurrences. */
|
|
807
1163
|
export const occurrencePageSchema = pageSchema(occurrenceSchema).extend({
|
|
808
1164
|
freshness: z.object({ kind: z.literal("authoritative") }),
|
|
809
1165
|
});
|
|
1166
|
+
/** A project-wide occurrence page and its plan's normal history boundary. */
|
|
1167
|
+
export const projectOccurrencePageSchema = pageSchema(occurrenceSchema).extend({
|
|
1168
|
+
freshness: z.object({ kind: z.literal("authoritative") }),
|
|
1169
|
+
retention: z.object({ from: timestampSchema }),
|
|
1170
|
+
});
|
|
810
1171
|
/** An authoritative occurrence detail response. */
|
|
811
1172
|
export const occurrenceViewSchema = z.object({
|
|
812
1173
|
freshness: z.object({ kind: z.literal("authoritative") }),
|
|
@@ -877,8 +1238,8 @@ export const scheduleRequestSchema = z
|
|
|
877
1238
|
/**
|
|
878
1239
|
* Customer data placed in the signed callback envelope.
|
|
879
1240
|
*
|
|
880
|
-
* Any JSON value is accepted, including `null`, arrays and scalars.
|
|
881
|
-
*
|
|
1241
|
+
* Any JSON value is accepted, including `null`, arrays and scalars. A
|
|
1242
|
+
* schedule read returns it; listings leave it off. The accepting adapter
|
|
882
1243
|
* serializes it once, enforces `SCHEDULING_BOUNDS.payloadBytes` against
|
|
883
1244
|
* those UTF-8 bytes, then encrypts those same bytes before persistence.
|
|
884
1245
|
*/
|
|
@@ -904,16 +1265,15 @@ export const scheduleRequestSchema = z
|
|
|
904
1265
|
*
|
|
905
1266
|
* **Grouped rather than three sibling fields**, because two of the three are
|
|
906
1267
|
* meaningless without the first. `onOverlap` only answers a question a long
|
|
907
|
-
* run can ask
|
|
908
|
-
*
|
|
909
|
-
* visible in the shape instead of only in a refusal message.
|
|
1268
|
+
* run can ask; beside a five-second delivery it is stored and never
|
|
1269
|
+
* consulted. Nesting makes the dependency visible in the shape.
|
|
910
1270
|
*/
|
|
911
1271
|
delivery: z
|
|
912
1272
|
.object({
|
|
913
1273
|
/**
|
|
914
1274
|
* What to do when this schedule comes due and its previous occurrence
|
|
915
|
-
* has not finished.
|
|
916
|
-
*
|
|
1275
|
+
* has not finished. Consulted only on a recurring schedule whose runs
|
|
1276
|
+
* can outlast their interval; accepted and ignored elsewhere.
|
|
917
1277
|
*/
|
|
918
1278
|
onOverlap: deliveryOverlapPolicySchema.optional(),
|
|
919
1279
|
/**
|
|
@@ -922,8 +1282,11 @@ export const scheduleRequestSchema = z
|
|
|
922
1282
|
*/
|
|
923
1283
|
completion: deliveryCompletionModeSchema.optional(),
|
|
924
1284
|
/**
|
|
925
|
-
* How long the receiver may take to answer, in seconds
|
|
926
|
-
* `
|
|
1285
|
+
* How long the receiver may take to answer, in whole seconds, up to
|
|
1286
|
+
* `MAX_DELIVERY_DURATION_SECONDS`. Above
|
|
1287
|
+
* `RESPONSE_DELIVERY_MAX_DURATION_SECONDS` the receiver has to answer
|
|
1288
|
+
* immediately and call the completion URL instead, which is what
|
|
1289
|
+
* `completion: "callback"` says.
|
|
927
1290
|
*/
|
|
928
1291
|
maxDurationSeconds: deliveryDurationSecondsSchema.optional(),
|
|
929
1292
|
})
|
|
@@ -965,6 +1328,17 @@ export const scheduleRequestSchema = z
|
|
|
965
1328
|
* than obtained more of the service.
|
|
966
1329
|
*/
|
|
967
1330
|
latencyClass: latencyClassSchema.optional(),
|
|
1331
|
+
/**
|
|
1332
|
+
* What to do when an occurrence reaches its first attempt late.
|
|
1333
|
+
*
|
|
1334
|
+
* Omitted policy defaults to `catchUp` for `at` and `delaySeconds`, and to
|
|
1335
|
+
* `skipAhead` for `everySeconds` and `cron`. `maxStalenessSeconds` overrides
|
|
1336
|
+
* the schedule-derived window with a positive whole number of seconds.
|
|
1337
|
+
* Retries are exempt: once an occurrence has made an attempt, retry and
|
|
1338
|
+
* give-up policy decide its outcome instead of measuring again from the
|
|
1339
|
+
* original planned instant.
|
|
1340
|
+
*/
|
|
1341
|
+
lateness: scheduleLatenessRequestSchema.optional(),
|
|
968
1342
|
/**
|
|
969
1343
|
* A fairness lane within the caller's own project and environment.
|
|
970
1344
|
*
|
|
@@ -983,6 +1357,15 @@ export const scheduleRequestSchema = z
|
|
|
983
1357
|
* somebody asks otherwise.
|
|
984
1358
|
*/
|
|
985
1359
|
partitionKey: partitionKeySchema.optional(),
|
|
1360
|
+
/**
|
|
1361
|
+
* A name for people (W4-14, FXO-2255). On a create, omitted means the
|
|
1362
|
+
* service generates one from the type, period and target domain —
|
|
1363
|
+
* "Every 60s → flowxo.com" — and stores it. On a replace, omitted means
|
|
1364
|
+
* the stored name stays as it is: a generated name is recomputed on
|
|
1365
|
+
* nothing, so editing the recurrence does not silently rewrite a name
|
|
1366
|
+
* the person kept or changed. Names may collide.
|
|
1367
|
+
*/
|
|
1368
|
+
name: scheduleNameSchema.optional(),
|
|
986
1369
|
/**
|
|
987
1370
|
* The IANA timezone `cron` is read against. `UTC` when omitted.
|
|
988
1371
|
*
|
|
@@ -1024,30 +1407,15 @@ export const scheduleRequestSchema = z
|
|
|
1024
1407
|
path: ["timeZone"],
|
|
1025
1408
|
});
|
|
1026
1409
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
// Only checked when the duration is in *this* request. A replace may carry
|
|
1039
|
-
// `onOverlap` while the stored schedule holds the duration, exactly as it
|
|
1040
|
-
// may carry `data` while the stored schedule holds the endpoint, and the
|
|
1041
|
-
// partition is where a request is resolved against what is stored.
|
|
1042
|
-
const duration = request.delivery.maxDurationSeconds;
|
|
1043
|
-
if (duration !== undefined && !isLongRunningDuration(duration)) {
|
|
1044
|
-
context.addIssue({
|
|
1045
|
-
code: "custom",
|
|
1046
|
-
message: `\`onOverlap\` is only meaningful above ${String(FREE_DELIVERY_DURATION_SECONDS)} seconds, where a run can still be going when the next one is due`,
|
|
1047
|
-
path: ["delivery", "onOverlap"],
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1410
|
+
// **`onOverlap` is accepted wherever it is given, and ignored where it
|
|
1411
|
+
// cannot apply** — a one-shot, or a run too short to outlast its own
|
|
1412
|
+
// interval. It used to be refused in both cases, and the refusal became a
|
|
1413
|
+
// trap: every stored schedule carries the default policy, the dashboard's
|
|
1414
|
+
// editor sent it back on every save, and a five-second delivery was
|
|
1415
|
+
// refused for a field nobody had touched, under a collapsed section. The
|
|
1416
|
+
// operator's ruling on the W4-10 walk: "so what if it's not meaningful —
|
|
1417
|
+
// then we ignore it." A policy that cannot fire is not a guarantee anyone
|
|
1418
|
+
// can rely on, and the schedule's read reports what is stored.
|
|
1051
1419
|
// Held open past the point a held-open request survives. Checked against the
|
|
1052
1420
|
// *derived* pair rather than against what the body names, because
|
|
1053
1421
|
// `completion` defaults to `response`: a caller who asks for twelve hours and
|
|
@@ -1067,6 +1435,20 @@ export const scheduleRequestSchema = z
|
|
|
1067
1435
|
});
|
|
1068
1436
|
}
|
|
1069
1437
|
});
|
|
1438
|
+
/** Resolve the lateness settings a create or replacement request runs under. */
|
|
1439
|
+
export function deriveLatenessSettings(request) {
|
|
1440
|
+
const recurrenceKind = request.cron !== undefined
|
|
1441
|
+
? "cron"
|
|
1442
|
+
: request.everySeconds !== undefined
|
|
1443
|
+
? "every"
|
|
1444
|
+
: "at";
|
|
1445
|
+
const policy = request.lateness?.policy ??
|
|
1446
|
+
defaultLatenessPolicyForRecurrence(recurrenceKind);
|
|
1447
|
+
const maxStalenessSeconds = request.lateness?.maxStalenessSeconds;
|
|
1448
|
+
return maxStalenessSeconds === undefined
|
|
1449
|
+
? { policy }
|
|
1450
|
+
: { maxStalenessSeconds, policy };
|
|
1451
|
+
}
|
|
1070
1452
|
/**
|
|
1071
1453
|
* The delivery settings a request runs under: the caller's, or the defaults.
|
|
1072
1454
|
*
|