@sixb/core 0.1.13 → 0.1.15

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 (236) hide show
  1. package/dist/actions/index.js +22 -22
  2. package/dist/agents/index.js +34 -34
  3. package/dist/agents/index.js.map +1 -1
  4. package/dist/agents/streams/index.js +2 -2
  5. package/dist/agents/streams/internal.js +2 -2
  6. package/dist/auth/errors.d.ts +12 -0
  7. package/dist/auth/errors.d.ts.map +1 -1
  8. package/dist/auth/index.d.ts +1 -1
  9. package/dist/auth/index.d.ts.map +1 -1
  10. package/dist/auth/index.js +7 -5
  11. package/dist/auth/index.js.map +1 -1
  12. package/dist/auth/runtime.d.ts.map +1 -1
  13. package/dist/auth/strategy.d.ts +2 -0
  14. package/dist/auth/strategy.d.ts.map +1 -1
  15. package/dist/auth/strategy.js +4 -2
  16. package/dist/auth/strategy.js.map +1 -1
  17. package/dist/auth/types.d.ts +8 -0
  18. package/dist/auth/types.d.ts.map +1 -1
  19. package/dist/authorization/index.js +2 -2
  20. package/dist/bootstrap/index.js +37 -37
  21. package/dist/{chunk-693f3zn8.js → chunk-1jvq82rp.js} +3 -3
  22. package/dist/{chunk-0321g649.js → chunk-2jntasmh.js} +13 -2
  23. package/dist/{chunk-0321g649.js.map → chunk-2jntasmh.js.map} +3 -3
  24. package/dist/{chunk-cvrbhvtr.js → chunk-2tz5vnvz.js} +28 -28
  25. package/dist/{chunk-cvrbhvtr.js.map → chunk-2tz5vnvz.js.map} +1 -1
  26. package/dist/{chunk-898y24dd.js → chunk-3c90jvfj.js} +2 -2
  27. package/dist/{chunk-vn3yh259.js → chunk-4gmng3vf.js} +1 -1
  28. package/dist/{chunk-91wthfwe.js → chunk-4jph1t3v.js} +7 -7
  29. package/dist/{chunk-znw0zvy4.js → chunk-4wn7bxxa.js} +25 -7
  30. package/dist/chunk-4wn7bxxa.js.map +12 -0
  31. package/dist/{chunk-3qpcrcj8.js → chunk-6aawea28.js} +2 -4
  32. package/dist/chunk-6aawea28.js.map +10 -0
  33. package/dist/{chunk-rzkr70gr.js → chunk-6fsr7y93.js} +5 -1
  34. package/dist/{chunk-rzkr70gr.js.map → chunk-6fsr7y93.js.map} +3 -3
  35. package/dist/{chunk-y487ywrv.js → chunk-6q7c68yx.js} +4 -4
  36. package/dist/{chunk-ypj2wqtj.js → chunk-70jmg2zx.js} +2 -2
  37. package/dist/{chunk-s24qbp32.js → chunk-7jc21943.js} +3 -3
  38. package/dist/{chunk-rzcjp5m2.js → chunk-8fjmqjyx.js} +1 -1
  39. package/dist/{chunk-m8zkvep1.js → chunk-8w27xf9e.js} +1 -1
  40. package/dist/{chunk-z9h698s8.js → chunk-a4g5yjm0.js} +1 -1
  41. package/dist/{chunk-gmt22nxx.js → chunk-adbve9zt.js} +1 -1
  42. package/dist/{chunk-mf6hwpzh.js → chunk-dqkd3kdj.js} +41 -23
  43. package/dist/{chunk-mf6hwpzh.js.map → chunk-dqkd3kdj.js.map} +3 -3
  44. package/dist/{chunk-gbcrz0gr.js → chunk-ex8s8wsk.js} +6 -6
  45. package/dist/{chunk-4tnrzqqh.js → chunk-fj1dvp37.js} +1 -1
  46. package/dist/{chunk-gc226ten.js → chunk-fjw417jf.js} +127 -69
  47. package/dist/{chunk-gc226ten.js.map → chunk-fjw417jf.js.map} +4 -4
  48. package/dist/{chunk-ht4a7mjg.js → chunk-g4rbm4a6.js} +42 -8
  49. package/dist/{chunk-ht4a7mjg.js.map → chunk-g4rbm4a6.js.map} +3 -3
  50. package/dist/{chunk-qma80ych.js → chunk-hn2r7z41.js} +3 -2
  51. package/dist/chunk-hn2r7z41.js.map +11 -0
  52. package/dist/{chunk-bdhfn0ww.js → chunk-kkqwzy6h.js} +1 -1
  53. package/dist/{chunk-bep5rrdq.js → chunk-mmac1mp6.js} +2 -2
  54. package/dist/{chunk-w9bp61e7.js → chunk-n5t4mq6z.js} +5 -5
  55. package/dist/{chunk-n33e8dz8.js → chunk-p0nbkys4.js} +1 -1
  56. package/dist/{chunk-04h4t2b5.js → chunk-py2dw8cw.js} +3 -3
  57. package/dist/{chunk-jhyc777j.js → chunk-q3grgzp1.js} +2 -2
  58. package/dist/{chunk-9pfw47qx.js → chunk-qne45xqq.js} +53 -48
  59. package/dist/{chunk-9pfw47qx.js.map → chunk-qne45xqq.js.map} +7 -7
  60. package/dist/{chunk-cbn96bve.js → chunk-r5sc1d2b.js} +2 -2
  61. package/dist/{chunk-y6vnvayj.js → chunk-vjcd8d27.js} +1 -1
  62. package/dist/{chunk-jmwy25mf.js → chunk-wd34wnsy.js} +6 -6
  63. package/dist/{chunk-63fp9x3q.js → chunk-wem5vqjw.js} +7 -7
  64. package/dist/{chunk-darm7eeg.js → chunk-xr7y35yj.js} +3 -3
  65. package/dist/{chunk-a69fkrtr.js → chunk-y0zmae99.js} +5 -5
  66. package/dist/{chunk-xgptywp0.js → chunk-y2aq6ag9.js} +1 -1
  67. package/dist/{chunk-2hbhn1ff.js → chunk-yfppyaty.js} +1 -1
  68. package/dist/{chunk-rqtdtm43.js → chunk-z708gemf.js} +129 -70
  69. package/dist/{chunk-rqtdtm43.js.map → chunk-z708gemf.js.map} +5 -5
  70. package/dist/deploy/define.d.ts +4 -0
  71. package/dist/deploy/define.d.ts.map +1 -0
  72. package/dist/deploy/index.d.ts +3 -0
  73. package/dist/deploy/index.d.ts.map +1 -0
  74. package/dist/deploy/index.js +10 -0
  75. package/dist/deploy/index.js.map +10 -0
  76. package/dist/deploy/internal.d.ts +3 -0
  77. package/dist/deploy/internal.d.ts.map +1 -0
  78. package/dist/deploy/internal.js +244 -0
  79. package/dist/deploy/internal.js.map +11 -0
  80. package/dist/deploy/services.d.ts +8 -0
  81. package/dist/deploy/services.d.ts.map +1 -0
  82. package/dist/deploy/types.d.ts +309 -0
  83. package/dist/deploy/types.d.ts.map +1 -0
  84. package/dist/deploy/validation.d.ts +7 -0
  85. package/dist/deploy/validation.d.ts.map +1 -0
  86. package/dist/error-reporting/internal.js +3 -3
  87. package/dist/errors/catalog.d.ts +4 -0
  88. package/dist/errors/catalog.d.ts.map +1 -1
  89. package/dist/errors/internal.js +1 -1
  90. package/dist/events/index.js +3 -3
  91. package/dist/execution/agent.js +33 -33
  92. package/dist/execution/primitive.js +29 -29
  93. package/dist/execution/request.js +31 -31
  94. package/dist/execution/request.js.map +1 -1
  95. package/dist/index.js +49 -49
  96. package/dist/index.js.map +1 -1
  97. package/dist/maintenance/ontology-maintenance.d.ts +2 -0
  98. package/dist/maintenance/ontology-maintenance.d.ts.map +1 -1
  99. package/dist/maintenance/types.d.ts +8 -2
  100. package/dist/maintenance/types.d.ts.map +1 -1
  101. package/dist/materialization/dataset-watermark.d.ts +6 -1
  102. package/dist/materialization/dataset-watermark.d.ts.map +1 -1
  103. package/dist/materialization/index.js +1 -1
  104. package/dist/materialization/model.d.ts +6 -0
  105. package/dist/materialization/model.d.ts.map +1 -1
  106. package/dist/materializer/index.js +18 -18
  107. package/dist/materializer/index.js.map +1 -1
  108. package/dist/materializer/projections/finish-run.d.ts.map +1 -1
  109. package/dist/materializer/projections/replace.d.ts.map +1 -1
  110. package/dist/materializer/projections/source-materialization.d.ts +6 -2
  111. package/dist/materializer/projections/source-materialization.d.ts.map +1 -1
  112. package/dist/models/execution/index.js +14 -14
  113. package/dist/objects/index.js +21 -21
  114. package/dist/objects/query/index.js +15 -15
  115. package/dist/pipelines/index.js +19 -19
  116. package/dist/projections/internal.js +16 -16
  117. package/dist/projections/types.d.ts +1 -1
  118. package/dist/projections/types.d.ts.map +1 -1
  119. package/dist/queues/in-memory.d.ts +1 -1
  120. package/dist/queues/in-memory.d.ts.map +1 -1
  121. package/dist/runtime/internal.js +19 -19
  122. package/dist/shares/internal.js +14 -14
  123. package/dist/shares/internal.js.map +1 -1
  124. package/dist/storage/action-runs/failure.js +2 -2
  125. package/dist/storage/ai-limits/enforcement.js +2 -2
  126. package/dist/storage/auth/errors.d.ts +1 -1
  127. package/dist/storage/auth/errors.d.ts.map +1 -1
  128. package/dist/storage/auth/in-memory/auth-storage.d.ts +2 -1
  129. package/dist/storage/auth/in-memory/auth-storage.d.ts.map +1 -1
  130. package/dist/storage/auth/types.d.ts +15 -7
  131. package/dist/storage/auth/types.d.ts.map +1 -1
  132. package/dist/storage/connector-connections/provider.js +1 -1
  133. package/dist/storage/file-upload-sessions/provider.js +5 -5
  134. package/dist/storage/index.d.ts +2 -2
  135. package/dist/storage/index.d.ts.map +1 -1
  136. package/dist/storage/index.js +29 -29
  137. package/dist/storage/index.js.map +1 -1
  138. package/dist/storage/internal.js +1 -1
  139. package/dist/storage/ontology/in-memory/sources.d.ts +6 -3
  140. package/dist/storage/ontology/in-memory/sources.d.ts.map +1 -1
  141. package/dist/storage/ontology/index.d.ts +1 -1
  142. package/dist/storage/ontology/index.d.ts.map +1 -1
  143. package/dist/storage/ontology/provider.d.ts +16 -3
  144. package/dist/storage/ontology/provider.d.ts.map +1 -1
  145. package/dist/storage/ontology/provider.js +8 -4
  146. package/dist/storage/ontology/provider.js.map +1 -1
  147. package/dist/storage/ontology/sources.d.ts +36 -8
  148. package/dist/storage/ontology/sources.d.ts.map +1 -1
  149. package/dist/storage/projection-runs/in-memory.d.ts +3 -1
  150. package/dist/storage/projection-runs/in-memory.d.ts.map +1 -1
  151. package/dist/storage/projection-runs/index.d.ts +1 -1
  152. package/dist/storage/projection-runs/index.d.ts.map +1 -1
  153. package/dist/storage/projection-runs/lifecycle.d.ts +12 -0
  154. package/dist/storage/projection-runs/lifecycle.d.ts.map +1 -1
  155. package/dist/storage/projection-runs/provider.js +10 -4
  156. package/dist/storage/projection-runs/provider.js.map +1 -1
  157. package/dist/storage/projection-runs/types.d.ts +32 -1
  158. package/dist/storage/projection-runs/types.d.ts.map +1 -1
  159. package/dist/storage/workflow-runs/provider.js +5 -5
  160. package/dist/syncs/index.js +20 -20
  161. package/dist/testing/auth-storage-contract.d.ts.map +1 -1
  162. package/dist/testing/index.js +381 -171
  163. package/dist/testing/index.js.map +5 -5
  164. package/dist/testing/ontology-storage-contract.d.ts.map +1 -1
  165. package/dist/testing/projection-run-storage-contract.d.ts.map +1 -1
  166. package/dist/webhooks/internal.js +15 -15
  167. package/dist/webhooks/internal.js.map +1 -1
  168. package/dist/workers/index.js +1 -1
  169. package/dist/workflows/index.js +34 -34
  170. package/package.json +13 -1
  171. package/src/auth/errors.ts +23 -0
  172. package/src/auth/index.ts +1 -1
  173. package/src/auth/runtime.ts +56 -22
  174. package/src/auth/strategy.ts +2 -0
  175. package/src/auth/types.ts +8 -0
  176. package/src/deploy/define.ts +6 -0
  177. package/src/deploy/index.ts +41 -0
  178. package/src/deploy/internal.ts +7 -0
  179. package/src/deploy/services.ts +51 -0
  180. package/src/deploy/types.ts +338 -0
  181. package/src/deploy/validation.ts +261 -0
  182. package/src/errors/catalog.ts +4 -0
  183. package/src/maintenance/ontology-maintenance.ts +32 -2
  184. package/src/maintenance/types.ts +8 -2
  185. package/src/materialization/dataset-watermark.ts +6 -4
  186. package/src/materialization/model.ts +7 -0
  187. package/src/materializer/README.md +26 -6
  188. package/src/materializer/projections/finish-run.ts +129 -26
  189. package/src/materializer/projections/replace.ts +67 -49
  190. package/src/materializer/projections/source-materialization.ts +11 -6
  191. package/src/projections/types.ts +1 -0
  192. package/src/storage/auth/errors.ts +0 -1
  193. package/src/storage/auth/in-memory/auth-storage.ts +44 -65
  194. package/src/storage/auth/types.ts +15 -7
  195. package/src/storage/index.ts +6 -1
  196. package/src/storage/ontology/in-memory/sources.ts +119 -55
  197. package/src/storage/ontology/index.ts +4 -1
  198. package/src/storage/ontology/provider.ts +64 -7
  199. package/src/storage/ontology/sources.ts +42 -8
  200. package/src/storage/projection-runs/in-memory.ts +41 -2
  201. package/src/storage/projection-runs/index.ts +2 -0
  202. package/src/storage/projection-runs/lifecycle.ts +61 -4
  203. package/src/storage/projection-runs/types.ts +41 -1
  204. package/src/testing/auth-storage-contract.ts +104 -130
  205. package/src/testing/ontology-storage-contract.ts +141 -15
  206. package/src/testing/projection-run-storage-contract.ts +174 -1
  207. package/dist/chunk-3qpcrcj8.js.map +0 -10
  208. package/dist/chunk-qma80ych.js.map +0 -11
  209. package/dist/chunk-znw0zvy4.js.map +0 -12
  210. /package/dist/{chunk-693f3zn8.js.map → chunk-1jvq82rp.js.map} +0 -0
  211. /package/dist/{chunk-898y24dd.js.map → chunk-3c90jvfj.js.map} +0 -0
  212. /package/dist/{chunk-vn3yh259.js.map → chunk-4gmng3vf.js.map} +0 -0
  213. /package/dist/{chunk-91wthfwe.js.map → chunk-4jph1t3v.js.map} +0 -0
  214. /package/dist/{chunk-y487ywrv.js.map → chunk-6q7c68yx.js.map} +0 -0
  215. /package/dist/{chunk-ypj2wqtj.js.map → chunk-70jmg2zx.js.map} +0 -0
  216. /package/dist/{chunk-s24qbp32.js.map → chunk-7jc21943.js.map} +0 -0
  217. /package/dist/{chunk-rzcjp5m2.js.map → chunk-8fjmqjyx.js.map} +0 -0
  218. /package/dist/{chunk-m8zkvep1.js.map → chunk-8w27xf9e.js.map} +0 -0
  219. /package/dist/{chunk-z9h698s8.js.map → chunk-a4g5yjm0.js.map} +0 -0
  220. /package/dist/{chunk-gmt22nxx.js.map → chunk-adbve9zt.js.map} +0 -0
  221. /package/dist/{chunk-gbcrz0gr.js.map → chunk-ex8s8wsk.js.map} +0 -0
  222. /package/dist/{chunk-4tnrzqqh.js.map → chunk-fj1dvp37.js.map} +0 -0
  223. /package/dist/{chunk-bdhfn0ww.js.map → chunk-kkqwzy6h.js.map} +0 -0
  224. /package/dist/{chunk-bep5rrdq.js.map → chunk-mmac1mp6.js.map} +0 -0
  225. /package/dist/{chunk-w9bp61e7.js.map → chunk-n5t4mq6z.js.map} +0 -0
  226. /package/dist/{chunk-n33e8dz8.js.map → chunk-p0nbkys4.js.map} +0 -0
  227. /package/dist/{chunk-04h4t2b5.js.map → chunk-py2dw8cw.js.map} +0 -0
  228. /package/dist/{chunk-jhyc777j.js.map → chunk-q3grgzp1.js.map} +0 -0
  229. /package/dist/{chunk-cbn96bve.js.map → chunk-r5sc1d2b.js.map} +0 -0
  230. /package/dist/{chunk-y6vnvayj.js.map → chunk-vjcd8d27.js.map} +0 -0
  231. /package/dist/{chunk-jmwy25mf.js.map → chunk-wd34wnsy.js.map} +0 -0
  232. /package/dist/{chunk-63fp9x3q.js.map → chunk-wem5vqjw.js.map} +0 -0
  233. /package/dist/{chunk-darm7eeg.js.map → chunk-xr7y35yj.js.map} +0 -0
  234. /package/dist/{chunk-a69fkrtr.js.map → chunk-y0zmae99.js.map} +0 -0
  235. /package/dist/{chunk-xgptywp0.js.map → chunk-y2aq6ag9.js.map} +0 -0
  236. /package/dist/{chunk-2hbhn1ff.js.map → chunk-yfppyaty.js.map} +0 -0
