@plitzi/sdk-server 0.37.1 → 0.37.3

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 (386) hide show
  1. package/CHANGELOG.md +1296 -0
  2. package/README.md +126 -7
  3. package/dist/actions.d.ts +20 -8
  4. package/dist/actions.js +11 -4
  5. package/dist/adapters/cloudAdapters.js +4 -3
  6. package/dist/adapters/jsonAdapters.js +28 -6
  7. package/dist/auth.d.ts +7 -4
  8. package/dist/auth.js +8 -6
  9. package/dist/core/auth/api.d.ts +29 -1
  10. package/dist/core/auth/api.js +47 -13
  11. package/dist/core/auth/createAuth.d.ts +4 -0
  12. package/dist/core/auth/createAuth.js +3 -3
  13. package/dist/core/auth/credentials.d.ts +3 -0
  14. package/dist/core/auth/credentials.js +1 -1
  15. package/dist/core/auth/csrf.d.ts +25 -1
  16. package/dist/core/auth/csrf.js +29 -4
  17. package/dist/core/auth/renewal.d.ts +26 -0
  18. package/dist/core/auth/renewal.js +24 -0
  19. package/dist/core/auth/routes.js +8 -6
  20. package/dist/core/auth/session.d.ts +45 -0
  21. package/dist/core/auth/session.js +47 -1
  22. package/dist/core/auth/sessionActivity.d.ts +11 -0
  23. package/dist/core/auth/sessionActivity.js +14 -0
  24. package/dist/core/auth/throttle.d.ts +15 -0
  25. package/dist/core/auth/throttle.js +29 -1
  26. package/dist/core/auth/totp.d.ts +9 -1
  27. package/dist/core/auth/totp.js +11 -6
  28. package/dist/core/createServer.actions.test.d.ts +1 -0
  29. package/dist/core/createServer.d.ts +14 -1
  30. package/dist/core/createServer.functions.test.d.ts +1 -0
  31. package/dist/core/createServer.js +22 -12
  32. package/dist/core/fontStore.js +1 -1
  33. package/dist/core/handlers/authRouteHandlers.d.ts +1 -1
  34. package/dist/core/handlers/authRouteHandlers.js +57 -21
  35. package/dist/core/handlers/csrfMiddleware.d.ts +23 -0
  36. package/dist/core/handlers/csrfMiddleware.js +31 -7
  37. package/dist/core/handlers/csrfMiddleware.test.d.ts +1 -0
  38. package/dist/core/handlers/oauthRouteHandlers.js +5 -3
  39. package/dist/core/handlers/socialRouteHandlers.d.ts +1 -1
  40. package/dist/core/handlers/socialRouteHandlers.js +2 -1
  41. package/dist/core/http/dispatcher.d.ts +9 -1
  42. package/dist/core/http/dispatcher.js +38 -11
  43. package/dist/core/http/navigation.d.ts +5 -2
  44. package/dist/core/http/navigation.js +7 -4
  45. package/dist/core/http/socketResponse.d.ts +18 -0
  46. package/dist/core/http/socketResponse.js +59 -0
  47. package/dist/core/http/sse.d.ts +26 -0
  48. package/dist/core/http/sse.js +78 -0
  49. package/dist/core/http/stages/authRoutes.js +3 -1
  50. package/dist/core/http/stages/exchangeRoute.test.d.ts +1 -0
  51. package/dist/core/http/stages/middlewares.d.ts +1 -1
  52. package/dist/core/http/stages/middlewares.js +16 -10
  53. package/dist/core/http/stages/middlewares.test.d.ts +1 -0
  54. package/dist/core/http/stages/oauth.js +2 -2
  55. package/dist/core/http/stages/sessionRenewal.d.ts +16 -0
  56. package/dist/core/http/stages/sessionRenewal.js +81 -0
  57. package/dist/core/http/stages/sessionRenewal.test.d.ts +1 -0
  58. package/dist/core/http/stages/signInRoute.d.ts +2 -0
  59. package/dist/core/http/stages/signInRoute.js +139 -0
  60. package/dist/core/http/stages/signInRoute.test.d.ts +1 -0
  61. package/dist/core/http/types.d.ts +9 -0
  62. package/dist/core/http/webExchange.d.ts +18 -0
  63. package/dist/core/http/webExchange.js +53 -0
  64. package/dist/core/oauth/authorize.js +37 -13
  65. package/dist/core/oauth/consentPage.js +11 -0
  66. package/dist/core/oauth/issueContext.d.ts +14 -0
  67. package/dist/core/oauth/issueContext.js +32 -0
  68. package/dist/core/oauth/records.d.ts +7 -0
  69. package/dist/core/oauth/register.d.ts +2 -0
  70. package/dist/core/oauth/register.js +32 -11
  71. package/dist/core/oauth/register.test.d.ts +1 -0
  72. package/dist/core/oauth/token.js +21 -6
  73. package/dist/core/oauth/token.test.d.ts +1 -0
  74. package/dist/core/requestParser.d.ts +4 -0
  75. package/dist/core/requestParser.js +14 -7
  76. package/dist/core/sdkAssets.js +1 -1
  77. package/dist/core/server/baseServer.d.ts +14 -1
  78. package/dist/core/server/baseServer.js +43 -11
  79. package/dist/core/server/closeOnSignals.d.ts +28 -0
  80. package/dist/core/server/closeOnSignals.js +47 -0
  81. package/dist/core/server/closeOnSignals.test.d.ts +1 -0
  82. package/dist/core/server/drain.d.ts +18 -0
  83. package/dist/core/server/drain.js +62 -0
  84. package/dist/core/server/drain.test.d.ts +1 -0
  85. package/dist/core/server/fleet/channel.d.ts +103 -0
  86. package/dist/core/server/fleet/channel.js +191 -0
  87. package/dist/core/server/fleet/channel.test.d.ts +1 -0
  88. package/dist/core/server/fleet/fixtures/fleetServer.d.ts +1 -0
  89. package/dist/core/server/fleet/fleet.integration.test.d.ts +1 -0
  90. package/dist/core/server/fleet/link.d.ts +23 -0
  91. package/dist/core/server/fleet/link.js +70 -0
  92. package/dist/core/server/fleet/relay.d.ts +11 -0
  93. package/dist/core/server/fleet/relay.js +104 -0
  94. package/dist/core/server/fleet/relay.test.d.ts +1 -0
  95. package/dist/core/server/fleet/role.d.ts +46 -0
  96. package/dist/core/server/fleet/role.js +59 -0
  97. package/dist/core/server/fleet/role.test.d.ts +1 -0
  98. package/dist/core/server/fleet/stores.d.ts +7 -0
  99. package/dist/core/server/fleet/stores.js +19 -0
  100. package/dist/core/server/fleet/supervisor.d.ts +26 -0
  101. package/dist/core/server/fleet/supervisor.js +108 -0
  102. package/dist/core/server/fleet/supervisor.test.d.ts +1 -0
  103. package/dist/core/server/pageServer.js +36 -5
  104. package/dist/core/services/action.js +10 -2
  105. package/dist/core/services/functionRoutes.d.ts +9 -0
  106. package/dist/core/services/functionRoutes.js +47 -0
  107. package/dist/core/services/realtime.d.ts +8 -0
  108. package/dist/core/services/realtime.js +57 -0
  109. package/dist/core/services/registry.js +6 -2
  110. package/dist/core/staticFiles.js +12 -4
  111. package/dist/core/staticFiles.test.d.ts +1 -0
  112. package/dist/core/transports.d.ts +13 -3
  113. package/dist/core/transports.js +14 -5
  114. package/dist/functions-api.d.ts +281 -0
  115. package/dist/functions.d.ts +19 -0
  116. package/dist/functions.js +5 -0
  117. package/dist/functionsRunner.d.ts +15 -0
  118. package/dist/functionsRunner.js +6 -0
  119. package/dist/handlers.d.ts +2 -2
  120. package/dist/handlers.js +2 -2
  121. package/dist/helpers/atomicFile.d.ts +10 -0
  122. package/dist/helpers/atomicFile.js +33 -0
  123. package/dist/helpers/atomicFile.test.d.ts +1 -0
  124. package/dist/helpers/buildResponseHelpers.js +35 -11
  125. package/dist/helpers/buildServerInfo.js +4 -2
  126. package/dist/helpers/buildServerInfo.test.d.ts +1 -0
  127. package/dist/helpers/cache/CompressedFileCache.d.ts +18 -0
  128. package/dist/helpers/cache/CompressedFileCache.js +44 -0
  129. package/dist/helpers/cache/CompressedFileCache.test.d.ts +1 -0
  130. package/dist/helpers/cache/cacheManager.d.ts +1 -1
  131. package/dist/helpers/cache/cacheManager.js +2 -1
  132. package/dist/helpers/cache/cacheManager.test.d.ts +1 -0
  133. package/dist/helpers/cache/index.d.ts +1 -1
  134. package/dist/helpers/cache/keys.d.ts +15 -1
  135. package/dist/helpers/cache/keys.js +47 -5
  136. package/dist/helpers/cache/serverCaches.d.ts +7 -1
  137. package/dist/helpers/compress.d.ts +3 -1
  138. package/dist/helpers/compress.js +11 -3
  139. package/dist/helpers/hydrationPayload.d.ts +11 -0
  140. package/dist/helpers/hydrationPayload.js +51 -0
  141. package/dist/helpers/hydrationPayload.test.d.ts +1 -0
  142. package/dist/helpers/metrics.d.ts +1 -1
  143. package/dist/helpers/metrics.js +3 -2
  144. package/dist/helpers/outboundGuard.d.ts +14 -6
  145. package/dist/helpers/outboundGuard.js +132 -8
  146. package/dist/helpers/paintedCookie.d.ts +8 -0
  147. package/dist/helpers/paintedCookie.js +13 -0
  148. package/dist/helpers/reportReactBuild.d.ts +9 -0
  149. package/dist/helpers/reportReactBuild.js +18 -0
  150. package/dist/helpers/reportReactBuild.test.d.ts +1 -0
  151. package/dist/helpers/serverLog.d.ts +25 -1
  152. package/dist/helpers/serverLog.js +68 -3
  153. package/dist/helpers/serverLog.test.d.ts +1 -0
  154. package/dist/helpers/wsFrame.d.ts +3 -0
  155. package/dist/helpers/wsFrame.js +5 -0
  156. package/dist/index.d.ts +8 -1
  157. package/dist/index.js +6 -2
  158. package/dist/kernel.d.ts +2 -1
  159. package/dist/kernel.js +3 -2
  160. package/dist/modules/actions/connectorWrite.js +2 -1
  161. package/dist/modules/actions/index.d.ts +41 -13
  162. package/dist/modules/actions/index.js +52 -9
  163. package/dist/modules/actions/jobs/failureStreak.d.ts +17 -0
  164. package/dist/modules/actions/jobs/failureStreak.js +47 -0
  165. package/dist/modules/actions/jobs/failureStreak.test.d.ts +1 -0
  166. package/dist/modules/actions/jobs/index.d.ts +33 -0
  167. package/dist/modules/actions/jobs/index.js +59 -0
  168. package/dist/modules/actions/jobs/memoryQueue.d.ts +33 -0
  169. package/dist/modules/actions/jobs/memoryQueue.js +194 -0
  170. package/dist/modules/actions/jobs/memoryStores.test.d.ts +1 -0
  171. package/dist/modules/actions/jobs/scheduler.d.ts +64 -0
  172. package/dist/modules/actions/jobs/scheduler.js +130 -0
  173. package/dist/modules/actions/jobs/scheduler.test.d.ts +1 -0
  174. package/dist/modules/actions/jobs/schedules.d.ts +23 -0
  175. package/dist/modules/actions/jobs/schedules.js +47 -0
  176. package/dist/modules/actions/jobs/storeClock.d.ts +16 -0
  177. package/dist/modules/actions/jobs/storeClock.js +32 -0
  178. package/dist/modules/actions/jobs/testing/jobQueueContract.d.ts +25 -0
  179. package/dist/modules/actions/jobs/worker.d.ts +64 -0
  180. package/dist/modules/actions/jobs/worker.js +267 -0
  181. package/dist/modules/actions/jobs/worker.test.d.ts +1 -0
  182. package/dist/modules/actions/moduleFor.js +6 -2
  183. package/dist/modules/actions/runtime/errors.d.ts +12 -0
  184. package/dist/modules/actions/runtime/errors.js +16 -1
  185. package/dist/modules/actions/runtime/kvChange.d.ts +27 -0
  186. package/dist/modules/actions/runtime/kvChange.js +23 -0
  187. package/dist/modules/actions/runtime/kvList.d.ts +51 -0
  188. package/dist/modules/actions/runtime/kvList.js +52 -0
  189. package/dist/modules/actions/runtime/kvStore.d.ts +8 -0
  190. package/dist/modules/actions/runtime/kvStore.js +46 -20
  191. package/dist/modules/actions/runtime/memoryKv.d.ts +3 -0
  192. package/dist/modules/actions/runtime/memoryKv.js +19 -1
  193. package/dist/modules/actions/runtime/namespaceKv.d.ts +4 -6
  194. package/dist/modules/actions/runtime/namespaceKv.js +15 -3
  195. package/dist/modules/actions/runtime/namespaceKv.test.d.ts +1 -0
  196. package/dist/modules/actions/runtime/precheck.js +5 -9
  197. package/dist/modules/actions/runtime/rateLimit.d.ts +19 -0
  198. package/dist/modules/actions/runtime/rateLimit.js +19 -0
  199. package/dist/modules/actions/runtime/redisKv.d.ts +27 -0
  200. package/dist/modules/actions/runtime/redisKv.integration.test.d.ts +1 -0
  201. package/dist/modules/actions/runtime/redisKv.js +46 -0
  202. package/dist/modules/actions/runtime/renderResolver.js +2 -2
  203. package/dist/modules/actions/runtime/report.js +2 -1
  204. package/dist/modules/actions/runtime/runAction.d.ts +10 -2
  205. package/dist/modules/actions/runtime/runAction.js +52 -116
  206. package/dist/modules/actions/runtime/runLogger.d.ts +5 -0
  207. package/dist/modules/actions/runtime/runLogger.js +8 -2
  208. package/dist/modules/actions/runtime/signing.d.ts +20 -0
  209. package/dist/modules/actions/runtime/signing.js +25 -0
  210. package/dist/modules/actions/runtime/signing.test.d.ts +1 -0
  211. package/dist/modules/actions/runtime/taskContext.d.ts +33 -0
  212. package/dist/modules/actions/runtime/taskContext.js +124 -0
  213. package/dist/modules/actions/tasks/builtins.js +5 -1
  214. package/dist/modules/actions/tasks/flow.js +81 -10
  215. package/dist/modules/actions/tasks/helpers.js +2 -2
  216. package/dist/modules/actions/tasks/http.js +2 -3
  217. package/dist/modules/actions/tasks/kv.js +39 -0
  218. package/dist/modules/actions/tasks/list.d.ts +2 -0
  219. package/dist/modules/actions/tasks/list.js +145 -0
  220. package/dist/modules/actions/tasks/realtime.d.ts +2 -0
  221. package/dist/modules/actions/tasks/realtime.js +104 -0
  222. package/dist/modules/actions/tasks/registry.d.ts +10 -1
  223. package/dist/modules/actions/tasks/registry.js +17 -6
  224. package/dist/modules/actions/transport/callHandler.js +6 -3
  225. package/dist/modules/actions/transport/catalogHandler.d.ts +5 -2
  226. package/dist/modules/actions/transport/catalogHandler.js +3 -2
  227. package/dist/modules/actions/transport/stream.d.ts +4 -5
  228. package/dist/modules/actions/transport/stream.js +12 -76
  229. package/dist/modules/actions/transport/webhookHandler.js +5 -4
  230. package/dist/modules/actions/types.d.ts +162 -4
  231. package/dist/modules/connectors/engine.js +3 -5
  232. package/dist/modules/functions/build.d.ts +22 -0
  233. package/dist/modules/functions/build.js +122 -0
  234. package/dist/modules/functions/build.test.d.ts +1 -0
  235. package/dist/modules/functions/capabilities.d.ts +22 -0
  236. package/dist/modules/functions/capabilities.js +228 -0
  237. package/dist/modules/functions/config.d.ts +34 -0
  238. package/dist/modules/functions/config.js +24 -0
  239. package/dist/modules/functions/context.d.ts +17 -0
  240. package/dist/modules/functions/context.js +88 -0
  241. package/dist/modules/functions/contract.d.ts +121 -0
  242. package/dist/modules/functions/contract.js +9 -0
  243. package/dist/modules/functions/driver.d.ts +36 -0
  244. package/dist/modules/functions/driver.js +150 -0
  245. package/dist/modules/functions/load.d.ts +15 -0
  246. package/dist/modules/functions/load.js +45 -0
  247. package/dist/modules/functions/load.test.d.ts +1 -0
  248. package/dist/modules/functions/local.d.ts +29 -0
  249. package/dist/modules/functions/local.js +61 -0
  250. package/dist/modules/functions/local.test.d.ts +1 -0
  251. package/dist/modules/functions/manifest.d.ts +24 -0
  252. package/dist/modules/functions/manifest.js +163 -0
  253. package/dist/modules/functions/manifest.test.d.ts +1 -0
  254. package/dist/modules/functions/native.d.ts +8 -0
  255. package/dist/modules/functions/native.js +21 -0
  256. package/dist/modules/functions/native.test.d.ts +1 -0
  257. package/dist/modules/functions/protocol.d.ts +216 -0
  258. package/dist/modules/functions/protocol.js +18 -0
  259. package/dist/modules/functions/routes.d.ts +36 -0
  260. package/dist/modules/functions/routes.js +181 -0
  261. package/dist/modules/functions/routes.test.d.ts +1 -0
  262. package/dist/modules/functions/runner/messages.d.ts +5 -0
  263. package/dist/modules/functions/runner/messages.js +188 -0
  264. package/dist/modules/functions/runner/remote.d.ts +19 -0
  265. package/dist/modules/functions/runner/remote.js +116 -0
  266. package/dist/modules/functions/runner/remote.test.d.ts +1 -0
  267. package/dist/modules/functions/runner/service.d.ts +32 -0
  268. package/dist/modules/functions/runner/service.js +209 -0
  269. package/dist/modules/functions/sandbox/guest.d.ts +45 -0
  270. package/dist/modules/functions/sandbox/guest.js +660 -0
  271. package/dist/modules/functions/sandbox/isolate.d.ts +37 -0
  272. package/dist/modules/functions/sandbox/isolate.js +347 -0
  273. package/dist/modules/functions/sandbox/isolate.test.d.ts +1 -0
  274. package/dist/modules/functions/sandbox/prelude.d.ts +2 -0
  275. package/dist/modules/functions/sandbox/prelude.js +49 -0
  276. package/dist/modules/functions/space.d.ts +40 -0
  277. package/dist/modules/functions/space.js +162 -0
  278. package/dist/modules/functions/testing/tasksOf.d.ts +4 -0
  279. package/dist/modules/functions/tryEntry.d.ts +9 -0
  280. package/dist/modules/functions/tryEntry.js +49 -0
  281. package/dist/modules/mongo/index.d.ts +4 -0
  282. package/dist/modules/mongo/jobQueue.d.ts +38 -0
  283. package/dist/modules/mongo/jobQueue.js +335 -0
  284. package/dist/modules/mongo/kv.d.ts +15 -0
  285. package/dist/modules/mongo/kv.js +129 -0
  286. package/dist/modules/mongo/stores.integration.test.d.ts +1 -0
  287. package/dist/modules/mysql/accounts.js +19 -9
  288. package/dist/modules/mysql/index.d.ts +2 -1
  289. package/dist/modules/mysql/jobs/index.d.ts +5 -0
  290. package/dist/modules/mysql/jobs/jobQueue.d.ts +40 -0
  291. package/dist/modules/mysql/jobs/jobQueue.js +375 -0
  292. package/dist/modules/mysql/jobs/kv.d.ts +18 -0
  293. package/dist/modules/mysql/jobs/kv.js +108 -0
  294. package/dist/modules/mysql/jobs/schema.d.ts +33 -0
  295. package/dist/modules/mysql/jobs/schema.js +113 -0
  296. package/dist/modules/mysql/jobs/stores.integration.test.d.ts +1 -0
  297. package/dist/modules/mysql/schema.js +25 -0
  298. package/dist/modules/mysql/sessions.d.ts +1 -11
  299. package/dist/modules/mysql/sessions.js +6 -1
  300. package/dist/modules/realtime/declarations.d.ts +11 -0
  301. package/dist/modules/realtime/declarations.js +26 -0
  302. package/dist/modules/realtime/grants.d.ts +23 -0
  303. package/dist/modules/realtime/grants.js +61 -0
  304. package/dist/modules/realtime/handlers.d.ts +74 -0
  305. package/dist/modules/realtime/handlers.js +224 -0
  306. package/dist/modules/realtime/hub.d.ts +62 -0
  307. package/dist/modules/realtime/hub.js +141 -0
  308. package/dist/modules/realtime/index.d.ts +51 -0
  309. package/dist/modules/realtime/index.js +62 -0
  310. package/dist/modules/realtime/memoryPubSub.d.ts +10 -0
  311. package/dist/modules/realtime/memoryPubSub.js +43 -0
  312. package/dist/modules/realtime/realtime.test.d.ts +1 -0
  313. package/dist/modules/realtime/redisPubSub.d.ts +24 -0
  314. package/dist/modules/realtime/redisPubSub.js +36 -0
  315. package/dist/modules/realtime/redisPubSub.test.d.ts +1 -0
  316. package/dist/modules/realtime/socket.d.ts +25 -0
  317. package/dist/modules/realtime/socket.js +122 -0
  318. package/dist/modules/realtime/socket.test.d.ts +1 -0
  319. package/dist/modules/rsc/handler.d.ts +16 -0
  320. package/dist/modules/rsc/handler.js +17 -10
  321. package/dist/modules/rsc/handler.test.d.ts +1 -0
  322. package/dist/modules/rsc/resolveRscData.js +2 -1
  323. package/dist/modules/runtime/bundle.d.ts +24 -0
  324. package/dist/modules/runtime/bundle.js +102 -0
  325. package/dist/modules/runtime/contract.d.ts +37 -0
  326. package/dist/modules/runtime/contract.js +21 -0
  327. package/dist/modules/runtime/entry.test.d.ts +1 -0
  328. package/dist/modules/runtime/host.d.ts +38 -0
  329. package/dist/modules/runtime/host.js +68 -0
  330. package/dist/modules/runtime/inside.d.ts +15 -0
  331. package/dist/modules/runtime/inside.js +47 -0
  332. package/dist/modules/runtime/inside.test.d.ts +1 -0
  333. package/dist/modules/runtime/runner.d.ts +11 -0
  334. package/dist/modules/runtime/runner.js +38 -0
  335. package/dist/modules/runtime/runtime.test.d.ts +1 -0
  336. package/dist/modules/runtime/stages.d.ts +50 -0
  337. package/dist/modules/runtime/stages.js +117 -0
  338. package/dist/modules/ssr/Component.d.ts +4 -5
  339. package/dist/modules/ssr/Component.js +2 -2
  340. package/dist/modules/ssr/handler.js +10 -2
  341. package/dist/modules/ssr/loadPluginComponents.js +2 -1
  342. package/dist/modules/ssr/prepareRender.js +39 -13
  343. package/dist/modules/ssr/preview.d.ts +3 -0
  344. package/dist/modules/ssr/preview.js +17 -7
  345. package/dist/modules/ssr/redirect.test.d.ts +1 -0
  346. package/dist/modules/ssr/registerExternalPlugins.d.ts +6 -1
  347. package/dist/modules/ssr/registerExternalPlugins.js +19 -16
  348. package/dist/modules/ssr/registerExternalPlugins.test.d.ts +1 -0
  349. package/dist/modules/ssr/reportMissingPlugins.d.ts +9 -0
  350. package/dist/modules/ssr/reportMissingPlugins.js +24 -0
  351. package/dist/modules/ssr/reportMissingPlugins.test.d.ts +1 -0
  352. package/dist/modules/ssr/streamBody.d.ts +2 -2
  353. package/dist/modules/ssr/streamBody.js +4 -1
  354. package/dist/modules/ssr/template.js +1 -1
  355. package/dist/modules/ssr/views/template.ejs +8 -5
  356. package/dist/mongo.d.ts +25 -0
  357. package/dist/mongo.js +3 -0
  358. package/dist/mysql.d.ts +11 -0
  359. package/dist/mysql.js +4 -1
  360. package/dist/oauth.js +2 -2
  361. package/dist/plugins/compile.js +9 -4
  362. package/dist/plugins/copy.js +3 -3
  363. package/dist/plugins/manager.d.ts +35 -3
  364. package/dist/plugins/manager.js +109 -38
  365. package/dist/runtime.d.ts +15 -0
  366. package/dist/runtime.js +7 -0
  367. package/package.json +52 -11
  368. package/skills/plitzi-authoring/SKILL.md +153 -263
  369. package/skills/plitzi-authoring/reference/accessibility.md +111 -0
  370. package/skills/plitzi-authoring/reference/authoring-errors.md +70 -0
  371. package/skills/plitzi-authoring/reference/data-and-visibility.md +180 -0
  372. package/skills/plitzi-authoring/reference/elements-and-styles.md +161 -0
  373. package/skills/plitzi-authoring/reference/flows.md +171 -0
  374. package/skills/plitzi-authoring/reference/layouts.md +140 -0
  375. package/skills/plitzi-authoring/reference/lists.md +74 -0
  376. package/skills/plitzi-authoring/reference/performance.md +67 -0
  377. package/skills/plitzi-authoring/reference/plugins.md +167 -0
  378. package/skills/plitzi-authoring/reference/review-checklist.md +67 -0
  379. package/skills/plitzi-authoring/reference/structure.md +83 -0
  380. package/skills/plitzi-authoring/reference/templates-and-export.md +54 -0
  381. package/skills/plitzi-authoring/reference/templates.md +109 -0
  382. package/skills/plitzi-authoring/reference/testing.md +119 -0
  383. package/skills/plitzi-authoring/reference/validation.md +73 -0
  384. package/dist/modules/actions/runtime/schedule.d.ts +0 -35
  385. package/dist/modules/actions/runtime/schedule.js +0 -76
  386. /package/dist/{modules/actions/runtime/schedule.test.d.ts → adapters/jsonAdapters.test.d.ts} +0 -0
