theokit 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/{actions-virtual-module-IY46EEEX.js → actions-virtual-module-DL74V6SR.js} +2 -2
  2. package/dist/actions-virtual-module-DL74V6SR.js.map +1 -0
  3. package/dist/{actions-virtual-module-XNHDNCZ6.js → actions-virtual-module-ZDD54ZZE.js} +2 -2
  4. package/dist/actions-virtual-module-ZDD54ZZE.js.map +1 -0
  5. package/dist/{app-typed-client-OUJO3V5J.js → app-typed-client-4GIKUKRT.js} +4 -4
  6. package/dist/app-typed-client-4GIKUKRT.js.map +1 -0
  7. package/dist/{app-typed-client-YOMWSA3F.js → app-typed-client-4M4NVSK3.js} +3 -3
  8. package/dist/app-typed-client-4M4NVSK3.js.map +1 -0
  9. package/dist/{aws-lambda-GKSTX6HD.js → aws-lambda-SCRGTGF5.js} +5 -5
  10. package/dist/aws-lambda-SCRGTGF5.js.map +1 -0
  11. package/dist/{build-D4J7XECU.js → build-R74EU6BP.js} +12 -12
  12. package/dist/build-R74EU6BP.js.map +1 -0
  13. package/dist/{bun-ISHSNFIO.js → bun-FURRCQMS.js} +7 -7
  14. package/dist/bun-FURRCQMS.js.map +1 -0
  15. package/dist/{chunk-FI7MPTJW.js → chunk-3IOFTG3L.js} +1 -1
  16. package/dist/chunk-3IOFTG3L.js.map +1 -0
  17. package/dist/{chunk-U6TPSW4L.js → chunk-DUKXQNM7.js} +2 -2
  18. package/dist/{chunk-4S2UCILN.js → chunk-FGOX37NB.js} +8 -8
  19. package/dist/chunk-FGOX37NB.js.map +1 -0
  20. package/dist/{chunk-MR7OLIC6.js → chunk-GPF64ENM.js} +2 -2
  21. package/dist/{chunk-4S6YHNG2.js → chunk-GUUWDMR2.js} +2 -2
  22. package/dist/chunk-GUUWDMR2.js.map +1 -0
  23. package/dist/{chunk-WEGALZEU.js → chunk-HDA6M7P4.js} +3 -3
  24. package/dist/{chunk-IEZCAT2I.js → chunk-HNBWZKIQ.js} +6 -5
  25. package/dist/chunk-HNBWZKIQ.js.map +1 -0
  26. package/dist/{chunk-K4M64WD6.js → chunk-IZQN5CDS.js} +4 -4
  27. package/dist/chunk-IZQN5CDS.js.map +1 -0
  28. package/dist/{chunk-M2EY7KDR.js → chunk-JD7AQNJS.js} +15 -15
  29. package/dist/chunk-JD7AQNJS.js.map +1 -0
  30. package/dist/{chunk-5Z2727GV.js → chunk-KJLECZWQ.js} +1 -1
  31. package/dist/chunk-KJLECZWQ.js.map +1 -0
  32. package/dist/{chunk-LC5PYNJP.js → chunk-MWYSRZI4.js} +7 -7
  33. package/dist/chunk-MWYSRZI4.js.map +1 -0
  34. package/dist/{chunk-VBZCVFSA.js → chunk-P2RS3WWY.js} +2 -2
  35. package/dist/{chunk-R7OC6UDK.js → chunk-P3SGM4NR.js} +1 -1
  36. package/dist/chunk-P3SGM4NR.js.map +1 -0
  37. package/dist/{chunk-ABVITXFH.js → chunk-S74FECKW.js} +1 -1
  38. package/dist/{chunk-ABVITXFH.js.map → chunk-S74FECKW.js.map} +1 -1
  39. package/dist/{chunk-COXLEZCN.js → chunk-UVHRD33F.js} +1 -1
  40. package/dist/chunk-UVHRD33F.js.map +1 -0
  41. package/dist/{chunk-27LWMYNK.js → chunk-X32KQH5C.js} +2 -2
  42. package/dist/chunk-X32KQH5C.js.map +1 -0
  43. package/dist/{chunk-2F4FROEQ.js → chunk-X3VNROZ7.js} +17 -17
  44. package/dist/chunk-X3VNROZ7.js.map +1 -0
  45. package/dist/{chunk-Y4H3JNVK.js → chunk-YB5DJJ4U.js} +1 -1
  46. package/dist/chunk-YB5DJJ4U.js.map +1 -0
  47. package/dist/cli/index.js +8 -8
  48. package/dist/{cloudflare-OJGJ7R2D.js → cloudflare-IZ6VJ2OU.js} +7 -7
  49. package/dist/cloudflare-IZ6VJ2OU.js.map +1 -0
  50. package/dist/{deno-deploy-BHWKFTOW.js → deno-deploy-O73NBXIW.js} +6 -6
  51. package/dist/deno-deploy-O73NBXIW.js.map +1 -0
  52. package/dist/{dev-MJVSRZGF.js → dev-ZU46C5AZ.js} +9 -9
  53. package/dist/{dev-emit-HHP2XJXE.js → dev-emit-66FCOS7V.js} +4 -4
  54. package/dist/{dev-emit-5VPRCHOD.js → dev-emit-JBVINGHU.js} +3 -3
  55. package/dist/devtools/entry.js +3 -4
  56. package/dist/devtools/entry.js.map +1 -1
  57. package/dist/{docker-EZDA5FT7.js → docker-AZSJFLP2.js} +3 -3
  58. package/dist/docker-AZSJFLP2.js.map +1 -0
  59. package/dist/{index-DWWEdFh5.d.ts → index-CuHICqb9.d.ts} +2 -2
  60. package/dist/index.d.ts +1 -1
  61. package/dist/index.js +3 -3
  62. package/dist/{info-47VB62MV.js → info-CORLCPEJ.js} +4 -4
  63. package/dist/{netlify-GSNSJGMN.js → netlify-O7G3RLK4.js} +3 -3
  64. package/dist/netlify-O7G3RLK4.js.map +1 -0
  65. package/dist/{node-6I5B6RL3.js → node-LXPQLMVB.js} +3 -3
  66. package/dist/{openapi-NFLSNNVQ.js → openapi-TEOUOIJ2.js} +7 -7
  67. package/dist/registry-KW4F4D2L.js +25 -0
  68. package/dist/{routes-3A4XGIEJ.js → routes-GAXVWJUK.js} +6 -6
  69. package/dist/{schema-KZVOV333.js → schema-Z5VJ2I76.js} +3 -3
  70. package/dist/server/cron/index.js +2 -2
  71. package/dist/server/http/index.d.ts +1 -1
  72. package/dist/server/http/index.js +2 -2
  73. package/dist/server/index.d.ts +3 -3
  74. package/dist/server/index.js +18 -11
  75. package/dist/server/index.js.map +1 -1
  76. package/dist/server/jobs/index.d.ts +1 -1
  77. package/dist/server/jobs/index.js +2 -2
  78. package/dist/server/scan/index.d.ts +1 -1
  79. package/dist/server/scan/index.js +2 -2
  80. package/dist/{services-json-Z2QNGVPW.js → services-json-6SHWMYJH.js} +1 -1
  81. package/dist/services-json-6SHWMYJH.js.map +1 -0
  82. package/dist/{services-typed-client-T7M5VPBP.js → services-typed-client-ASZL7FQV.js} +2 -2
  83. package/dist/{start-CGSZPBAG.js → start-HGUNNF5X.js} +11 -11
  84. package/dist/start-HGUNNF5X.js.map +1 -0
  85. package/dist/{static-QI5NQT2X.js → static-EO73I4C7.js} +6 -6
  86. package/dist/static-EO73I4C7.js.map +1 -0
  87. package/dist/{theo-cloud-RSQCBNXL.js → theo-cloud-HBCYEODQ.js} +2 -2
  88. package/dist/theo-cloud-HBCYEODQ.js.map +1 -0
  89. package/dist/{vercel-TKPZK62A.js → vercel-GSFDMF7K.js} +4 -4
  90. package/dist/vercel-GSFDMF7K.js.map +1 -0
  91. package/dist/vite-plugin/index.js +3 -3
  92. package/dist/{vite-plugin-CR4JDFUQ.js → vite-plugin-TX5H4MB6.js} +8 -8
  93. package/package.json +1 -1
  94. package/dist/actions-virtual-module-IY46EEEX.js.map +0 -1
  95. package/dist/actions-virtual-module-XNHDNCZ6.js.map +0 -1
  96. package/dist/app-typed-client-OUJO3V5J.js.map +0 -1
  97. package/dist/app-typed-client-YOMWSA3F.js.map +0 -1
  98. package/dist/aws-lambda-GKSTX6HD.js.map +0 -1
  99. package/dist/build-D4J7XECU.js.map +0 -1
  100. package/dist/bun-ISHSNFIO.js.map +0 -1
  101. package/dist/chunk-27LWMYNK.js.map +0 -1
  102. package/dist/chunk-2F4FROEQ.js.map +0 -1
  103. package/dist/chunk-4S2UCILN.js.map +0 -1
  104. package/dist/chunk-4S6YHNG2.js.map +0 -1
  105. package/dist/chunk-5Z2727GV.js.map +0 -1
  106. package/dist/chunk-COXLEZCN.js.map +0 -1
  107. package/dist/chunk-FI7MPTJW.js.map +0 -1
  108. package/dist/chunk-IEZCAT2I.js.map +0 -1
  109. package/dist/chunk-K4M64WD6.js.map +0 -1
  110. package/dist/chunk-LC5PYNJP.js.map +0 -1
  111. package/dist/chunk-M2EY7KDR.js.map +0 -1
  112. package/dist/chunk-R7OC6UDK.js.map +0 -1
  113. package/dist/chunk-Y4H3JNVK.js.map +0 -1
  114. package/dist/cloudflare-OJGJ7R2D.js.map +0 -1
  115. package/dist/deno-deploy-BHWKFTOW.js.map +0 -1
  116. package/dist/docker-EZDA5FT7.js.map +0 -1
  117. package/dist/netlify-GSNSJGMN.js.map +0 -1
  118. package/dist/registry-QHEZ3BWB.js +0 -25
  119. package/dist/services-json-Z2QNGVPW.js.map +0 -1
  120. package/dist/start-CGSZPBAG.js.map +0 -1
  121. package/dist/static-QI5NQT2X.js.map +0 -1
  122. package/dist/theo-cloud-RSQCBNXL.js.map +0 -1
  123. package/dist/vercel-TKPZK62A.js.map +0 -1
  124. /package/dist/{chunk-U6TPSW4L.js.map → chunk-DUKXQNM7.js.map} +0 -0
  125. /package/dist/{chunk-MR7OLIC6.js.map → chunk-GPF64ENM.js.map} +0 -0
  126. /package/dist/{chunk-WEGALZEU.js.map → chunk-HDA6M7P4.js.map} +0 -0
  127. /package/dist/{chunk-VBZCVFSA.js.map → chunk-P2RS3WWY.js.map} +0 -0
  128. /package/dist/{dev-MJVSRZGF.js.map → dev-ZU46C5AZ.js.map} +0 -0
  129. /package/dist/{dev-emit-HHP2XJXE.js.map → dev-emit-66FCOS7V.js.map} +0 -0
  130. /package/dist/{dev-emit-5VPRCHOD.js.map → dev-emit-JBVINGHU.js.map} +0 -0
  131. /package/dist/{info-47VB62MV.js.map → info-CORLCPEJ.js.map} +0 -0
  132. /package/dist/{node-6I5B6RL3.js.map → node-LXPQLMVB.js.map} +0 -0
  133. /package/dist/{openapi-NFLSNNVQ.js.map → openapi-TEOUOIJ2.js.map} +0 -0
  134. /package/dist/{registry-QHEZ3BWB.js.map → registry-KW4F4D2L.js.map} +0 -0
  135. /package/dist/{routes-3A4XGIEJ.js.map → routes-GAXVWJUK.js.map} +0 -0
  136. /package/dist/{schema-KZVOV333.js.map → schema-Z5VJ2I76.js.map} +0 -0
  137. /package/dist/{services-typed-client-T7M5VPBP.js.map → services-typed-client-ASZL7FQV.js.map} +0 -0
  138. /package/dist/{vite-plugin-CR4JDFUQ.js.map → vite-plugin-TX5H4MB6.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server/cron/cron-validate.ts","../src/server/cron/define-cron.ts","../src/server/cron/cron-manifest.ts","../src/server/cron/adapter-translators.ts","../src/server/cron/cron-runtime-node.ts","../src/server/cron/cron-scan.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser'\n\n/**\n * Validate a cron schedule string per ADR-0004 — 5-field UTC strict.\n *\n * Accepts:\n * - Standard 5-field expressions: `minute hour dayOfMonth month dayOfWeek`\n * - Step (`*\\/15`), range (`1-5`), list (`MON,TUE,FRI`), wildcards\n *\n * Rejects:\n * - 6-field with seconds (`* * * * * *`)\n * - 7-field with year\n * - Shorthand (`@daily`, `@hourly`, `@yearly`, `@reboot`)\n * - Timezone suffix\n * - Empty / whitespace-only\n * - Malformed grammar\n *\n * Throws on every invalid input. Every error message includes the\n * original input and the fix.\n *\n * @see docs/adr/0004-cron-schedule-5-field-utc-strict.md\n */\nexport function validateCronSchedule(schedule: string): void {\n if (typeof schedule !== 'string') {\n throw new TypeError(`Invalid cron schedule: expected string, got ${typeof schedule}.`)\n }\n const trimmed = schedule.trim()\n if (trimmed.length === 0) {\n throw new Error(\n 'Invalid cron schedule: empty string. TheoKit cron uses 5-field UTC strict format, e.g. \"0 9 * * *\".',\n )\n }\n\n // Shorthand check BEFORE field counting — `@daily` is one \"field\" but\n // semantically not in the 5-field grammar.\n if (trimmed.startsWith('@')) {\n throw new Error(\n `Invalid cron schedule \"${schedule}\": shorthand not supported. ` +\n 'Use the equivalent 5-field form, e.g. \"@daily\" → \"0 0 * * *\", \"@hourly\" → \"0 * * * *\".',\n )\n }\n\n const parts = trimmed.split(/\\s+/)\n if (parts.length !== 5) {\n throw new Error(\n `Invalid cron schedule \"${schedule}\": expected 5 fields ` +\n '(\"minute hour dayOfMonth month dayOfWeek\"), ' +\n `got ${parts.length}. TheoKit treats all schedules as UTC; ` +\n 'for second-precision or timezone, see docs/concepts/crons.md.',\n )\n }\n\n // Final grammar validation via cron-parser. We pass `tz: 'UTC'`\n // explicitly so any embedded timezone hint surfaces as an error.\n try {\n CronExpressionParser.parse(trimmed, { tz: 'UTC' })\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n throw new Error(`Invalid cron schedule \"${schedule}\": ${reason}`)\n }\n}\n","import type { CronDefinition, CronOptions } from './cron-types.js'\nimport { validateCronSchedule } from './cron-validate.js'\n\nconst NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/\n\nfunction validateName(name: string): void {\n if (typeof name !== 'string' || !NAME_RE.test(name)) {\n throw new Error(\n `defineCron: invalid name \"${name}\". ` +\n 'Must be 1-64 chars, lowercase alphanumeric + hyphen, starting with [a-z0-9].',\n )\n }\n}\n\n/**\n * Declare a time-triggered handler. Pure identity helper — no\n * registration side effect; the build-time scanner (T1.3) discovers\n * definitions by walking `server/crons/` and emits a manifest the\n * adapters translate at deploy.\n *\n * @example\n * ```ts\n * // server/crons/morning-summary.ts\n * export default defineCron('morning-summary', {\n * schedule: '0 9 * * *', // 09:00 UTC\n * async handler({ traceId, scheduledAt, signal }) {\n * // ...\n * },\n * })\n * ```\n */\nexport function defineCron(name: string, options: CronOptions): CronDefinition {\n validateName(name)\n validateCronSchedule(options.schedule)\n return {\n name,\n schedule: options.schedule,\n handler: options.handler,\n concurrency: options.concurrency ?? 'forbid',\n }\n}\n","import { writeAtomic } from '../_internal/atomic-write.js'\n\nimport type { CronNode } from './cron-scan.js'\n\nexport const CRON_MANIFEST_SCHEMA_VERSION = 1 as const\n\nexport interface CronManifestEntry {\n readonly name: string\n readonly filePath: string\n readonly schedule: string\n readonly concurrency: 'forbid' | 'allow'\n}\n\nexport interface CronManifest {\n readonly schemaVersion: typeof CRON_MANIFEST_SCHEMA_VERSION\n readonly generatedAt: string\n readonly crons: readonly CronManifestEntry[]\n}\n\n/**\n * Build a `CronManifest` from scanned `CronNode[]`. Filepaths are kept\n * relative to the caller's project root for portability.\n */\nexport function buildCronManifest(nodes: readonly CronNode[], projectRoot?: string): CronManifest {\n const crons: CronManifestEntry[] = nodes.map((n) => ({\n name: n.name,\n filePath: projectRoot ? relativize(n.filePath, projectRoot) : n.filePath,\n schedule: n.schedule,\n concurrency: n.concurrency,\n }))\n return {\n schemaVersion: CRON_MANIFEST_SCHEMA_VERSION,\n generatedAt: new Date().toISOString(),\n crons,\n }\n}\n\n/**\n * Write the cron manifest to `path` atomically (EC-106).\n *\n * Accepts either a pre-built `CronManifest` OR an array of `CronNode[]`\n * (which gets converted via `buildCronManifest`). The atomic-write\n * helper guarantees `path` always contains valid JSON, even under\n * concurrent writes (e.g., dev-server rescan + build).\n */\nexport function writeCronManifest(\n path: string,\n input: readonly CronNode[] | CronManifest,\n projectRoot?: string,\n): void {\n const manifest: CronManifest = isManifest(input) ? input : buildCronManifest(input, projectRoot)\n writeAtomic(path, JSON.stringify(manifest, null, 2))\n}\n\nfunction isManifest(value: unknown): value is CronManifest {\n return typeof value === 'object' && value !== null && 'schemaVersion' in value && 'crons' in value\n}\n\nfunction relativize(absPath: string, root: string): string {\n if (absPath.startsWith(root)) {\n const trimmed = absPath.slice(root.length)\n return trimmed.startsWith('/') ? trimmed.slice(1) : trimmed\n }\n return absPath\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time adapter translators: caller-controlled paths only.\n */\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { writeAtomic } from '../_internal/atomic-write.js'\n\nimport type { CronManifestEntry } from './cron-manifest.js'\n\n/**\n * Thrown when an existing platform-config file (vercel.json, wrangler.toml,\n * serverless.yml) cannot be parsed. Caller must fix the file before\n * re-running `theokit build`. We NEVER silently overwrite a user's config.\n */\nexport class ExistingConfigUnparseableError extends Error {\n readonly code = 'EXISTING_CONFIG_UNPARSEABLE'\n constructor(\n public readonly filePath: string,\n public readonly parseError: string,\n ) {\n super(\n `Existing config \"${filePath}\" could not be parsed: ${parseError}. ` +\n 'Fix or remove the file before re-running build — TheoKit never silently overwrites user configuration.',\n )\n this.name = 'ExistingConfigUnparseableError'\n }\n}\n\n// ──────────────────────────────────────────────────────────\n// Vercel — vercel.json crons[]\n// ──────────────────────────────────────────────────────────\n\ninterface VercelJson {\n crons?: { path: string; schedule: string }[]\n [key: string]: unknown\n}\n\n/**\n * Translate a TheoKit cron manifest into `vercel.json crons[]`.\n *\n * EC-105: existing fields (functions, headers, redirects, rewrites, env,\n * etc.) are preserved verbatim — ONLY the `crons[]` slice is replaced.\n */\nexport function translateCronToVercel(\n vercelJsonPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n let existing: VercelJson = {}\n if (existsSync(vercelJsonPath)) {\n const raw = readFileSync(vercelJsonPath, 'utf8')\n try {\n existing = JSON.parse(raw) as VercelJson\n } catch (err) {\n throw new ExistingConfigUnparseableError(\n vercelJsonPath,\n err instanceof Error ? err.message : String(err),\n )\n }\n }\n const merged: VercelJson = {\n ...existing,\n crons: crons.map((c) => ({\n path: `/api/__crons/${c.name}`,\n schedule: c.schedule,\n })),\n }\n writeAtomic(vercelJsonPath, JSON.stringify(merged, null, 2))\n}\n\n// ──────────────────────────────────────────────────────────\n// Cloudflare — wrangler.toml [triggers] crons\n// ──────────────────────────────────────────────────────────\n\n/**\n * Translate a TheoKit cron manifest into `wrangler.toml [triggers] crons`.\n *\n * EC-105: regex-based mutation that preserves comments + other sections\n * verbatim. Replaces only the `[triggers]` block (or appends if absent).\n */\nexport function translateCronToCloudflare(\n wranglerTomlPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n const schedules = crons.map((c) => `\"${c.schedule}\"`).join(', ')\n const triggersBlock = `[triggers]\\ncrons = [${schedules}]\\n`\n\n if (!existsSync(wranglerTomlPath)) {\n writeAtomic(wranglerTomlPath, triggersBlock)\n return\n }\n\n const existing = readFileSync(wranglerTomlPath, 'utf8')\n\n // Replace existing [triggers] section if present. Strategy: split into\n // line array, find the [triggers] header line, drop subsequent lines\n // until the next section header (line starting with `[`) or EOF.\n const lines = existing.split('\\n')\n const triggersIdx = lines.findIndex((l) => l.trim() === '[triggers]')\n\n let next: string\n if (triggersIdx !== -1) {\n let endIdx = lines.length\n for (let i = triggersIdx + 1; i < lines.length; i++) {\n if (lines[i].trim().startsWith('[')) {\n endIdx = i\n break\n }\n }\n const before = lines.slice(0, triggersIdx).join('\\n')\n const after = lines.slice(endIdx).join('\\n')\n next = `${before}\\n${triggersBlock.trimEnd()}\\n${after}`.replace(/\\n{3,}/g, '\\n\\n')\n } else {\n next = existing.endsWith('\\n')\n ? `${existing}\\n${triggersBlock}`\n : `${existing}\\n\\n${triggersBlock}`\n }\n writeAtomic(wranglerTomlPath, next)\n}\n\n// ──────────────────────────────────────────────────────────\n// AWS Lambda — serverless.yml functions.<fn>.events: schedule\n// ──────────────────────────────────────────────────────────\n\n/**\n * Convert TheoKit's 5-field UTC cron to AWS EventBridge's 6-field format.\n * EventBridge requires `?` in EITHER day-of-month OR day-of-week (not both `*`).\n *\n * Algorithm:\n * - If DOM is \"*\" and DOW is \"*\" → insert ? in DOW (default)\n * - If DOM is \"*\" and DOW is specific → insert ? in DOM\n * - If DOM is specific and DOW is \"*\" → insert ? in DOW\n * - Append \"*\" year field at end.\n */\nexport function convertToAwsCron(schedule: string): string {\n const parts = schedule.trim().split(/\\s+/)\n if (parts.length !== 5) {\n throw new Error(`Invalid 5-field schedule for AWS conversion: \"${schedule}\"`)\n }\n const [minute, hour, dom, month, dow] = parts\n let awsDom = dom\n let awsDow = dow\n if (dom === '*' && dow === '*') {\n awsDow = '?'\n } else if (dom === '*') {\n awsDom = '?'\n } else if (dow === '*') {\n awsDow = '?'\n }\n return `cron(${minute} ${hour} ${awsDom} ${month} ${awsDow} *)`\n}\n\n/**\n * Translate a TheoKit cron manifest into a `serverless.yml` functions\n * map with `events: - schedule: cron(...)` entries.\n *\n * EC-105: appends to `functions:` block, preserving all existing\n * functions/sections. Cron functions are named `cron_<name>` to avoid\n * collision with user-declared functions.\n */\nexport function translateCronToAws(\n serverlessYmlPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n const cronFunctionsYaml = crons\n .map(\n (c) => ` cron_${c.name}:\n handler: .theo/server/crons/${c.name}.handler\n events:\n - schedule: ${convertToAwsCron(c.schedule)}`,\n )\n .join('\\n')\n\n const block = `\\nfunctions:\\n${cronFunctionsYaml}\\n`\n\n if (!existsSync(serverlessYmlPath)) {\n // Minimal serverless.yml scaffold\n writeAtomic(\n serverlessYmlPath,\n `service: theokit-app\\nprovider:\\n name: aws\\n runtime: nodejs22.x\\n${block}`,\n )\n return\n }\n\n const existing = readFileSync(serverlessYmlPath, 'utf8')\n\n // Look for an existing top-level `functions:` block; if present, append our\n // cron entries under it (don't replace user-declared functions).\n const functionsRe = /^functions:\\s*\\n/m\n let next: string\n if (functionsRe.test(existing)) {\n next = existing.replace(functionsRe, (match) => `${match}${cronFunctionsYaml}\\n`)\n } else {\n next = existing.endsWith('\\n') ? `${existing}${block}` : `${existing}\\n${block}`\n }\n writeAtomic(serverlessYmlPath, next)\n}\n\n// ──────────────────────────────────────────────────────────\n// Deno Deploy — Deno.cron registrations\n// ──────────────────────────────────────────────────────────\n\n/**\n * Emit a Deno entry file that registers each cron via `Deno.cron`.\n *\n * Unlike Vercel/CF/AWS, Deno.cron is an in-process runtime API — the\n * entry file is a managed artifact (overwritten each build), not a user\n * config (so EC-105 doesn't apply here).\n */\nexport function translateCronToDeno(entryPath: string, crons: readonly CronManifestEntry[]): void {\n const lines = crons.map(\n (c) =>\n `Deno.cron(\"${c.name}\", \"${c.schedule}\", async () => {\n const mod = await import(\"../${c.filePath}\");\n const def = mod.default;\n await def.handler({\n traceId: crypto.randomUUID().replace(/-/g, \"\"),\n scheduledAt: new Date(),\n signal: AbortSignal.timeout(60_000),\n });\n});`,\n )\n const content = `// AUTOGENERATED by TheoKit cron build — DO NOT EDIT.\n// Source: server/crons/*.ts → .theo/crons.json → this file.\n${lines.join('\\n\\n')}\n`\n writeAtomic(entryPath, content)\n}\n","import { CronExpressionParser } from 'cron-parser'\n\nimport { generateNewTraceContext } from '../observability/trace-context-propagation.js'\n\nimport type { CronContext, CronDefinition } from './cron-types.js'\n\n/**\n * In-memory cron scheduler for `theokit dev` (T1.4).\n *\n * Algorithm:\n * - For each cron, compute `nextFireAt = cron-parser.next()`.\n * - Schedule a `setTimeout(handler, nextFireAt - now)`.\n * - After handler invocation (sync return or Promise scheduled), recompute\n * next fire from CURRENT time (drift-free vs scheduled time).\n *\n * Per-cron isolation (EC-109):\n * - Each cron's handler invocation is fire-and-forget (`void` scheduled).\n * A hanging handler does NOT block the scheduler loop nor other crons.\n * - `concurrency: 'forbid'` (default) tracks an in-flight flag per-cron;\n * subsequent ticks skip + warn while the in-flight flag is set.\n * - `concurrency: 'allow'` runs handlers concurrently — caller's responsibility.\n *\n * Production deploys use platform-native triggers (T1.5 adapter translators);\n * this scheduler exists only for local dev iteration.\n */\n\nexport interface CronScheduler {\n start(): void\n stop(): void\n}\n\ninterface CronJobState {\n readonly def: CronDefinition\n inFlight: boolean\n timer: NodeJS.Timeout | null\n abortController: AbortController | null\n}\n\nexport function createCronScheduler(definitions: readonly CronDefinition[]): CronScheduler {\n const states: CronJobState[] = definitions.map((def) => ({\n def,\n inFlight: false,\n timer: null,\n abortController: null,\n }))\n\n let started = false\n\n const fireAndReschedule = (state: CronJobState, scheduledAt: Date): void => {\n state.timer = null\n\n if (state.inFlight && state.def.concurrency === 'forbid') {\n console.warn(\n `[theokit:cron] \"${state.def.name}\" skipped tick at ${scheduledAt.toISOString()} ` +\n '— previous handler still running (concurrency: forbid).',\n )\n scheduleNext(state)\n return\n }\n\n state.inFlight = true\n state.abortController = new AbortController()\n const traceCtx = generateNewTraceContext()\n const ctx: CronContext = {\n traceId: traceCtx.trace_id,\n scheduledAt,\n signal: state.abortController.signal,\n }\n\n // Fire-and-forget: EC-109 — never await here so a hanging handler\n // can't block the scheduler loop or other crons.\n void Promise.resolve()\n .then(() => state.def.handler(ctx))\n .catch((err: unknown) => {\n console.error(\n `[theokit:cron] \"${state.def.name}\" handler error:`,\n err instanceof Error ? err.message : err,\n )\n })\n .finally(() => {\n state.inFlight = false\n })\n\n scheduleNext(state)\n }\n\n const scheduleNext = (state: CronJobState): void => {\n if (!started) return\n const interval = CronExpressionParser.parse(state.def.schedule, {\n tz: 'UTC',\n currentDate: new Date(),\n })\n const next = interval.next().toDate()\n const delayMs = Math.max(0, next.getTime() - Date.now())\n state.timer = setTimeout(() => {\n fireAndReschedule(state, next)\n }, delayMs)\n // setTimeout returns a Timeout object; in Node, .unref() is available but\n // we DO want this to keep the event loop alive in dev — explicit no-unref.\n }\n\n return {\n start(): void {\n if (started) return\n started = true\n for (const state of states) {\n scheduleNext(state)\n }\n },\n stop(): void {\n started = false\n for (const state of states) {\n if (state.timer) {\n clearTimeout(state.timer)\n state.timer = null\n }\n state.abortController?.abort()\n state.abortController = null\n }\n },\n }\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: caller-controlled directory paths only. No HTTP\n * input ever reaches these fs calls.\n */\nimport { existsSync } from 'node:fs'\nimport { basename } from 'node:path'\n\nimport { importUserModule } from '../../config/import-user-module.js'\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nimport type { CronConcurrencyPolicy, CronDefinition } from './cron-types.js'\n\n/**\n * One discovered cron from build-time scan. The handler is intentionally\n * NOT included — manifest is platform-neutral and consumed by adapters\n * that emit static config. Runtime dispatch loads the handler at fire time.\n */\nexport interface CronNode {\n readonly name: string\n readonly filePath: string\n readonly schedule: string\n readonly concurrency: CronConcurrencyPolicy\n}\n\nexport class DuplicateCronNameError extends Error {\n readonly code = 'DUPLICATE_CRON_NAME'\n constructor(\n public readonly cronName: string,\n public readonly filePaths: readonly string[],\n ) {\n super(\n `Duplicate cron name \"${cronName}\" defined in: ${filePaths.join(', ')}. ` +\n 'Cron names must be unique across server/crons/.',\n )\n this.name = 'DuplicateCronNameError'\n }\n}\n\nconst CRON_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.mjs'])\n\ninterface CronModule {\n default?: unknown\n}\n\nfunction isCronDefinition(value: unknown): value is CronDefinition {\n if (typeof value !== 'object' || value === null) return false\n const def = value as Record<string, unknown>\n return (\n typeof def.name === 'string' &&\n typeof def.schedule === 'string' &&\n typeof def.handler === 'function' &&\n (def.concurrency === 'forbid' || def.concurrency === 'allow')\n )\n}\n\n/**\n * Scan a directory for cron definition files and return the discovered\n * `CronNode[]` sorted by name. Throws `DuplicateCronNameError` on name\n * collision and `Error` on missing default export.\n *\n * Sequential by design — module imports are awaited one-by-one to keep\n * error messages anchored to the file that failed.\n */\nexport async function scanCrons(cronsDir: string): Promise<CronNode[]> {\n if (!existsSync(cronsDir)) return []\n\n const filePaths: string[] = []\n walkSourceFiles(cronsDir, { extensions: CRON_EXTENSIONS }, (p) => {\n const base = basename(p)\n // Skip private helpers (`_helper.ts`) and OS junk (`.DS_Store`).\n if (base.startsWith('_') || base.startsWith('.')) return\n filePaths.push(p)\n })\n\n const nodes: CronNode[] = []\n for (const filePath of filePaths) {\n let mod: CronModule\n try {\n mod = await importUserModule(filePath)\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n throw new Error(`Failed to import cron file \"${filePath}\": ${reason}`)\n }\n const exported = mod.default\n if (!isCronDefinition(exported)) {\n throw new Error(\n `Cron file \"${filePath}\" is missing a valid default export. ` +\n 'Expected `export default defineCron(name, { schedule, handler })`.',\n )\n }\n nodes.push({\n name: exported.name,\n filePath,\n schedule: exported.schedule,\n concurrency: exported.concurrency,\n })\n }\n\n // Dup-name detection\n const byName = new Map<string, string[]>()\n for (const node of nodes) {\n const bucket = byName.get(node.name) ?? []\n bucket.push(node.filePath)\n byName.set(node.name, bucket)\n }\n for (const [name, paths] of byName) {\n if (paths.length > 1) {\n throw new DuplicateCronNameError(name, paths)\n }\n }\n\n return nodes.sort((a, b) => a.name.localeCompare(b.name))\n}\n"],"mappings":"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","names":["CronExpressionParser","CronExpressionParser","existsSync","existsSync"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/node.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Node deploy adapter — derives all write paths from the trusted `cwd`\n * (CLI argument). The `.theo/docker-compose.yml` + `.theo/Caddyfile`\n * outputs use a fixed sub-path. Build-time tool — no HTTP input.\n */\nimport { writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { build as viteBuild } from 'vite'\n\nimport type { TheoConfig } from '../config/schema.js'\nimport { generateCaddyfile, generateComposeYaml, readManifest } from '../services/index.js'\n\nimport type { AdapterBuildContext, DeployAdapter } from './types.js'\n\nexport const nodeAdapter: DeployAdapter = {\n name: 'node',\n\n async build(config: TheoConfig, cwd: string, ctx?: AdapterBuildContext): Promise<void> {\n // T1.1 (architecture-cleanup) — Vite plugin composition is INJECTED via `ctx.makeVitePlugins`.\n // The CLI (which already imports both `adapters/*` and `vite-plugin/*`) composes the\n // Plugin[] and passes it in, eliminating the previous `adapters → vite-plugin` edge\n // (CRITICAL layering inversion per ADR-0001 v2; fixed by ADR-0001 v3).\n if (!ctx?.makeVitePlugins) {\n throw new Error(\n '[adapter-node] build requires ctx.makeVitePlugins (CLI must inject the Vite plugin factory). ' +\n 'This is a framework-internal invariant — see ADR-0001 v3 and docs/plans/architecture-cleanup-plan.md T1.1.',\n )\n }\n\n // Client build — `await` per ADR-0001 v3.1: makeVitePlugins may return\n // a Promise (theokit build uses theoPluginAsync to wire the full chain\n // including @theo/actions virtual module).\n await viteBuild({\n root: cwd,\n plugins: await ctx.makeVitePlugins({ root: cwd, ssr: config.ssr }),\n build: {\n outDir: '.theo/client',\n emptyOutDir: true,\n },\n logLevel: 'info',\n })\n\n // SSR build (only when ssr: true)\n if (config.ssr) {\n // eslint-disable-next-line no-console -- CLI build progress\n console.log('\\n Building SSR...\\n')\n await viteBuild({\n root: cwd,\n plugins: await ctx.makeVitePlugins({ root: cwd, ssr: true }),\n build: {\n ssr: true,\n outDir: '.theo/server',\n emptyOutDir: true,\n rollupOptions: {\n input: '/@theo/entry-server',\n },\n },\n logLevel: 'info',\n })\n }\n\n // Wave 2 (T2.1) — when polyglot services are declared, emit a\n // TheoCloud-shaped docker-compose harness + Caddyfile (with W3C\n // traceparent propagation via Caddy 2.11+ `tracing` directive).\n // Empty manifest → no emission (Wave 1 BC preserved).\n const manifest = readManifest(cwd)\n if (manifest && manifest.services.length > 0) {\n const yaml = generateComposeYaml(manifest, { webPort: config.port })\n const caddyfile = generateCaddyfile(manifest, {\n port: config.port,\n webHost: 'web',\n })\n writeFileSync(join(cwd, '.theo', 'docker-compose.yml'), yaml)\n writeFileSync(join(cwd, '.theo', 'Caddyfile'), caddyfile)\n // eslint-disable-next-line no-console -- CLI build progress\n console.log(\n ` ✓ TheoCloud-shaped harness: .theo/docker-compose.yml + .theo/Caddyfile ` +\n `(${String(manifest.services.length)} service(s))`,\n )\n }\n },\n}\n"],"mappings":";;;;;;;;;AAKA,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AAErB,SAAS,SAAS,iBAAiB;AAO5B,IAAM,cAA6B;AAAA,EACxC,MAAM;AAAA,EAEN,MAAM,MAAM,QAAoB,KAAa,KAA0C;AAKrF,QAAI,CAAC,KAAK,iBAAiB;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAKA,UAAM,UAAU;AAAA,MACd,MAAM;AAAA,MACN,SAAS,MAAM,IAAI,gBAAgB,EAAE,MAAM,KAAK,KAAK,OAAO,IAAI,CAAC;AAAA,MACjE,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,IACZ,CAAC;AAGD,QAAI,OAAO,KAAK;AAEd,cAAQ,IAAI,uBAAuB;AACnC,YAAM,UAAU;AAAA,QACd,MAAM;AAAA,QACN,SAAS,MAAM,IAAI,gBAAgB,EAAE,MAAM,KAAK,KAAK,KAAK,CAAC;AAAA,QAC3D,OAAO;AAAA,UACL,KAAK;AAAA,UACL,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,eAAe;AAAA,YACb,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAMA,UAAM,WAAW,aAAa,GAAG;AACjC,QAAI,YAAY,SAAS,SAAS,SAAS,GAAG;AAC5C,YAAM,OAAO,oBAAoB,UAAU,EAAE,SAAS,OAAO,KAAK,CAAC;AACnE,YAAM,YAAY,kBAAkB,UAAU;AAAA,QAC5C,MAAM,OAAO;AAAA,QACb,SAAS;AAAA,MACX,CAAC;AACD,oBAAc,KAAK,KAAK,SAAS,oBAAoB,GAAG,IAAI;AAC5D,oBAAc,KAAK,KAAK,SAAS,WAAW,GAAG,SAAS;AAExD,cAAQ;AAAA,QACN,kFACM,OAAO,SAAS,SAAS,MAAM,CAAC;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}