@ewhauser/world-celld 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ewhauser/world-celld",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "celld (self-hosted Durable Objects) World implementation for the Vercel Workflow DevKit",
5
5
  "keywords": [
6
6
  "celld",
@@ -1 +0,0 @@
1
- {"version":3,"file":"queue-delivery-DzNOtCYA.d.ts","names":[],"sources":["../src/queue-protocol.ts","../src/worker/queue-delivery.ts"],"mappings":";UAWiB;EACf;EACA;;EAEA;;UAGe;EACf;EACA;EACA;EACA;;;;;;;;;UAUe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;EACA;;;;KCnCU;EACN;;EACA;EAAiB;;EACjB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"queue-protocol-CtH7UUCO.js","names":[],"sources":["../src/validation.ts","../src/lifecycle.ts","../src/queue-protocol.ts"],"sourcesContent":["/** Largest epoch-ms deadline that fits the queue's fixed-width 13-digit keys. */\nexport const MAX_QUEUE_TIMESTAMP_MS = 9_999_999_999_999;\nexport const MAX_QUEUE_DELAY_SECONDS = Math.floor(MAX_QUEUE_TIMESTAMP_MS / 1000);\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nexport function isNonNegativeSafeInteger(value: unknown): value is number {\n return Number.isSafeInteger(value) && (value as number) >= 0;\n}\n\nexport function isPositiveSafeInteger(value: unknown): value is number {\n return Number.isSafeInteger(value) && (value as number) > 0;\n}\n\nexport function boundedIntegerOption(\n name: string,\n value: unknown,\n fallback: number,\n minimum: number,\n maximum = Number.MAX_SAFE_INTEGER,\n): number {\n if (value === undefined) return fallback;\n if (!Number.isSafeInteger(value) || (value as number) < minimum || (value as number) > maximum) {\n throw new Error(`${name} must be an integer between ${minimum} and ${maximum}`);\n }\n return value as number;\n}\n\n/**\n * Parse a deployment/runtime integer setting without accepting whitespace,\n * exponents, fractions, or numeric prefixes such as `5junk`.\n */\nexport function strictIntegerSetting(\n name: string,\n raw: string | number | undefined,\n fallback: number,\n minimum: number,\n maximum = Number.MAX_SAFE_INTEGER,\n): number {\n if (raw === undefined) return fallback;\n const value = typeof raw === 'number' ? raw : /^\\d+$/.test(raw) ? Number(raw) : Number.NaN;\n if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {\n throw new Error(`${name} must be an integer between ${minimum} and ${maximum}`);\n }\n return value;\n}\n\nexport function isValidQueueDelaySeconds(value: unknown, minimum: 0 | 1 = 0): value is number {\n return (\n Number.isSafeInteger(value) &&\n (value as number) >= minimum &&\n (value as number) <= MAX_QUEUE_DELAY_SECONDS\n );\n}\n\nexport function queueDelayDeadline(\n now: number,\n delaySeconds: unknown,\n minimum: 0 | 1 = 0,\n requiredHeadroomMs = 0,\n): number | null {\n if (!isNonNegativeSafeInteger(now) || !isValidQueueDelaySeconds(delaySeconds, minimum)) {\n return null;\n }\n return checkedQueueTimestampAdd(now, delaySeconds * 1000, requiredHeadroomMs);\n}\n\n/**\n * Add an offset to a queue timestamp while preserving its 13-digit key order.\n * Optional headroom reserves space for a later persisted derivative deadline.\n */\nexport function checkedQueueTimestampAdd(\n timestampMs: unknown,\n offsetMs: unknown,\n requiredHeadroomMs = 0,\n): number | null {\n if (\n !isNonNegativeSafeInteger(timestampMs) ||\n !isNonNegativeSafeInteger(offsetMs) ||\n !isNonNegativeSafeInteger(requiredHeadroomMs)\n ) {\n return null;\n }\n const timestamp = timestampMs;\n const offset = offsetMs;\n if (timestamp > MAX_QUEUE_TIMESTAMP_MS || offset > MAX_QUEUE_TIMESTAMP_MS - timestamp) {\n return null;\n }\n const result = timestamp + offset;\n return requiredHeadroomMs <= MAX_QUEUE_TIMESTAMP_MS - result ? result : null;\n}\n","import { MAX_QUEUE_TIMESTAMP_MS } from './validation.js';\n\n/**\n * Hard protocol bounds used by lifecycle leases and derivative-fence cleanup.\n *\n * These are not retention TTLs. They bound how long a request which was\n * admitted before expiry may still mutate a derivative Durable Object:\n *\n * - fleet calls have at most three 5-minute attempts plus bounded retry delay;\n * - one native Queue idempotency claim stays live for at most 15 minutes\n * without a successful delivery or explicit workflow suspension;\n * - hook admission spans reserve retries, one authoritative apply, and\n * finalize retries before the exact claim must be reacquired.\n */\nexport const MAX_FLEET_RPC_TIMEOUT_MS = 5 * 60 * 1000;\nexport const FLEET_IDEMPOTENT_ATTEMPTS = 3;\nexport const FLEET_RETRY_BACKOFF_MAX_MS = 900;\n\nexport const MAX_IDEMPOTENT_RPC_LIFETIME_MS =\n FLEET_IDEMPOTENT_ATTEMPTS * MAX_FLEET_RPC_TIMEOUT_MS + FLEET_RETRY_BACKOFF_MAX_MS;\nexport const MAX_RUN_INDEX_PUBLICATION_LIFETIME_MS =\n MAX_FLEET_RPC_TIMEOUT_MS + MAX_IDEMPOTENT_RPC_LIFETIME_MS;\n\nexport const MAX_QUEUE_DELIVERY_TIMEOUT_MS = 5 * 60 * 1000;\nexport const QUEUE_CLAIM_STALE_MS = 15 * 60 * 1000;\n/** A fresh delivery edge may advance an immediately-due message by one millisecond. */\nexport const QUEUE_ALARM_EDGE_MS = 1;\nexport const MAX_QUEUE_DERIVED_DEADLINE_HEADROOM_MS = QUEUE_CLAIM_STALE_MS + QUEUE_ALARM_EDGE_MS;\n/** Latest admissible due time with room for a fresh queue claim. */\nexport const MAX_QUEUE_SCHEDULE_TIMESTAMP_MS =\n MAX_QUEUE_TIMESTAMP_MS - MAX_QUEUE_DERIVED_DEADLINE_HEADROOM_MS;\n\nexport const CATALOG_FENCE_GRACE_MS = MAX_RUN_INDEX_PUBLICATION_LIFETIME_MS;\n\nexport const HOOK_CLAIM_LEASE_MS =\n (FLEET_IDEMPOTENT_ATTEMPTS * 2 + 1) * MAX_FLEET_RPC_TIMEOUT_MS + FLEET_RETRY_BACKOFF_MAX_MS * 2;\n\nexport const LIFECYCLE_COMPACTION_BATCH = 128;\nexport const LIFECYCLE_COMPACTION_RETRY_MS = 1000;\n","import { isNonNegativeSafeInteger, isRecord } from './validation.js';\nimport { MAX_QUEUE_SCHEDULE_TIMESTAMP_MS } from './lifecycle.js';\n\nexport { QUEUE_CLAIM_STALE_MS } from './lifecycle.js';\n\n/** celld v0.6.0 follows this native Queue producer-delay ceiling. */\nexport const NATIVE_QUEUE_MAX_DELAY_SECONDS = 86_400;\nexport const NATIVE_QUEUE_MAX_MESSAGE_BYTES = 128_000;\nexport const MAX_QUEUE_SUSPENSIONS = 256;\nexport const QUEUE_PAYLOAD_REGISTRY_PREFIX = 'queue-payload:';\n\nexport interface QueuePayloadRegistration {\n messageId: string;\n key: string;\n /** Delete an orphaned resilient-start payload after this deadline. */\n orphanExpiresAt: number;\n}\n\nexport interface QueuePayloadOrphan {\n messageId: string;\n runId: string;\n key: string;\n expiresAt: number;\n}\n\n/**\n * Small broker message carried by celld's native Queue.\n *\n * Run-bearing payload bytes live in object storage so the broker's 128 kB limit does not\n * narrow the Workflow World contract and run retention can delete those bytes\n * without waiting for the Queue's fixed four-day retention window.\n */\nexport interface NativeQueueEnvelope {\n version: 1;\n messageId: string;\n queueName: string;\n targetBaseUrl: string;\n runId?: string;\n idempotencyKey?: string;\n payloadKey?: string;\n /** Inline only for the small, run-less health-check envelope. */\n body?: string;\n /** Absolute workflow redelivery deadline; long waits are chained by the consumer. */\n notBefore?: number;\n /** Failed deliveries carried across suspension re-publishes. */\n deliveryFailures?: number;\n suspensionCount?: number;\n}\n\nexport interface NativeQueueSendOptions {\n delaySeconds?: number;\n}\n\nexport interface NativeQueueSendResult {\n messageId: string;\n}\n\nexport function queuePayloadRegistryKey(messageId: string): string {\n return `${QUEUE_PAYLOAD_REGISTRY_PREFIX}${messageId}`;\n}\n\nexport function queuePayloadObjectKey(runId: string, messageId: string): string {\n return `workflow-queue/${encodeURIComponent(runId)}/${encodeURIComponent(messageId)}`;\n}\n\nexport function queueClaimName(queueName: string, idempotencyKey: string): string {\n return `claim:${queueName.length}:${queueName}:${idempotencyKey}`;\n}\n\nexport function queueOrphanName(messageId: string): string {\n return `queue-orphan:${messageId}`;\n}\n\nexport function nativeQueueDelaySeconds(notBefore: number | undefined, now: number): number {\n if (notBefore === undefined || notBefore <= now) return 0;\n return Math.min(NATIVE_QUEUE_MAX_DELAY_SECONDS, Math.ceil((notBefore - now) / 1000));\n}\n\nexport function validateNativeQueueEnvelope(value: unknown): NativeQueueEnvelope {\n if (!isRecord(value)) throw new TypeError('world-celld native queue envelope must be an object');\n if (value.version !== 1)\n throw new TypeError('world-celld native queue envelope version must be 1');\n for (const field of ['messageId', 'queueName', 'targetBaseUrl'] as const) {\n if (typeof value[field] !== 'string' || value[field].length === 0) {\n throw new TypeError(`world-celld native queue envelope ${field} must be a non-empty string`);\n }\n }\n for (const field of ['runId', 'idempotencyKey', 'payloadKey', 'body'] as const) {\n if (value[field] !== undefined && typeof value[field] !== 'string') {\n throw new TypeError(`world-celld native queue envelope ${field} must be a string`);\n }\n }\n if ((value.payloadKey === undefined) === (value.body === undefined)) {\n throw new TypeError(\n 'world-celld native queue envelope must contain exactly one payload source',\n );\n }\n for (const field of ['notBefore', 'deliveryFailures', 'suspensionCount'] as const) {\n if (value[field] !== undefined && !isNonNegativeSafeInteger(value[field])) {\n throw new TypeError(\n `world-celld native queue envelope ${field} must be a non-negative safe integer`,\n );\n }\n }\n if (\n value.notBefore !== undefined &&\n (value.notBefore as number) > MAX_QUEUE_SCHEDULE_TIMESTAMP_MS\n ) {\n throw new TypeError(\n `world-celld native queue envelope notBefore must not exceed ${MAX_QUEUE_SCHEDULE_TIMESTAMP_MS}`,\n );\n }\n return value as unknown as NativeQueueEnvelope;\n}\n\nexport function validateNativeQueueSendOptions(value: unknown): NativeQueueSendOptions {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new TypeError('world-celld native queue options must be an object');\n const delaySeconds = value.delaySeconds;\n if (\n delaySeconds !== undefined &&\n (!Number.isSafeInteger(delaySeconds) ||\n (delaySeconds as number) < 0 ||\n (delaySeconds as number) > NATIVE_QUEUE_MAX_DELAY_SECONDS)\n ) {\n throw new TypeError(\n `world-celld native queue delaySeconds must be an integer between 0 and ${NATIVE_QUEUE_MAX_DELAY_SECONDS}`,\n );\n }\n return delaySeconds === undefined ? {} : { delaySeconds: delaySeconds as number };\n}\n"],"mappings":"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"}