@@ -0,0 +1,267 @@
1
+ import { serverLog } from "../../../helpers/serverLog.js";
2
+ import { createFailureStreak } from "./failureStreak.js";
3
+ import { ActionRunError } from "../runtime/errors.js";
4
+ import { randomUUID } from "node:crypto";
5
+ //#region src/modules/actions/jobs/worker.ts
6
+ /**
7
+ * Refusals that mean "not now" rather than "not ever".
8
+ *
9
+ * Both are the cluster protecting itself — another run holds the single-flight key, or this replica is already
10
+ * carrying as much as it will — and neither says anything about the job. So the job goes back to the queue with
11
+ * its attempt REFUNDED: burning one would let a busy afternoon retire a nightly digest as dead.
12
+ */
13
+ var TRY_AGAIN = /* @__PURE__ */ new Set(["duplicate", "over_capacity"]);
14
+ var messageOf = (error) => error instanceof Error ? error.message : String(error);
15
+ /**
16
+ * Why a run that ended badly did, in the words an operator can act on: the step that failed and what it said.
17
+ *
18
+ * Read from `steps` — the outline, whose errors are already redacted of every credential the run resolved — so
19
+ * nothing lands on the job that the run history would not show. A run that failed without any step failing (a
20
+ * timeout between steps, a budget spent) has no step to name, and says how it ended instead.
21
+ */
22
+ var failureOf = (result) => {
23
+ const failed = result.steps.find((step) => step.phase === "flow" && step.status === "failed");
24
+ return failed ? `step "${failed.id}" failed: ${failed.error ?? "no reason given"}` : `the flow ended ${result.status}`;
25
+ };
26
+ /**
27
+ * Refusals no number of attempts will change: the action was deleted, its schedule was switched off, or the
28
+ * document refuses this trigger. Retrying is noise, and the job stops where an operator can see why.
29
+ */
30
+ var GIVE_UP = /* @__PURE__ */ new Set([
31
+ "not_found",
32
+ "disabled",
33
+ "forbidden",
34
+ "recursion"
35
+ ]);
36
+ var createJobWorker = ({ queue, lookups, module, workers = 4, pollMs = 1e3, leaseMs = 3e4, backoff = {}, workerId = `${process.pid}-${randomUUID().slice(0, 8)}`, onError: customOnError }) => {
37
+ const onError = customOnError ?? ((error) => serverLog.error("Actions", "job worker failed", error));
38
+ const claimFailures = createFailureStreak("Actions", "job claim");
39
+ const heartbeatFailures = createFailureStreak("Actions", "job heartbeat");
40
+ const { baseMs = 3e4, maxMs = 9e5 } = backoff;
41
+ const active = /* @__PURE__ */ new Map();
42
+ /** Every job this worker started, until it has settled — what `stop()` waits for. */
43
+ const inFlight = /* @__PURE__ */ new Set();
44
+ let timer;
45
+ let heart;
46
+ let stopping = false;
47
+ let pass;
48
+ const isStopping = () => stopping;
49
+ /** Doubles per attempt, with a jitter so a batch that failed together does not retry together. */
50
+ const retryDelay = (attempts) => {
51
+ const step = Math.min(maxMs, baseMs * 2 ** Math.max(0, attempts - 1));
52
+ return step + Math.floor(Math.random() * Math.min(step, 3e4));
53
+ };
54
+ const settle = async (settlement) => {
55
+ try {
56
+ await queue.settle(settlement);
57
+ } catch (error) {
58
+ onError(error);
59
+ }
60
+ };
61
+ /** Back to the queue with nothing held against it — see {@link TRY_AGAIN}. */
62
+ const release = (job, delayMs, error) => settle({
63
+ jobId: job.id,
64
+ workerId,
65
+ status: "pending",
66
+ delayMs,
67
+ error
68
+ });
69
+ const fail = (job, runId, error) => {
70
+ const exhausted = job.attempts >= job.maxAttempts;
71
+ return settle({
72
+ jobId: job.id,
73
+ workerId,
74
+ status: exhausted ? "dead" : "pending",
75
+ ...exhausted ? {} : { delayMs: retryDelay(job.attempts) },
76
+ ...runId ? { runId } : {},
77
+ error,
78
+ attempt: {
79
+ status: "failed",
80
+ workerId,
81
+ ...runId ? { runId } : {},
82
+ error
83
+ }
84
+ });
85
+ };
86
+ const execute = async (job) => {
87
+ const entry = await lookups.getAction(job.spaceId, job.actionId);
88
+ if (!entry) {
89
+ await settle({
90
+ jobId: job.id,
91
+ workerId,
92
+ status: "dead",
93
+ error: "the action this job belongs to no longer exists",
94
+ attempt: {
95
+ status: "failed",
96
+ workerId,
97
+ error: "action not found"
98
+ }
99
+ });
100
+ return;
101
+ }
102
+ let run;
103
+ try {
104
+ run = await module.guards.begin({
105
+ spaceId: job.spaceId,
106
+ actionId: job.actionId,
107
+ callerId: "schedule",
108
+ input: job.input,
109
+ idempotencyKey: job.id,
110
+ sharedKey: true,
111
+ ttlMs: module.limitsFor(entry.document).timeoutMs
112
+ });
113
+ } catch (error) {
114
+ const refusal = error instanceof ActionRunError ? error.reason : void 0;
115
+ /**
116
+ * Two different things, and telling them apart is what decides whether the job survives.
117
+ *
118
+ * A REFUSAL is the cluster answering: another run holds the key, this replica is full. A throw that is not a
119
+ * refusal means the guards could not be consulted at all — the shared store is unreachable — and that is
120
+ * infrastructure rather than anything about this job. Both go back with the attempt REFUNDED.
121
+ *
122
+ * Counting an outage as attempts is how a queue loses work: with three attempts and a widening backoff, a
123
+ * Redis blip of two minutes retires every scheduled job in the cluster as `dead`, permanently, and the digest
124
+ * nobody got is indistinguishable from one nobody asked for. The job comes back instead, carrying why, and an
125
+ * operator sees a queue that is waiting rather than a queue that has been emptied.
126
+ */
127
+ if (!refusal || TRY_AGAIN.has(refusal)) {
128
+ await release(job, retryDelay(1), `not started: ${refusal ?? messageOf(error)}`);
129
+ return;
130
+ }
131
+ await fail(job, void 0, `could not start: ${refusal}`);
132
+ return;
133
+ }
134
+ active.set(job.id, run.controller);
135
+ try {
136
+ const result = await module.runAction({
137
+ entry,
138
+ input: job.input,
139
+ spaceId: job.spaceId,
140
+ environment: job.environment,
141
+ trigger: job.trigger,
142
+ callerId: "schedule",
143
+ runId: run.runId,
144
+ signal: run.controller.signal
145
+ });
146
+ if (result.status === "completed") {
147
+ await settle({
148
+ jobId: job.id,
149
+ workerId,
150
+ status: "succeeded",
151
+ runId: run.runId,
152
+ attempt: {
153
+ status: "succeeded",
154
+ workerId,
155
+ runId: run.runId
156
+ }
157
+ });
158
+ return;
159
+ }
160
+ if (result.status === "aborted" && run.controller.signal.aborted) {
161
+ await settle({
162
+ jobId: job.id,
163
+ workerId,
164
+ status: "cancelled",
165
+ runId: run.runId,
166
+ attempt: {
167
+ status: "cancelled",
168
+ workerId,
169
+ runId: run.runId
170
+ }
171
+ });
172
+ return;
173
+ }
174
+ await fail(job, run.runId, failureOf(result));
175
+ } catch (error) {
176
+ const reason = error instanceof ActionRunError ? error.reason : void 0;
177
+ if (reason && GIVE_UP.has(reason)) {
178
+ await settle({
179
+ jobId: job.id,
180
+ workerId,
181
+ status: "dead",
182
+ runId: run.runId,
183
+ error: reason,
184
+ attempt: {
185
+ status: "failed",
186
+ workerId,
187
+ runId: run.runId,
188
+ error: reason
189
+ }
190
+ });
191
+ return;
192
+ }
193
+ await fail(job, run.runId, messageOf(error));
194
+ } finally {
195
+ active.delete(job.id);
196
+ await module.guards.end(run);
197
+ }
198
+ };
199
+ const poll = async () => {
200
+ const free = workers - active.size;
201
+ if (stopping || free <= 0) return 0;
202
+ const claimed = await queue.claim({
203
+ workerId,
204
+ leaseMs,
205
+ limit: free
206
+ });
207
+ if (isStopping()) {
208
+ await Promise.all(claimed.map((job) => release(job, 0, "not started: the replica was shutting down")));
209
+ return 0;
210
+ }
211
+ for (const job of claimed) {
212
+ const running = execute(job).catch(onError);
213
+ inFlight.add(running);
214
+ running.finally(() => inFlight.delete(running));
215
+ }
216
+ return claimed.length;
217
+ };
218
+ /** Renews every claim this worker holds, and stops the ones an operator asked to cancel. */
219
+ const beat = async () => {
220
+ const held = [...active.keys()];
221
+ if (held.length === 0) return;
222
+ for (const jobId of await queue.heartbeat({
223
+ jobIds: held,
224
+ workerId,
225
+ leaseMs
226
+ })) active.get(jobId)?.abort();
227
+ };
228
+ /** One pass at a time, and the one under way is kept so `stop()` can wait for the claim it may be making. */
229
+ const guarded = async () => {
230
+ if (pass) return;
231
+ pass = poll().then(() => claimFailures.succeeded()).catch(customOnError ?? claimFailures.failed).finally(() => {
232
+ pass = void 0;
233
+ });
234
+ await pass;
235
+ };
236
+ return {
237
+ poll,
238
+ beat,
239
+ inFlight: () => active.size,
240
+ start: () => {
241
+ if (timer || workers <= 0) return;
242
+ timer = setInterval(() => void guarded(), pollMs);
243
+ timer.unref();
244
+ heart = setInterval(() => void beat().then(() => heartbeatFailures.succeeded()).catch(customOnError ?? heartbeatFailures.failed), Math.max(1e3, Math.floor(leaseMs / 3)));
245
+ heart.unref();
246
+ },
247
+ stop: async () => {
248
+ stopping = true;
249
+ clearInterval(timer);
250
+ timer = void 0;
251
+ await pass;
252
+ if (inFlight.size > 0) serverLog.info("Actions", `waiting for ${inFlight.size} running job(s) to finish before stopping`);
253
+ /**
254
+ * Drained rather than abandoned: a deploy finishes the jobs it started instead of leaving them to be retried.
255
+ *
256
+ * The heartbeat is stopped only AFTER, and that is the half that makes waiting safe. A job longer than its
257
+ * lease used to lose its claim the moment the drain began — another replica took it over and ran it again
258
+ * while this one was still finishing it, and the drain gave up at the lease regardless.
259
+ */
260
+ await Promise.allSettled([...inFlight]);
261
+ clearInterval(heart);
262
+ heart = void 0;
263
+ }
264
+ };
265
+ };
266
+ //#endregion
267
+ export { createJobWorker };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { createActionsModule } from "./index.js";
2
+ import { realtimeModuleFor } from "../realtime/index.js";
2
3
  //#region src/modules/actions/moduleFor.ts