@@ -0,0 +1,338 @@
1
+ /**
2
+ * A deployment of this project: which services run, how browsers reach them, and the target that
3
+ * runs them. `sixb deploy` loads it from `sixb.deploy.ts`; the running app never reads it.
4
+ */
5
+ export interface DeployConfig {
6
+ /**
7
+ * Names the deployment on its target and in default domains. Lowercase letters, numbers, and
8
+ * hyphens.
9
+ */
10
+ readonly name: string
11
+ /**
12
+ * Parent domain for HTTP services without their own `domain`: `<name>-<service>.<domain>`.
13
+ * Required unless every enabled HTTP service sets `domain`.
14
+ */
15
+ readonly domain?: string
16
+ /** Where the services run, such as `new SshTarget({ host })` from `@sixb/deploy-ssh`. */
17
+ readonly target: DeployTarget
18
+ /**
19
+ * Environment for every service and release step. Committed with the project, so secrets
20
+ * belong in the target's `.env` instead.
21
+ */
22
+ readonly env?: DeployEnv
23
+ /** Override or disable the Sixb services. Every service runs unless set to `false`. */
24
+ readonly services?: DeployServicesConfig
25
+ /** Long-running project scripts, supervised alongside the Sixb services. */
26
+ readonly processes?: Readonly<Record<string, DeployProcessDefinition>>
27
+ }
28
+
29
+ export type DeployEnv = Readonly<Record<string, string>>
30
+
31
+ /** The services a browser or client reaches through a public domain. */
32
+ export type DeployHttpServiceName = "api" | "atlas" | "app"
33
+
34
+ /** The services that must run as exactly one process: a second would duplicate their work. */
35
+ export type DeploySingletonServiceName = "orchestrator" | "scheduler" | "rules"
36
+
37
+ export type DeployServiceName = DeployHttpServiceName | DeploySingletonServiceName | "workers"
38
+
39
+ export type DeployWorkerType = "sync" | "action" | "agent" | "pipeline" | "projection" | "workflow"
40
+
41
+ /** Action jobs run one at a time, so their concurrency is fixed. */
42
+ export type DeployConfigurableWorkerType = Exclude<DeployWorkerType, "action">
43
+
44
+ export interface DeployServicesConfig {
45
+ readonly api?: boolean | DeployHttpServiceConfig
46
+ readonly atlas?: boolean | DeployHttpServiceConfig
47
+ readonly app?: boolean | DeployHttpServiceConfig
48
+ readonly orchestrator?: boolean | DeploySingletonServiceConfig
49
+ readonly scheduler?: boolean | DeploySingletonServiceConfig
50
+ readonly rules?: boolean | DeploySingletonServiceConfig
51
+ readonly workers?: boolean | DeployWorkersConfig
52
+ }
53
+
54
+ export interface DeployHttpServiceConfig {
55
+ /** Public hostname. Defaults to `<name>-<service>.<domain>`. */
56
+ readonly domain?: string
57
+ /** Added to the deployment's `env` for this service only. */
58
+ readonly env?: DeployEnv
59
+ readonly process?: DeployProcessOptions
60
+ }
61
+
62
+ export interface DeploySingletonServiceConfig {
63
+ /** Added to the deployment's `env` for this service only. */
64
+ readonly env?: DeployEnv
65
+ readonly process?: DeployProcessOptions
66
+ }
67
+
68
+ export interface DeployWorkersConfig {
69
+ /**
70
+ * The worker types to run. `"all"`, the default, runs every type the project registers work
71
+ * for.
72
+ */
73
+ readonly types?: readonly DeployWorkerType[] | "all"
74
+ /** Wall-clock budget for one agent turn, such as `"30s"`, `"10m"`, or `"1h"`. */
75
+ readonly agentTurnTimeout?: string
76
+ /** Jobs each worker type runs at once inside one process. */
77
+ readonly concurrency?: Readonly<Partial<Record<DeployConfigurableWorkerType, number>>>
78
+ /** Added to the deployment's `env` for the workers only. */
79
+ readonly env?: DeployEnv
80
+ readonly process?: DeployScalableProcessOptions
81
+ }
82
+
83
+ export interface DeployProcessDefinition {
84
+ /** Script to run with Bun, relative to the project directory. */
85
+ readonly entrypoint: string
86
+ readonly args?: readonly string[]
87
+ /** Added to the deployment's `env` for this process only. */
88
+ readonly env?: DeployEnv
89
+ readonly process?: DeployScalableProcessOptions
90
+ }
91
+
92
+ export interface DeployProcessOptions {
93
+ /** How long a stopping process may take before it is killed. Defaults to 10000. */
94
+ readonly killTimeoutMs?: number
95
+ /** Wait before restarting a process that exited, growing with each restart. Defaults to 1000. */
96
+ readonly restartDelayMs?: number
97
+ /** Restart the process when its memory passes this size, such as `"512M"` or `"2G"`. */
98
+ readonly maxMemory?: string
99
+ }
100
+
101
+ export interface DeployScalableProcessOptions extends DeployProcessOptions {
102
+ /** How many copies of the process to run. Defaults to 1. */
103
+ readonly instances?: number
104
+ }
105
+
106
+ /**
107
+ * Runs a deployment. `@sixb/deploy-ssh` provides the SSH target. Third-party targets are not
108
+ * supported in 0.1.x: this contract still grows with each thing `sixb deploy` learns to do.
109
+ */
110
+ export interface DeployTarget {
111
+ /** The kind of target, shown in `sixb deploy` output, such as `"ssh"`. */
112
+ readonly kind: string
113
+ /** Where the deployment runs, shown in output, such as `"sixb@203.0.113.10"`. */
114
+ readonly location: string
115
+ /** The address an HTTP service listens on, where the target's proxy reaches it. */
116
+ listenAddress(service: DeployHttpServiceName): DeployListenAddress
117
+ /**
118
+ * Ships the source, runs the release, and resolves once its services answer. Rejects with the
119
+ * reason when a step fails; the steps it reported say how far it got.
120
+ */
121
+ deploy(release: DeployRelease, source: DeploySource, context: DeployContext): Promise<void>
122
+ /** What the deployment runs right now, and which release it runs. */
123
+ status(context: DeployOperationContext): Promise<DeployStatus>
124
+ /** Writes recent log lines, then new ones as they arrive when `follow` is set. */
125
+ logs(options: DeployLogsOptions, context: DeployOperationContext): Promise<void>
126
+ /** Restarts, stops, or starts one service, or every service. */
127
+ control(
128
+ action: DeployControlAction,
129
+ service: string | undefined,
130
+ context: DeployOperationContext
131
+ ): Promise<void>
132
+ /** What stands between the target and a working deploy of the release, changing nothing. */
133
+ check(release: DeployRelease, context: DeployCheckContext): Promise<readonly DeployCheck[]>
134
+ /** Prepares the target for the release, fixing what `check` reports as fixable. */
135
+ setup?(release: DeployRelease, context: DeploySetupContext): Promise<void>
136
+ /** Who may deploy and operate the deployment, for a target reached with keys. */
137
+ readonly access?: DeployAccess
138
+ /** Credentials for a CI job that deploys, for a target reached with keys. */
139
+ readonly ci?: DeployCi
140
+ }
141
+
142
+ export interface DeployCheck {
143
+ readonly id: string
144
+ /** What was checked, such as `"SSH"` or `"api DNS"`. */
145
+ readonly label: string
146
+ /**
147
+ * `fixable`: `sixb deploy setup` fixes it. `manual`: only you can, as `remedy` says. Both stop
148
+ * a deploy; a `warning` does not.
149
+ */
150
+ readonly status: "ok" | "fixable" | "manual" | "warning"
151
+ readonly detail: string
152
+ /** What to do about it, when the status is not `ok`. */
153
+ readonly remedy?: string
154
+ }
155
+
156
+ export interface DeployCheckContext extends DeployOperationContext {
157
+ /** The project's directory inside its repository: `.` at the root. */
158
+ readonly projectPath: string
159
+ }
160
+
161
+ export interface DeploySetupContext extends DeployCheckContext {
162
+ /** The account with sudo that prepares a server, for a target that needs one. */
163
+ readonly admin?: string
164
+ /** A public key file to authorize for deploys, instead of the one the target would pick. */
165
+ readonly key?: string
166
+ /** A line of progress for the person running setup. */
167
+ write(line: string): void
168
+ }
169
+
170
+ export interface DeployAccess {
171
+ list(context: DeployOperationContext): Promise<readonly DeployAccessKey[]>
172
+ /** Authorizes a public key, given as its text (`ssh-ed25519 AAAA… comment`). */
173
+ add(key: string, context: DeployOperationContext): Promise<DeployAccessKey>
174
+ /** Revokes the keys whose fingerprint or comment matches, and returns them. */
175
+ remove(match: string, context: DeployOperationContext): Promise<readonly DeployAccessKey[]>
176
+ }
177
+
178
+ export interface DeployAccessKey {
179
+ readonly type: string
180
+ readonly fingerprint: string
181
+ readonly comment: string
182
+ /** Whether the key is limited to running commands: no forwarding, no terminal. */
183
+ readonly restricted: boolean
184
+ }
185
+
186
+ /**
187
+ * Lets a CI job deploy. `sixb deploy ci` stores what `create` returns as the CI system's secrets and
188
+ * writes the workflow around it.
189
+ */
190
+ export interface DeployCi {
191
+ /**
192
+ * Creates a credential for one CI job, such as `"github:acme/shop"`, and authorizes it on the
193
+ * target next to the job's earlier ones. Running it again for the same job rotates the credential.
194
+ */
195
+ create(job: string, context: DeployOperationContext): Promise<DeployCiCredential>
196
+ }
197
+
198
+ export interface DeployCiCredential {
199
+ /** Describes the credential, such as its key's fingerprint. */
200
+ readonly description: string
201
+ /** What the CI job needs, by environment variable name. CI stores them as secrets. */
202
+ readonly secrets: Readonly<Record<string, string>>
203
+ /** Shell lines that install the credential on the CI runner, with `secrets` in the environment. */
204
+ readonly install: readonly string[]
205
+ /** Revokes the job's earlier credentials once CI holds this one, and returns them. */
206
+ retireOthers(): Promise<readonly DeployAccessKey[]>
207
+ /** Revokes this credential, when CI could not store it. */
208
+ revoke(): Promise<void>
209
+ }
210
+
211
+ export interface DeployListenAddress {
212
+ readonly host: string
213
+ readonly port: number
214
+ }
215
+
216
+ /**
217
+ * Everything a deployment runs, resolved by `sixb deploy` from `sixb.deploy.ts`. A target decides
218
+ * where and how it runs; which processes exist, and the command each one starts with, come from
219
+ * Sixb.
220
+ */
221
+ export interface DeployRelease {
222
+ readonly name: string
223
+ readonly target: { readonly kind: string; readonly location: string }
224
+ /** The Bun version the project runs on. */
225
+ readonly bunVersion: string
226
+ /** The environment every step and service starts from. */
227
+ readonly env: DeployEnv
228
+ readonly steps: {
229
+ /** Runs while the previous release still serves. */
230
+ readonly build: readonly DeployCommand[]
231
+ /** Runs after the services stop and before they start again. */
232
+ readonly beforeStart: readonly DeployCommand[]
233
+ }
234
+ readonly services: readonly DeployReleaseService[]
235
+ }
236
+
237
+ export interface DeployCommand {
238
+ /** `sixb` is the CLI the project installs; `bun` runs a project script. */
239
+ readonly program: "sixb" | "bun"
240
+ readonly args: readonly string[]
241
+ }
242
+
243
+ export interface DeployReleaseService {
244
+ readonly name: string
245
+ readonly kind: "http" | "singleton" | "workers" | "script"
246
+ readonly command: DeployCommand
247
+ /** The complete environment: the release's, then the service's own additions. */
248
+ readonly env: DeployEnv
249
+ readonly instances: number
250
+ readonly http?: DeployReleaseHttp
251
+ readonly process: DeployReleaseProcess
252
+ }
253
+
254
+ export interface DeployReleaseHttp extends DeployListenAddress {
255
+ readonly domain: string
256
+ readonly publicOrigin: string
257
+ /** Answers once the service can take traffic, when the service has such a route. */
258
+ readonly readinessPath?: string
259
+ }
260
+
261
+ export interface DeployReleaseProcess {
262
+ readonly killTimeoutMs: number
263
+ readonly restartDelayMs: number
264
+ readonly maxMemory?: string
265
+ }
266
+
267
+ /** The committed files a deployment runs, read from git by `sixb deploy`. */
268
+ export interface DeploySource {
269
+ /** The commit being deployed. */
270
+ readonly commit: string
271
+ /** What the deployer asked for, such as `HEAD` or `main`. */
272
+ readonly ref: string
273
+ /** The project's directory inside the repository: `.` at its root. */
274
+ readonly projectPath: string
275
+ /** Tar archives of the committed files: the repository first, then each submodule. */
276
+ readonly archives: readonly DeploySourceArchive[]
277
+ }
278
+
279
+ export interface DeploySourceArchive {
280
+ /** Where the archive's files belong, relative to the repository root: `.` for the root. */
281
+ readonly path: string
282
+ open(): ReadableStream<Uint8Array>
283
+ }
284
+
285
+ export interface DeployOperationContext {
286
+ /** The deployment's `name`, which a target may run several of. */
287
+ readonly name: string
288
+ }
289
+
290
+ export interface DeployContext extends DeployOperationContext {
291
+ /** Who deployed, recorded with the release. */
292
+ readonly deployedBy: string
293
+ report(event: DeployEvent): void
294
+ }
295
+
296
+ export type DeployEvent =
297
+ /** The steps the deployment will run, reported before the first one starts. */
298
+ | { readonly type: "steps"; readonly labels: readonly string[] }
299
+ | { readonly type: "step"; readonly index: number; readonly status: "running" | "done" }
300
+ | { readonly type: "output"; readonly line: string }
301
+
302
+ export interface DeployStatus {
303
+ /** The release the target last deployed, if any. */
304
+ readonly release: DeployRecord | null
305
+ /** Whether the target's process supervisor is running. */
306
+ readonly running: boolean
307
+ readonly processes: readonly DeployProcessStatus[]
308
+ }
309
+
310
+ export interface DeployRecord {
311
+ readonly commit: string
312
+ readonly ref: string
313
+ readonly deployedAt: string
314
+ readonly deployedBy: string
315
+ }
316
+
317
+ export interface DeployProcessStatus {
318
+ readonly service: string
319
+ readonly instance: number
320
+ readonly status: "starting" | "running" | "stopping" | "stopped" | "exited"
321
+ readonly pid?: number
322
+ readonly restarts: number
323
+ readonly startedAt?: string
324
+ readonly cpuPercent?: number
325
+ readonly memoryBytes?: number
326
+ readonly lastError?: string
327
+ }
328
+
329
+ export interface DeployLogsOptions {
330
+ /** One service's logs, or every service's. */
331
+ readonly service?: string
332
+ /** How many recent lines to write first. */
333
+ readonly tail: number
334
+ readonly follow: boolean
335
+ write(line: string): void
336
+ }
337
+
338
+ export type DeployControlAction = "restart" | "start" | "stop"
@@ -0,0 +1,261 @@
1
+ import {
2
+ DEPLOY_CONFIGURABLE_WORKER_TYPES,
3
+ DEPLOY_SERVICES,
4
+ DEPLOY_WORKER_TYPES,
5
+ isDeployHttpService,
6
+ isDeploySingletonService,
7
+ } from "./services"
8
+ import type { DeployConfig } from "./types"
9
+
10
+ type UnknownRecord = Record<string, unknown>
11
+
12
+ const NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/
13
+ const DOMAIN_LABEL = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?"
14
+ const DOMAIN_PATTERN = new RegExp(`^${DOMAIN_LABEL}(?:\\.${DOMAIN_LABEL})*$`, "i")
15
+ const ENV_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/
16
+ const MEMORY_PATTERN = /^\d+(?:\.\d+)?\s*(?:b|k|kb|m|mb|g|gb)?$/i
17
+ /** Longest delay a timer honors; anything larger fires immediately. */
18
+ const MAX_TIMER_MS = 2_147_483_647
19
+
20
+ const PROCESS_OPTIONS = ["killTimeoutMs", "restartDelayMs", "maxMemory"] as const
21
+ const SCALABLE_PROCESS_OPTIONS = [...PROCESS_OPTIONS, "instances"] as const
22
+
23
+ /**
24
+ * Checks a loaded `sixb.deploy.ts` default export and returns it typed. Validates shape and
25
+ * values only; `sixb deploy` resolves defaults and checks what depends on the target.
26
+ */
27
+ export function validateDeployConfig(value: unknown): DeployConfig {
28
+ const config = record(value, "The deploy config")
29
+ knownKeys(config, ["name", "domain", "target", "env", "services", "processes"], "")
30
+
31
+ name(config.name, "name")
32
+ if (config.domain !== undefined) domain(config.domain, "domain")
33
+ target(config.target)
34
+ if (config.env !== undefined) env(config.env, "env")
35
+ if (config.services !== undefined) services(config.services)
36
+ if (config.processes !== undefined) processes(config.processes)
37
+
38
+ return value as DeployConfig
39
+ }
40
+
41
+ function target(value: unknown): void {
42
+ const hint = "Use a deploy target, such as `new SshTarget({ host })` from @sixb/deploy-ssh."
43
+ if (typeof value !== "object" || value === null) {
44
+ throw new Error(`[SixbDeploy] target is required. ${hint}`)
45
+ }
46
+ const candidate = value as UnknownRecord
47
+ const methods = ["listenAddress", "deploy", "status", "logs", "control", "check"]
48
+ if (
49
+ typeof candidate.kind !== "string" ||
50
+ typeof candidate.location !== "string" ||
51
+ methods.some((method) => typeof candidate[method] !== "function")
52
+ ) {
53
+ throw new Error(`[SixbDeploy] target is not a deploy target. ${hint}`)
54
+ }
55
+ }
56
+
57
+ function services(value: unknown): void {
58
+ const config = record(value, "services")
59
+ knownKeys(config, DEPLOY_SERVICES, "services")
60
+
61
+ for (const service of DEPLOY_SERVICES) {
62
+ const override = config[service]
63
+ if (override === undefined || typeof override === "boolean") continue
64
+ const path = `services.${service}`
65
+ const options = record(override, path)
66
+
67
+ if (isDeployHttpService(service)) {
68
+ knownKeys(options, ["domain", "env", "process"], path, {
69
+ port: "Ports belong to the target, such as `new SshTarget({ ports: { api: 3012 } })`.",
70
+ })
71
+ if (options.domain !== undefined) domain(options.domain, `${path}.domain`)
72
+ if (options.process !== undefined) {
73
+ processOptions(options.process, `${path}.process`, PROCESS_OPTIONS, {
74
+ instances: "HTTP services run one process each.",
75
+ })
76
+ }
77
+ } else if (isDeploySingletonService(service)) {
78
+ knownKeys(options, ["env", "process"], path)
79
+ if (options.process !== undefined) {
80
+ processOptions(options.process, `${path}.process`, PROCESS_OPTIONS, {
81
+ instances: `The ${service} runs exactly one process: a second would duplicate its work.`,
82
+ })
83
+ }
84
+ } else {
85
+ knownKeys(options, ["types", "agentTurnTimeout", "concurrency", "env", "process"], path)
86
+ if (options.types !== undefined) workerTypes(options.types, `${path}.types`)
87
+ if (options.agentTurnTimeout !== undefined) {
88
+ string(options.agentTurnTimeout, `${path}.agentTurnTimeout`)
89
+ }
90
+ if (options.concurrency !== undefined) {
91
+ workerConcurrency(options.concurrency, `${path}.concurrency`)
92
+ }
93
+ if (options.process !== undefined) {
94
+ processOptions(options.process, `${path}.process`, SCALABLE_PROCESS_OPTIONS)
95
+ }
96
+ }
97
+
98
+ if (options.env !== undefined) env(options.env, `${path}.env`)
99
+ }
100
+ }
101
+
102
+ function processes(value: unknown): void {
103
+ const config = record(value, "processes")
104
+
105
+ for (const [processName, definition] of Object.entries(config)) {
106
+ const path = `processes.${processName}`
107
+ name(processName, path)
108
+ if ((DEPLOY_SERVICES as readonly string[]).includes(processName)) {
109
+ throw new Error(`[SixbDeploy] ${path} uses the name of a Sixb service. Rename the process.`)
110
+ }
111
+
112
+ const options = record(definition, path)
113
+ knownKeys(options, ["entrypoint", "args", "env", "process"], path)
114
+ entrypoint(options.entrypoint, `${path}.entrypoint`)
115
+ if (options.args !== undefined) {
116
+ if (!Array.isArray(options.args) || options.args.some((arg) => typeof arg !== "string")) {
117
+ throw new Error(`[SixbDeploy] ${path}.args must be an array of strings.`)
118
+ }
119
+ }
120
+ if (options.env !== undefined) env(options.env, `${path}.env`)
121
+ if (options.process !== undefined) {
122
+ processOptions(options.process, `${path}.process`, SCALABLE_PROCESS_OPTIONS)
123
+ }
124
+ }
125
+ }
126
+
127
+ function workerTypes(value: unknown, path: string): void {
128
+ if (value === "all") return
129
+ if (!Array.isArray(value) || value.length === 0) {
130
+ throw new Error(`[SixbDeploy] ${path} must be "all" or a non-empty list of worker types.`)
131
+ }
132
+ const seen = new Set<string>()
133
+ for (const workerType of value) {
134
+ if (!(DEPLOY_WORKER_TYPES as readonly unknown[]).includes(workerType)) {
135
+ throw new Error(
136
+ `[SixbDeploy] ${path} has unknown worker type ${JSON.stringify(workerType)}. ` +
137
+ `Available: ${DEPLOY_WORKER_TYPES.join(", ")}.`
138
+ )
139
+ }
140
+ if (seen.has(workerType)) {
141
+ throw new Error(`[SixbDeploy] ${path} lists ${workerType} more than once.`)
142
+ }
143
+ seen.add(workerType)
144
+ }
145
+ }
146
+
147
+ function workerConcurrency(value: unknown, path: string): void {
148
+ const config = record(value, path)
149
+ for (const [workerType, concurrency] of Object.entries(config)) {
150
+ if (workerType === "action") {
151
+ throw new Error(`[SixbDeploy] ${path}.action cannot be set: action jobs run one at a time.`)
152
+ }
153
+ if (!(DEPLOY_CONFIGURABLE_WORKER_TYPES as readonly string[]).includes(workerType)) {
154
+ throw new Error(
155
+ `[SixbDeploy] ${path} has unknown worker type "${workerType}". ` +
156
+ `Available: ${DEPLOY_CONFIGURABLE_WORKER_TYPES.join(", ")}.`
157
+ )
158
+ }
159
+ positiveInteger(concurrency, `${path}.${workerType}`)
160
+ }
161
+ }
162
+
163
+ function processOptions(
164
+ value: unknown,
165
+ path: string,
166
+ allowed: readonly string[],
167
+ hints: Readonly<Record<string, string>> = {}
168
+ ): void {
169
+ const options = record(value, path)
170
+ knownKeys(options, allowed, path, hints)
171
+ if (options.instances !== undefined) positiveInteger(options.instances, `${path}.instances`)
172
+ if (options.killTimeoutMs !== undefined) {
173
+ positiveInteger(options.killTimeoutMs, `${path}.killTimeoutMs`, MAX_TIMER_MS)
174
+ }
175
+ if (options.restartDelayMs !== undefined) {
176
+ positiveInteger(options.restartDelayMs, `${path}.restartDelayMs`, MAX_TIMER_MS)
177
+ }
178
+ if (options.maxMemory !== undefined) {
179
+ if (typeof options.maxMemory !== "string" || !MEMORY_PATTERN.test(options.maxMemory.trim())) {
180
+ throw new Error(`[SixbDeploy] ${path}.maxMemory must be a size such as "512M" or "2G".`)
181
+ }
182
+ }
183
+ }
184
+
185
+ function entrypoint(value: unknown, path: string): void {
186
+ string(value, path)
187
+ const segments = value.split(/[\\/]/)
188
+ if (value.startsWith("/") || segments.includes("..")) {
189
+ throw new Error(
190
+ `[SixbDeploy] ${path} must be a path inside the project, relative to its directory.`
191
+ )
192
+ }
193
+ }
194
+
195
+ function env(value: unknown, path: string): void {
196
+ const variables = record(value, path)
197
+ for (const [key, variable] of Object.entries(variables)) {
198
+ if (!ENV_NAME_PATTERN.test(key)) {
199
+ throw new Error(
200
+ `[SixbDeploy] ${path}.${key} is not a valid environment variable name. ` +
201
+ "Use uppercase letters, digits, and underscores."
202
+ )
203
+ }
204
+ if (typeof variable !== "string") {
205
+ throw new Error(`[SixbDeploy] ${path}.${key} must be a string.`)
206
+ }
207
+ }
208
+ }
209
+
210
+ function name(value: unknown, path: string): void {
211
+ string(value, path)
212
+ if (!NAME_PATTERN.test(value) || value.length > 63) {
213
+ throw new Error(
214
+ `[SixbDeploy] ${path} must use lowercase letters, numbers, and hyphens, ` +
215
+ "start and end with a letter or number, and be at most 63 characters."
216
+ )
217
+ }
218
+ }
219
+
220
+ function domain(value: unknown, path: string): void {
221
+ string(value, path)
222
+ if (value.includes("://") || value.includes("/")) {
223
+ throw new Error(`[SixbDeploy] ${path} must be a hostname such as app.example.com, not a URL.`)
224
+ }
225
+ if (value.length > 253 || !DOMAIN_PATTERN.test(value)) {
226
+ throw new Error(`[SixbDeploy] ${path} must be a valid hostname such as app.example.com.`)
227
+ }
228
+ }
229
+
230
+ function positiveInteger(value: unknown, path: string, maximum = Number.MAX_SAFE_INTEGER): void {
231
+ if (!Number.isInteger(value) || (value as number) < 1 || (value as number) > maximum) {
232
+ throw new Error(`[SixbDeploy] ${path} must be a positive integer.`)
233
+ }
234
+ }
235
+
236
+ function string(value: unknown, path: string): asserts value is string {
237
+ if (typeof value !== "string" || value.trim() === "") {
238
+ throw new Error(`[SixbDeploy] ${path} must be a non-empty string.`)
239
+ }
240
+ }
241
+
242
+ function record(value: unknown, path: string): UnknownRecord {
243
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
244
+ throw new Error(`[SixbDeploy] ${path} must be an object.`)
245
+ }
246
+ return value as UnknownRecord
247
+ }
248
+
249
+ function knownKeys(
250
+ value: UnknownRecord,
251
+ allowed: readonly string[],
252
+ path: string,
253
+ hints: Readonly<Record<string, string>> = {}
254
+ ): void {
255
+ for (const key of Object.keys(value)) {
256
+ if (allowed.includes(key)) continue
257
+ const setting = path ? `${path}.${key}` : key
258
+ const hint = hints[key] ?? `Available: ${allowed.join(", ")}.`
259
+ throw new Error(`[SixbDeploy] Unknown deploy setting '${setting}'. ${hint}`)
260
+ }
261
+ }
@@ -146,6 +146,10 @@ export const SIXB_ERROR_DEFINITIONS = {
146
146
  publicMessage: "The embedding call was interrupted; its outcome and charges may be unknown.",
147
147
  retryable: false,
148
148
  },
