@superpanel-sdk/runtime 0.2.3 → 0.7.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.
Files changed (319) hide show
  1. package/README.md +30 -10
  2. package/dist/builder/codegen/emit.d.ts +25 -12
  3. package/dist/builder/codegen/emit.js +339 -78
  4. package/dist/builder/codegen/generate.d.ts +21 -2
  5. package/dist/builder/codegen/generate.js +147 -34
  6. package/dist/builder/company-import-graph.d.ts +2 -0
  7. package/dist/builder/company-import-graph.js +40 -8
  8. package/dist/builder/company-imports.d.ts +29 -0
  9. package/dist/builder/company-imports.js +122 -0
  10. package/dist/builder/compile/compile-sequence.d.ts +61 -8
  11. package/dist/builder/compile/compile-sequence.js +334 -80
  12. package/dist/builder/compile/function-code.d.ts +19 -3
  13. package/dist/builder/compile/function-code.js +123 -45
  14. package/dist/builder/compile/registry.d.ts +7 -3
  15. package/dist/builder/compile/registry.js +30 -27
  16. package/dist/builder/errors.d.ts +9 -0
  17. package/dist/builder/errors.js +17 -0
  18. package/dist/builder/function-params.d.ts +10 -0
  19. package/dist/builder/function-params.js +203 -0
  20. package/dist/builder/load.d.ts +70 -10
  21. package/dist/builder/load.js +258 -67
  22. package/dist/builder/paths.d.ts +9 -1
  23. package/dist/builder/paths.js +32 -2
  24. package/dist/builder/schedules.d.ts +25 -0
  25. package/dist/builder/schedules.js +101 -0
  26. package/dist/builder/slot-schema-validation.d.ts +4 -0
  27. package/dist/builder/slot-schema-validation.js +10 -0
  28. package/dist/builder/typecheck.d.ts +8 -0
  29. package/dist/builder/typecheck.js +102 -0
  30. package/dist/builder/types.d.ts +127 -19
  31. package/dist/builder/types.js +21 -1
  32. package/dist/cli/auth/auth.d.ts +37 -0
  33. package/dist/cli/auth/auth.js +211 -0
  34. package/dist/cli/auth/backend-url.d.ts +5 -0
  35. package/dist/cli/auth/backend-url.js +25 -0
  36. package/dist/cli/auth/clipboard.d.ts +9 -0
  37. package/dist/cli/auth/clipboard.js +38 -0
  38. package/dist/cli/auth/http.d.ts +20 -0
  39. package/dist/cli/auth/http.js +27 -0
  40. package/dist/cli/auth/index.d.ts +10 -0
  41. package/dist/cli/auth/index.js +27 -0
  42. package/dist/cli/auth/progress.d.ts +12 -0
  43. package/dist/cli/auth/progress.js +15 -0
  44. package/dist/cli/auth/serve-login.d.ts +8 -0
  45. package/dist/cli/auth/serve-login.js +24 -0
  46. package/dist/cli/calls/index.d.ts +177 -0
  47. package/dist/cli/calls/index.js +96 -0
  48. package/dist/cli/compile/index.js +3 -0
  49. package/dist/cli/config.d.ts +10 -0
  50. package/dist/cli/config.js +52 -0
  51. package/dist/cli/debug/index.d.ts +31 -0
  52. package/dist/cli/debug/index.js +119 -0
  53. package/dist/cli/exec/index.d.ts +39 -0
  54. package/dist/cli/exec/index.js +138 -0
  55. package/dist/cli/exec/session.d.ts +48 -0
  56. package/dist/cli/exec/session.js +82 -0
  57. package/dist/cli/exec/trace.d.ts +18 -0
  58. package/dist/cli/exec/trace.js +52 -0
  59. package/dist/cli/index.js +112 -9
  60. package/dist/cli/logs/index.d.ts +47 -0
  61. package/dist/cli/logs/index.js +113 -0
  62. package/dist/cli/mobile/index.d.ts +12 -0
  63. package/dist/cli/mobile/index.js +405 -0
  64. package/dist/cli/serve/index.d.ts +109 -3
  65. package/dist/cli/serve/index.js +506 -118
  66. package/dist/cli/turns/index.d.ts +105 -0
  67. package/dist/cli/turns/index.js +128 -0
  68. package/dist/cli/turns/run.d.ts +13 -0
  69. package/dist/cli/turns/run.js +57 -0
  70. package/dist/cli/turns/shadow.d.ts +112 -0
  71. package/dist/cli/turns/shadow.js +54 -0
  72. package/dist/cli/type-gen/index.js +9 -9
  73. package/dist/compiler.d.ts +7 -0
  74. package/dist/compiler.js +7 -0
  75. package/dist/compute-host/compute-host.d.ts +14 -23
  76. package/dist/compute-host/compute-host.js +18 -30
  77. package/dist/compute-host/index.d.ts +2 -4
  78. package/dist/compute-host/index.js +1 -2
  79. package/dist/compute-host/source-compute-host.d.ts +10 -10
  80. package/dist/compute-host/source-compute-host.js +33 -40
  81. package/dist/core/case-orchestrator/contact-hours.d.ts +70 -0
  82. package/dist/core/case-orchestrator/contact-hours.js +127 -0
  83. package/dist/core/case-orchestrator/index.d.ts +3 -0
  84. package/dist/core/case-orchestrator/index.js +3 -0
  85. package/dist/core/case-orchestrator/render.d.ts +75 -0
  86. package/dist/core/case-orchestrator/render.js +244 -0
  87. package/dist/core/case-orchestrator/time.d.ts +11 -0
  88. package/dist/core/case-orchestrator/time.js +29 -0
  89. package/dist/core/case-orchestrator/types.d.ts +708 -0
  90. package/dist/core/case-orchestrator/types.js +42 -0
  91. package/dist/core/context/call.d.ts +291 -0
  92. package/dist/core/context/call.js +94 -0
  93. package/dist/core/context/channels.d.ts +60 -0
  94. package/dist/core/context/channels.js +35 -0
  95. package/dist/core/context/contact.d.ts +96 -6
  96. package/dist/core/context/contact.js +48 -2
  97. package/dist/core/context/email-message.d.ts +198 -0
  98. package/dist/core/context/email-message.js +86 -0
  99. package/dist/core/context/email.d.ts +109 -0
  100. package/dist/core/context/email.js +90 -0
  101. package/dist/core/context/exchanges.d.ts +167 -0
  102. package/dist/core/context/exchanges.js +81 -0
  103. package/dist/core/context/instances.d.ts +307 -0
  104. package/dist/core/context/instances.js +137 -0
  105. package/dist/core/context/kv.d.ts +113 -0
  106. package/dist/core/context/kv.js +60 -0
  107. package/dist/core/context/platform-registry.d.ts +544 -4
  108. package/dist/core/context/platform-registry.js +14 -2
  109. package/dist/core/context/platform.d.ts +2 -1
  110. package/dist/core/context/sms.d.ts +177 -0
  111. package/dist/core/context/sms.js +77 -0
  112. package/dist/core/context/types.d.ts +79 -48
  113. package/dist/core/context/types.js +1 -7
  114. package/dist/core/conversation/index.d.ts +85 -0
  115. package/dist/core/conversation/index.js +133 -0
  116. package/dist/core/functions/errors.d.ts +74 -0
  117. package/dist/core/functions/errors.js +26 -0
  118. package/dist/core/functions/events.d.ts +192 -0
  119. package/dist/core/functions/events.js +23 -0
  120. package/dist/core/functions/schedules.d.ts +50 -0
  121. package/dist/core/functions/schedules.js +20 -0
  122. package/dist/core/functions/types.d.ts +29 -73
  123. package/dist/core/functions/types.js +1 -4
  124. package/dist/core/office-hours/index.d.ts +39 -0
  125. package/dist/core/office-hours/index.js +187 -0
  126. package/dist/core/page/define.d.ts +904 -0
  127. package/dist/core/page/define.js +518 -0
  128. package/dist/core/page/envelope.d.ts +55 -0
  129. package/dist/core/page/envelope.js +4 -0
  130. package/dist/core/page/fns.d.ts +448 -0
  131. package/dist/core/page/fns.js +539 -0
  132. package/dist/core/page/memory-reader.d.ts +27 -0
  133. package/dist/core/page/memory-reader.js +299 -0
  134. package/dist/core/page/ops.d.ts +58 -0
  135. package/dist/core/page/ops.js +220 -0
  136. package/dist/core/page/page-host.d.ts +20 -0
  137. package/dist/core/page/page-host.js +57 -0
  138. package/dist/core/page/records-query.d.ts +234 -0
  139. package/dist/core/page/records-query.js +780 -0
  140. package/dist/core/page/render.d.ts +295 -0
  141. package/dist/core/page/render.js +1060 -0
  142. package/dist/core/page/sandbox.d.ts +85 -0
  143. package/dist/core/page/sandbox.js +357 -0
  144. package/dist/core/page/time-format.d.ts +46 -0
  145. package/dist/core/page/time-format.js +125 -0
  146. package/dist/core/page/timeline.d.ts +40 -0
  147. package/dist/core/page/timeline.js +2 -0
  148. package/dist/core/page/typed.d.ts +648 -0
  149. package/dist/core/page/typed.js +2 -0
  150. package/dist/core/page/workspace.d.ts +57 -0
  151. package/dist/core/page/workspace.js +266 -0
  152. package/dist/core/party/index.d.ts +3 -0
  153. package/dist/core/party/index.js +3 -0
  154. package/dist/core/party/schema.d.ts +208 -0
  155. package/dist/core/party/schema.js +113 -0
  156. package/dist/core/party/types.d.ts +35 -0
  157. package/dist/core/party/types.js +15 -0
  158. package/dist/core/sequence/define.d.ts +56 -0
  159. package/dist/core/sequence/define.js +10 -0
  160. package/dist/core/sequence/index.d.ts +63 -0
  161. package/dist/core/sequence/index.js +231 -0
  162. package/dist/core/slot/binding.d.ts +40 -0
  163. package/dist/core/slot/binding.js +2 -0
  164. package/dist/core/slot/company-metadata.d.ts +102 -0
  165. package/dist/core/slot/company-metadata.js +536 -0
  166. package/dist/core/slot/compile-schema.d.ts +19 -13
  167. package/dist/core/slot/compile-schema.js +51 -248
  168. package/dist/core/slot/contact-timezone.d.ts +110 -0
  169. package/dist/core/slot/contact-timezone.js +186 -0
  170. package/dist/core/slot/field.d.ts +2 -25
  171. package/dist/core/slot/field.js +0 -71
  172. package/dist/core/slot/id-ref.d.ts +17 -2
  173. package/dist/core/slot/id-ref.js +16 -8
  174. package/dist/core/slot/index.d.ts +12 -13
  175. package/dist/core/slot/index.js +11 -12
  176. package/dist/core/slot/naming.d.ts +15 -0
  177. package/dist/core/slot/naming.js +96 -0
  178. package/dist/core/slot/platform-fields.d.ts +119 -0
  179. package/dist/core/slot/platform-fields.js +121 -0
  180. package/dist/core/slot/ref.d.ts +16 -0
  181. package/dist/core/slot/ref.js +14 -0
  182. package/dist/core/slot/row.d.ts +2 -9
  183. package/dist/core/slot/row.js +13 -23
  184. package/dist/core/slot/schema-compat.d.ts +17 -0
  185. package/dist/core/slot/schema-compat.js +103 -0
  186. package/dist/core/slot/schema.d.ts +353 -76
  187. package/dist/core/slot/schema.js +163 -10
  188. package/dist/core/slot/types.d.ts +30 -103
  189. package/dist/core/slot/value.d.ts +28 -3
  190. package/dist/core/slot/value.js +93 -57
  191. package/dist/dev-overlay/protocol.d.ts +74 -8
  192. package/dist/fn-context/build-context.d.ts +3 -1
  193. package/dist/fn-context/build-context.js +26 -10
  194. package/dist/fn-context/fake/empty-api.js +9 -28
  195. package/dist/fn-context/fake/index.d.ts +13 -4
  196. package/dist/fn-context/fake/index.js +29 -2
  197. package/dist/fn-context/fn-params.d.ts +3 -0
  198. package/dist/fn-context/fn-params.js +10 -0
  199. package/dist/fn-context/index.d.ts +5 -5
  200. package/dist/fn-context/index.js +4 -4
  201. package/dist/fn-context/log.js +2 -2
  202. package/dist/fn-context/make-ctx.d.ts +11 -0
  203. package/dist/fn-context/make-ctx.js +20 -9
  204. package/dist/fn-context/rpc/case-orchestrator-tools.d.ts +32 -0
  205. package/dist/fn-context/rpc/case-orchestrator-tools.js +12 -0
  206. package/dist/fn-context/rpc/errors.d.ts +13 -0
  207. package/dist/fn-context/rpc/errors.js +22 -0
  208. package/dist/fn-context/rpc/index.d.ts +4 -2
  209. package/dist/fn-context/rpc/index.js +4 -2
  210. package/dist/fn-context/rpc/integration/index.d.ts +15 -5
  211. package/dist/fn-context/rpc/integration/index.js +1 -1
  212. package/dist/fn-context/rpc/integration/list-surfaces.d.ts +21 -0
  213. package/dist/fn-context/rpc/integration/{list-bindings.js → list-surfaces.js} +4 -4
  214. package/dist/fn-context/rpc/log.d.ts +11 -1
  215. package/dist/fn-context/rpc/platform.d.ts +13 -3
  216. package/dist/fn-context/rpc/platform.js +1 -1
  217. package/dist/fn-context/rpc/records.d.ts +85 -0
  218. package/dist/fn-context/rpc/records.js +24 -0
  219. package/dist/fn-context/rpc/trpc-client.d.ts +44 -2
  220. package/dist/fn-context/rpc/trpc-client.js +222 -12
  221. package/dist/fn-context/rpc/trpc-init.d.ts +21 -3
  222. package/dist/fn-context/rpc/trpc-init.js +10 -4
  223. package/dist/fn-context/rpc/trpc-router-factory.d.ts +111 -7
  224. package/dist/fn-context/rpc/trpc-router-factory.js +2 -0
  225. package/dist/fn-context/run-scope.d.ts +4 -0
  226. package/dist/fn-context/run-scope.js +9 -0
  227. package/dist/fn-context/types.d.ts +52 -5
  228. package/dist/index.d.ts +36 -46
  229. package/dist/index.js +30 -33
  230. package/dist/logger.d.ts +11 -0
  231. package/dist/logger.js +49 -0
  232. package/dist/platform.d.ts +75 -0
  233. package/dist/platform.js +57 -0
  234. package/dist/testing-compile-child.d.ts +2 -0
  235. package/dist/testing-compile-child.js +20 -0
  236. package/dist/testing.d.ts +368 -0
  237. package/dist/testing.js +439 -0
  238. package/dist/voice-service/actions.d.ts +50 -6
  239. package/dist/voice-service/index.d.ts +2 -0
  240. package/dist/voice-service/index.js +2 -0
  241. package/dist/voice-service/protocol.d.ts +604 -0
  242. package/dist/voice-service/protocol.js +67 -0
  243. package/dist/voice-service/types.d.ts +55 -8
  244. package/dist/worker.d.ts +11 -0
  245. package/dist/worker.js +11 -0
  246. package/package.json +38 -15
  247. package/dist/builder/compile/compile-agenda.d.ts +0 -15
  248. package/dist/builder/compile/compile-agenda.js +0 -64
  249. package/dist/builder/merge.d.ts +0 -4
  250. package/dist/builder/merge.js +0 -19
  251. package/dist/compute-host/read-only-cache.d.ts +0 -20
  252. package/dist/compute-host/read-only-cache.js +0 -61
  253. package/dist/core/context/db.d.ts +0 -69
  254. package/dist/core/context/db.js +0 -44
  255. package/dist/core/context/docusign.d.ts +0 -134
  256. package/dist/core/context/docusign.js +0 -108
  257. package/dist/core/context/filevine.d.ts +0 -323
  258. package/dist/core/context/filevine.js +0 -254
  259. package/dist/core/context/http.d.ts +0 -9
  260. package/dist/core/context/http.js +0 -4
  261. package/dist/core/context/hubspot.d.ts +0 -250
  262. package/dist/core/context/hubspot.js +0 -191
  263. package/dist/core/context/twilio.d.ts +0 -50
  264. package/dist/core/context/twilio.js +0 -42
  265. package/dist/core/functions/define/case-context.d.ts +0 -14
  266. package/dist/core/functions/define/case-context.js +0 -15
  267. package/dist/core/functions/define/compute.d.ts +0 -7
  268. package/dist/core/functions/define/compute.js +0 -17
  269. package/dist/core/functions/define/custom.d.ts +0 -4
  270. package/dist/core/functions/define/custom.js +0 -8
  271. package/dist/core/functions/define/system.d.ts +0 -7
  272. package/dist/core/functions/define/system.js +0 -12
  273. package/dist/core/functions/func-ref.d.ts +0 -16
  274. package/dist/core/functions/func-ref.js +0 -20
  275. package/dist/core/slot/deps.d.ts +0 -15
  276. package/dist/core/slot/deps.js +0 -88
  277. package/dist/core/slot/derived.d.ts +0 -32
  278. package/dist/core/slot/derived.js +0 -115
  279. package/dist/core/slot/platform-session-fields.d.ts +0 -38
  280. package/dist/core/slot/platform-session-fields.js +0 -48
  281. package/dist/core/slot/sf-schema.d.ts +0 -23
  282. package/dist/core/slot/sf-schema.js +0 -125
  283. package/dist/core/slot/sf-validate.d.ts +0 -14
  284. package/dist/core/slot/sf-validate.js +0 -132
  285. package/dist/core/slot/snapshot.d.ts +0 -18
  286. package/dist/core/slot/snapshot.js +0 -138
  287. package/dist/core/slot/write.d.ts +0 -48
  288. package/dist/core/slot/write.js +0 -426
  289. package/dist/core/stage/builders.d.ts +0 -54
  290. package/dist/core/stage/builders.js +0 -31
  291. package/dist/core/stage/condition.d.ts +0 -17
  292. package/dist/core/stage/condition.js +0 -198
  293. package/dist/core/stage/drive.d.ts +0 -43
  294. package/dist/core/stage/drive.js +0 -78
  295. package/dist/core/stage/finish-when.d.ts +0 -20
  296. package/dist/core/stage/finish-when.js +0 -82
  297. package/dist/core/stage/flatten.d.ts +0 -27
  298. package/dist/core/stage/flatten.js +0 -466
  299. package/dist/core/stage/index.d.ts +0 -15
  300. package/dist/core/stage/index.js +0 -13
  301. package/dist/core/stage/interrupts.d.ts +0 -28
  302. package/dist/core/stage/interrupts.js +0 -120
  303. package/dist/core/stage/ivr-fields.d.ts +0 -9
  304. package/dist/core/stage/ivr-fields.js +0 -75
  305. package/dist/core/stage/simulate.d.ts +0 -24
  306. package/dist/core/stage/simulate.js +0 -33
  307. package/dist/core/stage/transfer-fields.d.ts +0 -19
  308. package/dist/core/stage/transfer-fields.js +0 -129
  309. package/dist/core/stage/transfer-spec.d.ts +0 -48
  310. package/dist/core/stage/transfer-spec.js +0 -113
  311. package/dist/core/stage/types.d.ts +0 -208
  312. package/dist/core/stage/types.js +0 -2
  313. package/dist/core/stage/validate.d.ts +0 -11
  314. package/dist/core/stage/validate.js +0 -330
  315. package/dist/core/stage/walk.d.ts +0 -39
  316. package/dist/core/stage/walk.js +0 -119
  317. package/dist/fn-context/rpc/integration/list-bindings.d.ts +0 -21
  318. package/dist/fn-context/system-args.d.ts +0 -6
  319. package/dist/fn-context/system-args.js +0 -14