3
4
  /**
4
5
  * The one actions module for a server, whoever asks first.
@@ -19,15 +20,18 @@ var actionsModuleFor = (config) => {
19
20
  if (existing) return existing;
20
21
  const module = createActionsModule({
21
22
  lookups,
22
- tasks: config.action?.tasks,
23
+ functions: config.functions,
23
24
  limits: config.action?.limits,
24
25
  concurrency: config.action?.concurrency,
25
26
  kv: config.action?.kv,
27
+ jobs: config.action?.jobs,
26
28
  rateLimit: config.action?.rateLimit,
27
29
  idempotency: config.action?.idempotency,
30
+ signingSecret: config.action?.signingSecret,
28
31
  dbDrivers: config.action?.dbDrivers,
29
32
  email: config.action?.email,
30
- onRun: config.action?.onRun
33
+ onRun: config.action?.onRun,
34
+ realtime: realtimeModuleFor(config)
31
35
  });
32
36
  modules.set(config, module);
33
37
  return module;
@@ -14,3 +14,15 @@ export declare class ActionRunError extends Error {
14
14
  steps?: ActionRunStep[];
15
15
  constructor(reason: ActionErrorReason, message: string);
16
16
  }
17
+ /**
18
+ * A step refusing to go on, with a reason written for whoever called — a password too easy to guess, a board that is
19
+ * read-only, a quota reached.
20
+ *
21
+ * What any other step throws stays in the run's record and never reaches the caller: an error can carry a query, a
22
+ * URL, a credential's name, and a page is shown to anybody. This one says, by its type, that its message was written
23
+ * to be read. A run that ends on it answers `status: 'failed'` with the message as `error`, which a page's flow reads
24
+ * as `{{ step.error }}` — the same place a refusal before the run began puts its own.
25
+ */
26
+ export declare class ActionRefusal extends Error {
27
+ constructor(message: string);
28
+ }
@@ -18,5 +18,20 @@ var ActionRunError = class extends Error {
18
18
  this.reason = reason;
19
19
  }
