@dragonmastery/tamer 0.40.0 → 0.41.0
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/{apply-CV4_3Jv4.mjs → apply-Mk9usR-U.mjs} +14 -14
- package/dist/{apply-CV4_3Jv4.mjs.map → apply-Mk9usR-U.mjs.map} +1 -1
- package/dist/{applyTarget-B1YPgkb3.mjs → applyTarget-CJSn9DeJ.mjs} +3 -3
- package/dist/{applyTarget-B1YPgkb3.mjs.map → applyTarget-CJSn9DeJ.mjs.map} +1 -1
- package/dist/{bootstrap-ilkixdmD.mjs → bootstrap-BCy6B-B6.mjs} +3 -3
- package/dist/{bootstrap-ilkixdmD.mjs.map → bootstrap-BCy6B-B6.mjs.map} +1 -1
- package/dist/{buildDispatchUploadForm-D_fM8JaL.mjs → buildDispatchUploadForm-XO8AxhXJ.mjs} +2 -6
- package/dist/buildDispatchUploadForm-XO8AxhXJ.mjs.map +1 -0
- package/dist/{cloudflareSnapshot-BAeNVohz.mjs → cloudflareSnapshot-DqP8v_xG.mjs} +4 -4
- package/dist/{cloudflareSnapshot-BAeNVohz.mjs.map → cloudflareSnapshot-DqP8v_xG.mjs.map} +1 -1
- package/dist/{deploy-BEaNADU6.mjs → deploy-DbXBHpCG.mjs} +9 -9
- package/dist/{deploy-BEaNADU6.mjs.map → deploy-DbXBHpCG.mjs.map} +1 -1
- package/dist/{destroy-Krf35oqE.mjs → destroy-CesVkyxf.mjs} +10 -10
- package/dist/{destroy-Krf35oqE.mjs.map → destroy-CesVkyxf.mjs.map} +1 -1
- package/dist/{destroy-tenant-C95ljuon.mjs → destroy-tenant-DMynv_u-.mjs} +1 -1
- package/dist/{destroy-tenant-C95ljuon.mjs.map → destroy-tenant-DMynv_u-.mjs.map} +1 -1
- package/dist/{dev-C__1rLos.mjs → dev-sk6JGKe4.mjs} +7 -7
- package/dist/{dev-C__1rLos.mjs.map → dev-sk6JGKe4.mjs.map} +1 -1
- package/dist/{dns-records.resolve-DwBR_1WI.mjs → dns-records.resolve-SPYGYNHa.mjs} +1 -1
- package/dist/{dns-records.resolve-DwBR_1WI.mjs.map → dns-records.resolve-SPYGYNHa.mjs.map} +1 -1
- package/dist/{dns-records.resolve-C2T0m4NG.mjs → dns-records.resolve-jECsH6N-.mjs} +1 -1
- package/dist/{dns-records.sync-Dfwk76J_.mjs → dns-records.sync-BORRvLky.mjs} +2 -2
- package/dist/{dns-records.sync-Dfwk76J_.mjs.map → dns-records.sync-BORRvLky.mjs.map} +1 -1
- package/dist/{doctor-BIaLEVFR.mjs → doctor-BgqnscIE.mjs} +1 -1
- package/dist/{doctor-BIaLEVFR.mjs.map → doctor-BgqnscIE.mjs.map} +1 -1
- package/dist/{drift-CLsSBorO.mjs → drift-C5r1cKxV.mjs} +6 -6
- package/dist/{drift-CLsSBorO.mjs.map → drift-C5r1cKxV.mjs.map} +1 -1
- package/dist/drift-oOUlh0u8.mjs +8 -0
- package/dist/{emit-Dh68dvo5.mjs → emit-C7KXAVP0.mjs} +2 -2
- package/dist/{emit-Dh68dvo5.mjs.map → emit-C7KXAVP0.mjs.map} +1 -1
- package/dist/{env-gc-0vX5Av4i.mjs → env-gc-DE4EV7j7.mjs} +10 -10
- package/dist/{env-gc-0vX5Av4i.mjs.map → env-gc-DE4EV7j7.mjs.map} +1 -1
- package/dist/{env-list-DYCprcLb.mjs → env-list-DKseThiA.mjs} +1 -1
- package/dist/{env-list-DYCprcLb.mjs.map → env-list-DKseThiA.mjs.map} +1 -1
- package/dist/{events-CnWvxyX_.mjs → events-CqbN9sbT.mjs} +1 -1
- package/dist/{events-CnWvxyX_.mjs.map → events-CqbN9sbT.mjs.map} +1 -1
- package/dist/{generator-DAU5K77L.mjs → generator-CbH3UZ3K.mjs} +2 -2
- package/dist/{generator-DAU5K77L.mjs.map → generator-CbH3UZ3K.mjs.map} +1 -1
- package/dist/{import-BNbHjR9t.mjs → import-leVD9Ryg.mjs} +5 -5
- package/dist/{import-BNbHjR9t.mjs.map → import-leVD9Ryg.mjs.map} +1 -1
- package/dist/index.d.mts +20 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/loader-B5iVsP6t.mjs +3 -0
- package/dist/{logpush-job-C_6uzGUC.mjs → logpush-job-Dqlt-wEw.mjs} +2 -2
- package/dist/{logpush-job-C_6uzGUC.mjs.map → logpush-job-Dqlt-wEw.mjs.map} +1 -1
- package/dist/{migrate-EVfFlJOM.mjs → migrate-DyrTw9ep.mjs} +5 -5
- package/dist/{migrate-EVfFlJOM.mjs.map → migrate-DyrTw9ep.mjs.map} +1 -1
- package/dist/normalize-DVSTRZhO.mjs.map +1 -1
- package/dist/{plan-tnUWkiM1.mjs → plan-D7UyLznb.mjs} +11 -11
- package/dist/{plan-tnUWkiM1.mjs.map → plan-D7UyLznb.mjs.map} +1 -1
- package/dist/{planFormat-DpA8XhzX.mjs → planFormat-CJw8Kq2s.mjs} +1 -1
- package/dist/{planFormat-DpA8XhzX.mjs.map → planFormat-CJw8Kq2s.mjs.map} +1 -1
- package/dist/{provision-tenant-DW4eg-7P.mjs → provision-tenant-z4DLxYAM.mjs} +25 -37
- package/dist/provision-tenant-z4DLxYAM.mjs.map +1 -0
- package/dist/{r2S3EmptyBucket-CXLmOrYF.mjs → r2S3EmptyBucket-DD81ZWQ7.mjs} +1 -1
- package/dist/{r2S3EmptyBucket-CXLmOrYF.mjs.map → r2S3EmptyBucket-DD81ZWQ7.mjs.map} +1 -1
- package/dist/{registry-X9dlQxG3.mjs → registry-CTerXUza.mjs} +81 -33
- package/dist/registry-CTerXUza.mjs.map +1 -0
- package/dist/resolveTenantBindings-BypqzcSn.mjs +65 -0
- package/dist/resolveTenantBindings-BypqzcSn.mjs.map +1 -0
- package/dist/{stackOutputs-CU2oxjpU.mjs → stackOutputs-BLp-dyzl.mjs} +1 -1
- package/dist/{stackOutputs-CU2oxjpU.mjs.map → stackOutputs-BLp-dyzl.mjs.map} +1 -1
- package/dist/{status-srUxsBIB.mjs → status-WMG5abrN.mjs} +6 -6
- package/dist/{status-srUxsBIB.mjs.map → status-WMG5abrN.mjs.map} +1 -1
- package/dist/sync-DSgJGQh1.mjs +7 -0
- package/dist/{sync-DfJGkOME.mjs → sync-Dii9n2nJ.mjs} +5 -5
- package/dist/{sync-DfJGkOME.mjs.map → sync-Dii9n2nJ.mjs.map} +1 -1
- package/dist/tamer.mjs +66 -25
- package/dist/tamer.mjs.map +1 -1
- package/dist/{tamerArtifactsR2-B9myb-IA.mjs → tamerArtifactsR2-B3X21TGV.mjs} +2 -2
- package/dist/{tamerArtifactsR2-B9myb-IA.mjs.map → tamerArtifactsR2-B3X21TGV.mjs.map} +1 -1
- package/dist/{tenant-MWIs0esz.mjs → tenant-WNYMWmIe.mjs} +1 -1
- package/dist/{tenant-MWIs0esz.mjs.map → tenant-WNYMWmIe.mjs.map} +1 -1
- package/dist/{tenant-migrate-CT-qyTUD.mjs → tenant-migrate-DBxTsvJ8.mjs} +20 -34
- package/dist/tenant-migrate-DBxTsvJ8.mjs.map +1 -0
- package/dist/{types-BPxuutXk.mjs → types-HPpAxgub.mjs} +5 -5
- package/dist/{types-BPxuutXk.mjs.map → types-HPpAxgub.mjs.map} +1 -1
- package/dist/{verifyPlanFile-CoAOsD3W.mjs → verifyPlanFile-B9VCcFIJ.mjs} +2 -2
- package/dist/{verifyPlanFile-CoAOsD3W.mjs.map → verifyPlanFile-B9VCcFIJ.mjs.map} +1 -1
- package/dist/{wfp-delete-CwWQFxxj.mjs → wfp-delete-TSVBw9XF.mjs} +1 -1
- package/dist/{wfp-delete-CwWQFxxj.mjs.map → wfp-delete-TSVBw9XF.mjs.map} +1 -1
- package/dist/{wfp-put-BBitXJep.mjs → wfp-put-5XZ-KC3g.mjs} +2 -2
- package/dist/{wfp-put-BBitXJep.mjs.map → wfp-put-5XZ-KC3g.mjs.map} +1 -1
- package/dist/{worker-route-CvuUPq1k.mjs → worker-route-BapxsQyX.mjs} +2 -2
- package/dist/{worker-route-CvuUPq1k.mjs.map → worker-route-BapxsQyX.mjs.map} +1 -1
- package/dist/{workers-DSlrKeNL.mjs → workers-D7ow_joN.mjs} +7 -13
- package/dist/{workers-DSlrKeNL.mjs.map → workers-D7ow_joN.mjs.map} +1 -1
- package/dist/wranglerSpawn-CfPkFLP3.mjs +3 -0
- package/dist/{wranglerSpawn-DWdgrsmQ.mjs → wranglerSpawn-VkSL0gZd.mjs} +1 -1
- package/dist/{wranglerSpawn-DWdgrsmQ.mjs.map → wranglerSpawn-VkSL0gZd.mjs.map} +1 -1
- package/dist/{zoneResolver-VoxLHM4N.mjs → zoneResolver-CamXJpSB.mjs} +1 -1
- package/dist/{zoneResolver-VoxLHM4N.mjs.map → zoneResolver-CamXJpSB.mjs.map} +1 -1
- package/package.json +5 -2
- package/dist/buildDispatchUploadForm-D_fM8JaL.mjs.map +0 -1
- package/dist/drift-08k11FV6.mjs +0 -8
- package/dist/provision-tenant-DW4eg-7P.mjs.map +0 -1
- package/dist/registry-X9dlQxG3.mjs.map +0 -1
- package/dist/resolveTenantBindings-DJDPhILD.mjs +0 -80
- package/dist/resolveTenantBindings-DJDPhILD.mjs.map +0 -1
- package/dist/sync-CfNyelDN.mjs +0 -7
- package/dist/tenant-migrate-CT-qyTUD.mjs.map +0 -1
- package/dist/wranglerSpawn-aARBLHpA.mjs +0 -3
- /package/dist/{secrets-2Hy5LMHs.mjs → secrets-DJ1yUy01.mjs} +0 -0
- /package/dist/{wranglerOutFile-f08VsoAj.mjs → wranglerOutFile-DDFKeKwO.mjs} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logpush-job-C_6uzGUC.mjs","names":["WORKERS_TRACE_PIPELINES_SCHEMA_FIELDS: Array<\n Record<string, unknown>\n>","trunc","r2DataCatalogTableName: string | undefined","r2DataCatalogTableNamePipelines: string | undefined","entry: LogpushPipelinesStateEntry","baseline: Record<string, unknown>","filter","output_options","destination_conf","destination_conf: string","sourceJobId: number | undefined","changes: PlanFieldChange[]","items: PlanItem[]","liveFull: Awaited<ReturnType<CFApiClient[\"logpushAccountJobGet\"]>>","lastError: unknown","drift: ResourceDrift","rows: LogpushJobStatusRow[]"],"sources":["../src/features/logpush-job/logpush-job.resolve.ts","../src/features/logpush-job/logpush-pipelines-key.ts","../src/features/logpush-job/logpush-pipelines-trace-schema.ts","../src/features/logpush-job/logpush-pipelines-account-tokens.ts","../src/features/logpush-job/pipelinesSinkCatalogTableError.ts","../src/features/logpush-job/logpush-pipelines-provision.ts","../src/features/logpush-job/logpush-job.desired.ts","../src/features/logpush-job/logpush-job.diff.ts","../src/features/logpush-job/logpush-job.apply.ts","../src/features/logpush-job/logpush-job.sync.ts","../src/features/logpush-job/logpush-job.drift.ts","../src/features/logpush-job/logpush-job.destroy.ts","../src/features/logpush-job/logpush-job.status.ts"],"sourcesContent":["import type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\n\nexport const DEFAULT_WORKERS_TRACE_LOG_FIELD_NAMES = [\n \"Event\",\n \"EventTimestampMs\",\n \"Outcome\",\n \"Exceptions\",\n \"Logs\",\n \"ScriptName\",\n] as const;\n\nexport function logpushJobStateKey(\n tenantId: string,\n logicalName: string,\n env: string,\n): string {\n return `logpush_job:t-${tenantId}-${logicalName}-${env}`;\n}\n\nexport function logpushJobCloudflareName(\n config: LogpushJobResourceConfig,\n tenant: TenantMeta,\n env: string,\n): string {\n return (\n config.jobName?.trim() ||\n `tamer-${tenant.slug}-${config.logicalName}-${env}`\n );\n}\n\nexport function findR2BucketDerivedName(\n state: StateManager,\n logicalName: string,\n): string | undefined {\n for (const e of Object.values(state.getAll())) {\n if (e.type === \"r2_bucket\" && e.logicalName === logicalName) {\n return e.derivedName;\n }\n }\n return undefined;\n}\n\nexport function buildR2WorkersTraceDestinationConf(args: {\n bucketDerivedName: string;\n pathPrefix: string;\n accountId: string;\n accessKeyId: string;\n secretAccessKey: string;\n}): string {\n const prefix = args.pathPrefix.replace(/^\\/+|\\/+$/g, \"\");\n const path = prefix\n ? `${args.bucketDerivedName}/${prefix}/{DATE}`\n : `${args.bucketDerivedName}/{DATE}`;\n const ak = encodeURIComponent(args.accessKeyId);\n const sk = encodeURIComponent(args.secretAccessKey);\n return `r2://${path}?account-id=${args.accountId}&access-key-id=${ak}&secret-access-key=${sk}`;\n}\n\nconst PIPELINES_INGEST_HOST_SUFFIX = \"ingest.cloudflare.com\";\n\n/**\n * Pipelines list/create may return `endpoint` (full ingest URL). Prefer its\n * origin for Logpush `destination_conf` so the hostname matches what Cloudflare\n * registered for the stream.\n */\nexport function ingestOriginFromPipelinesStreamEndpoint(\n endpoint: string | undefined,\n): string | undefined {\n const raw = endpoint?.trim();\n if (!raw) return undefined;\n try {\n return new URL(raw).origin;\n } catch {\n return undefined;\n }\n}\n\n/** Normalize a Pipelines id to 32 lowercase hex digits (strip UUID dashes). */\nexport function normalizePipelinesHexId(raw: string, label: string): string {\n const s = raw.trim().toLowerCase().replace(/-/g, \"\");\n if (!/^[0-9a-f]{32}$/.test(s)) {\n throw new Error(\n `${label} must be 32 hex characters (UUID with or without dashes); got \"${raw}\"`,\n );\n }\n return s;\n}\n\n/**\n * Logpush `destination_conf` for Workers trace → Pipelines stream HTTP ingest.\n * Matches the shape produced by Cloudflare when creating a Pipelines Logpush job\n * (`pipeline_id` + `header_Authorization` query params).\n */\nexport function buildPipelinesIngestDestinationConf(args: {\n streamId: string;\n pipelineId: string;\n bearerToken: string;\n /** From {@link ingestOriginFromPipelinesStreamEndpoint}; overrides synthesized host. */\n ingestBaseUrl?: string;\n}): string {\n const pipeline = normalizePipelinesHexId(\n args.pipelineId,\n \"pipelinesIngest.pipelineId\",\n );\n const header = encodeURIComponent(`Bearer ${args.bearerToken}`);\n const trimmedBase = args.ingestBaseUrl?.trim().replace(/\\/+$/, \"\");\n const origin =\n trimmedBase ||\n `https://${normalizePipelinesHexId(args.streamId, \"pipelinesIngest.streamId\")}.${PIPELINES_INGEST_HOST_SUFFIX}`;\n return `${origin}?pipeline_id=${pipeline}&header_Authorization=${header}`;\n}\n","/** State row for a provisioned Pipelines graph backing a `pipelinesAuto` Logpush job. */\nexport function logpushPipelinesStateKey(\n tenantId: string,\n logicalName: string,\n env: string,\n): string {\n return `logpush_pipelines:t-${tenantId}-${logicalName}-${env}`;\n}\n","/**\n * Pipelines stream schema for `workers_trace_events` (matches Logpush field set).\n * @see https://developers.cloudflare.com/logs/logpush/logpush-job/datasets/workers-trace/\n */\nexport const WORKERS_TRACE_PIPELINES_SCHEMA_FIELDS: Array<\n Record<string, unknown>\n> = [\n { name: \"CPUTimeMs\", type: \"int64\", required: false },\n { name: \"DispatchNamespace\", type: \"string\", required: false },\n { name: \"Entrypoint\", type: \"string\", required: false },\n { name: \"Event\", type: \"json\", required: false },\n { name: \"EventTimestampMs\", type: \"int64\", required: false },\n { name: \"EventType\", type: \"string\", required: false },\n {\n name: \"Exceptions\",\n type: \"list\",\n required: false,\n items: { name: \"item\", type: \"json\" },\n },\n {\n name: \"Logs\",\n type: \"list\",\n required: false,\n items: { name: \"item\", type: \"json\" },\n },\n { name: \"Outcome\", type: \"string\", required: false },\n { name: \"ScriptName\", type: \"string\", required: false },\n {\n name: \"ScriptTags\",\n type: \"list\",\n required: false,\n items: { name: \"item\", type: \"string\" },\n },\n { name: \"ScriptVersion\", type: \"json\", required: false },\n { name: \"WallTimeMs\", type: \"int64\", required: false },\n];\n\nexport function buildWorkersTracePipelinesStreamBody(streamName: string): Record<\n string,\n unknown\n> {\n return {\n name: streamName,\n format: { type: \"json\", timestamp_format: \"rfc3339\" },\n schema: { fields: WORKERS_TRACE_PIPELINES_SCHEMA_FIELDS },\n http: { enabled: true, authentication: true, cors: {} },\n worker_binding: { enabled: true },\n };\n}\n","/**\n * Mints the two **account** API tokens used by `pipelinesAuto` via the\n * Cloudflare Account Token API (same capability as the dashboard), then Tamer\n * stores the secrets in `logpush_pipelines` state for catalog/sink/Logpush.\n */\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { TenantMeta } from \"../../types.js\";\n\nconst MAX_TOKEN_NAME = 200;\n\nfunction trunc(s: string, max: number): string {\n return s.length <= max ? s : s.slice(0, max);\n}\n\ntype PermissionGroups = {\n r2DataCatalogId: string;\n /** Account-scoped R2 object access (warehouse data files); not bucket-item-only scopes. */\n r2AccountStorageWriteId: string;\n pipelinesSendId: string;\n};\n\nfunction findGroupId(\n groups: Array<{ id: string; name: string }>,\n match: (name: string) => boolean,\n label: string,\n): string {\n const g = groups.find((x) => match(x.name));\n if (g) return g.id;\n const sample = groups\n .filter((x) => /r2|pipeline|data catalog|send/i.test(x.name))\n .map((x) => x.name)\n .slice(0, 12);\n throw new Error(\n `logpushJobs pipelinesAuto: could not find permission group for ${label} (R2 / Pipelines names change over time). ` +\n `Sample matching names: ${sample.length ? sample.join(\"; \") : \"(none)\"}. ` +\n `Total groups listed: ${groups.length}.`,\n );\n}\n\nexport async function resolvePipelinesAutoPermissionGroups(\n api: CFApiClient,\n): Promise<PermissionGroups> {\n const groups = await api.accountTokenPermissionGroupsListAll();\n return {\n r2DataCatalogId: findGroupId(\n groups,\n (n) =>\n n === \"Workers R2 Data Catalog Write\" ||\n n === \"Workers R2 Data Catalog Edit\" ||\n (/r2/i.test(n) && /data catalog/i.test(n) && /(write|edit)/i.test(n)),\n \"Workers R2 Data Catalog (Edit / Write — same as dashboard token builder)\",\n ),\n r2AccountStorageWriteId: findGroupId(\n groups,\n (n) =>\n !/bucket item/i.test(n) &&\n (n === \"Workers R2 Storage Write\" ||\n n === \"Workers R2 Storage Edit\" ||\n (/workers r2 storage/i.test(n) && /(write|edit)/i.test(n))),\n \"Workers R2 Storage Write (account — required for R2 Data Catalog warehouse / Iceberg data paths)\",\n ),\n pipelinesSendId: findGroupId(\n groups,\n (n) =>\n (/pipelines/i.test(n) && /send/i.test(n)) ||\n n === \"Workers Pipelines Send\",\n \"Workers Pipelines Send (stream ingest)\",\n ),\n };\n}\n\nexport type PipelinesAutoMintedTokens = {\n r2CatalogTokenId: string;\n r2CatalogTokenValue: string;\n pipelinesSendTokenId: string;\n pipelinesSendTokenValue: string;\n};\n\n/**\n * Create two account tokens to match the Cloudflare dashboard:\n * - **Pipelines Send** only (Logpush → stream HTTP ingest).\n * - **R2 Data Catalog** only (account scope), for `POST …/r2-catalog/…/credential` and\n * the `r2_data_catalog` sink `config.token` — like `[Pipelines] Data Catalog: …` in the UI.\n */\nexport async function mintPipelinesAutoAccountTokens(\n api: CFApiClient,\n accountId: string,\n tokenNamePrefix: string,\n): Promise<PipelinesAutoMintedTokens> {\n console.log(\n \"pipelinesAuto: fetching API token permission groups (paginated) and minting two account tokens…\",\n );\n const g = await resolvePipelinesAutoPermissionGroups(api);\n const accountRes = `com.cloudflare.api.account.${accountId}`;\n\n const dataCatalogName = trunc(`${tokenNamePrefix} data-catalog`, MAX_TOKEN_NAME);\n const r2Result = await api.accountTokenCreate({\n name: dataCatalogName,\n policies: [\n {\n effect: \"allow\",\n permission_groups: [\n { id: g.r2DataCatalogId },\n { id: g.r2AccountStorageWriteId },\n ],\n resources: { [accountRes]: \"*\" },\n },\n ],\n });\n if (!r2Result.value) {\n throw new Error(\n \"Account Token API: expected result.value (secret) for R2 Data Catalog token — token create response incomplete\",\n );\n }\n\n const sendName = trunc(`${tokenNamePrefix} pipelines-send`, MAX_TOKEN_NAME);\n const sendResult = await api.accountTokenCreate({\n name: sendName,\n policies: [\n {\n effect: \"allow\",\n permission_groups: [{ id: g.pipelinesSendId }],\n resources: { [accountRes]: \"*\" },\n },\n ],\n });\n if (!sendResult.value) {\n try {\n await api.accountTokenDelete(r2Result.id);\n } catch {\n // best effort rollback\n }\n throw new Error(\n \"Account Token API: expected result.value (secret) for Pipelines Send token — token create response incomplete\",\n );\n }\n\n return {\n r2CatalogTokenId: r2Result.id,\n r2CatalogTokenValue: r2Result.value,\n pipelinesSendTokenId: sendResult.id,\n pipelinesSendTokenValue: sendResult.value,\n };\n}\n\n/**\n * Display names (after truncation) for the two minted account tokens. Must\n * match {@link mintPipelinesAutoAccountTokens} and `tokenPrefix` in\n * `ensurePipelinesLogpushProvision` (`Tamer {slug} {logical} {env}`).\n */\nexport function derivePipelinesAutoMintedTokenNames(\n tenant: TenantMeta,\n logicalName: string,\n env: string,\n): { dataCatalog: string; pipelinesSend: string } {\n const tokenPrefix = `Tamer ${tenant.slug} ${logicalName} ${env}`;\n return {\n dataCatalog: trunc(`${tokenPrefix} data-catalog`, MAX_TOKEN_NAME),\n pipelinesSend: trunc(`${tokenPrefix} pipelines-send`, MAX_TOKEN_NAME),\n };\n}\n\n/**\n * List account API tokens and revoke any that match the minted `pipelinesAuto`\n * names. Use on destroy when state did not have token ids, deletes failed, or\n * ids were wrong — same names as at mint time.\n */\nexport async function reconcilePipelinesAutoMintedAccountTokensByName(\n api: CFApiClient,\n tenant: TenantMeta,\n logicalName: string,\n env: string,\n): Promise<void> {\n const { dataCatalog, pipelinesSend } = derivePipelinesAutoMintedTokenNames(\n tenant,\n logicalName,\n env,\n );\n const tokenPrefix = `Tamer ${tenant.slug} ${logicalName} ${env}`;\n const legacyDataCatalog = trunc(\n `${tokenPrefix} r2+catalog+sink`,\n MAX_TOKEN_NAME,\n );\n const want = new Set([dataCatalog, pipelinesSend, legacyDataCatalog]);\n const tokens = await api.accountTokenListAll();\n for (const t of tokens) {\n const name = t.name ?? \"\";\n if (!t.id || !want.has(name)) continue;\n try {\n await api.accountTokenDelete(t.id);\n console.log(\n `Pipelines: destroy reconcile — revoked account API token \"${name}\" [id ${t.id}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile — account API token \"${name}\" [id ${t.id}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n}\n","/**\n * Pipelines v1 `r2_data_catalog` sink HTTP 422 / code 1012: existing Iceberg\n * table in the R2 Data Catalog — not “wrong token”.\n */\n\nexport function isPipelinesSinkExistingCatalogTableError(err: unknown): boolean {\n const m = err instanceof Error ? err.message : String(err);\n return (\n /\\b(code:\\s*1012|1012)\\b/i.test(m) &&\n /existing catalog|not yet supported|sink definition was invalid/i.test(m)\n );\n}\n\n/**\n * Actionable copy for `tamer apply` when Cloudflare refuses to create a sink\n * that would write to an existing table.\n */\nexport function pipelinesSink1012RecoveryMessage(\n logicalName: string,\n namespace: string,\n tableName: string,\n catalogBucket: string,\n): string {\n return [\n `logpushJobs.${logicalName}: Pipelines r2_data_catalog sink was rejected (HTTP 422, code 1012).`,\n `The Iceberg table \"${namespace}\" / \"${tableName}\" already exists in the R2 Data Catalog for bucket \"${catalogBucket}\".`,\n `This API path only creates a new table — it does not attach to an existing one.`,\n ``,\n `What to do:`,\n ` • In tamer/project.config.ts, set a new pipelinesAuto.tableName (e.g. \"${tableName}_v2\" or a dated suffix), and/or change pipelinesAuto.namespace.`,\n ` • Or use a new catalog R2 bucket with no prior Iceberg layout.`,\n ` • Removing old catalog data in the bucket is possible but advanced — only if you intend to delete that data.`,\n ].join(\"\\n\");\n}\n","/**\n * Provisions **`pipelinesAuto`**: **R2 Data Catalog** on the catalog bucket\n * (enable + credentials), Pipelines **stream** → **`r2_data_catalog` sink** →\n * **SQL pipeline**, then Logpush `workers_trace_events` can use HTTP ingest\n * `destination_conf`. The two runtime tokens (R2+Data Catalog for catalog/sink\n * and **Pipelines Send** for stream ingest) are **minted** via the account\n * Token API and stored in Tamer `logpush_pipelines` state (see README).\n */\nimport type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { LogpushPipelinesStateEntry } from \"../../types.js\";\nimport {\n emptyR2BucketViaS3,\n r2S3CredentialsFromEnv,\n} from \"../r2/r2S3EmptyBucket.js\";\nimport {\n findR2BucketDerivedName,\n ingestOriginFromPipelinesStreamEndpoint,\n} from \"./logpush-job.resolve.js\";\nimport { logpushPipelinesStateKey } from \"./logpush-pipelines-key.js\";\nimport { buildWorkersTracePipelinesStreamBody } from \"./logpush-pipelines-trace-schema.js\";\nimport { mintPipelinesAutoAccountTokens } from \"./logpush-pipelines-account-tokens.js\";\nimport {\n isPipelinesSinkExistingCatalogTableError,\n pipelinesSink1012RecoveryMessage,\n} from \"./pipelinesSinkCatalogTableError.js\";\n\nconst MAX_CF_NAME = 200;\n\n/**\n * Resolves the Iceberg `table_name` for a **new** `r2_data_catalog` sink.\n * When `tableNameAppendTimestamp` is not `false`, appends `_${ms}` to avoid\n * HTTP 422 / 1012 if a table with the same base name is still in the catalog.\n */\nexport function pipelinesAutoIcebergTableNameForNewSink(\n baseTable: string,\n tableNameAppendTimestamp: boolean | undefined,\n now: number = Date.now(),\n): string {\n const base = baseTable.trim();\n if (!base) {\n throw new Error(\"pipelinesAuto: tableName (base) is required\");\n }\n if (tableNameAppendTimestamp === false) {\n return base;\n }\n return `${base}_${now}`;\n}\n\n/**\n * Pipelines `GET /sinks/{id}` is the source of truth for how the catalog\n * registered the table (may differ from the `table_name` sent on create, e.g.\n * suffix normalization).\n */\nfunction r2DataCatalogFromSinkGet(result: {\n type?: string;\n config?: Record<string, unknown>;\n}): { tableName?: string; namespace?: string } {\n if (result.type !== \"r2_data_catalog\" || !result.config) {\n return {};\n }\n const c = result.config;\n const tableName =\n typeof c.table_name === \"string\" ? c.table_name.trim() : undefined;\n const namespace =\n typeof c.namespace === \"string\" ? c.namespace.trim() : undefined;\n return {\n tableName: tableName || undefined,\n namespace: namespace || undefined,\n };\n}\n\n/**\n * Pipelines may report `worker_trace_events_<ms>` from create/GET while R2 SQL\n * `SHOW TABLES` only lists the base name (e.g. `worker_trace_events`). Use the\n * base + long-numeric-suffix pattern so stack outputs match R2 SQL.\n */\nexport function normalizeIcebergTableNameForR2SqlState(\n pipelinesReported: string,\n configBaseTable: string,\n): string {\n const p = pipelinesReported.trim();\n const base = configBaseTable.trim();\n if (!base) return p;\n if (p === base) return p;\n const escaped = base.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n if (new RegExp(`^${escaped}_(\\\\d{10,})$`).test(p)) {\n return base;\n }\n return p;\n}\n\n/**\n * R2 Data Catalog `GET …/r2-catalog` and `POST …/r2-catalog/…/enable` use\n * **CLOUDFLARE_API_TOKEN** (apply token) — not the minted sub-tokens.\n */\nfunction throwPipelinesAutoR2CatalogApplyTokenError(\n logicalName: string,\n step: string,\n cause: unknown,\n): never {\n const orig = cause instanceof Error ? cause.message : String(cause);\n throw new Error(\n `logpushJobs.${logicalName} pipelinesAuto: ${step} — ` +\n \"your **apply** API token (CLOUDFLARE_API_TOKEN) must be allowed to use the **R2 Data Catalog** \" +\n \"account API (`/accounts/.../r2-catalog/...` — e.g. **Workers R2 Data Catalog: Edit** in the \" +\n \"API token permissions for this account). \" +\n \"That is separate from **Account API Tokens: Edit** (used only to mint the two sub-tokens) and \" +\n \"separate from those minted tokens, which are only for the catalog credential + Pipelines. \" +\n `Original: ${orig}`,\n );\n}\n\nfunction safeIdPart(s: string): string {\n return s.replace(/[^a-zA-Z0-9_]/g, \"_\").toLowerCase();\n}\n\nfunction trunc(s: string): string {\n return s.length <= MAX_CF_NAME ? s : s.slice(0, MAX_CF_NAME);\n}\n\n/** Stream / sink / pipeline names safe for Pipelines SQL identifiers. */\nexport function derivePipelinesLogpushResourceNames(\n tenant: TenantMeta,\n logicalName: string,\n env: string,\n): { streamName: string; sinkName: string; pipelineName: string } {\n const base = trunc(\n `tamer_${safeIdPart(tenant.slug)}_${safeIdPart(logicalName)}_${safeIdPart(env)}_wtr`,\n );\n return {\n streamName: trunc(`${base}_stream`),\n sinkName: trunc(`${base}_sink`),\n pipelineName: trunc(`${base}_pl`),\n };\n}\n\n/**\n * Idempotent: enables R2 catalog, stores credentials, creates stream, sink, pipeline.\n */\nexport async function ensurePipelinesLogpushProvision(\n config: LogpushJobResourceConfig,\n auto: NonNullable<LogpushJobResourceConfig[\"pipelinesAuto\"]>,\n tenant: TenantMeta,\n env: string,\n accountId: string,\n state: StateManager,\n api: CFApiClient,\n): Promise<void> {\n const pkey = logpushPipelinesStateKey(tenant.id, config.logicalName, env);\n const prior = state.get(pkey) as LogpushPipelinesStateEntry | undefined;\n const tokenPrefix = `Tamer ${tenant.slug} ${config.logicalName} ${env}`;\n\n const bucketDerived = findR2BucketDerivedName(\n state,\n auto.catalogBucketLogicalName,\n );\n if (!bucketDerived) {\n throw new Error(\n `logpushJobs.${config.logicalName}: R2 bucket \"${auto.catalogBucketLogicalName}\" is not in state — apply R2 first`,\n );\n }\n\n let r2CatId = prior?.type === \"logpush_pipelines\" ? prior.mintedR2CatalogTokenId : undefined;\n let r2CatVal =\n prior?.type === \"logpush_pipelines\" ? prior.mintedR2CatalogTokenValue : undefined;\n let sendId =\n prior?.type === \"logpush_pipelines\" ? prior.mintedPipelinesSendTokenId : undefined;\n let sendVal =\n prior?.type === \"logpush_pipelines\" ? prior.mintedPipelinesSendTokenValue : undefined;\n\n if (!r2CatId || !r2CatVal || !sendId || !sendVal) {\n if (\n prior?.type === \"logpush_pipelines\" &&\n (r2CatId || r2CatVal || sendId || sendVal)\n ) {\n console.warn(\n `logpushJobs.${config.logicalName}: state has partial minted token fields — creating fresh account tokens; old token ids may need manual cleanup in the dashboard.`,\n );\n }\n const minted = await mintPipelinesAutoAccountTokens(\n api,\n accountId,\n tokenPrefix,\n );\n r2CatId = minted.r2CatalogTokenId;\n r2CatVal = minted.r2CatalogTokenValue;\n sendId = minted.pipelinesSendTokenId;\n sendVal = minted.pipelinesSendTokenValue;\n }\n\n const cred = r2CatVal;\n\n console.log(\n `logpushJobs.${config.logicalName}: enabling R2 Data Catalog on \\\"${bucketDerived}\\\" (uses **CLOUDFLARE_API_TOKEN**; see README)…`,\n );\n try {\n await api.r2DataCatalogEnable(bucketDerived);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (!/exists|already|active/i.test(msg)) {\n throwPipelinesAutoR2CatalogApplyTokenError(\n config.logicalName,\n \"R2 Data Catalog enable (POST .../r2-catalog/{bucket}/enable)\",\n e,\n );\n }\n }\n\n try {\n await api.r2DataCatalogStoreCredential(bucketDerived, cred);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (!/exists|already|present/i.test(msg)) {\n throwPipelinesAutoR2CatalogApplyTokenError(\n config.logicalName,\n \"store R2 Data Catalog credentials (POST .../r2-catalog/{bucket}/credential)\",\n e,\n );\n }\n }\n\n const ns = (auto.namespace ?? \"default\").trim() || \"default\";\n const baseTable = auto.tableName.trim();\n if (!baseTable) {\n throw new Error(\n `logpushJobs.${config.logicalName}: pipelinesAuto.tableName is required`,\n );\n }\n\n const names = derivePipelinesLogpushResourceNames(\n tenant,\n config.logicalName,\n env,\n );\n\n const streams = await api.pipelinesV1StreamListAll();\n let stream = streams.find((s) => s.name === names.streamName);\n let streamCreatedFresh = false;\n if (!stream) {\n const body = buildWorkersTracePipelinesStreamBody(names.streamName);\n stream = await api.pipelinesV1StreamCreate(body);\n streamCreatedFresh = true;\n console.log(\n `Created Pipelines stream \"${names.streamName}\" [id ${stream.id}] (Workers trace schema).`,\n );\n } else {\n console.log(\n `Pipelines stream \"${names.streamName}\" already exists [id ${stream.id}].`,\n );\n }\n\n const streamIngestBaseUrl =\n ingestOriginFromPipelinesStreamEndpoint(stream.endpoint);\n\n /** Logpush validates HTTPS to ingest; new stream subdomains can lag DNS briefly. */\n if (streamCreatedFresh) {\n const settleMs = 2500;\n console.log(\n `logpushJobs.${config.logicalName}: waiting ${settleMs}ms for new stream ingest hostname before Logpush job step…`,\n );\n await new Promise<void>((r) => setTimeout(r, settleMs));\n }\n\n const sinks = await api.pipelinesV1SinkListAll();\n const rowGroup = auto.sinkRowGroupBytes ?? 134_217_728;\n const fileSize = auto.sinkRollingFileSizeBytes ?? 100 * 1024 * 1024;\n const interval = auto.sinkRollingIntervalSeconds ?? 300;\n let sink = sinks.find((s) => s.name === names.sinkName);\n let r2DataCatalogTableName: string | undefined =\n prior?.type === \"logpush_pipelines\"\n ? prior.r2DataCatalogTableName\n : undefined;\n let r2DataCatalogTableNamePipelines: string | undefined =\n prior?.type === \"logpush_pipelines\"\n ? prior.r2DataCatalogTableNamePipelines\n : undefined;\n if (!sink) {\n const icebergTable = pipelinesAutoIcebergTableNameForNewSink(\n baseTable,\n auto.tableNameAppendTimestamp,\n );\n try {\n sink = await api.pipelinesV1SinkCreate({\n name: names.sinkName,\n type: \"r2_data_catalog\",\n format: {\n type: \"parquet\",\n compression: \"zstd\",\n row_group_bytes: rowGroup,\n },\n schema: { fields: [] },\n config: {\n token: cred,\n account_id: accountId,\n bucket: bucketDerived,\n namespace: ns,\n table_name: icebergTable,\n rolling_policy: {\n file_size_bytes: fileSize,\n interval_seconds: interval,\n },\n },\n });\n } catch (e) {\n if (isPipelinesSinkExistingCatalogTableError(e)) {\n throw new Error(\n pipelinesSink1012RecoveryMessage(\n config.logicalName,\n ns,\n icebergTable,\n bucketDerived,\n ),\n { cause: e },\n );\n }\n throw e;\n }\n r2DataCatalogTableName = icebergTable;\n console.log(\n `Created Pipelines sink \"${names.sinkName}\" (r2_data_catalog) [id ${sink.id}] ` +\n `→ Iceberg table \"${ns}\" / \"${icebergTable}\".`,\n );\n } else {\n console.log(\n `Pipelines sink \"${names.sinkName}\" already exists [id ${sink.id}].`,\n );\n }\n\n let catalogNs = ns;\n try {\n const fullSink = await api.pipelinesV1SinkGet(sink.id);\n const fromApi = r2DataCatalogFromSinkGet(fullSink);\n if (fromApi.tableName) {\n r2DataCatalogTableName = fromApi.tableName;\n }\n if (fromApi.namespace) {\n catalogNs = fromApi.namespace;\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.warn(\n `logpushJobs.${config.logicalName}: Pipelines GET sink not used (${msg}) — ` +\n \"Iceberg name falls back to create-time value in state; re-run apply after fixing API access if needed.\",\n );\n }\n\n if (r2DataCatalogTableName) {\n const raw = r2DataCatalogTableName;\n r2DataCatalogTableNamePipelines = raw;\n const normalized = normalizeIcebergTableNameForR2SqlState(raw, baseTable);\n if (normalized !== raw) {\n console.log(\n `logpushJobs.${config.logicalName}: R2 SQL table name for stack outputs: ` +\n `\"${raw}\" → \"${normalized}\" (Pipelines reports a suffixed id; catalog uses the base name).`,\n );\n }\n r2DataCatalogTableName = normalized;\n }\n\n const sql = `INSERT INTO ${names.sinkName} SELECT * FROM ${names.streamName};`;\n const pipelines = await api.pipelineListAll();\n let pipeline = pipelines.find((p) => p.name === names.pipelineName);\n if (!pipeline) {\n pipeline = await api.pipelineCreate({ name: names.pipelineName, sql });\n console.log(\n `Created Pipelines job \"${names.pipelineName}\" [id ${pipeline.id}].`,\n );\n } else {\n if (pipeline.sql?.trim() !== sql.trim()) {\n console.warn(\n `Pipelines job \"${names.pipelineName}\" exists with different SQL — not updating (replace manually or delete & re-apply).`,\n );\n } else {\n console.log(\n `Pipelines job \"${names.pipelineName}\" already exists [id ${pipeline.id}].`,\n );\n }\n }\n\n const ts = new Date().toISOString();\n const existing = state.get(pkey) as LogpushPipelinesStateEntry | undefined;\n const entry: LogpushPipelinesStateEntry = {\n type: \"logpush_pipelines\",\n logicalName: config.logicalName,\n streamId: stream.id,\n streamIngestBaseUrl,\n sinkId: sink.id,\n pipelineId: pipeline.id,\n streamName: names.streamName,\n sinkName: names.sinkName,\n pipelineName: names.pipelineName,\n r2DataCatalogTableName,\n r2DataCatalogTableNamePipelines,\n r2DataCatalogNamespace: catalogNs,\n catalogBucketDerivedName: bucketDerived,\n mintedR2CatalogTokenId: r2CatId,\n mintedR2CatalogTokenValue: r2CatVal,\n mintedPipelinesSendTokenId: sendId,\n mintedPipelinesSendTokenValue: sendVal,\n createdAt: existing?.type === \"logpush_pipelines\" ? existing.createdAt : ts,\n updatedAt: ts,\n };\n state.set(pkey, entry);\n}\n\nfunction samePipelinesV1Id(a: string, b: string): boolean {\n return a.replace(/-/g, \"\").toLowerCase() === b.replace(/-/g, \"\").toLowerCase();\n}\n\n/**\n * Pipelines v1 may 404 on DELETE when state stores 32-hex ids but the path must\n * be a hyphenated UUID, or the id drifts — list by name and retry once.\n */\nasync function deletePipelinesV1StreamWithLookup(\n api: CFApiClient,\n streamId: string,\n streamName: string,\n): Promise<void> {\n try {\n await api.pipelinesV1StreamDelete(streamId);\n return;\n } catch (e) {\n const streams = await api.pipelinesV1StreamListAll();\n const hit =\n streams.find((s) => s.name === streamName) ??\n streams.find((s) => samePipelinesV1Id(s.id, streamId));\n if (hit) {\n await api.pipelinesV1StreamDelete(hit.id);\n return;\n }\n throw e;\n }\n}\n\nasync function deletePipelinesV1SinkWithLookup(\n api: CFApiClient,\n sinkId: string,\n sinkName: string,\n): Promise<void> {\n try {\n await api.pipelinesV1SinkDelete(sinkId);\n return;\n } catch (e) {\n const sinks = await api.pipelinesV1SinkListAll();\n const hit =\n sinks.find((s) => s.name === sinkName) ??\n sinks.find((s) => samePipelinesV1Id(s.id, sinkId));\n if (hit) {\n await api.pipelinesV1SinkDelete(hit.id);\n return;\n }\n throw e;\n }\n}\n\n/**\n * Best-effort teardown: SQL pipeline, then sink, then stream (after Logpush job is gone).\n * Order avoids leaving the sink (e.g. r2_data_catalog) while the stream still exists.\n */\nexport async function deletePipelinesLogpushGraph(\n api: CFApiClient,\n entry: LogpushPipelinesStateEntry,\n): Promise<void> {\n const { pipelineId, streamId, sinkId } = entry;\n for (const [label, id] of [\n [\"Pipelines Send\", entry.mintedPipelinesSendTokenId],\n [\"Data Catalog (minted)\", entry.mintedR2CatalogTokenId],\n ] as const) {\n if (!id) continue;\n try {\n await api.accountTokenDelete(id);\n console.log(`Deleted account API token [${label}] [id ${id}].`);\n } catch (e) {\n console.warn(\n `Account API token delete [${label}] [id ${id}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n try {\n await api.pipelineDelete(pipelineId);\n console.log(\n `Deleted Pipelines job \"${entry.pipelineName}\" [id ${pipelineId}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines job delete [id ${pipelineId}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n try {\n await deletePipelinesV1SinkWithLookup(api, sinkId, entry.sinkName);\n console.log(`Deleted Pipelines sink \"${entry.sinkName}\" [id ${sinkId}].`);\n } catch (e) {\n console.warn(\n `Pipelines sink delete [id ${sinkId}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n try {\n await deletePipelinesV1StreamWithLookup(api, streamId, entry.streamName);\n console.log(\n `Deleted Pipelines stream \"${entry.streamName}\" [id ${streamId}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines stream delete [id ${streamId}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n await pipelinesAutoTeardownR2DataCatalog(\n api,\n entry.catalogBucketDerivedName,\n );\n}\n\n/**\n * Stopping the Pipelines `r2_data_catalog` sink does not drop the Iceberg\n * table. Disable the Data Catalog, then (when `R2_ACCESS_KEY_ID` is set) empty\n * the catalog bucket so a later `tamer apply` is not blocked by HTTP 422 / 1012\n * (table already exists in this catalog).\n */\nexport async function pipelinesAutoTeardownR2DataCatalog(\n api: CFApiClient,\n catalogBucketDerivedName: string,\n): Promise<void> {\n try {\n await api.r2DataCatalogDisable(catalogBucketDerivedName);\n console.log(\n `Pipelines: disabled R2 Data Catalog on \"${catalogBucketDerivedName}\" (pipelinesAuto teardown).`,\n );\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (!/\\b(404|10006|not found)\\b/i.test(msg)) {\n console.warn(\n `Pipelines: R2 Data Catalog disable for \"${catalogBucketDerivedName}\": ${msg}`,\n );\n }\n }\n const s3creds = r2S3CredentialsFromEnv();\n if (!s3creds) {\n console.warn(\n `Pipelines: set R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY to empty the catalog bucket and remove Iceberg table metadata; ` +\n \"otherwise a future `tamer apply` may hit HTTP 422 / 1012 (table already exists). \" +\n \"Alternatively bump `pipelinesAuto.tableName` or use a new catalog bucket.\",\n );\n return;\n }\n try {\n const accountId = api.getAccountId();\n console.log(\n `Pipelines: emptying catalog bucket \"${catalogBucketDerivedName}\" via S3 (removes Iceberg layout)…`,\n );\n const { objectsDeleted, uploadsAborted } = await emptyR2BucketViaS3(\n accountId,\n catalogBucketDerivedName,\n s3creds,\n );\n console.log(\n `Pipelines: catalog bucket empty — ${objectsDeleted} object(s), ${uploadsAborted} incomplete multipart upload(s) aborted.`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: S3 empty of catalog bucket \"${catalogBucketDerivedName}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n}\n\n/**\n * After state-driven `deletePipelinesLogpushGraph`, or when `logpush_pipelines`\n * state is missing, delete the SQL pipeline / sink / stream by the names derived\n * from `derivePipelinesLogpushResourceNames` (config + env). Uses live ids from\n * list APIs so stale state ids do not block teardown.\n */\nexport async function reconcilePipelinesAutoCloudResourcesByName(\n api: CFApiClient,\n tenant: TenantMeta,\n logicalName: string,\n env: string,\n opts?: { catalogBucketDerivedName?: string },\n): Promise<void> {\n const names = derivePipelinesLogpushResourceNames(tenant, logicalName, env);\n const pipelines = await api.pipelineListAll();\n const pl = pipelines.find((p) => p.name === names.pipelineName);\n if (pl) {\n try {\n await api.pipelineDelete(pl.id);\n console.log(\n `Pipelines: destroy reconcile — removed SQL pipeline \"${names.pipelineName}\" [id ${pl.id}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile — SQL pipeline \"${names.pipelineName}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n const sinks = await api.pipelinesV1SinkListAll();\n const sk = sinks.find((s) => s.name === names.sinkName);\n if (sk) {\n try {\n await deletePipelinesV1SinkWithLookup(api, sk.id, sk.name);\n console.log(\n `Pipelines: destroy reconcile — removed sink \"${names.sinkName}\" [id ${sk.id}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile — sink \"${names.sinkName}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n const streams = await api.pipelinesV1StreamListAll();\n const st = streams.find((s) => s.name === names.streamName);\n if (st) {\n try {\n await deletePipelinesV1StreamWithLookup(api, st.id, st.name);\n console.log(\n `Pipelines: destroy reconcile — removed stream \"${names.streamName}\" [id ${st.id}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile — stream \"${names.streamName}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n const cat = opts?.catalogBucketDerivedName?.trim();\n if (cat) {\n await pipelinesAutoTeardownR2DataCatalog(api, cat);\n }\n}\n","/**\n * Resolved Logpush job shape Tamer expects on Cloudflare (for create + update +\n * plan diff).\n */\nimport type { LogpushJobResourceConfig } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { logpushPipelinesStateKey } from \"./logpush-pipelines-key.js\";\nimport {\n DEFAULT_WORKERS_TRACE_LOG_FIELD_NAMES,\n buildPipelinesIngestDestinationConf,\n buildR2WorkersTraceDestinationConf,\n findR2BucketDerivedName,\n} from \"./logpush-job.resolve.js\";\n\nexport type LogpushJobDesired = {\n destination_conf: string;\n filter?: string;\n output_options: Record<string, unknown>;\n};\n\nfunction resolvedFilter(\n config: LogpushJobResourceConfig,\n templateFilter?: string,\n): string | undefined {\n const explicit = config.filter?.trim();\n if (explicit) return explicit;\n const t = templateFilter?.trim();\n if (t) return t;\n return undefined;\n}\n\n/**\n * Merge `output_options` from a template job (dashboard bootstrap) with\n * optional `fieldNames` override from config.\n */\nexport function mergeLogpushOutputOptions(\n config: LogpushJobResourceConfig,\n template?: Record<string, unknown> | null | undefined,\n): Record<string, unknown> {\n const baseline: Record<string, unknown> =\n template && typeof template === \"object\" && !Array.isArray(template)\n ? { ...template }\n : {};\n if (config.fieldNames?.length) {\n baseline.field_names = config.fieldNames;\n return baseline;\n }\n if (!template || typeof template !== \"object\") {\n baseline.field_names = [...DEFAULT_WORKERS_TRACE_LOG_FIELD_NAMES];\n return baseline;\n }\n if (!Array.isArray(baseline.field_names)) {\n baseline.field_names = [...DEFAULT_WORKERS_TRACE_LOG_FIELD_NAMES];\n }\n return baseline;\n}\n\nexport async function resolveLogpushJobDesired(\n config: LogpushJobResourceConfig,\n accountId: string,\n state: StateManager,\n api: CFApiClient,\n tenantId: string,\n env: string,\n): Promise<LogpushJobDesired> {\n if (config.destinationConfEnv) {\n const raw = process.env[config.destinationConfEnv]?.trim();\n if (!raw) {\n throw new Error(\n `logpushJobs.${config.logicalName}: environment variable \"${config.destinationConfEnv}\" is empty (set it to the full Logpush destination_conf, or remove this logpushJobs entry)`,\n );\n }\n const filter = resolvedFilter(config, undefined);\n const output_options = mergeLogpushOutputOptions(config, undefined);\n return {\n destination_conf: raw,\n ...(filter ? { filter } : {}),\n output_options,\n };\n }\n\n if (config.pipelinesAuto) {\n const pkey = logpushPipelinesStateKey(\n tenantId,\n config.logicalName,\n env,\n );\n const lpp = state.get(pkey);\n if (!lpp || lpp.type !== \"logpush_pipelines\") {\n throw new Error(\n `logpushJobs.${config.logicalName}: pipelinesAuto graph not in state — \\`tamer apply\\` provisions stream/sink/pipeline first`,\n );\n }\n const token = lpp.mintedPipelinesSendTokenValue?.trim();\n if (!token) {\n throw new Error(\n `logpushJobs.${config.logicalName}: pipelinesAuto minted Pipelines Send token missing from state — run \\`tamer apply\\` to provision tokens and graph`,\n );\n }\n const destination_conf = buildPipelinesIngestDestinationConf({\n streamId: lpp.streamId,\n pipelineId: lpp.pipelineId,\n bearerToken: token,\n ingestBaseUrl: lpp.streamIngestBaseUrl,\n });\n const filter = resolvedFilter(config, undefined);\n const output_options = mergeLogpushOutputOptions(config, undefined);\n return {\n destination_conf,\n ...(filter ? { filter } : {}),\n output_options,\n };\n }\n\n if (config.pipelinesIngest) {\n const pi = config.pipelinesIngest;\n const token = process.env[pi.bearerTokenEnv]?.trim();\n if (!token) {\n throw new Error(\n `logpushJobs.${config.logicalName}: environment variable \"${pi.bearerTokenEnv}\" is empty (Pipelines stream ingest Bearer token for Logpush)`,\n );\n }\n let destination_conf: string;\n try {\n destination_conf = buildPipelinesIngestDestinationConf({\n streamId: pi.streamId,\n pipelineId: pi.pipelineId,\n bearerToken: token,\n });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`logpushJobs.${config.logicalName}: ${msg}`);\n }\n const filter = resolvedFilter(config, undefined);\n const output_options = mergeLogpushOutputOptions(config, undefined);\n return {\n destination_conf,\n ...(filter ? { filter } : {}),\n output_options,\n };\n }\n\n let sourceJobId: number | undefined;\n if (config.destinationConfFromJobId != null) {\n sourceJobId = config.destinationConfFromJobId;\n } else if (config.destinationConfFromJobIdEnv) {\n const raw = process.env[config.destinationConfFromJobIdEnv]?.trim();\n if (!raw) {\n throw new Error(\n `logpushJobs.${config.logicalName}: environment variable \"${config.destinationConfFromJobIdEnv}\" is empty (set it to the numeric Cloudflare Logpush job id whose destination_conf should be copied)`,\n );\n }\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n throw new Error(\n `logpushJobs.${config.logicalName}: \"${config.destinationConfFromJobIdEnv}\" must be a positive integer job id (got \"${raw}\")`,\n );\n }\n sourceJobId = n;\n }\n\n if (sourceJobId != null) {\n const src = await api.logpushAccountJobGet(sourceJobId);\n const conf = src.destination_conf?.trim();\n if (!conf) {\n throw new Error(\n `logpushJobs.${config.logicalName}: Logpush job ${sourceJobId} has no destination_conf`,\n );\n }\n if (src.dataset && src.dataset !== config.dataset) {\n throw new Error(\n `logpushJobs.${config.logicalName}: source job ${sourceJobId} dataset is \"${src.dataset}\" but this job declares \"${config.dataset}\"`,\n );\n }\n const filter = resolvedFilter(config, src.filter);\n const templateOpts = src.output_options as\n | Record<string, unknown>\n | undefined;\n const output_options = mergeLogpushOutputOptions(config, templateOpts);\n return {\n destination_conf: conf,\n ...(filter ? { filter } : {}),\n output_options,\n };\n }\n\n const r2 = config.r2;\n if (!r2) {\n throw new Error(\n `logpushJobs.${config.logicalName}: internal: no destination source (expected loader to enforce r2 | pipelinesIngest | pipelinesAuto | env | job id)`,\n );\n }\n const bucket = findR2BucketDerivedName(state, r2.bucketLogicalName);\n if (!bucket) {\n throw new Error(\n `logpushJobs.${config.logicalName}: R2 bucket logical \"${r2.bucketLogicalName}\" is not in state — run \\`tamer apply\\` so the bucket is created first, then re-run apply`,\n );\n }\n const ak = process.env[r2.accessKeyIdEnv]?.trim();\n const sk = process.env[r2.secretAccessKeyEnv]?.trim();\n if (!ak || !sk) {\n throw new Error(\n `logpushJobs.${config.logicalName}: set ${r2.accessKeyIdEnv} and ${r2.secretAccessKeyEnv} (R2 S3-compatible credentials for Logpush)`,\n );\n }\n const pathPrefix = r2.pathPrefix ?? \"workers-trace-events\";\n const destination_conf = buildR2WorkersTraceDestinationConf({\n bucketDerivedName: bucket,\n pathPrefix,\n accountId,\n accessKeyId: ak,\n secretAccessKey: sk,\n });\n const filter = resolvedFilter(config, undefined);\n const output_options = mergeLogpushOutputOptions(config, undefined);\n return {\n destination_conf,\n ...(filter ? { filter } : {}),\n output_options,\n };\n}\n","/**\n * Plan-time diff + apply-time reconciliation for account Logpush jobs.\n */\nimport type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { PlanFieldChange, PlanItem } from \"../../core/plan/plan.types.js\";\nimport { logpushPipelinesStateKey } from \"./logpush-pipelines-key.js\";\nimport {\n logpushJobCloudflareName,\n logpushJobStateKey,\n} from \"./logpush-job.resolve.js\";\nimport {\n resolveLogpushJobDesired,\n type LogpushJobDesired,\n} from \"./logpush-job.desired.js\";\n\nexport type LiveLogpushJobDetails = {\n destination_conf?: string;\n filter?: string;\n output_options?: Record<string, unknown>;\n enabled?: boolean;\n};\n\nexport function normalizeLogpushOutputOptions(o: unknown): unknown {\n if (!o || typeof o !== \"object\" || Array.isArray(o)) return o;\n const x = { ...(o as Record<string, unknown>) };\n if (Array.isArray(x.field_names)) {\n x.field_names = [...(x.field_names as string[])].sort();\n }\n return x;\n}\n\nexport function normLogpushFilter(f?: string | null): string | undefined {\n const t = f?.trim();\n return t || undefined;\n}\n\n/** Build `filter` for PUT when it must change (including clear). */\nexport function logpushFilterPutBody(\n desired: LogpushJobDesired,\n live: Pick<LiveLogpushJobDetails, \"filter\">,\n): { filter?: string } {\n const d = normLogpushFilter(desired.filter);\n const l = normLogpushFilter(live.filter);\n if (d === l) return {};\n return { filter: d ?? \"\" };\n}\n\nexport function logpushJobFieldChanges(\n desired: LogpushJobDesired,\n live: LiveLogpushJobDetails,\n enabledDesired: boolean,\n): PlanFieldChange[] {\n const changes: PlanFieldChange[] = [];\n const dc = desired.destination_conf.trim();\n const lc = (live.destination_conf ?? \"\").trim();\n if (dc !== lc) {\n changes.push({\n field: \"destination_conf\",\n from: lc,\n to: dc,\n kind: \"mutable\",\n });\n }\n const df = normLogpushFilter(desired.filter);\n const lf = normLogpushFilter(live.filter);\n if (df !== lf) {\n changes.push({\n field: \"filter\",\n from: lf ?? \"(none)\",\n to: df ?? \"(none)\",\n kind: \"mutable\",\n });\n }\n const doo = JSON.stringify(\n normalizeLogpushOutputOptions(desired.output_options),\n );\n const loo = JSON.stringify(\n normalizeLogpushOutputOptions(live.output_options ?? {}),\n );\n if (doo !== loo) {\n changes.push({\n field: \"output_options\",\n from: JSON.parse(loo) as unknown,\n to: JSON.parse(doo) as unknown,\n kind: \"mutable\",\n });\n }\n const enabledLive = live.enabled !== false;\n if (enabledLive !== enabledDesired) {\n changes.push({\n field: \"enabled\",\n from: enabledLive,\n to: enabledDesired,\n kind: \"mutable\",\n });\n }\n return changes;\n}\n\nexport async function logpushJobDiffPlanItems(args: {\n resources: LogpushJobResourceConfig[];\n tenant: TenantMeta;\n env: string;\n state: StateManager;\n api: CFApiClient;\n accountId: string;\n}): Promise<PlanItem[]> {\n const { resources, tenant, env, state, api, accountId } = args;\n if (env === \"local\") return [];\n const items: PlanItem[] = [];\n\n for (const config of resources) {\n if (config.dataset !== \"workers_trace_events\") continue;\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const entry = state.get(key);\n if (!entry || entry.type !== \"logpush_job\") continue;\n\n if (config.pipelinesAuto) {\n const pkey = logpushPipelinesStateKey(\n tenant.id,\n config.logicalName,\n env,\n );\n const lpp = state.get(pkey);\n if (!lpp || lpp.type !== \"logpush_pipelines\") continue;\n }\n\n const cfName = logpushJobCloudflareName(config, tenant, env);\n let liveFull: Awaited<ReturnType<CFApiClient[\"logpushAccountJobGet\"]>>;\n try {\n liveFull = await api.logpushAccountJobGet(entry.cfJobId);\n } catch {\n continue;\n }\n\n const desired = await resolveLogpushJobDesired(\n config,\n accountId,\n state,\n api,\n tenant.id,\n env,\n );\n\n const enabledDesired = config.enabled !== false;\n const changes = logpushJobFieldChanges(\n desired,\n {\n destination_conf: liveFull.destination_conf,\n filter: liveFull.filter,\n output_options: liveFull.output_options as\n | Record<string, unknown>\n | undefined,\n enabled: liveFull.enabled,\n },\n enabledDesired,\n );\n if (changes.length === 0) continue;\n\n items.push({\n kind: \"logpush_job\",\n action: \"update\",\n logicalName: config.logicalName,\n derivedName: cfName,\n detail: changes.map((c) => `${c.field}`).join(\", \"),\n changes,\n });\n }\n\n return items;\n}\n","import type {\n LogpushJobResourceConfig,\n TenantMeta,\n} from \"../../types.js\";\nimport type { LogpushJobStateEntry } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport {\n logpushJobCloudflareName,\n logpushJobStateKey,\n} from \"./logpush-job.resolve.js\";\n\n/** Logpush validates the Pipelines ingest URL; new streams can return DNS 1002 until the hostname propagates. */\nfunction isRetriableLogpushIngestDnsError(message: string): boolean {\n return /\\bDNSError\\b/i.test(message) && /\\b1002\\b/.test(message);\n}\n\nasync function logpushAccountJobCreateWithIngestRetry(\n api: CFApiClient,\n body: Parameters<CFApiClient[\"logpushAccountJobCreate\"]>[0],\n): Promise<{ id: number }> {\n const maxAttempts = 8;\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await api.logpushAccountJobCreate(body);\n } catch (e) {\n lastError = e;\n const msg = e instanceof Error ? e.message : String(e);\n if (attempt === maxAttempts || !isRetriableLogpushIngestDnsError(msg)) {\n throw e;\n }\n const waitMs = Math.min(30_000, 1500 * 2 ** (attempt - 1));\n console.warn(\n `Logpush create \"${body.name}\": destination validation error, retry ${attempt}/${maxAttempts} in ${waitMs}ms ` +\n `(ingest DNS may lag right after a new Pipelines stream). ${msg.slice(0, 240)}`,\n );\n await new Promise<void>((r) => setTimeout(r, waitMs));\n }\n }\n throw lastError;\n}\nimport { ensurePipelinesLogpushProvision } from \"./logpush-pipelines-provision.js\";\nimport { resolveLogpushJobDesired } from \"./logpush-job.desired.js\";\nimport {\n logpushFilterPutBody,\n logpushJobFieldChanges,\n} from \"./logpush-job.diff.js\";\n\nfunction writeState(\n state: StateManager,\n key: string,\n config: LogpushJobResourceConfig,\n cfName: string,\n cfJobId: number,\n dataset: string,\n existingCreatedAt?: string,\n): void {\n const ts = new Date().toISOString();\n state.set(key, {\n type: \"logpush_job\",\n logicalName: config.logicalName,\n derivedName: cfName,\n cfJobId,\n dataset,\n createdAt: existingCreatedAt ?? ts,\n updatedAt: ts,\n });\n}\n\nasync function reconcileLogpushJob(\n config: LogpushJobResourceConfig,\n cfJobId: number,\n cfName: string,\n accountId: string,\n tenant: TenantMeta,\n env: string,\n state: StateManager,\n api: CFApiClient,\n key: string,\n st: LogpushJobStateEntry,\n): Promise<void> {\n const fullLive = await api.logpushAccountJobGet(cfJobId);\n const desired = await resolveLogpushJobDesired(\n config,\n accountId,\n state,\n api,\n tenant.id,\n env,\n );\n const enabledDesired = config.enabled !== false;\n const changes = logpushJobFieldChanges(\n desired,\n {\n destination_conf: fullLive.destination_conf,\n filter: fullLive.filter,\n output_options: fullLive.output_options as\n | Record<string, unknown>\n | undefined,\n enabled: fullLive.enabled,\n },\n enabledDesired,\n );\n if (changes.length === 0) return;\n\n await api.logpushAccountJobUpdate(cfJobId, {\n destination_conf: desired.destination_conf,\n enabled: enabledDesired,\n output_options: desired.output_options,\n ...logpushFilterPutBody(desired, fullLive),\n });\n\n const ts = new Date().toISOString();\n state.set(key, { ...st, updatedAt: ts });\n console.log(\n `Updated Logpush job \"${cfName}\" (${config.dataset}) [id ${cfJobId}]: ${changes.map((c) => c.field).join(\", \")}`,\n );\n}\n\nexport async function logpushJobApply(\n resources: LogpushJobResourceConfig[],\n tenant: TenantMeta,\n env: string,\n accountId: string,\n api: CFApiClient,\n state: StateManager,\n): Promise<void> {\n if (resources.length === 0 || env === \"local\") return;\n\n const jobs = await api.logpushAccountJobsList();\n const byNameDataset = new Map<string, (typeof jobs)[0]>();\n for (const j of jobs) {\n if (j.name && j.dataset) {\n byNameDataset.set(`${j.dataset}\\0${j.name}`, j);\n }\n }\n\n for (const config of resources) {\n if (config.dataset !== \"workers_trace_events\") {\n throw new Error(\n `logpushJobs.${config.logicalName}: unsupported dataset \"${config.dataset}\" (only workers_trace_events)`,\n );\n }\n if (config.pipelinesAuto) {\n await ensurePipelinesLogpushProvision(\n config,\n config.pipelinesAuto,\n tenant,\n env,\n accountId,\n state,\n api,\n );\n }\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const cfName = logpushJobCloudflareName(config, tenant, env);\n const mapKey = `${config.dataset}\\0${cfName}`;\n const existingState = state.get(key);\n\n if (existingState?.type === \"logpush_job\") {\n const live = jobs.find((j) => j.id === existingState.cfJobId);\n if (live) {\n await reconcileLogpushJob(\n config,\n existingState.cfJobId,\n cfName,\n accountId,\n tenant,\n env,\n state,\n api,\n key,\n existingState,\n );\n continue;\n }\n }\n\n const hit = byNameDataset.get(mapKey);\n if (hit?.id != null) {\n writeState(\n state,\n key,\n config,\n cfName,\n hit.id,\n config.dataset,\n existingState?.type === \"logpush_job\"\n ? existingState.createdAt\n : undefined,\n );\n console.log(\n `Logpush job \"${cfName}\" (${config.dataset}) already exists [id ${hit.id}] — recorded in state.`,\n );\n const st = state.get(key);\n if (st?.type === \"logpush_job\") {\n await reconcileLogpushJob(\n config,\n hit.id,\n cfName,\n accountId,\n tenant,\n env,\n state,\n api,\n key,\n st,\n );\n }\n continue;\n }\n\n const desired = await resolveLogpushJobDesired(\n config,\n accountId,\n state,\n api,\n tenant.id,\n env,\n );\n\n const created = await logpushAccountJobCreateWithIngestRetry(api, {\n name: cfName,\n dataset: config.dataset,\n destination_conf: desired.destination_conf,\n enabled: config.enabled !== false,\n ...logpushFilterPutBody(desired, {}),\n output_options: desired.output_options,\n });\n\n writeState(\n state,\n key,\n config,\n cfName,\n created.id,\n config.dataset,\n existingState?.type === \"logpush_job\"\n ? existingState.createdAt\n : undefined,\n );\n console.log(\n `Created Logpush job \"${cfName}\" (${config.dataset}) [id ${created.id}].`,\n );\n }\n}\n","import type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { logpushJobCloudflareName, logpushJobStateKey } from \"./logpush-job.resolve.js\";\n\nexport async function logpushJobSync(\n resources: LogpushJobResourceConfig[],\n tenant: TenantMeta,\n env: string,\n api: CFApiClient,\n state: StateManager,\n): Promise<void> {\n if (resources.length === 0 || env === \"local\") return;\n\n const jobs = await api.logpushAccountJobsList();\n for (const config of resources) {\n const cfName = logpushJobCloudflareName(config, tenant, env);\n const hit = jobs.find(\n (j) => j.name === cfName && j.dataset === config.dataset,\n );\n if (!hit?.id) continue;\n\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const existing = state.get(key);\n const ts = new Date().toISOString();\n state.set(key, {\n type: \"logpush_job\",\n logicalName: config.logicalName,\n derivedName: cfName,\n cfJobId: hit.id,\n dataset: config.dataset,\n createdAt:\n existing?.type === \"logpush_job\" ? existing.createdAt : ts,\n updatedAt: ts,\n });\n }\n}\n","import type { LogpushJobResourceConfig } from \"../../types.js\";\nimport type { TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { ResourceDrift } from \"../../core/drift/drift.types.js\";\nimport { logpushJobCloudflareName, logpushJobStateKey } from \"./logpush-job.resolve.js\";\n\ninterface CFLogpushJob {\n id: number;\n name?: string;\n dataset?: string;\n}\n\nexport function logpushJobDrift(\n allJobs: CFLogpushJob[],\n resources: LogpushJobResourceConfig[],\n env: string,\n tenant: TenantMeta,\n state: StateManager,\n): ResourceDrift {\n const drift: ResourceDrift = {\n kind: \"logpush_job\",\n missingFromCloudflare: [],\n unrecordedInState: [],\n undeployed: [],\n };\n\n for (const config of resources) {\n const cfName = logpushJobCloudflareName(config, tenant, env);\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const tracked = state.get(key);\n const st = tracked?.type === \"logpush_job\" ? tracked : undefined;\n const onCf = allJobs.find(\n (j) => j.name === cfName && j.dataset === config.dataset,\n );\n const idStillThere =\n st && allJobs.some((j) => j.id === st.cfJobId);\n\n if (st && !idStillThere) {\n drift.missingFromCloudflare.push({\n logicalName: config.logicalName,\n derivedName: cfName,\n cfId: String(st.cfJobId),\n });\n } else if (onCf && !st) {\n drift.unrecordedInState.push({\n logicalName: config.logicalName,\n derivedName: cfName,\n cfId: String(onCf.id),\n });\n } else if (!onCf && !st) {\n drift.undeployed.push({\n logicalName: config.logicalName,\n derivedName: cfName,\n });\n }\n }\n\n return drift;\n}\n","import type { CfiConfig } from \"../../types.js\";\nimport { getLogpushJobs } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type {\n LogpushJobStateEntry,\n LogpushPipelinesStateEntry,\n} from \"../../types.js\";\nimport { logpushPipelinesStateKey } from \"./logpush-pipelines-key.js\";\nimport { reconcilePipelinesAutoMintedAccountTokensByName } from \"./logpush-pipelines-account-tokens.js\";\nimport {\n deletePipelinesLogpushGraph,\n reconcilePipelinesAutoCloudResourcesByName,\n} from \"./logpush-pipelines-provision.js\";\nimport { findR2BucketDerivedName } from \"./logpush-job.resolve.js\";\n\n/**\n * `logpush_pipelines:t-${tenantId}-${logicalName}-${env}` (logical name may\n * contain hyphens, so we match prefix+suffix to scope to this stack).\n */\nfunction isLogpushPipelinesKeyForStack(\n key: string,\n tenantId: string,\n env: string,\n): boolean {\n const prefix = `logpush_pipelines:t-${tenantId}-`;\n const suffix = `-${env}`;\n return key.startsWith(prefix) && key.endsWith(suffix);\n}\n\n/**\n * Delete Logpush jobs declared in config and remove their state rows.\n * Runs before R2 teardown so the job stops writing to the bucket.\n * For `pipelinesAuto`, deletes the Logpush job first, then stream / sink / SQL pipeline.\n *\n * Also sweeps any remaining `logpush_pipelines` state for this stack (e.g. config\n * no longer lists logpush, or the job row was lost while pipelines state remained).\n */\nexport async function logpushJobDestroy(\n env: string,\n state: StateManager,\n api: CFApiClient,\n config: CfiConfig,\n): Promise<void> {\n if (env === \"local\") return;\n const jobs = getLogpushJobs(config);\n const allowed = new Set(jobs.map((j) => j.logicalName));\n\n const autoByLogical = new Map(\n jobs.filter((j) => j.pipelinesAuto).map((j) => [j.logicalName, j]),\n );\n\n for (const [key, entry] of Object.entries(state.getAll())) {\n if (entry.type !== \"logpush_job\") continue;\n const job = entry as LogpushJobStateEntry;\n if (allowed.size > 0 && !allowed.has(job.logicalName)) continue;\n try {\n await api.logpushAccountJobDelete(job.cfJobId);\n state.delete(key);\n console.log(\n `Deleted Logpush job \"${job.derivedName}\" [id ${job.cfJobId}].`,\n );\n } catch (err) {\n console.warn(\n `Failed to delete Logpush job ${job.derivedName} [id ${job.cfJobId}]:`,\n err instanceof Error ? err.message : err,\n );\n }\n\n if (autoByLogical.has(job.logicalName)) {\n const pkey = logpushPipelinesStateKey(\n config.tenant.id,\n job.logicalName,\n env,\n );\n const lpp = state.get(pkey) as LogpushPipelinesStateEntry | undefined;\n if (lpp?.type === \"logpush_pipelines\") {\n await deletePipelinesLogpushGraph(api, lpp);\n state.delete(pkey);\n }\n }\n }\n\n for (const [key, entry] of Object.entries(state.getAll())) {\n if (entry.type !== \"logpush_pipelines\") continue;\n if (!isLogpushPipelinesKeyForStack(key, config.tenant.id, env)) continue;\n const lpp = entry as LogpushPipelinesStateEntry;\n try {\n await deletePipelinesLogpushGraph(api, lpp);\n state.delete(key);\n } catch (err) {\n console.warn(\n `Failed to destroy Pipelines graph for [${key}]:`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n\n for (const j of jobs) {\n if (!j.pipelinesAuto) continue;\n const catBucket = findR2BucketDerivedName(\n state,\n j.pipelinesAuto.catalogBucketLogicalName,\n );\n try {\n await reconcilePipelinesAutoCloudResourcesByName(\n api,\n config.tenant,\n j.logicalName,\n env,\n catBucket ? { catalogBucketDerivedName: catBucket } : undefined,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile (SQL/sink/stream) for logpush ${j.logicalName}:`,\n e instanceof Error ? e.message : e,\n );\n }\n try {\n await reconcilePipelinesAutoMintedAccountTokensByName(\n api,\n config.tenant,\n j.logicalName,\n env,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile (account API tokens) for logpush ${j.logicalName}:`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n}\n","import type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport { logpushJobStateKey } from \"./logpush-job.resolve.js\";\n\nexport interface LogpushJobStatusRow {\n logicalName: string;\n jobName: string;\n cfJobId?: number;\n status: \"ok\" | \"missing\";\n /** Present when `liveJobs` matched this job id (Cloudflare list/get). */\n cfEnabled?: boolean;\n cfError?: string | null;\n}\n\nexport function logpushJobStatus(\n resources: LogpushJobResourceConfig[],\n tenant: TenantMeta,\n env: string,\n state: StateManager,\n liveJobs?: Array<{\n id: number;\n enabled?: boolean;\n error_message?: string | null;\n }>,\n): LogpushJobStatusRow[] {\n const rows: LogpushJobStatusRow[] = [];\n for (const config of resources) {\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const entry = state.get(key);\n const st = entry?.type === \"logpush_job\" ? entry : undefined;\n const live =\n liveJobs && st?.cfJobId != null\n ? liveJobs.find((j) => j.id === st.cfJobId)\n : undefined;\n rows.push({\n logicalName: config.logicalName,\n jobName: st?.derivedName ?? \"(unknown)\",\n cfJobId: st?.cfJobId,\n status: st ? \"ok\" : \"missing\",\n cfEnabled: live?.enabled,\n cfError: live?.error_message,\n });\n }\n return rows;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"logpush-job-Dqlt-wEw.mjs","names":["WORKERS_TRACE_PIPELINES_SCHEMA_FIELDS: Array<\n Record<string, unknown>\n>","trunc","r2DataCatalogTableName: string | undefined","r2DataCatalogTableNamePipelines: string | undefined","entry: LogpushPipelinesStateEntry","baseline: Record<string, unknown>","filter","output_options","destination_conf","destination_conf: string","sourceJobId: number | undefined","changes: PlanFieldChange[]","items: PlanItem[]","liveFull: Awaited<ReturnType<CFApiClient[\"logpushAccountJobGet\"]>>","lastError: unknown","drift: ResourceDrift","rows: LogpushJobStatusRow[]"],"sources":["../src/features/logpush-job/logpush-job.resolve.ts","../src/features/logpush-job/logpush-pipelines-key.ts","../src/features/logpush-job/logpush-pipelines-trace-schema.ts","../src/features/logpush-job/logpush-pipelines-account-tokens.ts","../src/features/logpush-job/pipelinesSinkCatalogTableError.ts","../src/features/logpush-job/logpush-pipelines-provision.ts","../src/features/logpush-job/logpush-job.desired.ts","../src/features/logpush-job/logpush-job.diff.ts","../src/features/logpush-job/logpush-job.apply.ts","../src/features/logpush-job/logpush-job.sync.ts","../src/features/logpush-job/logpush-job.drift.ts","../src/features/logpush-job/logpush-job.destroy.ts","../src/features/logpush-job/logpush-job.status.ts"],"sourcesContent":["import type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\n\nexport const DEFAULT_WORKERS_TRACE_LOG_FIELD_NAMES = [\n \"Event\",\n \"EventTimestampMs\",\n \"Outcome\",\n \"Exceptions\",\n \"Logs\",\n \"ScriptName\",\n] as const;\n\nexport function logpushJobStateKey(\n tenantId: string,\n logicalName: string,\n env: string,\n): string {\n return `logpush_job:t-${tenantId}-${logicalName}-${env}`;\n}\n\nexport function logpushJobCloudflareName(\n config: LogpushJobResourceConfig,\n tenant: TenantMeta,\n env: string,\n): string {\n return (\n config.jobName?.trim() ||\n `tamer-${tenant.slug}-${config.logicalName}-${env}`\n );\n}\n\nexport function findR2BucketDerivedName(\n state: StateManager,\n logicalName: string,\n): string | undefined {\n for (const e of Object.values(state.getAll())) {\n if (e.type === \"r2_bucket\" && e.logicalName === logicalName) {\n return e.derivedName;\n }\n }\n return undefined;\n}\n\nexport function buildR2WorkersTraceDestinationConf(args: {\n bucketDerivedName: string;\n pathPrefix: string;\n accountId: string;\n accessKeyId: string;\n secretAccessKey: string;\n}): string {\n const prefix = args.pathPrefix.replace(/^\\/+|\\/+$/g, \"\");\n const path = prefix\n ? `${args.bucketDerivedName}/${prefix}/{DATE}`\n : `${args.bucketDerivedName}/{DATE}`;\n const ak = encodeURIComponent(args.accessKeyId);\n const sk = encodeURIComponent(args.secretAccessKey);\n return `r2://${path}?account-id=${args.accountId}&access-key-id=${ak}&secret-access-key=${sk}`;\n}\n\nconst PIPELINES_INGEST_HOST_SUFFIX = \"ingest.cloudflare.com\";\n\n/**\n * Pipelines list/create may return `endpoint` (full ingest URL). Prefer its\n * origin for Logpush `destination_conf` so the hostname matches what Cloudflare\n * registered for the stream.\n */\nexport function ingestOriginFromPipelinesStreamEndpoint(\n endpoint: string | undefined,\n): string | undefined {\n const raw = endpoint?.trim();\n if (!raw) return undefined;\n try {\n return new URL(raw).origin;\n } catch {\n return undefined;\n }\n}\n\n/** Normalize a Pipelines id to 32 lowercase hex digits (strip UUID dashes). */\nexport function normalizePipelinesHexId(raw: string, label: string): string {\n const s = raw.trim().toLowerCase().replace(/-/g, \"\");\n if (!/^[0-9a-f]{32}$/.test(s)) {\n throw new Error(\n `${label} must be 32 hex characters (UUID with or without dashes); got \"${raw}\"`,\n );\n }\n return s;\n}\n\n/**\n * Logpush `destination_conf` for Workers trace → Pipelines stream HTTP ingest.\n * Matches the shape produced by Cloudflare when creating a Pipelines Logpush job\n * (`pipeline_id` + `header_Authorization` query params).\n */\nexport function buildPipelinesIngestDestinationConf(args: {\n streamId: string;\n pipelineId: string;\n bearerToken: string;\n /** From {@link ingestOriginFromPipelinesStreamEndpoint}; overrides synthesized host. */\n ingestBaseUrl?: string;\n}): string {\n const pipeline = normalizePipelinesHexId(\n args.pipelineId,\n \"pipelinesIngest.pipelineId\",\n );\n const header = encodeURIComponent(`Bearer ${args.bearerToken}`);\n const trimmedBase = args.ingestBaseUrl?.trim().replace(/\\/+$/, \"\");\n const origin =\n trimmedBase ||\n `https://${normalizePipelinesHexId(args.streamId, \"pipelinesIngest.streamId\")}.${PIPELINES_INGEST_HOST_SUFFIX}`;\n return `${origin}?pipeline_id=${pipeline}&header_Authorization=${header}`;\n}\n","/** State row for a provisioned Pipelines graph backing a `pipelinesAuto` Logpush job. */\nexport function logpushPipelinesStateKey(\n tenantId: string,\n logicalName: string,\n env: string,\n): string {\n return `logpush_pipelines:t-${tenantId}-${logicalName}-${env}`;\n}\n","/**\n * Pipelines stream schema for `workers_trace_events` (matches Logpush field set).\n * @see https://developers.cloudflare.com/logs/logpush/logpush-job/datasets/workers-trace/\n */\nexport const WORKERS_TRACE_PIPELINES_SCHEMA_FIELDS: Array<\n Record<string, unknown>\n> = [\n { name: \"CPUTimeMs\", type: \"int64\", required: false },\n { name: \"DispatchNamespace\", type: \"string\", required: false },\n { name: \"Entrypoint\", type: \"string\", required: false },\n { name: \"Event\", type: \"json\", required: false },\n { name: \"EventTimestampMs\", type: \"int64\", required: false },\n { name: \"EventType\", type: \"string\", required: false },\n {\n name: \"Exceptions\",\n type: \"list\",\n required: false,\n items: { name: \"item\", type: \"json\" },\n },\n {\n name: \"Logs\",\n type: \"list\",\n required: false,\n items: { name: \"item\", type: \"json\" },\n },\n { name: \"Outcome\", type: \"string\", required: false },\n { name: \"ScriptName\", type: \"string\", required: false },\n {\n name: \"ScriptTags\",\n type: \"list\",\n required: false,\n items: { name: \"item\", type: \"string\" },\n },\n { name: \"ScriptVersion\", type: \"json\", required: false },\n { name: \"WallTimeMs\", type: \"int64\", required: false },\n];\n\nexport function buildWorkersTracePipelinesStreamBody(streamName: string): Record<\n string,\n unknown\n> {\n return {\n name: streamName,\n format: { type: \"json\", timestamp_format: \"rfc3339\" },\n schema: { fields: WORKERS_TRACE_PIPELINES_SCHEMA_FIELDS },\n http: { enabled: true, authentication: true, cors: {} },\n worker_binding: { enabled: true },\n };\n}\n","/**\n * Mints the two **account** API tokens used by `pipelinesAuto` via the\n * Cloudflare Account Token API (same capability as the dashboard), then Tamer\n * stores the secrets in `logpush_pipelines` state for catalog/sink/Logpush.\n */\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { TenantMeta } from \"../../types.js\";\n\nconst MAX_TOKEN_NAME = 200;\n\nfunction trunc(s: string, max: number): string {\n return s.length <= max ? s : s.slice(0, max);\n}\n\ntype PermissionGroups = {\n r2DataCatalogId: string;\n /** Account-scoped R2 object access (warehouse data files); not bucket-item-only scopes. */\n r2AccountStorageWriteId: string;\n pipelinesSendId: string;\n};\n\nfunction findGroupId(\n groups: Array<{ id: string; name: string }>,\n match: (name: string) => boolean,\n label: string,\n): string {\n const g = groups.find((x) => match(x.name));\n if (g) return g.id;\n const sample = groups\n .filter((x) => /r2|pipeline|data catalog|send/i.test(x.name))\n .map((x) => x.name)\n .slice(0, 12);\n throw new Error(\n `logpushJobs pipelinesAuto: could not find permission group for ${label} (R2 / Pipelines names change over time). ` +\n `Sample matching names: ${sample.length ? sample.join(\"; \") : \"(none)\"}. ` +\n `Total groups listed: ${groups.length}.`,\n );\n}\n\nexport async function resolvePipelinesAutoPermissionGroups(\n api: CFApiClient,\n): Promise<PermissionGroups> {\n const groups = await api.accountTokenPermissionGroupsListAll();\n return {\n r2DataCatalogId: findGroupId(\n groups,\n (n) =>\n n === \"Workers R2 Data Catalog Write\" ||\n n === \"Workers R2 Data Catalog Edit\" ||\n (/r2/i.test(n) && /data catalog/i.test(n) && /(write|edit)/i.test(n)),\n \"Workers R2 Data Catalog (Edit / Write — same as dashboard token builder)\",\n ),\n r2AccountStorageWriteId: findGroupId(\n groups,\n (n) =>\n !/bucket item/i.test(n) &&\n (n === \"Workers R2 Storage Write\" ||\n n === \"Workers R2 Storage Edit\" ||\n (/workers r2 storage/i.test(n) && /(write|edit)/i.test(n))),\n \"Workers R2 Storage Write (account — required for R2 Data Catalog warehouse / Iceberg data paths)\",\n ),\n pipelinesSendId: findGroupId(\n groups,\n (n) =>\n (/pipelines/i.test(n) && /send/i.test(n)) ||\n n === \"Workers Pipelines Send\",\n \"Workers Pipelines Send (stream ingest)\",\n ),\n };\n}\n\nexport type PipelinesAutoMintedTokens = {\n r2CatalogTokenId: string;\n r2CatalogTokenValue: string;\n pipelinesSendTokenId: string;\n pipelinesSendTokenValue: string;\n};\n\n/**\n * Create two account tokens to match the Cloudflare dashboard:\n * - **Pipelines Send** only (Logpush → stream HTTP ingest).\n * - **R2 Data Catalog** only (account scope), for `POST …/r2-catalog/…/credential` and\n * the `r2_data_catalog` sink `config.token` — like `[Pipelines] Data Catalog: …` in the UI.\n */\nexport async function mintPipelinesAutoAccountTokens(\n api: CFApiClient,\n accountId: string,\n tokenNamePrefix: string,\n): Promise<PipelinesAutoMintedTokens> {\n console.log(\n \"pipelinesAuto: fetching API token permission groups (paginated) and minting two account tokens…\",\n );\n const g = await resolvePipelinesAutoPermissionGroups(api);\n const accountRes = `com.cloudflare.api.account.${accountId}`;\n\n const dataCatalogName = trunc(`${tokenNamePrefix} data-catalog`, MAX_TOKEN_NAME);\n const r2Result = await api.accountTokenCreate({\n name: dataCatalogName,\n policies: [\n {\n effect: \"allow\",\n permission_groups: [\n { id: g.r2DataCatalogId },\n { id: g.r2AccountStorageWriteId },\n ],\n resources: { [accountRes]: \"*\" },\n },\n ],\n });\n if (!r2Result.value) {\n throw new Error(\n \"Account Token API: expected result.value (secret) for R2 Data Catalog token — token create response incomplete\",\n );\n }\n\n const sendName = trunc(`${tokenNamePrefix} pipelines-send`, MAX_TOKEN_NAME);\n const sendResult = await api.accountTokenCreate({\n name: sendName,\n policies: [\n {\n effect: \"allow\",\n permission_groups: [{ id: g.pipelinesSendId }],\n resources: { [accountRes]: \"*\" },\n },\n ],\n });\n if (!sendResult.value) {\n try {\n await api.accountTokenDelete(r2Result.id);\n } catch {\n // best effort rollback\n }\n throw new Error(\n \"Account Token API: expected result.value (secret) for Pipelines Send token — token create response incomplete\",\n );\n }\n\n return {\n r2CatalogTokenId: r2Result.id,\n r2CatalogTokenValue: r2Result.value,\n pipelinesSendTokenId: sendResult.id,\n pipelinesSendTokenValue: sendResult.value,\n };\n}\n\n/**\n * Display names (after truncation) for the two minted account tokens. Must\n * match {@link mintPipelinesAutoAccountTokens} and `tokenPrefix` in\n * `ensurePipelinesLogpushProvision` (`Tamer {slug} {logical} {env}`).\n */\nexport function derivePipelinesAutoMintedTokenNames(\n tenant: TenantMeta,\n logicalName: string,\n env: string,\n): { dataCatalog: string; pipelinesSend: string } {\n const tokenPrefix = `Tamer ${tenant.slug} ${logicalName} ${env}`;\n return {\n dataCatalog: trunc(`${tokenPrefix} data-catalog`, MAX_TOKEN_NAME),\n pipelinesSend: trunc(`${tokenPrefix} pipelines-send`, MAX_TOKEN_NAME),\n };\n}\n\n/**\n * List account API tokens and revoke any that match the minted `pipelinesAuto`\n * names. Use on destroy when state did not have token ids, deletes failed, or\n * ids were wrong — same names as at mint time.\n */\nexport async function reconcilePipelinesAutoMintedAccountTokensByName(\n api: CFApiClient,\n tenant: TenantMeta,\n logicalName: string,\n env: string,\n): Promise<void> {\n const { dataCatalog, pipelinesSend } = derivePipelinesAutoMintedTokenNames(\n tenant,\n logicalName,\n env,\n );\n const tokenPrefix = `Tamer ${tenant.slug} ${logicalName} ${env}`;\n const legacyDataCatalog = trunc(\n `${tokenPrefix} r2+catalog+sink`,\n MAX_TOKEN_NAME,\n );\n const want = new Set([dataCatalog, pipelinesSend, legacyDataCatalog]);\n const tokens = await api.accountTokenListAll();\n for (const t of tokens) {\n const name = t.name ?? \"\";\n if (!t.id || !want.has(name)) continue;\n try {\n await api.accountTokenDelete(t.id);\n console.log(\n `Pipelines: destroy reconcile — revoked account API token \"${name}\" [id ${t.id}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile — account API token \"${name}\" [id ${t.id}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n}\n","/**\n * Pipelines v1 `r2_data_catalog` sink HTTP 422 / code 1012: existing Iceberg\n * table in the R2 Data Catalog — not “wrong token”.\n */\n\nexport function isPipelinesSinkExistingCatalogTableError(err: unknown): boolean {\n const m = err instanceof Error ? err.message : String(err);\n return (\n /\\b(code:\\s*1012|1012)\\b/i.test(m) &&\n /existing catalog|not yet supported|sink definition was invalid/i.test(m)\n );\n}\n\n/**\n * Actionable copy for `tamer apply` when Cloudflare refuses to create a sink\n * that would write to an existing table.\n */\nexport function pipelinesSink1012RecoveryMessage(\n logicalName: string,\n namespace: string,\n tableName: string,\n catalogBucket: string,\n): string {\n return [\n `logpushJobs.${logicalName}: Pipelines r2_data_catalog sink was rejected (HTTP 422, code 1012).`,\n `The Iceberg table \"${namespace}\" / \"${tableName}\" already exists in the R2 Data Catalog for bucket \"${catalogBucket}\".`,\n `This API path only creates a new table — it does not attach to an existing one.`,\n ``,\n `What to do:`,\n ` • In tamer/project.config.ts, set a new pipelinesAuto.tableName (e.g. \"${tableName}_v2\" or a dated suffix), and/or change pipelinesAuto.namespace.`,\n ` • Or use a new catalog R2 bucket with no prior Iceberg layout.`,\n ` • Removing old catalog data in the bucket is possible but advanced — only if you intend to delete that data.`,\n ].join(\"\\n\");\n}\n","/**\n * Provisions **`pipelinesAuto`**: **R2 Data Catalog** on the catalog bucket\n * (enable + credentials), Pipelines **stream** → **`r2_data_catalog` sink** →\n * **SQL pipeline**, then Logpush `workers_trace_events` can use HTTP ingest\n * `destination_conf`. The two runtime tokens (R2+Data Catalog for catalog/sink\n * and **Pipelines Send** for stream ingest) are **minted** via the account\n * Token API and stored in Tamer `logpush_pipelines` state (see README).\n */\nimport type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { LogpushPipelinesStateEntry } from \"../../types.js\";\nimport {\n emptyR2BucketViaS3,\n r2S3CredentialsFromEnv,\n} from \"../r2/r2S3EmptyBucket.js\";\nimport {\n findR2BucketDerivedName,\n ingestOriginFromPipelinesStreamEndpoint,\n} from \"./logpush-job.resolve.js\";\nimport { logpushPipelinesStateKey } from \"./logpush-pipelines-key.js\";\nimport { buildWorkersTracePipelinesStreamBody } from \"./logpush-pipelines-trace-schema.js\";\nimport { mintPipelinesAutoAccountTokens } from \"./logpush-pipelines-account-tokens.js\";\nimport {\n isPipelinesSinkExistingCatalogTableError,\n pipelinesSink1012RecoveryMessage,\n} from \"./pipelinesSinkCatalogTableError.js\";\n\nconst MAX_CF_NAME = 200;\n\n/**\n * Resolves the Iceberg `table_name` for a **new** `r2_data_catalog` sink.\n * When `tableNameAppendTimestamp` is not `false`, appends `_${ms}` to avoid\n * HTTP 422 / 1012 if a table with the same base name is still in the catalog.\n */\nexport function pipelinesAutoIcebergTableNameForNewSink(\n baseTable: string,\n tableNameAppendTimestamp: boolean | undefined,\n now: number = Date.now(),\n): string {\n const base = baseTable.trim();\n if (!base) {\n throw new Error(\"pipelinesAuto: tableName (base) is required\");\n }\n if (tableNameAppendTimestamp === false) {\n return base;\n }\n return `${base}_${now}`;\n}\n\n/**\n * Pipelines `GET /sinks/{id}` is the source of truth for how the catalog\n * registered the table (may differ from the `table_name` sent on create, e.g.\n * suffix normalization).\n */\nfunction r2DataCatalogFromSinkGet(result: {\n type?: string;\n config?: Record<string, unknown>;\n}): { tableName?: string; namespace?: string } {\n if (result.type !== \"r2_data_catalog\" || !result.config) {\n return {};\n }\n const c = result.config;\n const tableName =\n typeof c.table_name === \"string\" ? c.table_name.trim() : undefined;\n const namespace =\n typeof c.namespace === \"string\" ? c.namespace.trim() : undefined;\n return {\n tableName: tableName || undefined,\n namespace: namespace || undefined,\n };\n}\n\n/**\n * Pipelines may report `worker_trace_events_<ms>` from create/GET while R2 SQL\n * `SHOW TABLES` only lists the base name (e.g. `worker_trace_events`). Use the\n * base + long-numeric-suffix pattern so stack outputs match R2 SQL.\n */\nexport function normalizeIcebergTableNameForR2SqlState(\n pipelinesReported: string,\n configBaseTable: string,\n): string {\n const p = pipelinesReported.trim();\n const base = configBaseTable.trim();\n if (!base) return p;\n if (p === base) return p;\n const escaped = base.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n if (new RegExp(`^${escaped}_(\\\\d{10,})$`).test(p)) {\n return base;\n }\n return p;\n}\n\n/**\n * R2 Data Catalog `GET …/r2-catalog` and `POST …/r2-catalog/…/enable` use\n * **CLOUDFLARE_API_TOKEN** (apply token) — not the minted sub-tokens.\n */\nfunction throwPipelinesAutoR2CatalogApplyTokenError(\n logicalName: string,\n step: string,\n cause: unknown,\n): never {\n const orig = cause instanceof Error ? cause.message : String(cause);\n throw new Error(\n `logpushJobs.${logicalName} pipelinesAuto: ${step} — ` +\n \"your **apply** API token (CLOUDFLARE_API_TOKEN) must be allowed to use the **R2 Data Catalog** \" +\n \"account API (`/accounts/.../r2-catalog/...` — e.g. **Workers R2 Data Catalog: Edit** in the \" +\n \"API token permissions for this account). \" +\n \"That is separate from **Account API Tokens: Edit** (used only to mint the two sub-tokens) and \" +\n \"separate from those minted tokens, which are only for the catalog credential + Pipelines. \" +\n `Original: ${orig}`,\n );\n}\n\nfunction safeIdPart(s: string): string {\n return s.replace(/[^a-zA-Z0-9_]/g, \"_\").toLowerCase();\n}\n\nfunction trunc(s: string): string {\n return s.length <= MAX_CF_NAME ? s : s.slice(0, MAX_CF_NAME);\n}\n\n/** Stream / sink / pipeline names safe for Pipelines SQL identifiers. */\nexport function derivePipelinesLogpushResourceNames(\n tenant: TenantMeta,\n logicalName: string,\n env: string,\n): { streamName: string; sinkName: string; pipelineName: string } {\n const base = trunc(\n `tamer_${safeIdPart(tenant.slug)}_${safeIdPart(logicalName)}_${safeIdPart(env)}_wtr`,\n );\n return {\n streamName: trunc(`${base}_stream`),\n sinkName: trunc(`${base}_sink`),\n pipelineName: trunc(`${base}_pl`),\n };\n}\n\n/**\n * Idempotent: enables R2 catalog, stores credentials, creates stream, sink, pipeline.\n */\nexport async function ensurePipelinesLogpushProvision(\n config: LogpushJobResourceConfig,\n auto: NonNullable<LogpushJobResourceConfig[\"pipelinesAuto\"]>,\n tenant: TenantMeta,\n env: string,\n accountId: string,\n state: StateManager,\n api: CFApiClient,\n): Promise<void> {\n const pkey = logpushPipelinesStateKey(tenant.id, config.logicalName, env);\n const prior = state.get(pkey) as LogpushPipelinesStateEntry | undefined;\n const tokenPrefix = `Tamer ${tenant.slug} ${config.logicalName} ${env}`;\n\n const bucketDerived = findR2BucketDerivedName(\n state,\n auto.catalogBucketLogicalName,\n );\n if (!bucketDerived) {\n throw new Error(\n `logpushJobs.${config.logicalName}: R2 bucket \"${auto.catalogBucketLogicalName}\" is not in state — apply R2 first`,\n );\n }\n\n let r2CatId = prior?.type === \"logpush_pipelines\" ? prior.mintedR2CatalogTokenId : undefined;\n let r2CatVal =\n prior?.type === \"logpush_pipelines\" ? prior.mintedR2CatalogTokenValue : undefined;\n let sendId =\n prior?.type === \"logpush_pipelines\" ? prior.mintedPipelinesSendTokenId : undefined;\n let sendVal =\n prior?.type === \"logpush_pipelines\" ? prior.mintedPipelinesSendTokenValue : undefined;\n\n if (!r2CatId || !r2CatVal || !sendId || !sendVal) {\n if (\n prior?.type === \"logpush_pipelines\" &&\n (r2CatId || r2CatVal || sendId || sendVal)\n ) {\n console.warn(\n `logpushJobs.${config.logicalName}: state has partial minted token fields — creating fresh account tokens; old token ids may need manual cleanup in the dashboard.`,\n );\n }\n const minted = await mintPipelinesAutoAccountTokens(\n api,\n accountId,\n tokenPrefix,\n );\n r2CatId = minted.r2CatalogTokenId;\n r2CatVal = minted.r2CatalogTokenValue;\n sendId = minted.pipelinesSendTokenId;\n sendVal = minted.pipelinesSendTokenValue;\n }\n\n const cred = r2CatVal;\n\n console.log(\n `logpushJobs.${config.logicalName}: enabling R2 Data Catalog on \\\"${bucketDerived}\\\" (uses **CLOUDFLARE_API_TOKEN**; see README)…`,\n );\n try {\n await api.r2DataCatalogEnable(bucketDerived);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (!/exists|already|active/i.test(msg)) {\n throwPipelinesAutoR2CatalogApplyTokenError(\n config.logicalName,\n \"R2 Data Catalog enable (POST .../r2-catalog/{bucket}/enable)\",\n e,\n );\n }\n }\n\n try {\n await api.r2DataCatalogStoreCredential(bucketDerived, cred);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (!/exists|already|present/i.test(msg)) {\n throwPipelinesAutoR2CatalogApplyTokenError(\n config.logicalName,\n \"store R2 Data Catalog credentials (POST .../r2-catalog/{bucket}/credential)\",\n e,\n );\n }\n }\n\n const ns = (auto.namespace ?? \"default\").trim() || \"default\";\n const baseTable = auto.tableName.trim();\n if (!baseTable) {\n throw new Error(\n `logpushJobs.${config.logicalName}: pipelinesAuto.tableName is required`,\n );\n }\n\n const names = derivePipelinesLogpushResourceNames(\n tenant,\n config.logicalName,\n env,\n );\n\n const streams = await api.pipelinesV1StreamListAll();\n let stream = streams.find((s) => s.name === names.streamName);\n let streamCreatedFresh = false;\n if (!stream) {\n const body = buildWorkersTracePipelinesStreamBody(names.streamName);\n stream = await api.pipelinesV1StreamCreate(body);\n streamCreatedFresh = true;\n console.log(\n `Created Pipelines stream \"${names.streamName}\" [id ${stream.id}] (Workers trace schema).`,\n );\n } else {\n console.log(\n `Pipelines stream \"${names.streamName}\" already exists [id ${stream.id}].`,\n );\n }\n\n const streamIngestBaseUrl =\n ingestOriginFromPipelinesStreamEndpoint(stream.endpoint);\n\n /** Logpush validates HTTPS to ingest; new stream subdomains can lag DNS briefly. */\n if (streamCreatedFresh) {\n const settleMs = 2500;\n console.log(\n `logpushJobs.${config.logicalName}: waiting ${settleMs}ms for new stream ingest hostname before Logpush job step…`,\n );\n await new Promise<void>((r) => setTimeout(r, settleMs));\n }\n\n const sinks = await api.pipelinesV1SinkListAll();\n const rowGroup = auto.sinkRowGroupBytes ?? 134_217_728;\n const fileSize = auto.sinkRollingFileSizeBytes ?? 100 * 1024 * 1024;\n const interval = auto.sinkRollingIntervalSeconds ?? 300;\n let sink = sinks.find((s) => s.name === names.sinkName);\n let r2DataCatalogTableName: string | undefined =\n prior?.type === \"logpush_pipelines\"\n ? prior.r2DataCatalogTableName\n : undefined;\n let r2DataCatalogTableNamePipelines: string | undefined =\n prior?.type === \"logpush_pipelines\"\n ? prior.r2DataCatalogTableNamePipelines\n : undefined;\n if (!sink) {\n const icebergTable = pipelinesAutoIcebergTableNameForNewSink(\n baseTable,\n auto.tableNameAppendTimestamp,\n );\n try {\n sink = await api.pipelinesV1SinkCreate({\n name: names.sinkName,\n type: \"r2_data_catalog\",\n format: {\n type: \"parquet\",\n compression: \"zstd\",\n row_group_bytes: rowGroup,\n },\n schema: { fields: [] },\n config: {\n token: cred,\n account_id: accountId,\n bucket: bucketDerived,\n namespace: ns,\n table_name: icebergTable,\n rolling_policy: {\n file_size_bytes: fileSize,\n interval_seconds: interval,\n },\n },\n });\n } catch (e) {\n if (isPipelinesSinkExistingCatalogTableError(e)) {\n throw new Error(\n pipelinesSink1012RecoveryMessage(\n config.logicalName,\n ns,\n icebergTable,\n bucketDerived,\n ),\n { cause: e },\n );\n }\n throw e;\n }\n r2DataCatalogTableName = icebergTable;\n console.log(\n `Created Pipelines sink \"${names.sinkName}\" (r2_data_catalog) [id ${sink.id}] ` +\n `→ Iceberg table \"${ns}\" / \"${icebergTable}\".`,\n );\n } else {\n console.log(\n `Pipelines sink \"${names.sinkName}\" already exists [id ${sink.id}].`,\n );\n }\n\n let catalogNs = ns;\n try {\n const fullSink = await api.pipelinesV1SinkGet(sink.id);\n const fromApi = r2DataCatalogFromSinkGet(fullSink);\n if (fromApi.tableName) {\n r2DataCatalogTableName = fromApi.tableName;\n }\n if (fromApi.namespace) {\n catalogNs = fromApi.namespace;\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.warn(\n `logpushJobs.${config.logicalName}: Pipelines GET sink not used (${msg}) — ` +\n \"Iceberg name falls back to create-time value in state; re-run apply after fixing API access if needed.\",\n );\n }\n\n if (r2DataCatalogTableName) {\n const raw = r2DataCatalogTableName;\n r2DataCatalogTableNamePipelines = raw;\n const normalized = normalizeIcebergTableNameForR2SqlState(raw, baseTable);\n if (normalized !== raw) {\n console.log(\n `logpushJobs.${config.logicalName}: R2 SQL table name for stack outputs: ` +\n `\"${raw}\" → \"${normalized}\" (Pipelines reports a suffixed id; catalog uses the base name).`,\n );\n }\n r2DataCatalogTableName = normalized;\n }\n\n const sql = `INSERT INTO ${names.sinkName} SELECT * FROM ${names.streamName};`;\n const pipelines = await api.pipelineListAll();\n let pipeline = pipelines.find((p) => p.name === names.pipelineName);\n if (!pipeline) {\n pipeline = await api.pipelineCreate({ name: names.pipelineName, sql });\n console.log(\n `Created Pipelines job \"${names.pipelineName}\" [id ${pipeline.id}].`,\n );\n } else {\n if (pipeline.sql?.trim() !== sql.trim()) {\n console.warn(\n `Pipelines job \"${names.pipelineName}\" exists with different SQL — not updating (replace manually or delete & re-apply).`,\n );\n } else {\n console.log(\n `Pipelines job \"${names.pipelineName}\" already exists [id ${pipeline.id}].`,\n );\n }\n }\n\n const ts = new Date().toISOString();\n const existing = state.get(pkey) as LogpushPipelinesStateEntry | undefined;\n const entry: LogpushPipelinesStateEntry = {\n type: \"logpush_pipelines\",\n logicalName: config.logicalName,\n streamId: stream.id,\n streamIngestBaseUrl,\n sinkId: sink.id,\n pipelineId: pipeline.id,\n streamName: names.streamName,\n sinkName: names.sinkName,\n pipelineName: names.pipelineName,\n r2DataCatalogTableName,\n r2DataCatalogTableNamePipelines,\n r2DataCatalogNamespace: catalogNs,\n catalogBucketDerivedName: bucketDerived,\n mintedR2CatalogTokenId: r2CatId,\n mintedR2CatalogTokenValue: r2CatVal,\n mintedPipelinesSendTokenId: sendId,\n mintedPipelinesSendTokenValue: sendVal,\n createdAt: existing?.type === \"logpush_pipelines\" ? existing.createdAt : ts,\n updatedAt: ts,\n };\n state.set(pkey, entry);\n}\n\nfunction samePipelinesV1Id(a: string, b: string): boolean {\n return a.replace(/-/g, \"\").toLowerCase() === b.replace(/-/g, \"\").toLowerCase();\n}\n\n/**\n * Pipelines v1 may 404 on DELETE when state stores 32-hex ids but the path must\n * be a hyphenated UUID, or the id drifts — list by name and retry once.\n */\nasync function deletePipelinesV1StreamWithLookup(\n api: CFApiClient,\n streamId: string,\n streamName: string,\n): Promise<void> {\n try {\n await api.pipelinesV1StreamDelete(streamId);\n return;\n } catch (e) {\n const streams = await api.pipelinesV1StreamListAll();\n const hit =\n streams.find((s) => s.name === streamName) ??\n streams.find((s) => samePipelinesV1Id(s.id, streamId));\n if (hit) {\n await api.pipelinesV1StreamDelete(hit.id);\n return;\n }\n throw e;\n }\n}\n\nasync function deletePipelinesV1SinkWithLookup(\n api: CFApiClient,\n sinkId: string,\n sinkName: string,\n): Promise<void> {\n try {\n await api.pipelinesV1SinkDelete(sinkId);\n return;\n } catch (e) {\n const sinks = await api.pipelinesV1SinkListAll();\n const hit =\n sinks.find((s) => s.name === sinkName) ??\n sinks.find((s) => samePipelinesV1Id(s.id, sinkId));\n if (hit) {\n await api.pipelinesV1SinkDelete(hit.id);\n return;\n }\n throw e;\n }\n}\n\n/**\n * Best-effort teardown: SQL pipeline, then sink, then stream (after Logpush job is gone).\n * Order avoids leaving the sink (e.g. r2_data_catalog) while the stream still exists.\n */\nexport async function deletePipelinesLogpushGraph(\n api: CFApiClient,\n entry: LogpushPipelinesStateEntry,\n): Promise<void> {\n const { pipelineId, streamId, sinkId } = entry;\n for (const [label, id] of [\n [\"Pipelines Send\", entry.mintedPipelinesSendTokenId],\n [\"Data Catalog (minted)\", entry.mintedR2CatalogTokenId],\n ] as const) {\n if (!id) continue;\n try {\n await api.accountTokenDelete(id);\n console.log(`Deleted account API token [${label}] [id ${id}].`);\n } catch (e) {\n console.warn(\n `Account API token delete [${label}] [id ${id}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n try {\n await api.pipelineDelete(pipelineId);\n console.log(\n `Deleted Pipelines job \"${entry.pipelineName}\" [id ${pipelineId}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines job delete [id ${pipelineId}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n try {\n await deletePipelinesV1SinkWithLookup(api, sinkId, entry.sinkName);\n console.log(`Deleted Pipelines sink \"${entry.sinkName}\" [id ${sinkId}].`);\n } catch (e) {\n console.warn(\n `Pipelines sink delete [id ${sinkId}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n try {\n await deletePipelinesV1StreamWithLookup(api, streamId, entry.streamName);\n console.log(\n `Deleted Pipelines stream \"${entry.streamName}\" [id ${streamId}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines stream delete [id ${streamId}]:`,\n e instanceof Error ? e.message : e,\n );\n }\n await pipelinesAutoTeardownR2DataCatalog(\n api,\n entry.catalogBucketDerivedName,\n );\n}\n\n/**\n * Stopping the Pipelines `r2_data_catalog` sink does not drop the Iceberg\n * table. Disable the Data Catalog, then (when `R2_ACCESS_KEY_ID` is set) empty\n * the catalog bucket so a later `tamer apply` is not blocked by HTTP 422 / 1012\n * (table already exists in this catalog).\n */\nexport async function pipelinesAutoTeardownR2DataCatalog(\n api: CFApiClient,\n catalogBucketDerivedName: string,\n): Promise<void> {\n try {\n await api.r2DataCatalogDisable(catalogBucketDerivedName);\n console.log(\n `Pipelines: disabled R2 Data Catalog on \"${catalogBucketDerivedName}\" (pipelinesAuto teardown).`,\n );\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (!/\\b(404|10006|not found)\\b/i.test(msg)) {\n console.warn(\n `Pipelines: R2 Data Catalog disable for \"${catalogBucketDerivedName}\": ${msg}`,\n );\n }\n }\n const s3creds = r2S3CredentialsFromEnv();\n if (!s3creds) {\n console.warn(\n `Pipelines: set R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY to empty the catalog bucket and remove Iceberg table metadata; ` +\n \"otherwise a future `tamer apply` may hit HTTP 422 / 1012 (table already exists). \" +\n \"Alternatively bump `pipelinesAuto.tableName` or use a new catalog bucket.\",\n );\n return;\n }\n try {\n const accountId = api.getAccountId();\n console.log(\n `Pipelines: emptying catalog bucket \"${catalogBucketDerivedName}\" via S3 (removes Iceberg layout)…`,\n );\n const { objectsDeleted, uploadsAborted } = await emptyR2BucketViaS3(\n accountId,\n catalogBucketDerivedName,\n s3creds,\n );\n console.log(\n `Pipelines: catalog bucket empty — ${objectsDeleted} object(s), ${uploadsAborted} incomplete multipart upload(s) aborted.`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: S3 empty of catalog bucket \"${catalogBucketDerivedName}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n}\n\n/**\n * After state-driven `deletePipelinesLogpushGraph`, or when `logpush_pipelines`\n * state is missing, delete the SQL pipeline / sink / stream by the names derived\n * from `derivePipelinesLogpushResourceNames` (config + env). Uses live ids from\n * list APIs so stale state ids do not block teardown.\n */\nexport async function reconcilePipelinesAutoCloudResourcesByName(\n api: CFApiClient,\n tenant: TenantMeta,\n logicalName: string,\n env: string,\n opts?: { catalogBucketDerivedName?: string },\n): Promise<void> {\n const names = derivePipelinesLogpushResourceNames(tenant, logicalName, env);\n const pipelines = await api.pipelineListAll();\n const pl = pipelines.find((p) => p.name === names.pipelineName);\n if (pl) {\n try {\n await api.pipelineDelete(pl.id);\n console.log(\n `Pipelines: destroy reconcile — removed SQL pipeline \"${names.pipelineName}\" [id ${pl.id}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile — SQL pipeline \"${names.pipelineName}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n const sinks = await api.pipelinesV1SinkListAll();\n const sk = sinks.find((s) => s.name === names.sinkName);\n if (sk) {\n try {\n await deletePipelinesV1SinkWithLookup(api, sk.id, sk.name);\n console.log(\n `Pipelines: destroy reconcile — removed sink \"${names.sinkName}\" [id ${sk.id}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile — sink \"${names.sinkName}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n const streams = await api.pipelinesV1StreamListAll();\n const st = streams.find((s) => s.name === names.streamName);\n if (st) {\n try {\n await deletePipelinesV1StreamWithLookup(api, st.id, st.name);\n console.log(\n `Pipelines: destroy reconcile — removed stream \"${names.streamName}\" [id ${st.id}].`,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile — stream \"${names.streamName}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n const cat = opts?.catalogBucketDerivedName?.trim();\n if (cat) {\n await pipelinesAutoTeardownR2DataCatalog(api, cat);\n }\n}\n","/**\n * Resolved Logpush job shape Tamer expects on Cloudflare (for create + update +\n * plan diff).\n */\nimport type { LogpushJobResourceConfig } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { logpushPipelinesStateKey } from \"./logpush-pipelines-key.js\";\nimport {\n DEFAULT_WORKERS_TRACE_LOG_FIELD_NAMES,\n buildPipelinesIngestDestinationConf,\n buildR2WorkersTraceDestinationConf,\n findR2BucketDerivedName,\n} from \"./logpush-job.resolve.js\";\n\nexport type LogpushJobDesired = {\n destination_conf: string;\n filter?: string;\n output_options: Record<string, unknown>;\n};\n\nfunction resolvedFilter(\n config: LogpushJobResourceConfig,\n templateFilter?: string,\n): string | undefined {\n const explicit = config.filter?.trim();\n if (explicit) return explicit;\n const t = templateFilter?.trim();\n if (t) return t;\n return undefined;\n}\n\n/**\n * Merge `output_options` from a template job (dashboard bootstrap) with\n * optional `fieldNames` override from config.\n */\nexport function mergeLogpushOutputOptions(\n config: LogpushJobResourceConfig,\n template?: Record<string, unknown> | null | undefined,\n): Record<string, unknown> {\n const baseline: Record<string, unknown> =\n template && typeof template === \"object\" && !Array.isArray(template)\n ? { ...template }\n : {};\n if (config.fieldNames?.length) {\n baseline.field_names = config.fieldNames;\n return baseline;\n }\n if (!template || typeof template !== \"object\") {\n baseline.field_names = [...DEFAULT_WORKERS_TRACE_LOG_FIELD_NAMES];\n return baseline;\n }\n if (!Array.isArray(baseline.field_names)) {\n baseline.field_names = [...DEFAULT_WORKERS_TRACE_LOG_FIELD_NAMES];\n }\n return baseline;\n}\n\nexport async function resolveLogpushJobDesired(\n config: LogpushJobResourceConfig,\n accountId: string,\n state: StateManager,\n api: CFApiClient,\n tenantId: string,\n env: string,\n): Promise<LogpushJobDesired> {\n if (config.destinationConfEnv) {\n const raw = process.env[config.destinationConfEnv]?.trim();\n if (!raw) {\n throw new Error(\n `logpushJobs.${config.logicalName}: environment variable \"${config.destinationConfEnv}\" is empty (set it to the full Logpush destination_conf, or remove this logpushJobs entry)`,\n );\n }\n const filter = resolvedFilter(config, undefined);\n const output_options = mergeLogpushOutputOptions(config, undefined);\n return {\n destination_conf: raw,\n ...(filter ? { filter } : {}),\n output_options,\n };\n }\n\n if (config.pipelinesAuto) {\n const pkey = logpushPipelinesStateKey(\n tenantId,\n config.logicalName,\n env,\n );\n const lpp = state.get(pkey);\n if (!lpp || lpp.type !== \"logpush_pipelines\") {\n throw new Error(\n `logpushJobs.${config.logicalName}: pipelinesAuto graph not in state — \\`tamer apply\\` provisions stream/sink/pipeline first`,\n );\n }\n const token = lpp.mintedPipelinesSendTokenValue?.trim();\n if (!token) {\n throw new Error(\n `logpushJobs.${config.logicalName}: pipelinesAuto minted Pipelines Send token missing from state — run \\`tamer apply\\` to provision tokens and graph`,\n );\n }\n const destination_conf = buildPipelinesIngestDestinationConf({\n streamId: lpp.streamId,\n pipelineId: lpp.pipelineId,\n bearerToken: token,\n ingestBaseUrl: lpp.streamIngestBaseUrl,\n });\n const filter = resolvedFilter(config, undefined);\n const output_options = mergeLogpushOutputOptions(config, undefined);\n return {\n destination_conf,\n ...(filter ? { filter } : {}),\n output_options,\n };\n }\n\n if (config.pipelinesIngest) {\n const pi = config.pipelinesIngest;\n const token = process.env[pi.bearerTokenEnv]?.trim();\n if (!token) {\n throw new Error(\n `logpushJobs.${config.logicalName}: environment variable \"${pi.bearerTokenEnv}\" is empty (Pipelines stream ingest Bearer token for Logpush)`,\n );\n }\n let destination_conf: string;\n try {\n destination_conf = buildPipelinesIngestDestinationConf({\n streamId: pi.streamId,\n pipelineId: pi.pipelineId,\n bearerToken: token,\n });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`logpushJobs.${config.logicalName}: ${msg}`);\n }\n const filter = resolvedFilter(config, undefined);\n const output_options = mergeLogpushOutputOptions(config, undefined);\n return {\n destination_conf,\n ...(filter ? { filter } : {}),\n output_options,\n };\n }\n\n let sourceJobId: number | undefined;\n if (config.destinationConfFromJobId != null) {\n sourceJobId = config.destinationConfFromJobId;\n } else if (config.destinationConfFromJobIdEnv) {\n const raw = process.env[config.destinationConfFromJobIdEnv]?.trim();\n if (!raw) {\n throw new Error(\n `logpushJobs.${config.logicalName}: environment variable \"${config.destinationConfFromJobIdEnv}\" is empty (set it to the numeric Cloudflare Logpush job id whose destination_conf should be copied)`,\n );\n }\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n throw new Error(\n `logpushJobs.${config.logicalName}: \"${config.destinationConfFromJobIdEnv}\" must be a positive integer job id (got \"${raw}\")`,\n );\n }\n sourceJobId = n;\n }\n\n if (sourceJobId != null) {\n const src = await api.logpushAccountJobGet(sourceJobId);\n const conf = src.destination_conf?.trim();\n if (!conf) {\n throw new Error(\n `logpushJobs.${config.logicalName}: Logpush job ${sourceJobId} has no destination_conf`,\n );\n }\n if (src.dataset && src.dataset !== config.dataset) {\n throw new Error(\n `logpushJobs.${config.logicalName}: source job ${sourceJobId} dataset is \"${src.dataset}\" but this job declares \"${config.dataset}\"`,\n );\n }\n const filter = resolvedFilter(config, src.filter);\n const templateOpts = src.output_options as\n | Record<string, unknown>\n | undefined;\n const output_options = mergeLogpushOutputOptions(config, templateOpts);\n return {\n destination_conf: conf,\n ...(filter ? { filter } : {}),\n output_options,\n };\n }\n\n const r2 = config.r2;\n if (!r2) {\n throw new Error(\n `logpushJobs.${config.logicalName}: internal: no destination source (expected loader to enforce r2 | pipelinesIngest | pipelinesAuto | env | job id)`,\n );\n }\n const bucket = findR2BucketDerivedName(state, r2.bucketLogicalName);\n if (!bucket) {\n throw new Error(\n `logpushJobs.${config.logicalName}: R2 bucket logical \"${r2.bucketLogicalName}\" is not in state — run \\`tamer apply\\` so the bucket is created first, then re-run apply`,\n );\n }\n const ak = process.env[r2.accessKeyIdEnv]?.trim();\n const sk = process.env[r2.secretAccessKeyEnv]?.trim();\n if (!ak || !sk) {\n throw new Error(\n `logpushJobs.${config.logicalName}: set ${r2.accessKeyIdEnv} and ${r2.secretAccessKeyEnv} (R2 S3-compatible credentials for Logpush)`,\n );\n }\n const pathPrefix = r2.pathPrefix ?? \"workers-trace-events\";\n const destination_conf = buildR2WorkersTraceDestinationConf({\n bucketDerivedName: bucket,\n pathPrefix,\n accountId,\n accessKeyId: ak,\n secretAccessKey: sk,\n });\n const filter = resolvedFilter(config, undefined);\n const output_options = mergeLogpushOutputOptions(config, undefined);\n return {\n destination_conf,\n ...(filter ? { filter } : {}),\n output_options,\n };\n}\n","/**\n * Plan-time diff + apply-time reconciliation for account Logpush jobs.\n */\nimport type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { PlanFieldChange, PlanItem } from \"../../core/plan/plan.types.js\";\nimport { logpushPipelinesStateKey } from \"./logpush-pipelines-key.js\";\nimport {\n logpushJobCloudflareName,\n logpushJobStateKey,\n} from \"./logpush-job.resolve.js\";\nimport {\n resolveLogpushJobDesired,\n type LogpushJobDesired,\n} from \"./logpush-job.desired.js\";\n\nexport type LiveLogpushJobDetails = {\n destination_conf?: string;\n filter?: string;\n output_options?: Record<string, unknown>;\n enabled?: boolean;\n};\n\nexport function normalizeLogpushOutputOptions(o: unknown): unknown {\n if (!o || typeof o !== \"object\" || Array.isArray(o)) return o;\n const x = { ...(o as Record<string, unknown>) };\n if (Array.isArray(x.field_names)) {\n x.field_names = [...(x.field_names as string[])].sort();\n }\n return x;\n}\n\nexport function normLogpushFilter(f?: string | null): string | undefined {\n const t = f?.trim();\n return t || undefined;\n}\n\n/** Build `filter` for PUT when it must change (including clear). */\nexport function logpushFilterPutBody(\n desired: LogpushJobDesired,\n live: Pick<LiveLogpushJobDetails, \"filter\">,\n): { filter?: string } {\n const d = normLogpushFilter(desired.filter);\n const l = normLogpushFilter(live.filter);\n if (d === l) return {};\n return { filter: d ?? \"\" };\n}\n\nexport function logpushJobFieldChanges(\n desired: LogpushJobDesired,\n live: LiveLogpushJobDetails,\n enabledDesired: boolean,\n): PlanFieldChange[] {\n const changes: PlanFieldChange[] = [];\n const dc = desired.destination_conf.trim();\n const lc = (live.destination_conf ?? \"\").trim();\n if (dc !== lc) {\n changes.push({\n field: \"destination_conf\",\n from: lc,\n to: dc,\n kind: \"mutable\",\n });\n }\n const df = normLogpushFilter(desired.filter);\n const lf = normLogpushFilter(live.filter);\n if (df !== lf) {\n changes.push({\n field: \"filter\",\n from: lf ?? \"(none)\",\n to: df ?? \"(none)\",\n kind: \"mutable\",\n });\n }\n const doo = JSON.stringify(\n normalizeLogpushOutputOptions(desired.output_options),\n );\n const loo = JSON.stringify(\n normalizeLogpushOutputOptions(live.output_options ?? {}),\n );\n if (doo !== loo) {\n changes.push({\n field: \"output_options\",\n from: JSON.parse(loo) as unknown,\n to: JSON.parse(doo) as unknown,\n kind: \"mutable\",\n });\n }\n const enabledLive = live.enabled !== false;\n if (enabledLive !== enabledDesired) {\n changes.push({\n field: \"enabled\",\n from: enabledLive,\n to: enabledDesired,\n kind: \"mutable\",\n });\n }\n return changes;\n}\n\nexport async function logpushJobDiffPlanItems(args: {\n resources: LogpushJobResourceConfig[];\n tenant: TenantMeta;\n env: string;\n state: StateManager;\n api: CFApiClient;\n accountId: string;\n}): Promise<PlanItem[]> {\n const { resources, tenant, env, state, api, accountId } = args;\n if (env === \"local\") return [];\n const items: PlanItem[] = [];\n\n for (const config of resources) {\n if (config.dataset !== \"workers_trace_events\") continue;\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const entry = state.get(key);\n if (!entry || entry.type !== \"logpush_job\") continue;\n\n if (config.pipelinesAuto) {\n const pkey = logpushPipelinesStateKey(\n tenant.id,\n config.logicalName,\n env,\n );\n const lpp = state.get(pkey);\n if (!lpp || lpp.type !== \"logpush_pipelines\") continue;\n }\n\n const cfName = logpushJobCloudflareName(config, tenant, env);\n let liveFull: Awaited<ReturnType<CFApiClient[\"logpushAccountJobGet\"]>>;\n try {\n liveFull = await api.logpushAccountJobGet(entry.cfJobId);\n } catch {\n continue;\n }\n\n const desired = await resolveLogpushJobDesired(\n config,\n accountId,\n state,\n api,\n tenant.id,\n env,\n );\n\n const enabledDesired = config.enabled !== false;\n const changes = logpushJobFieldChanges(\n desired,\n {\n destination_conf: liveFull.destination_conf,\n filter: liveFull.filter,\n output_options: liveFull.output_options as\n | Record<string, unknown>\n | undefined,\n enabled: liveFull.enabled,\n },\n enabledDesired,\n );\n if (changes.length === 0) continue;\n\n items.push({\n kind: \"logpush_job\",\n action: \"update\",\n logicalName: config.logicalName,\n derivedName: cfName,\n detail: changes.map((c) => `${c.field}`).join(\", \"),\n changes,\n });\n }\n\n return items;\n}\n","import type {\n LogpushJobResourceConfig,\n TenantMeta,\n} from \"../../types.js\";\nimport type { LogpushJobStateEntry } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport {\n logpushJobCloudflareName,\n logpushJobStateKey,\n} from \"./logpush-job.resolve.js\";\n\n/** Logpush validates the Pipelines ingest URL; new streams can return DNS 1002 until the hostname propagates. */\nfunction isRetriableLogpushIngestDnsError(message: string): boolean {\n return /\\bDNSError\\b/i.test(message) && /\\b1002\\b/.test(message);\n}\n\nasync function logpushAccountJobCreateWithIngestRetry(\n api: CFApiClient,\n body: Parameters<CFApiClient[\"logpushAccountJobCreate\"]>[0],\n): Promise<{ id: number }> {\n const maxAttempts = 8;\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await api.logpushAccountJobCreate(body);\n } catch (e) {\n lastError = e;\n const msg = e instanceof Error ? e.message : String(e);\n if (attempt === maxAttempts || !isRetriableLogpushIngestDnsError(msg)) {\n throw e;\n }\n const waitMs = Math.min(30_000, 1500 * 2 ** (attempt - 1));\n console.warn(\n `Logpush create \"${body.name}\": destination validation error, retry ${attempt}/${maxAttempts} in ${waitMs}ms ` +\n `(ingest DNS may lag right after a new Pipelines stream). ${msg.slice(0, 240)}`,\n );\n await new Promise<void>((r) => setTimeout(r, waitMs));\n }\n }\n throw lastError;\n}\nimport { ensurePipelinesLogpushProvision } from \"./logpush-pipelines-provision.js\";\nimport { resolveLogpushJobDesired } from \"./logpush-job.desired.js\";\nimport {\n logpushFilterPutBody,\n logpushJobFieldChanges,\n} from \"./logpush-job.diff.js\";\n\nfunction writeState(\n state: StateManager,\n key: string,\n config: LogpushJobResourceConfig,\n cfName: string,\n cfJobId: number,\n dataset: string,\n existingCreatedAt?: string,\n): void {\n const ts = new Date().toISOString();\n state.set(key, {\n type: \"logpush_job\",\n logicalName: config.logicalName,\n derivedName: cfName,\n cfJobId,\n dataset,\n createdAt: existingCreatedAt ?? ts,\n updatedAt: ts,\n });\n}\n\nasync function reconcileLogpushJob(\n config: LogpushJobResourceConfig,\n cfJobId: number,\n cfName: string,\n accountId: string,\n tenant: TenantMeta,\n env: string,\n state: StateManager,\n api: CFApiClient,\n key: string,\n st: LogpushJobStateEntry,\n): Promise<void> {\n const fullLive = await api.logpushAccountJobGet(cfJobId);\n const desired = await resolveLogpushJobDesired(\n config,\n accountId,\n state,\n api,\n tenant.id,\n env,\n );\n const enabledDesired = config.enabled !== false;\n const changes = logpushJobFieldChanges(\n desired,\n {\n destination_conf: fullLive.destination_conf,\n filter: fullLive.filter,\n output_options: fullLive.output_options as\n | Record<string, unknown>\n | undefined,\n enabled: fullLive.enabled,\n },\n enabledDesired,\n );\n if (changes.length === 0) return;\n\n await api.logpushAccountJobUpdate(cfJobId, {\n destination_conf: desired.destination_conf,\n enabled: enabledDesired,\n output_options: desired.output_options,\n ...logpushFilterPutBody(desired, fullLive),\n });\n\n const ts = new Date().toISOString();\n state.set(key, { ...st, updatedAt: ts });\n console.log(\n `Updated Logpush job \"${cfName}\" (${config.dataset}) [id ${cfJobId}]: ${changes.map((c) => c.field).join(\", \")}`,\n );\n}\n\nexport async function logpushJobApply(\n resources: LogpushJobResourceConfig[],\n tenant: TenantMeta,\n env: string,\n accountId: string,\n api: CFApiClient,\n state: StateManager,\n): Promise<void> {\n if (resources.length === 0 || env === \"local\") return;\n\n const jobs = await api.logpushAccountJobsList();\n const byNameDataset = new Map<string, (typeof jobs)[0]>();\n for (const j of jobs) {\n if (j.name && j.dataset) {\n byNameDataset.set(`${j.dataset}\\0${j.name}`, j);\n }\n }\n\n for (const config of resources) {\n if (config.dataset !== \"workers_trace_events\") {\n throw new Error(\n `logpushJobs.${config.logicalName}: unsupported dataset \"${config.dataset}\" (only workers_trace_events)`,\n );\n }\n if (config.pipelinesAuto) {\n await ensurePipelinesLogpushProvision(\n config,\n config.pipelinesAuto,\n tenant,\n env,\n accountId,\n state,\n api,\n );\n }\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const cfName = logpushJobCloudflareName(config, tenant, env);\n const mapKey = `${config.dataset}\\0${cfName}`;\n const existingState = state.get(key);\n\n if (existingState?.type === \"logpush_job\") {\n const live = jobs.find((j) => j.id === existingState.cfJobId);\n if (live) {\n await reconcileLogpushJob(\n config,\n existingState.cfJobId,\n cfName,\n accountId,\n tenant,\n env,\n state,\n api,\n key,\n existingState,\n );\n continue;\n }\n }\n\n const hit = byNameDataset.get(mapKey);\n if (hit?.id != null) {\n writeState(\n state,\n key,\n config,\n cfName,\n hit.id,\n config.dataset,\n existingState?.type === \"logpush_job\"\n ? existingState.createdAt\n : undefined,\n );\n console.log(\n `Logpush job \"${cfName}\" (${config.dataset}) already exists [id ${hit.id}] — recorded in state.`,\n );\n const st = state.get(key);\n if (st?.type === \"logpush_job\") {\n await reconcileLogpushJob(\n config,\n hit.id,\n cfName,\n accountId,\n tenant,\n env,\n state,\n api,\n key,\n st,\n );\n }\n continue;\n }\n\n const desired = await resolveLogpushJobDesired(\n config,\n accountId,\n state,\n api,\n tenant.id,\n env,\n );\n\n const created = await logpushAccountJobCreateWithIngestRetry(api, {\n name: cfName,\n dataset: config.dataset,\n destination_conf: desired.destination_conf,\n enabled: config.enabled !== false,\n ...logpushFilterPutBody(desired, {}),\n output_options: desired.output_options,\n });\n\n writeState(\n state,\n key,\n config,\n cfName,\n created.id,\n config.dataset,\n existingState?.type === \"logpush_job\"\n ? existingState.createdAt\n : undefined,\n );\n console.log(\n `Created Logpush job \"${cfName}\" (${config.dataset}) [id ${created.id}].`,\n );\n }\n}\n","import type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { logpushJobCloudflareName, logpushJobStateKey } from \"./logpush-job.resolve.js\";\n\nexport async function logpushJobSync(\n resources: LogpushJobResourceConfig[],\n tenant: TenantMeta,\n env: string,\n api: CFApiClient,\n state: StateManager,\n): Promise<void> {\n if (resources.length === 0 || env === \"local\") return;\n\n const jobs = await api.logpushAccountJobsList();\n for (const config of resources) {\n const cfName = logpushJobCloudflareName(config, tenant, env);\n const hit = jobs.find(\n (j) => j.name === cfName && j.dataset === config.dataset,\n );\n if (!hit?.id) continue;\n\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const existing = state.get(key);\n const ts = new Date().toISOString();\n state.set(key, {\n type: \"logpush_job\",\n logicalName: config.logicalName,\n derivedName: cfName,\n cfJobId: hit.id,\n dataset: config.dataset,\n createdAt:\n existing?.type === \"logpush_job\" ? existing.createdAt : ts,\n updatedAt: ts,\n });\n }\n}\n","import type { LogpushJobResourceConfig } from \"../../types.js\";\nimport type { TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { ResourceDrift } from \"../../core/drift/drift.types.js\";\nimport { logpushJobCloudflareName, logpushJobStateKey } from \"./logpush-job.resolve.js\";\n\ninterface CFLogpushJob {\n id: number;\n name?: string;\n dataset?: string;\n}\n\nexport function logpushJobDrift(\n allJobs: CFLogpushJob[],\n resources: LogpushJobResourceConfig[],\n env: string,\n tenant: TenantMeta,\n state: StateManager,\n): ResourceDrift {\n const drift: ResourceDrift = {\n kind: \"logpush_job\",\n missingFromCloudflare: [],\n unrecordedInState: [],\n undeployed: [],\n };\n\n for (const config of resources) {\n const cfName = logpushJobCloudflareName(config, tenant, env);\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const tracked = state.get(key);\n const st = tracked?.type === \"logpush_job\" ? tracked : undefined;\n const onCf = allJobs.find(\n (j) => j.name === cfName && j.dataset === config.dataset,\n );\n const idStillThere =\n st && allJobs.some((j) => j.id === st.cfJobId);\n\n if (st && !idStillThere) {\n drift.missingFromCloudflare.push({\n logicalName: config.logicalName,\n derivedName: cfName,\n cfId: String(st.cfJobId),\n });\n } else if (onCf && !st) {\n drift.unrecordedInState.push({\n logicalName: config.logicalName,\n derivedName: cfName,\n cfId: String(onCf.id),\n });\n } else if (!onCf && !st) {\n drift.undeployed.push({\n logicalName: config.logicalName,\n derivedName: cfName,\n });\n }\n }\n\n return drift;\n}\n","import type { CfiConfig } from \"../../types.js\";\nimport { getLogpushJobs } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type {\n LogpushJobStateEntry,\n LogpushPipelinesStateEntry,\n} from \"../../types.js\";\nimport { logpushPipelinesStateKey } from \"./logpush-pipelines-key.js\";\nimport { reconcilePipelinesAutoMintedAccountTokensByName } from \"./logpush-pipelines-account-tokens.js\";\nimport {\n deletePipelinesLogpushGraph,\n reconcilePipelinesAutoCloudResourcesByName,\n} from \"./logpush-pipelines-provision.js\";\nimport { findR2BucketDerivedName } from \"./logpush-job.resolve.js\";\n\n/**\n * `logpush_pipelines:t-${tenantId}-${logicalName}-${env}` (logical name may\n * contain hyphens, so we match prefix+suffix to scope to this stack).\n */\nfunction isLogpushPipelinesKeyForStack(\n key: string,\n tenantId: string,\n env: string,\n): boolean {\n const prefix = `logpush_pipelines:t-${tenantId}-`;\n const suffix = `-${env}`;\n return key.startsWith(prefix) && key.endsWith(suffix);\n}\n\n/**\n * Delete Logpush jobs declared in config and remove their state rows.\n * Runs before R2 teardown so the job stops writing to the bucket.\n * For `pipelinesAuto`, deletes the Logpush job first, then stream / sink / SQL pipeline.\n *\n * Also sweeps any remaining `logpush_pipelines` state for this stack (e.g. config\n * no longer lists logpush, or the job row was lost while pipelines state remained).\n */\nexport async function logpushJobDestroy(\n env: string,\n state: StateManager,\n api: CFApiClient,\n config: CfiConfig,\n): Promise<void> {\n if (env === \"local\") return;\n const jobs = getLogpushJobs(config);\n const allowed = new Set(jobs.map((j) => j.logicalName));\n\n const autoByLogical = new Map(\n jobs.filter((j) => j.pipelinesAuto).map((j) => [j.logicalName, j]),\n );\n\n for (const [key, entry] of Object.entries(state.getAll())) {\n if (entry.type !== \"logpush_job\") continue;\n const job = entry as LogpushJobStateEntry;\n if (allowed.size > 0 && !allowed.has(job.logicalName)) continue;\n try {\n await api.logpushAccountJobDelete(job.cfJobId);\n state.delete(key);\n console.log(\n `Deleted Logpush job \"${job.derivedName}\" [id ${job.cfJobId}].`,\n );\n } catch (err) {\n console.warn(\n `Failed to delete Logpush job ${job.derivedName} [id ${job.cfJobId}]:`,\n err instanceof Error ? err.message : err,\n );\n }\n\n if (autoByLogical.has(job.logicalName)) {\n const pkey = logpushPipelinesStateKey(\n config.tenant.id,\n job.logicalName,\n env,\n );\n const lpp = state.get(pkey) as LogpushPipelinesStateEntry | undefined;\n if (lpp?.type === \"logpush_pipelines\") {\n await deletePipelinesLogpushGraph(api, lpp);\n state.delete(pkey);\n }\n }\n }\n\n for (const [key, entry] of Object.entries(state.getAll())) {\n if (entry.type !== \"logpush_pipelines\") continue;\n if (!isLogpushPipelinesKeyForStack(key, config.tenant.id, env)) continue;\n const lpp = entry as LogpushPipelinesStateEntry;\n try {\n await deletePipelinesLogpushGraph(api, lpp);\n state.delete(key);\n } catch (err) {\n console.warn(\n `Failed to destroy Pipelines graph for [${key}]:`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n\n for (const j of jobs) {\n if (!j.pipelinesAuto) continue;\n const catBucket = findR2BucketDerivedName(\n state,\n j.pipelinesAuto.catalogBucketLogicalName,\n );\n try {\n await reconcilePipelinesAutoCloudResourcesByName(\n api,\n config.tenant,\n j.logicalName,\n env,\n catBucket ? { catalogBucketDerivedName: catBucket } : undefined,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile (SQL/sink/stream) for logpush ${j.logicalName}:`,\n e instanceof Error ? e.message : e,\n );\n }\n try {\n await reconcilePipelinesAutoMintedAccountTokensByName(\n api,\n config.tenant,\n j.logicalName,\n env,\n );\n } catch (e) {\n console.warn(\n `Pipelines: destroy reconcile (account API tokens) for logpush ${j.logicalName}:`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n}\n","import type { LogpushJobResourceConfig, TenantMeta } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport { logpushJobStateKey } from \"./logpush-job.resolve.js\";\n\nexport interface LogpushJobStatusRow {\n logicalName: string;\n jobName: string;\n cfJobId?: number;\n status: \"ok\" | \"missing\";\n /** Present when `liveJobs` matched this job id (Cloudflare list/get). */\n cfEnabled?: boolean;\n cfError?: string | null;\n}\n\nexport function logpushJobStatus(\n resources: LogpushJobResourceConfig[],\n tenant: TenantMeta,\n env: string,\n state: StateManager,\n liveJobs?: Array<{\n id: number;\n enabled?: boolean;\n error_message?: string | null;\n }>,\n): LogpushJobStatusRow[] {\n const rows: LogpushJobStatusRow[] = [];\n for (const config of resources) {\n const key = logpushJobStateKey(tenant.id, config.logicalName, env);\n const entry = state.get(key);\n const st = entry?.type === \"logpush_job\" ? entry : undefined;\n const live =\n liveJobs && st?.cfJobId != null\n ? liveJobs.find((j) => j.id === st.cfJobId)\n : undefined;\n rows.push({\n logicalName: config.logicalName,\n jobName: st?.derivedName ?? \"(unknown)\",\n cfJobId: st?.cfJobId,\n status: st ? \"ok\" : \"missing\",\n cfEnabled: live?.enabled,\n cfError: live?.error_message,\n });\n }\n return rows;\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { H as getWorkers, N as wranglerConfigCliArgs, R as CFApiClient, U as loadConfig, V as getConfigBaseDir, f as fetchStackImports, h as StateManager, k as resolveWorkerConfig, u as namingFromConfig, w as stackNameForConfig, z as cloudflareAccountIdFromEnv } from "./tamer.mjs";
|
|
2
|
-
import { o as d1CloudflareDatabaseName, s as d1SkipsProvisionAndMigrate } from "./registry-
|
|
3
|
-
import "./r2S3EmptyBucket-
|
|
4
|
-
import { n as writeWranglerJson, t as generateWranglerConfig } from "./generator-
|
|
5
|
-
import { n as spawnWranglerSync } from "./wranglerSpawn-
|
|
2
|
+
import { o as d1CloudflareDatabaseName, s as d1SkipsProvisionAndMigrate } from "./registry-CTerXUza.mjs";
|
|
3
|
+
import "./r2S3EmptyBucket-DD81ZWQ7.mjs";
|
|
4
|
+
import { n as writeWranglerJson, t as generateWranglerConfig } from "./generator-CbH3UZ3K.mjs";
|
|
5
|
+
import { n as spawnWranglerSync } from "./wranglerSpawn-VkSL0gZd.mjs";
|
|
6
6
|
|
|
7
7
|
//#region src/features/d1/d1.migrate.ts
|
|
8
8
|
/**
|
|
@@ -82,4 +82,4 @@ async function runMigrate(options) {
|
|
|
82
82
|
|
|
83
83
|
//#endregion
|
|
84
84
|
export { runMigrate };
|
|
85
|
-
//# sourceMappingURL=migrate-
|
|
85
|
+
//# sourceMappingURL=migrate-DyrTw9ep.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate-
|
|
1
|
+
{"version":3,"file":"migrate-DyrTw9ep.mjs","names":["databases: string[]"],"sources":["../src/features/d1/d1.migrate.ts","../src/cli/commands/migrate.ts"],"sourcesContent":["import type { D1ResourceConfig, D1StateEntry } from \"./d1.types.js\";\nimport { wranglerConfigCliArgs } from \"../../core/wrangler/wranglerOutFile.js\";\nimport { spawnWranglerSync } from \"../../core/wrangler/wranglerSpawn.js\";\nimport type { NamingEngine } from \"../../core/naming/NamingEngine.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport {\n d1CloudflareDatabaseName,\n d1SkipsProvisionAndMigrate,\n} from \"./d1.ownership.js\";\n\n/**\n * Apply Cloudflare D1 migrations for every D1 in `resources`.\n *\n * Wrangler requires `d1 migrations apply <DATABASE>` (positional). Tamer\n * resolves the derived database name per env (single) or per shard, and\n * invokes wrangler once per database.\n *\n * Adds `--remote` for non-`local` envs so migrations target the live D1, not\n * the local SQLite cache. Skips D1s owned externally or flagged\n * `preserveOnDestroy` (cross-stack).\n */\nexport async function d1Migrate(\n resources: D1ResourceConfig[],\n workerDir: string,\n env: string,\n wranglerOutFile: string = \"wrangler.json\",\n naming: NamingEngine,\n state: StateManager,\n): Promise<void> {\n const d1Configs = resources.filter(\n (r) => r.migrationsDir && !d1SkipsProvisionAndMigrate(r),\n );\n if (d1Configs.length === 0) return;\n\n const databases: string[] = [];\n for (const config of d1Configs) {\n if (config.type === \"single\") {\n databases.push(d1CloudflareDatabaseName(config, env, naming));\n continue;\n }\n const all = state.getAll();\n const shards = Object.values(all).filter(\n (e): e is D1StateEntry =>\n e.type === \"d1_database\" && e.logicalName === config.logicalName,\n );\n for (const s of shards) databases.push(s.derivedName);\n }\n\n for (const dbName of databases) {\n const args = [\n \"wrangler\",\n ...wranglerConfigCliArgs(wranglerOutFile),\n \"d1\",\n \"migrations\",\n \"apply\",\n dbName,\n ];\n if (env !== \"local\") args.push(\"--remote\");\n\n // Wrangler prompts for confirmation when stdin is a TTY. Tamer is an\n // automation path (CI / scripts): skip prompts like `d1 migrations apply`\n // does when `CI` is set or stdin is not a TTY (see wrangler help text).\n const result = spawnWranglerSync(args, {\n cwd: workerDir,\n stdio: [\"ignore\", \"inherit\", \"inherit\"],\n env: { CI: \"1\" },\n });\n\n if (result.status !== 0) {\n throw new Error(\n `wrangler d1 migrations apply failed for ${dbName} (exit ${result.status})`,\n );\n }\n }\n}\n","import { loadConfig, getWorkers, getConfigBaseDir } from \"../../core/config/loader.js\";\nimport { cloudflareAccountIdFromEnv } from \"../../core/cloudflareEnv.js\";\nimport { namingFromConfig } from \"../../core/config/namingFromConfig.js\";\nimport { StateManager } from \"../../core/state/StateManager.js\";\nimport { stackNameForConfig } from \"../../core/state/stackName.js\";\nimport { fetchStackImports } from \"../../core/imports/fetchStackImports.js\";\nimport { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\nimport {\n generateWranglerConfig,\n writeWranglerJson,\n} from \"../../core/wrangler/generator.js\";\nimport { d1Migrate } from \"../../features/d1/index.js\";\n\nexport async function runMigrate(options: {\n worker?: string;\n env?: string;\n configPath?: string;\n}): Promise<void> {\n const workerFilter = options.worker;\n const env = options.env ?? \"local\";\n const configPath = options.configPath;\n\n const config = await loadConfig(configPath, { env });\n const baseDir = getConfigBaseDir();\n const accountId =\n config.account_id ?? cloudflareAccountIdFromEnv();\n if (!accountId) {\n throw new Error(\n \"account_id required in config or CLOUDFLARE_ACCOUNT_ID env var\",\n );\n }\n\n const naming = namingFromConfig(config);\n const api = new CFApiClient(accountId);\n const state = new StateManager(\n config.tenant.id,\n env,\n stackNameForConfig(config),\n );\n await state.hydrate(api);\n const imports = await fetchStackImports(api, config, env);\n\n const workers = await getWorkers(config, baseDir);\n const toRun = workerFilter\n ? workers.filter(([k]) => k === workerFilter)\n : workers;\n\n if (toRun.length === 0) {\n throw new Error(\n workerFilter\n ? `Worker \"${workerFilter}\" not found`\n : \"No workers configured\",\n );\n }\n\n for (const [workerKey, workerConfig] of toRun) {\n const resolved = await resolveWorkerConfig(\n config,\n workerKey,\n workerConfig,\n env,\n baseDir,\n accountId,\n naming,\n state,\n { imports },\n );\n const wranglerConfig = generateWranglerConfig(resolved, state, naming);\n writeWranglerJson(resolved.workerDir, wranglerConfig, resolved.wranglerOutFile);\n\n const resources = resolved.resources ?? {};\n await d1Migrate(\n resources.d1 ?? [],\n resolved.workerDir,\n env,\n resolved.wranglerOutFile,\n naming,\n state,\n );\n console.log(`Migrations applied for ${workerKey}`);\n }\n\n console.log(`Migrate complete for env: ${env}`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,eAAsB,UACpB,WACA,WACA,KACA,kBAA0B,iBAC1B,QACA,OACe;CACf,MAAM,YAAY,UAAU,QACzB,MAAM,EAAE,iBAAiB,CAAC,2BAA2B,EAAE,CACzD;AACD,KAAI,UAAU,WAAW,EAAG;CAE5B,MAAMA,YAAsB,EAAE;AAC9B,MAAK,MAAM,UAAU,WAAW;AAC9B,MAAI,OAAO,SAAS,UAAU;AAC5B,aAAU,KAAK,yBAAyB,QAAQ,KAAK,OAAO,CAAC;AAC7D;;EAEF,MAAM,MAAM,MAAM,QAAQ;EAC1B,MAAM,SAAS,OAAO,OAAO,IAAI,CAAC,QAC/B,MACC,EAAE,SAAS,iBAAiB,EAAE,gBAAgB,OAAO,YACxD;AACD,OAAK,MAAM,KAAK,OAAQ,WAAU,KAAK,EAAE,YAAY;;AAGvD,MAAK,MAAM,UAAU,WAAW;EAC9B,MAAM,OAAO;GACX;GACA,GAAG,sBAAsB,gBAAgB;GACzC;GACA;GACA;GACA;GACD;AACD,MAAI,QAAQ,QAAS,MAAK,KAAK,WAAW;EAK1C,MAAM,SAAS,kBAAkB,MAAM;GACrC,KAAK;GACL,OAAO;IAAC;IAAU;IAAW;IAAU;GACvC,KAAK,EAAE,IAAI,KAAK;GACjB,CAAC;AAEF,MAAI,OAAO,WAAW,EACpB,OAAM,IAAI,MACR,2CAA2C,OAAO,SAAS,OAAO,OAAO,GAC1E;;;;;;ACzDP,eAAsB,WAAW,SAIf;CAChB,MAAM,eAAe,QAAQ;CAC7B,MAAM,MAAM,QAAQ,OAAO;CAC3B,MAAM,aAAa,QAAQ;CAE3B,MAAM,SAAS,MAAM,WAAW,YAAY,EAAE,KAAK,CAAC;CACpD,MAAM,UAAU,kBAAkB;CAClC,MAAM,YACJ,OAAO,cAAc,4BAA4B;AACnD,KAAI,CAAC,UACH,OAAM,IAAI,MACR,iEACD;CAGH,MAAM,SAAS,iBAAiB,OAAO;CACvC,MAAM,MAAM,IAAI,YAAY,UAAU;CACtC,MAAM,QAAQ,IAAI,aAChB,OAAO,OAAO,IACd,KACA,mBAAmB,OAAO,CAC3B;AACD,OAAM,MAAM,QAAQ,IAAI;CACxB,MAAM,UAAU,MAAM,kBAAkB,KAAK,QAAQ,IAAI;CAEzD,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;CACjD,MAAM,QAAQ,eACV,QAAQ,QAAQ,CAAC,OAAO,MAAM,aAAa,GAC3C;AAEJ,KAAI,MAAM,WAAW,EACnB,OAAM,IAAI,MACR,eACI,WAAW,aAAa,eACxB,wBACL;AAGH,MAAK,MAAM,CAAC,WAAW,iBAAiB,OAAO;EAC7C,MAAM,WAAW,MAAM,oBACrB,QACA,WACA,cACA,KACA,SACA,WACA,QACA,OACA,EAAE,SAAS,CACZ;EACD,MAAM,iBAAiB,uBAAuB,UAAU,OAAO,OAAO;AACtE,oBAAkB,SAAS,WAAW,gBAAgB,SAAS,gBAAgB;AAG/E,QAAM,WADY,SAAS,aAAa,EAAE,EAE9B,MAAM,EAAE,EAClB,SAAS,WACT,KACA,SAAS,iBACT,QACA,MACD;AACD,UAAQ,IAAI,0BAA0B,YAAY;;AAGpD,SAAQ,IAAI,6BAA6B,MAAM"}
|