@@ -1,8 +1,13 @@
1
1
  import { existsSync, readdirSync, statSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
- import { resolveCaseContextViewFn } from '../core/functions/define/case-context.js';
5
- import { isAgendaTree, isSequenceAgendas } from '../core/stage/flatten.js';
4
+ import { runInNewContext } from 'node:vm';
5
+ import { ORCHESTRATOR_FUNCTION } from '../core/case-orchestrator/types.js';
6
+ import { RESOLVE_FUNCTION } from '../core/party/types.js';
7
+ import { isSystemEventName, } from '../core/functions/events.js';
8
+ import { parseOfficeHours } from '../core/office-hours/index.js';
9
+ import { sessionSchemaIssues } from '../core/slot/company-metadata.js';
10
+ import { companySchemaSchema, EMPTY_SESSION_SCHEMA, isCompanySchema, REMOVED_DERIVED_KEYS, REMOVED_IDENTIFIER_KEY, REMOVED_NULLABLE_KEY, REMOVED_SOURCE_KEY, } from '../core/slot/schema.js';
6
11
  import { companyImportGraphHref } from './company-import-graph.js';
7
12
  import { findTsModule } from './paths.js';
8
13
  async function importModule(filePath) {
@@ -14,37 +19,35 @@ function isFuncMap(value) {
14
19
  if (value == null || typeof value !== 'object' || Array.isArray(value)) {
15
20
  return false;
16
21
  }
17
- for (const entry of Object.values(value)) {
18
- if (entry == null || typeof entry !== 'object' || Array.isArray(entry)) {
19
- return false;
22
+ for (const [name, entry] of Object.entries(value)) {
23
+ if (typeof entry === 'function')
24
+ continue;
25
+ // A `defineCaseFunc(…)` / `defineNoCaseFunc(…)` result: functions are plain `(ctx, args)` now (38 §2.1).
26
+ if (entry != null && typeof entry === 'object' && 'main' in entry) {
27
+ throw new Error(`functions.${name}: write it as a plain function (ctx, args), its parameters its args type (docs 38; AGENTS.md, "Functions")`);
20
28
  }
21
- const kind = entry.kind;
22
- if (kind !== 'compute' && kind !== 'boolCompute' && kind !== 'custom' && kind !== 'system') {
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ function isEventHandlers(value) {
34
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
35
+ return false;
36
+ }
37
+ for (const [name, handler] of Object.entries(value)) {
38
+ if (!isSystemEventName(name) || typeof handler !== 'function')
23
39
  return false;
24
- }
25
40
  }
26
41
  return true;
27
42
  }
28
43
  function isPlainObject(value) {
29
44
  return value != null && typeof value === 'object' && !Array.isArray(value);
30
45
  }
31
- function looksLikeTable(value) {
32
- return isPlainObject(value) && isPlainObject(value.fields);
33
- }
34
- function isCompanySchema(value) {
35
- if (!isPlainObject(value))
36
- return false;
37
- const entries = Object.values(value);
38
- return entries.length > 0 && entries.every(looksLikeTable);
39
- }
40
- function isSequenceSlots(value) {
41
- return isPlainObject(value) && Object.values(value).every(looksLikeTable);
42
- }
43
46
  /**
44
47
  * Resolve the config object from a dynamically imported module.
45
48
  *
46
49
  * Accepted authoring styles (first match wins):
47
- * 1. Named export — `export const slots = { session, … }`
50
+ * 1. Named export — `export const companySchema = { … }`
48
51
  * 2. Default export is the config
49
52
  * 3. Default export is a bag that holds a named key
50
53
  */
@@ -67,7 +70,17 @@ function pickExport(mod, names, label, isConfig) {
67
70
  }
68
71
  throw new Error(`Could not find ${label} export (${names.join(' | ')}) in module`);
69
72
  }
70
- /** Sequence ids that have a slots.ts / slots/index.ts module. */
73
+ /**
74
+ * A sequence's leftover `slots.ts` (or `slots/index.ts`): the sequence layer has no schema any more
75
+ * (15 §10.17 第 1 条). Its tables and fields are the company's; how a cell is filled is the page's
76
+ * `$extraction`.
77
+ */
78
+ export function assertNoSlotsModule(sequenceDir, sequenceId) {
79
+ if (findTsModule(sequenceDir, 'slots') === undefined)
80
+ return;
81
+ throw new Error(`sequences/${sequenceId}/slots.ts is no longer supported (slots-removed): delete it. Every sequence reads all of the company schema; write how to fill a field on the page in its $extraction, and session fields in schema/session.ts`);
82
+ }
83
+ /** Sequence ids: the directories under `sequences/` with an `index.ts` (`defineSequence`). */
71
84
  export function listSequenceIds(repoRoot) {
72
85
  const sequencesDir = path.join(repoRoot, 'sequences');
73
86
  if (!existsSync(sequencesDir)) {
@@ -77,7 +90,7 @@ export function listSequenceIds(repoRoot) {
77
90
  .filter((name) => {
78
91
  try {
79
92
  return (statSync(path.join(sequencesDir, name)).isDirectory() &&
80
- findTsModule(path.join(sequencesDir, name), 'slots') !== undefined);
93
+ findTsModule(path.join(sequencesDir, name), 'index') !== undefined);
81
94
  }
82
95
  catch {
83
96
  return false;
@@ -85,13 +98,109 @@ export function listSequenceIds(repoRoot) {
85
98
  })
86
99
  .sort();
87
100
  }
101
+ /**
102
+ * Leftover removed keys, as `table.field: remove …`: derived fields (13-data-model-v1 §21) and
103
+ * `source` (19-page-writable-cells).
104
+ */
105
+ function leftoverDerivedKeys(value) {
106
+ const leftovers = [];
107
+ if (!isPlainObject(value))
108
+ return leftovers;
109
+ for (const [tableName, table] of Object.entries(value)) {
110
+ const fields = isPlainObject(table) && isPlainObject(table.fields) ? table.fields : {};
111
+ for (const [fieldName, field] of Object.entries(fields)) {
112
+ const keys = isPlainObject(field) ? REMOVED_DERIVED_KEYS.filter((key) => Object.hasOwn(field, key)) : [];
113
+ if (keys.length > 0) {
114
+ leftovers.push(`${tableName}.${fieldName}: remove ${keys.join(', ')} (derived fields were removed)`);
115
+ }
116
+ if (isPlainObject(field) && Object.hasOwn(field, REMOVED_SOURCE_KEY)) {
117
+ leftovers.push(`${tableName}.${fieldName}: remove source (source-removed: a page decides what the AI may write; field(...) opens a cell)`);
118
+ }
119
+ if (isPlainObject(field) && Object.hasOwn(field, REMOVED_NULLABLE_KEY)) {
120
+ leftovers.push(`${tableName}.${fieldName}: remove nullable (nullable-removed: every field starts empty and can be cleared; a page's required says when one must be filled)`);
121
+ }
122
+ if (isPlainObject(field) && Object.hasOwn(field, REMOVED_IDENTIFIER_KEY)) {
123
+ leftovers.push(`${tableName}.${fieldName}: remove identifier (identifier-removed: resolve.ts decides who a conversation is with)`);
124
+ }
125
+ }
126
+ }
127
+ return leftovers;
128
+ }
129
+ const COMPANY_SCHEMA_EXPORTS = ['companySchema', 'schema'];
130
+ /**
131
+ * Everything the module may offer as its company schema, in {@link pickExport}'s order: the named
132
+ * exports, the default export's named keys, then the default export itself.
133
+ */
134
+ function companySchemaCandidates(mod) {
135
+ const candidates = COMPANY_SCHEMA_EXPORTS.map((name) => mod[name]);
136
+ const defaultExport = Object.hasOwn(mod, 'default') ? mod.default : undefined;
137
+ if (isPlainObject(defaultExport))
138
+ candidates.push(...COMPANY_SCHEMA_EXPORTS.map((name) => defaultExport[name]));
139
+ candidates.push(defaultExport);
140
+ return candidates.filter((candidate) => candidate !== undefined);
141
+ }
142
+ /**
143
+ * Why none of the candidates parses: the leftover derived keys of the first candidate that has any
144
+ * (a default-exported schema with a table named `schema` is a candidate both as a whole and as that
145
+ * table, so taking the first candidate alone could pick the table), otherwise the first candidate's
146
+ * first Zod issues.
147
+ */
148
+ function companySchemaProblems(candidates) {
149
+ for (const candidate of candidates) {
150
+ const leftovers = leftoverDerivedKeys(candidate);
151
+ if (leftovers.length > 0)
152
+ return leftovers;
153
+ }
154
+ const issues = companySchemaSchema.safeParse(candidates[0]).error?.issues ?? [];
155
+ return issues.slice(0, 5).map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`);
156
+ }
88
157
  export async function loadCompanySchema(schemaPath) {
89
158
  const mod = await importModule(schemaPath);
90
- return pickExport(mod, ['companySchema', 'schema'], 'company schema', isCompanySchema);
159
+ try {
160
+ return pickExport(mod, COMPANY_SCHEMA_EXPORTS, 'company schema', isCompanySchema);
161
+ }
162
+ catch (error) {
163
+ // Say what is wrong with the schema it does export, in whichever supported form.
164
+ const candidates = companySchemaCandidates(mod);
165
+ if (candidates.length === 0)
166
+ throw error;
167
+ throw new Error(`Invalid company schema in ${schemaPath}: ${companySchemaProblems(candidates).join('; ')}`);
168
+ }
169
+ }
170
+ /**
171
+ * The company's session fields from `schema/session.ts` (or `schema/session/index.ts`), its
172
+ * `sessionSchema` export (11-remove-sequence-schema §3.1). No file: sessions have no fields.
173
+ */
174
+ export async function loadSessionSchema(repoRoot) {
175
+ const filePath = findTsModule(path.join(repoRoot, 'schema'), 'session');
176
+ if (!filePath)
177
+ return EMPTY_SESSION_SCHEMA;
178
+ const mod = await importModule(filePath);
179
+ if (!Object.hasOwn(mod, 'sessionSchema')) {
180
+ throw new Error(`Could not find sessionSchema export in ${filePath}`);
181
+ }
182
+ const issues = sessionSchemaIssues(mod.sessionSchema);
183
+ if (issues.length > 0)
184
+ throw new Error(issues.map((issue) => issue.message).join('; '));
185
+ return mod.sessionSchema;
91
186
  }
92
- export async function loadSequenceSlots(slotsPath) {
93
- const mod = await importModule(slotsPath);
94
- return pickExport(mod, ['slots'], 'sequence slots', isSequenceSlots);
187
+ /**
188
+ * Sequence office hours from `sequences/<id>/office-hours.ts`. Missing file is `null` (always open).
189
+ * A present file that does not export a valid `officeHours` fails compile.
190
+ */
191
+ export async function loadOfficeHours(sequenceDir) {
192
+ const filePath = findTsModule(sequenceDir, 'office-hours');
193
+ if (!filePath)
194
+ return null;
195
+ const mod = await importModule(filePath);
196
+ if (!Object.hasOwn(mod, 'officeHours')) {
197
+ throw new Error(`Could not find officeHours export in ${filePath}`);
198
+ }
199
+ const parsed = parseOfficeHours(mod.officeHours);
200
+ if (!parsed.ok) {
201
+ throw new Error(`office-hours.ts: ${parsed.issues.join('; ')}`);
202
+ }
203
+ return parsed.value;
95
204
  }
96
205
  /**
97
206
  * Load sequence `functions` map from functions.ts or functions/index.ts.
@@ -115,55 +224,137 @@ export async function loadSequenceFunctions(functionsPath) {
115
224
  throw new Error(`Could not find functions export (functions | default) as a FuncMap in ${functionsPath}`);
116
225
  }
117
226
  /**
118
- * Load optional `caseContextView` from view.ts / view/index.ts.
227
+ * Load optional sequence `events` map from events.ts / events/index.ts.
228
+ * Keys must be platform event names; values are plain handler functions.
119
229
  */
120
- export async function loadSequenceCaseContextView(viewPath) {
121
- const mod = await importModule(viewPath);
122
- const fromNamed = resolveCaseContextViewFn(mod.caseContextView);
123
- if (fromNamed)
124
- return fromNamed;
230
+ export async function loadSequenceEvents(eventsPath) {
231
+ const mod = await importModule(eventsPath);
232
+ for (const handlers of [mod.events, mod.default, mod.default?.events]) {
233
+ if (isPlainObject(handlers) && Object.hasOwn(handlers, 'webhook.received')) {
234
+ throw new Error(`${eventsPath}: webhook.received is no longer a sequence event; export received from ${WEBHOOK_MODULE}.ts at the repository root (22 §9)`);
235
+ }
236
+ }
237
+ if (Object.hasOwn(mod, 'events')) {
238
+ if (isEventHandlers(mod.events))
239
+ return mod.events;
240
+ throw new Error(`events export in ${eventsPath} has unknown event names or non-function handlers`);
241
+ }
125
242
  const defaultExport = Object.hasOwn(mod, 'default') ? mod.default : undefined;
126
- const fromDefault = resolveCaseContextViewFn(defaultExport);
127
- if (fromDefault)
128
- return fromDefault;
243
+ if (isEventHandlers(defaultExport)) {
244
+ return defaultExport;
245
+ }
129
246
  if (defaultExport != null && typeof defaultExport === 'object' && !Array.isArray(defaultExport)) {
130
- const fromBag = resolveCaseContextViewFn(defaultExport.caseContextView);
131
- if (fromBag)
132
- return fromBag;
247
+ const bag = defaultExport;
248
+ if (Object.hasOwn(bag, 'events') && isEventHandlers(bag.events)) {
249
+ return bag.events;
250
+ }
133
251
  }
134
- throw new Error(`Could not find caseContextView export (caseContextView | default) as a function in ${viewPath}`);
252
+ throw new Error(`Could not find events export (events | default) as an EventHandlers map in ${eventsPath}`);
135
253
  }
136
- function isAgendasExport(value) {
137
- if (value == null)
138
- return false;
139
- if (isSequenceAgendas(value) || isAgendaTree(value))
140
- return true;
141
- if (Array.isArray(value) && value.length > 0 && value.every(isAgendaTree))
142
- return true;
143
- if (typeof value === 'object' && !Array.isArray(value)) {
144
- const values = Object.values(value);
145
- return values.length > 0 && values.every(isAgendaTree);
146
- }
147
- return false;
254
+ /**
255
+ * The channels `context.page.ts` has a page function for (17b §9.1). Read from the compiled page code in
256
+ * a context of its own, as the platform runs it: importing the page module here would register its
257
+ * actions in this process, and a second compile would find them named twice. `persona` and `faq` are
258
+ * called when a conversation starts (24 §4), not here.
259
+ */
260
+ export function loadPageChannels(pageCode, sequenceId) {
261
+ return loadPageModule(pageCode, sequenceId).channels;
148
262
  }
149
263
  /**
150
- * Load optional sequence agendas from agendas.ts / agendas/index.ts.
151
- * Missing module → null (compile produces an empty agendas map).
264
+ * What the compiled page code says of itself as it loads: its channels, and the page actions it
265
+ * registered (76 §2.11), read in a context of its own as `loadPageChannels` does.
152
266
  */
153
- export async function loadSequenceAgendas(agendasPath) {
154
- const mod = await importModule(agendasPath);
155
- if (Object.hasOwn(mod, 'agendas') && isAgendasExport(mod.agendas)) {
156
- return mod.agendas;
267
+ export function loadPageModule(pageCode, sequenceId) {
268
+ try {
269
+ const entry = runInNewContext(pageCode);
270
+ const out = entry({ mode: 'channels' });
271
+ // Copied out of the other context: its arrays are not this one's.
272
+ return { channels: [...(out.channels ?? [])].map(String), actions: [...(out.actions ?? [])].map(String) };
157
273
  }
158
- const defaultExport = Object.hasOwn(mod, 'default') ? mod.default : undefined;
159
- if (isAgendasExport(defaultExport))
160
- return defaultExport;
161
- if (defaultExport != null && typeof defaultExport === 'object' && !Array.isArray(defaultExport)) {
162
- const bag = defaultExport;
163
- if (Object.hasOwn(bag, 'agendas') && isAgendasExport(bag.agendas)) {
164
- return bag.agendas;
165
- }
274
+ catch (error) {
275
+ throw new Error(`sequences/${sequenceId}/context.page.ts: ${error instanceof Error ? error.message : String(error)}`);
276
+ }
277
+ }
278
+ /** The Case Orchestrator from orchestrator.ts / orchestrator/index.ts: `orchestrator`, or the default export. */
279
+ export async function loadSequenceCaseOrchestrator(caseOrchestratorPath) {
280
+ const mod = await importModule(caseOrchestratorPath);
281
+ const orchestrator = [mod[ORCHESTRATOR_FUNCTION], mod.default].find((value) => typeof value === 'function');
282
+ if (!orchestrator) {
283
+ throw new Error(`Could not find ${ORCHESTRATOR_FUNCTION} export (${ORCHESTRATOR_FUNCTION} | default) as a function in ${caseOrchestratorPath}`);
284
+ }
285
+ // Whom a Run may contact is the Orchestrator's to read now (38 §2.8).
286
+ if (Object.hasOwn(mod, 'contacts')) {
287
+ throw new Error(`${caseOrchestratorPath}: contacts is no longer supported: delete it. The orchestrator reads whom to contact from the business data, and names them on an Agent request (contacts: [...])`);
166
288
  }
167
- throw new Error(`Could not find agendas export (agendas | default) in ${agendasPath}`);
289
+ return orchestrator;
290
+ }
291
+ /** A `resolve.ts` / `resolve/index.ts`: its exported function `resolve` (or the default export). */
292
+ async function loadResolveExport(resolvePath) {
293
+ const mod = await importModule(resolvePath);
294
+ const entry = [mod[RESOLVE_FUNCTION], mod.default].find((value) => typeof value === 'function');
295
+ if (entry)
296
+ return entry;
297
+ throw new Error(`Could not find ${RESOLVE_FUNCTION} export (${RESOLVE_FUNCTION} | default) as a function in ${resolvePath}`);
298
+ }
299
+ /** A sequence's `resolve`: `(ctx, conversation)`. */
300
+ export async function loadSequenceResolver(resolvePath) {
301
+ return (await loadResolveExport(resolvePath));
302
+ }
303
+ /** The company `resolve` (§18.2), a function of the company part (22 §5): `(ctx, conversation)`. */
304
+ export async function loadCompanyResolver(resolvePath) {
305
+ return (await loadResolveExport(resolvePath));
306
+ }
307
+ /** The module of the company's webhooks (22 §9) at the repository root. */
308
+ export const WEBHOOK_MODULE = 'webhook';
309
+ /** `received` of `webhook.ts`, a function of the company part: `(ctx, { event })`. */
310
+ export async function loadCompanyWebhook(webhookPath) {
311
+ const mod = await importModule(webhookPath);
312
+ const entry = [mod.received, mod.default].find((value) => typeof value === 'function');
313
+ if (!entry)
314
+ throw new Error(`Could not find received export (received | default) as a function in ${webhookPath}`);
315
+ return entry;
316
+ }
317
+ /** A name the platform keeps (a sequence's directory, a webhook): letters, numbers and single hyphens. */
318
+ const PLATFORM_NAME = /^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$/;
319
+ const PLATFORM_NAME_MAX = 40;
320
+ export function isPlatformName(name) {
321
+ return name.length <= PLATFORM_NAME_MAX && PLATFORM_NAME.test(name);
322
+ }
323
+ /**
324
+ * A leftover root `channels.ts` (or `channels/index.ts`): webhook URLs are made and named on the
325
+ * Webhooks page now, like phone numbers, and code refers to them by name only.
326
+ */
327
+ export function assertNoChannelsModule(repoRoot) {
328
+ if (!findTsModule(repoRoot, 'channels'))
329
+ return;
330
+ throw new Error(`channels.ts is no longer supported (channels-removed): delete it. Make webhook URLs on the Webhooks page of each environment and tell them apart in ${WEBHOOK_MODULE}.ts by webhook.name`);
331
+ }
332
+ /**
333
+ * `sequences/<id>/index.ts` default export (`defineSequence({ subject })`): the sequence's business
334
+ * root. Without the file or the declaration, `subject` is undefined and the compile reports it.
335
+ */
336
+ export async function loadSequenceDefinition(sequenceDir) {
337
+ const indexPath = findTsModule(sequenceDir, 'index');
338
+ if (!indexPath)
339
+ return { subject: undefined, label: undefined };
340
+ const mod = await importModule(indexPath);
341
+ const definition = isPlainObject(mod.default) ? mod.default : isPlainObject(mod.sequence) ? mod.sequence : {};
342
+ const { subject, meta } = definition;
343
+ return { subject, label: isPlainObject(meta) ? meta.label : undefined };
344
+ }
345
+ /**
346
+ * `sequences/<id>/variables.ts` (14-sequence-instance §4.4): `variables` (from `defineVariables`) and
347
+ * the optional `experiments`. Both undefined without the file.
348
+ */
349
+ export async function loadSequenceVariables(sequenceDir) {
350
+ const variablesPath = findTsModule(sequenceDir, 'variables');
351
+ if (!variablesPath)
352
+ return { variables: undefined, experiments: undefined };
353
+ const mod = await importModule(variablesPath);
354
+ const bag = isPlainObject(mod.default) ? mod.default : {};
355
+ return {
356
+ variables: mod.variables ?? bag.variables,
357
+ experiments: mod.experiments ?? bag.experiments,
358
+ };
168
359
  }
169
360
  //# sourceMappingURL=load.js.map
@@ -1,10 +1,18 @@
1
1
  /**
2
- * A TS module may live as `<dir>/<name>.ts` or `<dir>/<name>/index.ts`.
2
+ * A TS module may live as `<dir>/<name>.ts` or `<dir>/<name>/index.ts`, or `.tsx` (a page in JSX, 31 §10).
3
3
  * File form is preferred when both exist.
4
4
  */
5
5
  export declare function tsModuleCandidates(dir: string, name: string): string[];
6
+ /**
7
+ * A sequence's page script (15 §10.2): `context.page.ts`, written with functions (76). A page in JSX
8
+ * (`context.page.tsx`, 31) is no longer one.
9
+ */
10
+ export declare function requirePagePath(sequenceDir: string, sequenceId: string): string;
6
11
  /** Return the first existing candidate, or `undefined` if none exist. */
7
12
  export declare function findTsModule(dir: string, name: string): string | undefined;
8
13
  /** Return the first existing candidate, or throw with `missingMessage`. */
9
14
  export declare function resolveTsModule(dir: string, name: string, missingMessage: string): string;
15
+ export declare function requireResolveModule(sequenceDir: string, sequenceId: string): string;
16
+ /** The company `resolve.ts` at the repo root (14-sequence-instance §18.2): required. */
17
+ export declare function requireCompanyResolveModule(repoRoot: string): string;
10
18
  //# sourceMappingURL=paths.d.ts.map
@@ -1,11 +1,26 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { RESOLVE_FUNCTION, RESOLVE_MODULE } from '../core/party/types.js';
3
4
  /**
4
- * A TS module may live as `<dir>/<name>.ts` or `<dir>/<name>/index.ts`.
5
+ * A TS module may live as `<dir>/<name>.ts` or `<dir>/<name>/index.ts`, or `.tsx` (a page in JSX, 31 §10).
5
6
  * File form is preferred when both exist.
6
7
  */
7
8
  export function tsModuleCandidates(dir, name) {
8
- return [path.join(dir, `${name}.ts`), path.join(dir, name, 'index.ts')];
9
+ return [path.join(dir, `${name}.ts`), path.join(dir, `${name}.tsx`), path.join(dir, name, 'index.ts'), path.join(dir, name, 'index.tsx')];
10
+ }
11
+ /**
12
+ * A sequence's page script (15 §10.2): `context.page.ts`, written with functions (76). A page in JSX
13
+ * (`context.page.tsx`, 31) is no longer one.
14
+ */
15
+ export function requirePagePath(sequenceDir, sequenceId) {
16
+ const jsx = path.join(sequenceDir, 'context.page.tsx');
17
+ if (existsSync(jsx)) {
18
+ throw new Error(`sequences/${sequenceId}/context.page.tsx: a page is written with functions in context.page.ts (76), not JSX: page({ guide }, { name: block({ title }, { … }) }), button({ title, does, action: run(fn.x, { … }) })`);
19
+ }
20
+ const page = path.join(sequenceDir, 'context.page.ts');
21
+ if (!existsSync(page))
22
+ throw new Error(`sequences/${sequenceId}: context.page.ts is required (the conversation's page, 15 §10.2)`);
23
+ return page;
9
24
  }
10
25
  /** Return the first existing candidate, or `undefined` if none exist. */
11
26
  export function findTsModule(dir, name) {
@@ -19,4 +34,19 @@ export function resolveTsModule(dir, name, missingMessage) {
19
34
  }
20
35
  return found;
21
36
  }
37
+ export function requireResolveModule(sequenceDir, sequenceId) {
38
+ const resolvePath = findTsModule(sequenceDir, RESOLVE_MODULE);
39
+ if (!resolvePath) {
40
+ throw new Error(`sequences/${sequenceId}/${RESOLVE_MODULE}.ts is required: export ${RESOLVE_FUNCTION} from it to decide which contact and instance a conversation opens on`);
41
+ }
42
+ return resolvePath;
43
+ }
44
+ /** The company `resolve.ts` at the repo root (14-sequence-instance §18.2): required. */
45
+ export function requireCompanyResolveModule(repoRoot) {
46
+ const resolvePath = findTsModule(repoRoot, RESOLVE_MODULE);
47
+ if (!resolvePath) {
48
+ throw new Error(`${RESOLVE_MODULE}.ts is required at the company repo root: export ${RESOLVE_FUNCTION} from it to decide which sequence an inbound conversation goes to`);
49
+ }
50
+ return resolvePath;
51
+ }
22
52
  //# sourceMappingURL=paths.js.map
@@ -0,0 +1,25 @@
1
+ import { type ScheduleDefinition, type ScheduleManifest } from '../core/functions/schedules.js';
2
+ import type { FuncMap, FunctionRegistry } from '../core/functions/types.js';
3
+ import type { FunctionSourceMap } from './types.js';
4
+ /**
5
+ * Why one job's `cron` / `timezone` cannot run, or `null`. The cron is read by the same parser the
6
+ * worker's job scheduler uses (BullMQ's cron-parser); five fields only, so nothing runs every second.
7
+ */
8
+ export declare function scheduleTimingIssue(cron: unknown, timezone: unknown): string | null;
9
+ /** Read a `schedules` export: every job named, timed and runnable, else one error listing what is wrong. */
10
+ export declare function parseSchedules(value: unknown, where: string): Record<string, ScheduleDefinition>;
11
+ /** A directory's `schedules.ts`, loaded: what serve runs and what the build records. `null` without the file. */
12
+ export type LoadedSchedules = {
13
+ path: string;
14
+ /** `schedule.<name>` → the job's `run`, a function `(ctx, { schedule })`. */
15
+ functions: FuncMap;
16
+ manifest: ScheduleManifest;
17
+ };
18
+ /** Load and check the repository root's `schedules.ts`, the company's only one (25 §6.4). */
19
+ export declare function loadSchedules(repoRoot: string): Promise<LoadedSchedules | null>;
20
+ /**
21
+ * The build's registry entries of the jobs: `schedule.<name>` (`code` left out on the serve overlay). Each
22
+ * compiled job's source map goes to `sourceMaps`, by the same name.
23
+ */
24
+ export declare function scheduleRegistry(loaded: LoadedSchedules, includeCode: boolean, sourceMaps?: Record<string, FunctionSourceMap>): Promise<FunctionRegistry>;
25
+ //# sourceMappingURL=schedules.d.ts.map
@@ -0,0 +1,101 @@
1
+ import { pathToFileURL } from 'node:url';
2
+ import { CronExpressionParser } from 'cron-parser';
3
+ import { SCHEDULES_MODULE, scheduleFunctionName, } from '../core/functions/schedules.js';
4
+ import { compileScheduleModuleCode } from './compile/function-code.js';
5
+ import { companyImportGraphHref } from './company-import-graph.js';
6
+ import { findTsModule } from './paths.js';
7
+ /** A job name: an identifier, so `schedule.<name>` stays one plain function name. */
8
+ const SCHEDULE_NAME = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
9
+ function isPlainObject(value) {
10
+ return value != null && typeof value === 'object' && !Array.isArray(value);
11
+ }
12
+ /** Whether `timezone` is an IANA name this runtime knows. */
13
+ function knownTimezone(timezone) {
14
+ try {
15
+ new Intl.DateTimeFormat('en-US', { timeZone: timezone });
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ /**
23
+ * Why one job's `cron` / `timezone` cannot run, or `null`. The cron is read by the same parser the
24
+ * worker's job scheduler uses (BullMQ's cron-parser); five fields only, so nothing runs every second.
25
+ */
26
+ export function scheduleTimingIssue(cron, timezone) {
27
+ if (typeof timezone !== 'string' || !knownTimezone(timezone)) {
28
+ return `timezone ${JSON.stringify(timezone)} is not an IANA timezone (e.g. 'UTC', 'America/Phoenix')`;
29
+ }
30
+ if (typeof cron !== 'string' || cron.trim().split(/\s+/).length !== 5) {
31
+ return `cron ${JSON.stringify(cron)} must have five fields: minute hour day-of-month month day-of-week (e.g. '0 2 * * *')`;
32
+ }
33
+ try {
34
+ CronExpressionParser.parse(cron.trim(), { tz: timezone }).next();
35
+ }
36
+ catch (error) {
37
+ return `cron ${JSON.stringify(cron)} is not valid: ${error instanceof Error ? error.message : String(error)}`;
38
+ }
39
+ return null;
40
+ }
41
+ /** Read a `schedules` export: every job named, timed and runnable, else one error listing what is wrong. */
42
+ export function parseSchedules(value, where) {
43
+ if (!isPlainObject(value)) {
44
+ throw new Error(`${where}: export const schedules = defineSchedules({ name: { cron, timezone, run } })`);
45
+ }
46
+ const issues = [];
47
+ for (const [name, entry] of Object.entries(value)) {
48
+ if (!SCHEDULE_NAME.test(name)) {
49
+ issues.push(`'${name}': a job name is letters, digits and _ starting with a letter (at most 64)`);
50
+ continue;
51
+ }
52
+ if (!isPlainObject(entry)) {
53
+ issues.push(`'${name}': expected { cron, timezone, run }`);
54
+ continue;
55
+ }
56
+ if (typeof entry.run !== 'function')
57
+ issues.push(`'${name}': run must be an async function (ctx) => { … }`);
58
+ const timing = scheduleTimingIssue(entry.cron, entry.timezone);
59
+ if (timing)
60
+ issues.push(`'${name}': ${timing}`);
61
+ }
62
+ if (issues.length > 0)
63
+ throw new Error(`${where}: ${issues.join('; ')} (25 §6.4)`);
64
+ return value;
65
+ }
66
+ async function importSchedules(modulePath, where) {
67
+ const mod = (await import(companyImportGraphHref(pathToFileURL(modulePath).href)));
68
+ const fromDefault = isPlainObject(mod.default) && Object.hasOwn(mod.default, 'schedules') ? mod.default.schedules : mod.default;
69
+ return parseSchedules(mod.schedules ?? fromDefault, where);
70
+ }
71
+ /** Load and check the repository root's `schedules.ts`, the company's only one (25 §6.4). */
72
+ export async function loadSchedules(repoRoot) {
73
+ const modulePath = findTsModule(repoRoot, SCHEDULES_MODULE);
74
+ if (!modulePath)
75
+ return null;
76
+ const schedules = await importSchedules(modulePath, `${SCHEDULES_MODULE}.ts`);
77
+ const functions = {};
78
+ const manifest = {};
79
+ for (const [name, { cron, timezone, run }] of Object.entries(schedules)) {
80
+ functions[scheduleFunctionName(name)] = run;
81
+ manifest[name] = { cron: cron.trim(), timezone };
82
+ }
83
+ return { path: modulePath, functions, manifest };
84
+ }
85
+ /**
86
+ * The build's registry entries of the jobs: `schedule.<name>` (`code` left out on the serve overlay). Each
87
+ * compiled job's source map goes to `sourceMaps`, by the same name.
88
+ */
89
+ export async function scheduleRegistry(loaded, includeCode, sourceMaps) {
90
+ const entries = await Promise.all(Object.keys(loaded.manifest).map(async (name) => {
91
+ const fn = scheduleFunctionName(name);
92
+ if (!includeCode)
93
+ return [fn, {}];
94
+ const compiled = await compileScheduleModuleCode(loaded.path, name);
95
+ if (sourceMaps)
96
+ sourceMaps[fn] = compiled.map;
97
+ return [fn, { code: compiled.code }];
98
+ }));
99
+ return Object.fromEntries(entries);
100
+ }
101
+ //# sourceMappingURL=schedules.js.map
@@ -0,0 +1,4 @@
1
+ import type { ValidationIssue } from '../core/slot/types.js';
2
+ /** The issues that stop codegen and compile: all but {@link WARNING_SLOT_SCHEMA_CODES}. */
3
+ export declare function fatalSlotSchemaIssues(issues: ValidationIssue[]): ValidationIssue[];
4
+ //# sourceMappingURL=slot-schema-validation.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Slot schema issues that only warn. Every other issue fails codegen and compile: a new issue code
3
+ * blocks the build unless it is listed here on purpose. None warns today.
4
+ */
5
+ const WARNING_SLOT_SCHEMA_CODES = new Set([]);
6
+ /** The issues that stop codegen and compile: all but {@link WARNING_SLOT_SCHEMA_CODES}. */
7
+ export function fatalSlotSchemaIssues(issues) {
8
+ return issues.filter((issue) => !WARNING_SLOT_SCHEMA_CODES.has(issue.code));
9
+ }
10
+ //# sourceMappingURL=slot-schema-validation.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The repository's type check, as `npm run typecheck` runs it locally: `type:gen`, then `tsc --noEmit`
3
+ * with the repo's own `tsconfig.json`. With no sequence it writes only the root `__gen` (the local
4
+ * `type:gen` needs a sequence). The compile runs it itself, so a type error blocks a release in every
5
+ * environment; the platform never calls the repo's `typecheck` script.
6
+ */
7
+ export declare function typecheckRepo(repoRoot: string, onLog?: (text: string) => void): Promise<void>;
8
+ //# sourceMappingURL=typecheck.d.ts.map