149
+ "storage.unavailable": {
150
+ publicMessage: "Storage is temporarily unavailable.",
151
+ retryable: true,
152
+ },
149
153
  "sync.execution_failed": {
150
154
  publicMessage: "Sync execution failed.",
151
155
  retryable: false,
@@ -24,6 +24,9 @@ const DEGRADED_FAILURE_THRESHOLD = 2
24
24
  // run ahead of the database's; the sweep leaves sessions alone until this long past expiry.
25
25
  const ABANDONED_UPLOAD_SWEEP_DELAY_MS = 5 * 60_000
26
26
  const ABANDONED_UPLOAD_ABORT_GRACE_MS = 15 * 60_000
27
+ // One abandoned candidate of a large projection holds hundreds of thousands of rows. Batches this
28
+ // size keep each delete short while clearing such a candidate within a pass.
29
+ const ABANDONED_SOURCE_PURGE_BATCH = 50_000
27
30
 
28
31
  interface OntologyMaintenanceDependencies {
29
32
  readonly projectId: string
@@ -188,6 +191,11 @@ export class OntologyMaintenance {
188
191
  failures,
189
192
  { rowsDeleted: 0, materializationsDeleted: 0 }
190
193
  )
194
+ const abandonedSources = await captureFailure(
195
+ () => this.purgeAbandonedSources(started),
196
+ failures,
197
+ { rowsDeleted: 0, materializationsDeleted: 0 }
198
+ )
191
199
  const uploads = this.storage.fileUploadSessions
192
200
  if (uploads) {
193
201
  // Retire abandoned uploads first: the store never reaps one that is still pending.
@@ -197,8 +205,30 @@ export class OntologyMaintenance {
197
205
 
198
206
  return {
199
207
  publishedOutboxRowsDeleted,
200
- terminalSourceRowsDeleted: terminalSources.rowsDeleted,
201
- terminalSourceMaterializationsDeleted: terminalSources.materializationsDeleted,
208
+ terminalSourceRowsDeleted: terminalSources.rowsDeleted + abandonedSources.rowsDeleted,
209
+ terminalSourceMaterializationsDeleted:
210
+ terminalSources.materializationsDeleted + abandonedSources.materializationsDeleted,
211
+ }
212
+ }
213
+
214
+ /** Deletes abandoned candidates batch after batch, until none is left or the pass runs long. */
215
+ private async purgeAbandonedSources(
216
+ started: Date
217
+ ): Promise<{ readonly rowsDeleted: number; readonly materializationsDeleted: number }> {
218
+ let rowsDeleted = 0
219
+ let materializationsDeleted = 0
220
+ while (true) {
221
+ const purged = await this.storage.ontology.sources.purgeAbandoned({
222
+ projectId: this.projectId,
223
+ limit: ABANDONED_SOURCE_PURGE_BATCH,
224
+ })
225
+ rowsDeleted += purged.rowsDeleted
226
+ materializationsDeleted += purged.materializationsDeleted
227
+ const exhausted =
228
+ purged.rowsDeleted + purged.materializationsDeleted < ABANDONED_SOURCE_PURGE_BATCH
229
+ if (exhausted || this.now().getTime() - started.getTime() >= this.intervalMs) {
230
+ return { rowsDeleted, materializationsDeleted }
231
+ }
202
232
  }
203
233
  }
204
234
 
@@ -3,9 +3,15 @@ export interface OntologyMaintenanceOptions {
3
3
  readonly intervalMs?: number
4
4
  /** Published outbox retention. Defaults to 24 hours. */
5
5
  readonly publishedOutboxRetentionMs?: number
6
- /** Superseded/abandoned source retention. Defaults to 24 hours. */
6
+ /**
7
+ * Retention of superseded source versions. Defaults to 24 hours. Abandoned candidates have none:
8
+ * nothing reads them, so the next pass deletes them whole.
9
+ */
7
10
  readonly terminalSourceRetentionMs?: number
8
- /** Maximum deletions attempted per cleanup domain and pass. Defaults to 1,000. */
11
+ /**
12
+ * Maximum deletions attempted per cleanup domain and pass. Defaults to 1,000. Abandoned
13
+ * candidates are deleted in larger batches until none is left or the pass reaches `intervalMs`.
14
+ */
9
15
  readonly cleanupLimit?: number
10
16
  /** Warn after this shutdown grace period, then wait for active operations. Defaults to 30 seconds. */
11
17
  readonly shutdownTimeoutMs?: number