20
20
  };
21
+ /**
22
+ * A step refusing to go on, with a reason written for whoever called — a password too easy to guess, a board that is
23
+ * read-only, a quota reached.
24
+ *
25
+ * What any other step throws stays in the run's record and never reaches the caller: an error can carry a query, a
26
+ * URL, a credential's name, and a page is shown to anybody. This one says, by its type, that its message was written
27
+ * to be read. A run that ends on it answers `status: 'failed'` with the message as `error`, which a page's flow reads
28
+ * as `{{ step.error }}` — the same place a refusal before the run began puts its own.
29
+ */
30
+ var ActionRefusal = class extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.name = "ActionRefusal";
34
+ }
35
+ };
21
36
  //#endregion
22
- export { ActionRunError };
37
+ export { ActionRefusal, ActionRunError };
@@ -0,0 +1,27 @@
1
+ /** What a change needs of a store: read a key, and write it only if it still holds what was read. */
2
+ export type KvChangeStore<V> = {
3
+ get: (key: string) => Promise<V | undefined>;
4
+ swap: (key: string, expected: V | undefined, next: V, ttlSeconds?: number) => Promise<boolean>;
5
+ };
6
+ /**
7
+ * The lifetime a change writes with: a number of seconds, one worked out from the value written, or none.
8
+ */
9
+ export type KvChangeLifetime<T> = number | ((next: T) => number | undefined);
10
+ export type KvChangeOptions<T> = {
11
+ lifetime?: KvChangeLifetime<T> | undefined;
12
+ /**
13
+ * The error a change that kept losing ends with — an `ActionRefusal` where there is one to make: being too busy is
14
+ * something to tell whoever asked, not a fault. Handed in because this function may import nothing.
15
+ */
16
+ refusal: (message: string) => Error;
17
+ };
18
+ /**
19
+ * Reads `key`, hands its value to `change`, and writes back what that answers — unless somebody wrote in between, and
20
+ * then it reads again and asks again. `change` answering `undefined` writes nothing. Answers what was written, or
21
+ * `undefined` when nothing was. Losing every time ends in `refusal`, whose message names no key: it is for a visitor.
22
+ *
23
+ * The one read-change-write there is: `ctx.kv.change`, a list's writes, and the sandbox's `ctx.kv.change` too — its
24
+ * guest is handed THIS function's source (`isolate.ts`), so it must stay self-contained: no import, nothing of this
25
+ * module's, only what the guest's globals also have (`setTimeout`, `Math`).
26
+ */
27
+ export declare const changeKv: <V, T extends V>(store: KvChangeStore<V>, key: string, change: (current: V | undefined) => T | undefined | Promise<T | undefined>, { lifetime, refusal }: KvChangeOptions<T>) => Promise<T | undefined>;
@@ -0,0 +1,23 @@
1
+ //#region src/modules/actions/runtime/kvChange.ts
2
+ /**
3
+ * Reads `key`, hands its value to `change`, and writes back what that answers — unless somebody wrote in between, and
4
+ * then it reads again and asks again. `change` answering `undefined` writes nothing. Answers what was written, or
5
+ * `undefined` when nothing was. Losing every time ends in `refusal`, whose message names no key: it is for a visitor.
6
+ *
7
+ * The one read-change-write there is: `ctx.kv.change`, a list's writes, and the sandbox's `ctx.kv.change` too — its
8
+ * guest is handed THIS function's source (`isolate.ts`), so it must stay self-contained: no import, nothing of this
9
+ * module's, only what the guest's globals also have (`setTimeout`, `Math`).
10
+ */
11
+ var changeKv = async (store, key, change, { lifetime, refusal }) => {
12
+ const attempts = 25;
13
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
14
+ const current = await store.get(key);
15
+ const next = await change(current);
16
+ if (next === void 0) return;
17
+ if (await store.swap(key, current, next, typeof lifetime === "function" ? lifetime(next) : lifetime)) return next;
18
+ await new Promise((resolve) => setTimeout(resolve, Math.random() * Math.min(5 + attempt * 5, 50)));
19
+ }
20
+ throw refusal("Many people are changing this at once — try again in a moment");
21
+ };
22
+ //#endregion
23
+ export { changeKv };
@@ -0,0 +1,51 @@
1
+ /** One entry of a list: what it is called, where it sorts, and what it carries. */
2
+ export type KvListEntry = {
3
+ id: string;
4
+ score: number;
5
+ value: unknown;
6
+ };
7
+ export type KvListRange = {
8
+ /** How many entries to skip first. 0 when absent. */
9
+ offset?: number;
10
+ /** How many to answer. All of them when absent. */
11
+ limit?: number;
12
+ /** `desc` — the highest score first — when absent. */
13
+ order?: 'asc' | 'desc';
14
+ };
15
+ /**
16
+ * The most a list holds. A list is one value of the store, read and written whole, so it is for what a page shows at
17
+ * once — the latest twenty, a leaderboard, a gallery — not for a table.
18
+ */
19
+ export declare const MAX_LIST_ENTRIES = 500;
20
+ /**
21
+ * Its largest size, as JSON. Every store holds far more; this is what a list is read and written whole for — a page's
22
+ * worth of entries, each an id and what the page shows — and past it, a list is being used as a table.
23
+ */
24
+ export declare const MAX_LIST_BYTES = 128000;
25
+ export declare const listIdProblem: (id: unknown) => string | undefined;
26
+ /** The entries a stored list holds — none for a key that holds nothing, or something that is not a list. */
27
+ export declare const parseList: (raw: string | undefined) => KvListEntry[];
28
+ export type KvListPutOptions = {
29
+ /** How many to keep, the highest scores; what falls past it is dropped. {@link MAX_LIST_ENTRIES} at most. */
30
+ keep?: number;
31
+ /**
32
+ * Replace an entry already there only with a score at least as high — a player's best, or the latest of two writes
33
+ * that arrived out of order. Otherwise the one there stays and nothing is written.
34
+ */
35
+ higherOnly?: boolean;
36
+ };
37
+ /** What a put did: whether the entry went in, and what `keep` dropped to make room. */
38
+ export type KvListPut = {
39
+ stored: boolean;
40
+ dropped: KvListEntry[];
41
+ };
42
+ /**
43
+ * The list with `entry` in it — replacing the one with its id — sorted and cut to the highest `keep`, and what the cut
44
+ * dropped; `undefined` when `higherOnly` keeps the one there. Refused, with what to do instead, when the result would
45
+ * be larger than a list may be.
46
+ */
47
+ export declare const withEntry: (entries: KvListEntry[], entry: KvListEntry, { keep, higherOnly }?: KvListPutOptions) => {
48
+ kept: KvListEntry[];
49
+ dropped: KvListEntry[];
50
+ } | undefined;
51
+ export declare const rangeOf: (entries: KvListEntry[], { offset, limit, order }?: KvListRange) => KvListEntry[];
@@ -0,0 +1,52 @@
1
+ //#region src/modules/actions/runtime/kvList.ts
2
+ /**
3
+ * The most a list holds. A list is one value of the store, read and written whole, so it is for what a page shows at
4
+ * once — the latest twenty, a leaderboard, a gallery — not for a table.
5
+ */
6
+ var MAX_LIST_ENTRIES = 500;
7
+ /**
8
+ * Its largest size, as JSON. Every store holds far more; this is what a list is read and written whole for — a page's
9
+ * worth of entries, each an id and what the page shows — and past it, a list is being used as a table.
10
+ */
11
+ var MAX_LIST_BYTES = 128e3;
12
+ var ID = /^[A-Za-z0-9:_.@-]{1,128}$/;
13
+ var listIdProblem = (id) => typeof id === "string" && ID.test(id) ? void 0 : "A list entry’s id is 1-128 of A-Z a-z 0-9 : _ . @ -";
14
+ var isRecord = (value) => typeof value === "object" && value !== null;
15
+ var isEntry = (value) => isRecord(value) && typeof value.id === "string" && typeof value.score === "number";
16
+ /** The entries a stored list holds — none for a key that holds nothing, or something that is not a list. */
17
+ var parseList = (raw) => {
18
+ if (raw === void 0) return [];
19
+ try {
20
+ const value = JSON.parse(raw);
21
+ return Array.isArray(value) ? value.filter(isEntry) : [];
22
+ } catch {
23
+ return [];
24
+ }
25
+ };
26
+ /** Highest score first; entries that tie keep a stable order, by id. */
27
+ var byScore = (a, b) => b.score - a.score || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
28
+ /**
29
+ * The list with `entry` in it — replacing the one with its id — sorted and cut to the highest `keep`, and what the cut
30
+ * dropped; `undefined` when `higherOnly` keeps the one there. Refused, with what to do instead, when the result would
31
+ * be larger than a list may be.
32
+ */
33
+ var withEntry = (entries, entry, { keep = 500, higherOnly = false } = {}) => {
34
+ const there = entries.find((existing) => existing.id === entry.id);
35
+ if (higherOnly && there && there.score > entry.score) return;
36
+ const sorted = [...entries.filter((existing) => existing.id !== entry.id), entry].sort(byScore);
37
+ const cut = Math.min(Math.max(0, Math.floor(keep)), 500);
38
+ const kept = sorted.slice(0, cut);
39
+ const bytes = Buffer.byteLength(JSON.stringify(kept));
40
+ if (bytes > 128e3) throw new Error(`A list is at most ${MAX_LIST_BYTES} bytes and this one would be ${bytes}: keep fewer entries (\`keep\`), or store less in each one — an id and what a page shows, with the rest under its own key`);
41
+ return {
42
+ kept,
43
+ dropped: sorted.slice(cut)
44
+ };
45
+ };
46
+ var rangeOf = (entries, { offset = 0, limit, order = "desc" } = {}) => {
47
+ const ordered = order === "asc" ? entries.toReversed() : entries;
48
+ const start = Math.max(0, Math.floor(offset));
49
+ return ordered.slice(start, limit === void 0 ? void 0 : start + Math.max(0, Math.floor(limit)));
50
+ };
51
+ //#endregion
52
+ export { MAX_LIST_BYTES, MAX_LIST_ENTRIES, listIdProblem, parseList, rangeOf, withEntry };
@@ -23,5 +23,13 @@ export type KvStoreConfig = {
23
23
  * table.
24
24
  * 3. **Nothing is caught.** This is not a cache: a miss means the rate limit did not count and the idempotency key
25
25
  * was not seen, so an adapter that cannot answer fails the run rather than being read as "no value".
26
+ *
27
+ * A list is one value, changed only by `swap`: read, change, write back if nobody wrote in between, and read again if
28
+ * somebody did. Every adapter can do that much, so lists work on all of them without a command of their own. They
29
+ * live under `list:` rather than beside the values, so no key a flow writes can be one.
26
30
  */
31
+ /** What every key is prefixed with unless the store was given another. */
32
+ export declare const DEFAULT_KV_PREFIX = "kv:";
33
+ /** Where a list lives, after the prefix: its own segment, so a list and a value of the same name never meet. */
34
+ export declare const LIST_SEGMENT = "list:";
27
35
  export declare const createKvStore: (adapter: ActionKvAdapter, { prefix }?: KvStoreConfig) => ActionKvStore;
@@ -1,23 +1,20 @@
1
- //#region src/modules/actions/runtime/kvStore.ts
2
- /**
3
- * The `kv` tasks, over whatever a deployment gave them to write into.
4
- *
5
- * The adapter is transport: strings in, strings out. Everything a counter's BEHAVIOUR depends on is here, in one
6
- * place, for every deployment at once — which is the point of the split. Three rules:
7
- *
8
- * 1. **Values round-trip through JSON**, so a flow reads back what it wrote instead of its own `toString`. A value
9
- * written by something else is answered as the string it is rather than failing.
10
- * 2. **A counter's TTL is set once, by whoever created it, and never extended.** A window refreshed on every hit
11
- * never closes while traffic keeps arriving — which is a rate limit that stops limiting exactly when it is
12
- * being leant on. The counter is the one that created it precisely when the increment returns its own amount,
13
- * so this needs no conditional-expire support from the adapter and works the same on Redis, Memcached or a
14
- * table.
15
- * 3. **Nothing is caught.** This is not a cache: a miss means the rate limit did not count and the idempotency key
16
- * was not seen, so an adapter that cannot answer fails the run rather than being read as "no value".
17
- */
1
+ import { ActionRefusal } from "./errors.js";
2
+ import { changeKv } from "./kvChange.js";
3
+ import { listIdProblem, parseList, rangeOf, withEntry } from "./kvList.js";
4
+ /** Where a list lives, after the prefix: its own segment, so a list and a value of the same name never meet. */
5
+ var LIST_SEGMENT = "list:";
6
+ var refusal = (message) => new ActionRefusal(message);
18
7
  var createKvStore = (adapter, { prefix = "kv:" } = {}) => {
19
8
  const prefixed = (key) => `${prefix}${key}`;
20
- return {
9
+ const listKey = (list) => `${prefix}${LIST_SEGMENT}${list}`;
10
+ /** Applies `change` to the list and writes it back unless somebody wrote first — then reads again. */
11
+ const changeList = async (list, change) => {
12
+ return await changeKv(adapter, listKey(list), (raw) => {
13
+ const next = change(parseList(raw));
14
+ return next ? JSON.stringify(next) : void 0;
15
+ }, { refusal }) !== void 0;
16
+ };
17
+ const store = {
21
18
  get: async (key) => {
22
19
  const raw = await adapter.get(prefixed(key));
23
20
  if (raw === void 0) return;
@@ -34,8 +31,37 @@ var createKvStore = (adapter, { prefix = "kv:" } = {}) => {
34
31
  const value = await adapter.increment(full, amount);
35
32
  if (ttlSeconds !== void 0 && value === amount) await adapter.expire(full, ttlSeconds);
36
33
  return value;
37
- }
34
+ },
35
+ swap: (key, expected, next, ttlSeconds) => adapter.swap(prefixed(key), expected === void 0 ? void 0 : JSON.stringify(expected), JSON.stringify(next), ttlSeconds),
36
+ listPut: async (list, entry, options = {}) => {
37
+ const problem = listIdProblem(entry.id);
38
+ if (problem) throw new Error(problem);
39
+ if (!Number.isFinite(entry.score)) throw new Error("A list entry’s score is a number");
40
+ let put = {
41
+ stored: false,
42
+ dropped: []
43
+ };
44
+ await changeList(list, (entries) => {
45
+ const next = withEntry(entries, entry, options);
46
+ put = next ? {
47
+ stored: true,
48
+ dropped: next.dropped
49
+ } : {
50
+ stored: false,
51
+ dropped: []
52
+ };
53
+ return next?.kept;
54
+ });
55
+ return put;
56
+ },
57
+ listRange: async (list, range) => rangeOf(parseList(await adapter.get(listKey(list))), range),
58
+ listRemove: (list, id) => changeList(list, (entries) => entries.some((entry) => entry.id === id) ? entries.filter((entry) => entry.id !== id) : void 0),
59
+ change: (key, change, lifetime) => changeKv(store, key, change, {
60
+ lifetime,
61
+ refusal
62
+ })
38
63
  };
64
+ return store;
39
65
  };
40
66
  //#endregion
41
- export { createKvStore };
67
+ export { LIST_SEGMENT, createKvStore };
@@ -1,2 +1,5 @@
1
+ import { StoreMethods } from '../../../core/server/fleet/channel';
1
2
  import { ActionKvAdapter } from '../types';
3
+ /** Every method of the store, for the copy a server's workers share (see `fleet/stores.ts`). */
4
+ export declare const KV_METHODS: StoreMethods<ActionKvAdapter>;
2
5
  export declare const createMemoryKv: () => ActionKvAdapter;
@@ -11,6 +11,15 @@
11
11
  */
12
12
  /** How many writes go by before the map is swept. Cheap enough to be unnoticeable, often enough to bound it. */
13
13
  var SWEEP_EVERY = 256;
14
+ /** Every method of the store, for the copy a server's workers share (see `fleet/stores.ts`). */
15
+ var KV_METHODS = {
16
+ get: true,
17
+ set: true,
18
+ delete: true,
19
+ increment: true,
20
+ expire: true,
21
+ swap: true
22
+ };
14
23
  var createMemoryKv = () => {
15
24
  const entries = /* @__PURE__ */ new Map();
16
25
  let writes = 0;
@@ -70,8 +79,17 @@ var createMemoryKv = () => {
70
79
  expiresAt: expiry(ttlSeconds)
71
80
  });
72
81
  return Promise.resolve();
82
+ },
83
+ swap: (key, expected, next, ttlSeconds) => {
84
+ if (read(key)?.value !== expected) return Promise.resolve(false);
85
+ entries.set(key, {
86
+ value: next,
87
+ expiresAt: expiry(ttlSeconds)
88
+ });
89
+ sweep();
90
+ return Promise.resolve(true);
73
91
  }
74
92
  };
75
93
  };
76
94
  //#endregion
77
- export { createMemoryKv };
95
+ export { KV_METHODS, createMemoryKv };
@@ -1,9 +1,7 @@
1
1
  import { ActionKvStore } from '../types';
2
+ export declare const namespaceKv: (store: ActionKvStore, spaceId: number) => ActionKvStore;
2
3
  /**
3
- * Prefixes every key with the space that wrote it.
4
- *
5
- * A shared store with unprefixed keys is one space reading — or overwriting — another's counters, which is the
6
- * cross-tenant leak this whole design exists to avoid. Done at the runner rather than in each task so a
7
- * deployment's own tasks inherit it too.
4
+ * Every key a space's store holds, as patterns over the adapter's keys (`createKvStore`'s `prefix`, `kv:` unless it was
5
+ * given another) — for whoever has to let all of them go at once: a space that is deleted, or reset to how it started.
8
6
  */
9
- export declare const namespaceKv: (store: ActionKvStore, spaceId: number) => ActionKvStore;
7
+ export declare const spaceKvPatterns: (spaceId: number, prefix?: string) => string[];