@plitzi/sdk-server 0.37.2 → 0.37.4

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 (359) hide show
  1. package/CHANGELOG.md +1170 -0
  2. package/README.md +100 -7
  3. package/dist/actions.d.ts +6 -2
  4. package/dist/actions.js +5 -2
  5. package/dist/adapters/cloudAdapters.js +4 -3
  6. package/dist/adapters/jsonAdapters.js +28 -6
  7. package/dist/adapters/jsonAdapters.test.d.ts +1 -0
  8. package/dist/auth.d.ts +7 -4
  9. package/dist/auth.js +8 -6
  10. package/dist/core/auth/api.d.ts +29 -1
  11. package/dist/core/auth/api.js +47 -13
  12. package/dist/core/auth/createAuth.d.ts +4 -0
  13. package/dist/core/auth/createAuth.js +3 -3
  14. package/dist/core/auth/credentials.d.ts +3 -0
  15. package/dist/core/auth/credentials.js +1 -1
  16. package/dist/core/auth/csrf.d.ts +25 -1
  17. package/dist/core/auth/csrf.js +29 -4
  18. package/dist/core/auth/renewal.d.ts +26 -0
  19. package/dist/core/auth/renewal.js +24 -0
  20. package/dist/core/auth/routes.js +8 -6
  21. package/dist/core/auth/session.d.ts +45 -0
  22. package/dist/core/auth/session.js +47 -1
  23. package/dist/core/auth/sessionActivity.d.ts +11 -0
  24. package/dist/core/auth/sessionActivity.js +14 -0
  25. package/dist/core/auth/throttle.d.ts +15 -0
  26. package/dist/core/auth/throttle.js +29 -1
  27. package/dist/core/auth/totp.d.ts +9 -1
  28. package/dist/core/auth/totp.js +11 -6
  29. package/dist/core/createServer.actions.test.d.ts +1 -0
  30. package/dist/core/createServer.d.ts +14 -1
  31. package/dist/core/createServer.functions.test.d.ts +1 -0
  32. package/dist/core/createServer.js +22 -12
  33. package/dist/core/fontStore.js +1 -1
  34. package/dist/core/handlers/authRouteHandlers.d.ts +1 -1
  35. package/dist/core/handlers/authRouteHandlers.js +57 -21
  36. package/dist/core/handlers/csrfMiddleware.d.ts +23 -0
  37. package/dist/core/handlers/csrfMiddleware.js +31 -7
  38. package/dist/core/handlers/csrfMiddleware.test.d.ts +1 -0
  39. package/dist/core/handlers/oauthRouteHandlers.js +5 -3
  40. package/dist/core/handlers/socialRouteHandlers.d.ts +1 -1
  41. package/dist/core/handlers/socialRouteHandlers.js +2 -1
  42. package/dist/core/http/dispatcher.d.ts +9 -1
  43. package/dist/core/http/dispatcher.js +38 -11
  44. package/dist/core/http/navigation.d.ts +5 -2
  45. package/dist/core/http/navigation.js +7 -4
  46. package/dist/core/http/socketResponse.d.ts +18 -0
  47. package/dist/core/http/socketResponse.js +59 -0
  48. package/dist/core/http/sse.d.ts +26 -0
  49. package/dist/core/http/sse.js +78 -0
  50. package/dist/core/http/stages/authRoutes.js +3 -1
  51. package/dist/core/http/stages/exchangeRoute.test.d.ts +1 -0
  52. package/dist/core/http/stages/middlewares.d.ts +1 -1
  53. package/dist/core/http/stages/middlewares.js +16 -10
  54. package/dist/core/http/stages/middlewares.test.d.ts +1 -0
  55. package/dist/core/http/stages/oauth.js +2 -2
  56. package/dist/core/http/stages/sessionRenewal.d.ts +16 -0
  57. package/dist/core/http/stages/sessionRenewal.js +81 -0
  58. package/dist/core/http/stages/sessionRenewal.test.d.ts +1 -0
  59. package/dist/core/http/stages/signInRoute.d.ts +2 -0
  60. package/dist/core/http/stages/signInRoute.js +139 -0
  61. package/dist/core/http/stages/signInRoute.test.d.ts +1 -0
  62. package/dist/core/http/types.d.ts +9 -0
  63. package/dist/core/http/webExchange.d.ts +18 -0
  64. package/dist/core/http/webExchange.js +53 -0
  65. package/dist/core/oauth/authorize.js +37 -13
  66. package/dist/core/oauth/consentPage.js +11 -0
  67. package/dist/core/oauth/issueContext.d.ts +14 -0
  68. package/dist/core/oauth/issueContext.js +32 -0
  69. package/dist/core/oauth/records.d.ts +7 -0
  70. package/dist/core/oauth/register.d.ts +2 -0
  71. package/dist/core/oauth/register.js +32 -11
  72. package/dist/core/oauth/register.test.d.ts +1 -0
  73. package/dist/core/oauth/token.js +21 -6
  74. package/dist/core/oauth/token.test.d.ts +1 -0
  75. package/dist/core/requestParser.d.ts +4 -0
  76. package/dist/core/requestParser.js +14 -7
  77. package/dist/core/sdkAssets.js +1 -1
  78. package/dist/core/server/baseServer.d.ts +10 -0
  79. package/dist/core/server/baseServer.js +42 -11
  80. package/dist/core/server/closeOnSignals.js +4 -3
  81. package/dist/core/server/drain.d.ts +18 -0
  82. package/dist/core/server/drain.js +62 -0
  83. package/dist/core/server/drain.test.d.ts +1 -0
  84. package/dist/core/server/fleet/channel.d.ts +103 -0
  85. package/dist/core/server/fleet/channel.js +191 -0
  86. package/dist/core/server/fleet/channel.test.d.ts +1 -0
  87. package/dist/core/server/fleet/fixtures/fleetServer.d.ts +1 -0
  88. package/dist/core/server/fleet/fleet.integration.test.d.ts +1 -0
  89. package/dist/core/server/fleet/link.d.ts +23 -0
  90. package/dist/core/server/fleet/link.js +70 -0
  91. package/dist/core/server/fleet/relay.d.ts +11 -0
  92. package/dist/core/server/fleet/relay.js +104 -0
  93. package/dist/core/server/fleet/relay.test.d.ts +1 -0
  94. package/dist/core/server/fleet/role.d.ts +46 -0
  95. package/dist/core/server/fleet/role.js +59 -0
  96. package/dist/core/server/fleet/role.test.d.ts +1 -0
  97. package/dist/core/server/fleet/stores.d.ts +7 -0
  98. package/dist/core/server/fleet/stores.js +19 -0
  99. package/dist/core/server/fleet/supervisor.d.ts +26 -0
  100. package/dist/core/server/fleet/supervisor.js +108 -0
  101. package/dist/core/server/fleet/supervisor.test.d.ts +1 -0
  102. package/dist/core/server/pageServer.js +34 -5
  103. package/dist/core/services/action.js +10 -2
  104. package/dist/core/services/functionRoutes.d.ts +9 -0
  105. package/dist/core/services/functionRoutes.js +47 -0
  106. package/dist/core/services/realtime.d.ts +8 -0
  107. package/dist/core/services/realtime.js +57 -0
  108. package/dist/core/services/registry.js +6 -2
  109. package/dist/core/staticFiles.js +12 -4
  110. package/dist/core/staticFiles.test.d.ts +1 -0
  111. package/dist/core/transports.d.ts +13 -3
  112. package/dist/core/transports.js +14 -5
  113. package/dist/functions-api.d.ts +281 -0
  114. package/dist/functions.d.ts +19 -0
  115. package/dist/functions.js +5 -0
  116. package/dist/functionsRunner.d.ts +15 -0
  117. package/dist/functionsRunner.js +6 -0
  118. package/dist/handlers.d.ts +2 -2
  119. package/dist/handlers.js +2 -2
  120. package/dist/helpers/atomicFile.d.ts +10 -0
  121. package/dist/helpers/atomicFile.js +33 -0
  122. package/dist/helpers/atomicFile.test.d.ts +1 -0
  123. package/dist/helpers/buildResponseHelpers.js +35 -11
  124. package/dist/helpers/buildServerInfo.js +4 -2
  125. package/dist/helpers/buildServerInfo.test.d.ts +1 -0
  126. package/dist/helpers/cache/CompressedFileCache.d.ts +18 -0
  127. package/dist/helpers/cache/CompressedFileCache.js +44 -0
  128. package/dist/helpers/cache/CompressedFileCache.test.d.ts +1 -0
  129. package/dist/helpers/cache/cacheManager.d.ts +1 -1
  130. package/dist/helpers/cache/cacheManager.js +2 -1
  131. package/dist/helpers/cache/cacheManager.test.d.ts +1 -0
  132. package/dist/helpers/cache/index.d.ts +1 -1
  133. package/dist/helpers/cache/keys.d.ts +15 -1
  134. package/dist/helpers/cache/keys.js +47 -5
  135. package/dist/helpers/cache/serverCaches.d.ts +7 -1
  136. package/dist/helpers/compress.d.ts +3 -1
  137. package/dist/helpers/compress.js +11 -3
  138. package/dist/helpers/hydrationPayload.d.ts +11 -0
  139. package/dist/helpers/hydrationPayload.js +51 -0
  140. package/dist/helpers/hydrationPayload.test.d.ts +1 -0
  141. package/dist/helpers/metrics.d.ts +1 -1
  142. package/dist/helpers/metrics.js +3 -2
  143. package/dist/helpers/outboundGuard.d.ts +14 -6
  144. package/dist/helpers/outboundGuard.js +132 -8
  145. package/dist/helpers/paintedCookie.d.ts +8 -0
  146. package/dist/helpers/paintedCookie.js +13 -0
  147. package/dist/helpers/reportReactBuild.d.ts +9 -0
  148. package/dist/helpers/reportReactBuild.js +18 -0
  149. package/dist/helpers/reportReactBuild.test.d.ts +1 -0
  150. package/dist/helpers/serverLog.d.ts +25 -1
  151. package/dist/helpers/serverLog.js +68 -3
  152. package/dist/helpers/serverLog.test.d.ts +1 -0
  153. package/dist/helpers/wsFrame.d.ts +3 -0
  154. package/dist/helpers/wsFrame.js +5 -0
  155. package/dist/index.d.ts +6 -1
  156. package/dist/index.js +5 -2
  157. package/dist/kernel.d.ts +2 -1
  158. package/dist/kernel.js +3 -2
  159. package/dist/modules/actions/connectorWrite.js +2 -1
  160. package/dist/modules/actions/index.d.ts +29 -10
  161. package/dist/modules/actions/index.js +43 -6
  162. package/dist/modules/actions/jobs/failureStreak.d.ts +17 -0
  163. package/dist/modules/actions/jobs/failureStreak.js +47 -0
  164. package/dist/modules/actions/jobs/failureStreak.test.d.ts +1 -0
  165. package/dist/modules/actions/jobs/index.d.ts +2 -1
  166. package/dist/modules/actions/jobs/index.js +5 -3
  167. package/dist/modules/actions/jobs/memoryQueue.d.ts +3 -0
  168. package/dist/modules/actions/jobs/memoryQueue.js +17 -1
  169. package/dist/modules/actions/jobs/scheduler.d.ts +2 -0
  170. package/dist/modules/actions/jobs/scheduler.js +12 -7
  171. package/dist/modules/actions/jobs/worker.d.ts +3 -1
  172. package/dist/modules/actions/jobs/worker.js +9 -4
  173. package/dist/modules/actions/moduleFor.js +5 -2
  174. package/dist/modules/actions/runtime/errors.d.ts +12 -0
  175. package/dist/modules/actions/runtime/errors.js +16 -1
  176. package/dist/modules/actions/runtime/kvChange.d.ts +27 -0
  177. package/dist/modules/actions/runtime/kvChange.js +23 -0
  178. package/dist/modules/actions/runtime/kvList.d.ts +51 -0
  179. package/dist/modules/actions/runtime/kvList.js +52 -0
  180. package/dist/modules/actions/runtime/kvStore.d.ts +8 -0
  181. package/dist/modules/actions/runtime/kvStore.js +46 -20
  182. package/dist/modules/actions/runtime/memoryKv.d.ts +3 -0
  183. package/dist/modules/actions/runtime/memoryKv.js +19 -1
  184. package/dist/modules/actions/runtime/namespaceKv.d.ts +4 -6
  185. package/dist/modules/actions/runtime/namespaceKv.js +15 -3
  186. package/dist/modules/actions/runtime/namespaceKv.test.d.ts +1 -0
  187. package/dist/modules/actions/runtime/precheck.js +4 -8
  188. package/dist/modules/actions/runtime/rateLimit.d.ts +19 -0
  189. package/dist/modules/actions/runtime/rateLimit.js +19 -0
  190. package/dist/modules/actions/runtime/redisKv.d.ts +27 -0
  191. package/dist/modules/actions/runtime/redisKv.integration.test.d.ts +1 -0
  192. package/dist/modules/actions/runtime/redisKv.js +46 -0
  193. package/dist/modules/actions/runtime/report.js +2 -1
  194. package/dist/modules/actions/runtime/runAction.d.ts +10 -2
  195. package/dist/modules/actions/runtime/runAction.js +52 -116
  196. package/dist/modules/actions/runtime/runLogger.d.ts +5 -0
  197. package/dist/modules/actions/runtime/runLogger.js +8 -2
  198. package/dist/modules/actions/runtime/signing.d.ts +20 -0
  199. package/dist/modules/actions/runtime/signing.js +25 -0
  200. package/dist/modules/actions/runtime/signing.test.d.ts +1 -0
  201. package/dist/modules/actions/runtime/taskContext.d.ts +33 -0
  202. package/dist/modules/actions/runtime/taskContext.js +124 -0
  203. package/dist/modules/actions/tasks/builtins.js +5 -1
  204. package/dist/modules/actions/tasks/flow.js +81 -10
  205. package/dist/modules/actions/tasks/helpers.js +2 -2
  206. package/dist/modules/actions/tasks/http.js +2 -3
  207. package/dist/modules/actions/tasks/kv.js +39 -0
  208. package/dist/modules/actions/tasks/list.d.ts +2 -0
  209. package/dist/modules/actions/tasks/list.js +145 -0
  210. package/dist/modules/actions/tasks/realtime.d.ts +2 -0
  211. package/dist/modules/actions/tasks/realtime.js +104 -0
  212. package/dist/modules/actions/tasks/registry.d.ts +10 -1
  213. package/dist/modules/actions/tasks/registry.js +17 -6
  214. package/dist/modules/actions/transport/callHandler.js +5 -2
  215. package/dist/modules/actions/transport/catalogHandler.d.ts +5 -2
  216. package/dist/modules/actions/transport/catalogHandler.js +3 -2
  217. package/dist/modules/actions/transport/stream.d.ts +4 -5
  218. package/dist/modules/actions/transport/stream.js +12 -76
  219. package/dist/modules/actions/transport/webhookHandler.js +4 -3
  220. package/dist/modules/actions/types.d.ts +86 -3
  221. package/dist/modules/connectors/engine.js +3 -5
  222. package/dist/modules/functions/build.d.ts +22 -0
  223. package/dist/modules/functions/build.js +122 -0
  224. package/dist/modules/functions/build.test.d.ts +1 -0
  225. package/dist/modules/functions/capabilities.d.ts +22 -0
  226. package/dist/modules/functions/capabilities.js +228 -0
  227. package/dist/modules/functions/config.d.ts +34 -0
  228. package/dist/modules/functions/config.js +24 -0
  229. package/dist/modules/functions/context.d.ts +17 -0
  230. package/dist/modules/functions/context.js +88 -0
  231. package/dist/modules/functions/contract.d.ts +121 -0
  232. package/dist/modules/functions/contract.js +9 -0
  233. package/dist/modules/functions/driver.d.ts +36 -0
  234. package/dist/modules/functions/driver.js +150 -0
  235. package/dist/modules/functions/load.d.ts +15 -0
  236. package/dist/modules/functions/load.js +45 -0
  237. package/dist/modules/functions/load.test.d.ts +1 -0
  238. package/dist/modules/functions/local.d.ts +29 -0
  239. package/dist/modules/functions/local.js +61 -0
  240. package/dist/modules/functions/local.test.d.ts +1 -0
  241. package/dist/modules/functions/manifest.d.ts +24 -0
  242. package/dist/modules/functions/manifest.js +163 -0
  243. package/dist/modules/functions/manifest.test.d.ts +1 -0
  244. package/dist/modules/functions/native.d.ts +8 -0
  245. package/dist/modules/functions/native.js +21 -0
  246. package/dist/modules/functions/native.test.d.ts +1 -0
  247. package/dist/modules/functions/protocol.d.ts +216 -0
  248. package/dist/modules/functions/protocol.js +18 -0
  249. package/dist/modules/functions/routes.d.ts +36 -0
  250. package/dist/modules/functions/routes.js +181 -0
  251. package/dist/modules/functions/routes.test.d.ts +1 -0
  252. package/dist/modules/functions/runner/messages.d.ts +5 -0
  253. package/dist/modules/functions/runner/messages.js +188 -0
  254. package/dist/modules/functions/runner/remote.d.ts +19 -0
  255. package/dist/modules/functions/runner/remote.js +116 -0
  256. package/dist/modules/functions/runner/remote.test.d.ts +1 -0
  257. package/dist/modules/functions/runner/service.d.ts +32 -0
  258. package/dist/modules/functions/runner/service.js +209 -0
  259. package/dist/modules/functions/sandbox/guest.d.ts +45 -0
  260. package/dist/modules/functions/sandbox/guest.js +660 -0
  261. package/dist/modules/functions/sandbox/isolate.d.ts +37 -0
  262. package/dist/modules/functions/sandbox/isolate.js +347 -0
  263. package/dist/modules/functions/sandbox/isolate.test.d.ts +1 -0
  264. package/dist/modules/functions/sandbox/prelude.d.ts +2 -0
  265. package/dist/modules/functions/sandbox/prelude.js +49 -0
  266. package/dist/modules/functions/space.d.ts +40 -0
  267. package/dist/modules/functions/space.js +162 -0
  268. package/dist/modules/functions/testing/tasksOf.d.ts +4 -0
  269. package/dist/modules/functions/tryEntry.d.ts +9 -0
  270. package/dist/modules/functions/tryEntry.js +49 -0
  271. package/dist/modules/mongo/kv.js +31 -0
  272. package/dist/modules/mysql/accounts.js +19 -9
  273. package/dist/modules/mysql/index.d.ts +2 -1
  274. package/dist/modules/mysql/jobs/index.d.ts +1 -1
  275. package/dist/modules/mysql/jobs/kv.js +30 -0
  276. package/dist/modules/mysql/jobs/schema.d.ts +9 -0
  277. package/dist/modules/mysql/jobs/schema.js +21 -4
  278. package/dist/modules/mysql/schema.js +25 -0
  279. package/dist/modules/mysql/sessions.d.ts +1 -11
  280. package/dist/modules/mysql/sessions.js +6 -1
  281. package/dist/modules/realtime/declarations.d.ts +11 -0
  282. package/dist/modules/realtime/declarations.js +26 -0
  283. package/dist/modules/realtime/grants.d.ts +23 -0
  284. package/dist/modules/realtime/grants.js +61 -0
  285. package/dist/modules/realtime/handlers.d.ts +74 -0
  286. package/dist/modules/realtime/handlers.js +224 -0
  287. package/dist/modules/realtime/hub.d.ts +62 -0
  288. package/dist/modules/realtime/hub.js +141 -0
  289. package/dist/modules/realtime/index.d.ts +51 -0
  290. package/dist/modules/realtime/index.js +62 -0
  291. package/dist/modules/realtime/memoryPubSub.d.ts +10 -0
  292. package/dist/modules/realtime/memoryPubSub.js +43 -0
  293. package/dist/modules/realtime/realtime.test.d.ts +1 -0
  294. package/dist/modules/realtime/redisPubSub.d.ts +24 -0
  295. package/dist/modules/realtime/redisPubSub.js +36 -0
  296. package/dist/modules/realtime/redisPubSub.test.d.ts +1 -0
  297. package/dist/modules/realtime/socket.d.ts +25 -0
  298. package/dist/modules/realtime/socket.js +122 -0
  299. package/dist/modules/realtime/socket.test.d.ts +1 -0
  300. package/dist/modules/rsc/handler.d.ts +16 -0
  301. package/dist/modules/rsc/handler.js +17 -10
  302. package/dist/modules/rsc/handler.test.d.ts +1 -0
  303. package/dist/modules/rsc/resolveRscData.js +2 -1
  304. package/dist/modules/runtime/bundle.d.ts +24 -0
  305. package/dist/modules/runtime/bundle.js +102 -0
  306. package/dist/modules/runtime/contract.d.ts +37 -0
  307. package/dist/modules/runtime/contract.js +21 -0
  308. package/dist/modules/runtime/entry.test.d.ts +1 -0
  309. package/dist/modules/runtime/host.d.ts +38 -0
  310. package/dist/modules/runtime/host.js +68 -0
  311. package/dist/modules/runtime/inside.d.ts +15 -0
  312. package/dist/modules/runtime/inside.js +47 -0
  313. package/dist/modules/runtime/inside.test.d.ts +1 -0
  314. package/dist/modules/runtime/runner.d.ts +11 -0
  315. package/dist/modules/runtime/runner.js +38 -0
  316. package/dist/modules/runtime/runtime.test.d.ts +1 -0
  317. package/dist/modules/runtime/stages.d.ts +50 -0
  318. package/dist/modules/runtime/stages.js +117 -0
  319. package/dist/modules/ssr/Component.d.ts +3 -1
  320. package/dist/modules/ssr/Component.js +2 -1
  321. package/dist/modules/ssr/handler.js +10 -2
  322. package/dist/modules/ssr/loadPluginComponents.js +2 -1
  323. package/dist/modules/ssr/prepareRender.js +39 -10
  324. package/dist/modules/ssr/preview.d.ts +3 -0
  325. package/dist/modules/ssr/preview.js +17 -7
  326. package/dist/modules/ssr/redirect.test.d.ts +1 -0
  327. package/dist/modules/ssr/registerExternalPlugins.d.ts +6 -1
  328. package/dist/modules/ssr/registerExternalPlugins.js +19 -16
  329. package/dist/modules/ssr/registerExternalPlugins.test.d.ts +1 -0
  330. package/dist/modules/ssr/reportMissingPlugins.d.ts +9 -0
  331. package/dist/modules/ssr/reportMissingPlugins.js +24 -0
  332. package/dist/modules/ssr/reportMissingPlugins.test.d.ts +1 -0
  333. package/dist/modules/ssr/streamBody.d.ts +2 -2
  334. package/dist/modules/ssr/streamBody.js +4 -1
  335. package/dist/modules/ssr/template.js +1 -1
  336. package/dist/modules/ssr/views/template.ejs +8 -5
  337. package/dist/mysql.d.ts +1 -1
  338. package/dist/mysql.js +2 -2
  339. package/dist/oauth.js +2 -2
  340. package/dist/plugins/compile.js +9 -4
  341. package/dist/plugins/copy.js +3 -3
  342. package/dist/plugins/manager.d.ts +35 -3
  343. package/dist/plugins/manager.js +109 -38
  344. package/dist/runtime.d.ts +15 -0
  345. package/dist/runtime.js +7 -0
  346. package/package.json +39 -8
  347. package/skills/plitzi-authoring/SKILL.md +98 -14
  348. package/skills/plitzi-authoring/reference/accessibility.md +111 -0
  349. package/skills/plitzi-authoring/reference/authoring-errors.md +70 -0
  350. package/skills/plitzi-authoring/reference/data-and-visibility.md +84 -18
  351. package/skills/plitzi-authoring/reference/elements-and-styles.md +61 -7
  352. package/skills/plitzi-authoring/reference/flows.md +108 -17
  353. package/skills/plitzi-authoring/reference/lists.md +74 -0
  354. package/skills/plitzi-authoring/reference/performance.md +67 -0
  355. package/skills/plitzi-authoring/reference/plugins.md +167 -0
  356. package/skills/plitzi-authoring/reference/review-checklist.md +19 -2
  357. package/skills/plitzi-authoring/reference/templates.md +67 -27
  358. package/skills/plitzi-authoring/reference/testing.md +72 -5
  359. package/skills/plitzi-authoring/reference/validation.md +73 -0
@@ -1,7 +1,21 @@
1
+ import { ssrPaintedCookieName } from "../paintedCookie.js";
1
2
  import { readCookie } from "../readCookie.js";
2
3
  import { debugCookieName } from "@plitzi/sdk-shared/devTools";
3
4
  import { themeFromCookies } from "@plitzi/sdk-shared/theme";
4
5
  //#region src/helpers/cache/keys.ts
6
+ /** The fields of an HTML key, in the order they are joined. */
7
+ var HTML_KEY_FIELDS = [
8
+ "accessToken",
9
+ "spaceId",
10
+ "environment",
11
+ "revision",
12
+ "theme",
13
+ "painted",
14
+ "debugHidden",
15
+ "hostname",
16
+ "path",
17
+ "search"
18
+ ];
5
19
  /**
6
20
  * The key a rendered document is cached under.
7
21
  *
@@ -14,15 +28,43 @@ import { themeFromCookies } from "@plitzi/sdk-shared/theme";
14
28
  * one keyed — a space that switched dev tools on for its published site would otherwise hand the first visitor's
15
29
  * choice to the rest.
16
30
  *
17
- * Read from the request here rather than handed in, so a call site cannot key the page without it. Only those two
31
+ * So is the kept state the first paint depends on (`settings.paintedState`): `prepareRender` renders with that cookie's
32
+ * values, so a page drawn with one visitor's toolbar must not be served to the next. Keyed by its raw value — this
33
+ * key does not know which keys a space declares, and a space that declares none has no such cookie to split on.
34
+ *
35
+ * Read from the request here rather than handed in, so a call site cannot key the page without it. Only those
18
36
  * cookies are read: keying the whole header would split the cache on every analytics cookie a visitor carries.
19
37
  */
20
38
  var buildHtmlCacheKey = (accessToken = "anonymous", spaceId, environment, revision, req) => {
21
- const theme = themeFromCookies(req.headers.cookie) ?? "";
22
- const debugHidden = readCookie(req.headers.cookie, debugCookieName(req.headers.host)) === "false" ? "debug-off" : "";
23
- return `${accessToken}\0${spaceId ?? 1}\0${environment}\0${revision}\0${theme}\0${debugHidden}\0${req.hostname}\0${req.path}\0${req.search}`;
39
+ const fields = {
40
+ accessToken,
41
+ spaceId: String(spaceId ?? 1),
42
+ environment,
43
+ revision: String(revision),
44
+ theme: themeFromCookies(req.headers.cookie) ?? "",
45
+ painted: readCookie(req.headers.cookie, ssrPaintedCookieName(req.headers.host)) ?? "",
46
+ debugHidden: readCookie(req.headers.cookie, debugCookieName(req.headers.host)) === "false" ? "debug-off" : "",
47
+ hostname: req.hostname,
48
+ path: req.path,
49
+ search: req.search
50
+ };
51
+ return HTML_KEY_FIELDS.map((field) => fields[field]).join("\0");
52
+ };
53
+ /**
54
+ * What an HTML key says about the page it holds, read by the one list that also writes it. `server.cache.invalidate`
55
+ * filters on these; it used to split the key by positions of its own, and when the key grew a field in front the
56
+ * filter went on reading the wrong ones — every invalidation by space matched nothing.
57
+ */
58
+ var readHtmlCacheKey = (key) => {
59
+ const parts = key.split("\0");
60
+ const at = (field) => parts[HTML_KEY_FIELDS.indexOf(field)] ?? "";
61
+ return {
62
+ spaceId: at("spaceId"),
63
+ environment: at("environment"),
64
+ hostname: at("hostname")
65
+ };
24
66
  };
25
67
  var buildOfflineDataCacheKey = (spaceId, environment, revision) => `${spaceId}|${environment}|${revision}`;
26
68
  var buildRscCacheKey = (spaceId, environment, revision, userId, idsParam, req) => `${spaceId}\0${environment}\0${revision}\0${userId ?? "anon"}\0${idsParam ?? ""}\0${req.hostname}\0${req.path}\0${req.search}`;
27
69
  //#endregion
28
- export { buildHtmlCacheKey, buildOfflineDataCacheKey, buildRscCacheKey };
70
+ export { buildHtmlCacheKey, buildOfflineDataCacheKey, buildRscCacheKey, readHtmlCacheKey };
@@ -1,6 +1,12 @@
1
1
  import { TtlCache } from './TtlCache';
2
+ import { CompressedBodies } from '@plitzi/sdk-shared';
3
+ /** A rendered page, and the compressed forms of it that have been sent — kept together, so they expire together. */
4
+ export type CachedPage = {
5
+ html: string;
6
+ compressed: CompressedBodies;
7
+ };
2
8
  export type ServerCaches = {
3
- html: TtlCache<string> | undefined;
9
+ html: TtlCache<CachedPage> | undefined;
4
10
  rsc: TtlCache<string> | undefined;
5
11
  /** JSON-serialized OfflineDataRaw — keyed by spaceId|env|revision, same TTL as html. */
6
12
  offlineData: TtlCache<string> | undefined;
@@ -5,6 +5,7 @@ export declare const DEFAULT_COMPRESSION: {
5
5
  encodings: ("br" | "gzip")[];
6
6
  threshold: number;
7
7
  brotliQuality: number;
8
+ keptBrotliQuality: number;
8
9
  gzipLevel: number;
9
10
  };
10
11
  export type ResolvedCompression = typeof DEFAULT_COMPRESSION;
@@ -15,4 +16,5 @@ export type ResolvedCompression = typeof DEFAULT_COMPRESSION;
15
16
  export declare const resolveCompression: (config?: SSRCompressionConfig | false) => ResolvedCompression;
16
17
  /** The first encoding this server prefers that the client actually takes. `*` counts as taking anything. */
17
18
  export declare const selectEncoding: (acceptEncoding: string | undefined, compression?: ResolvedCompression) => ContentEncoding;
18
- export declare const compressBody: (body: string, encoding: ContentEncoding, compression?: ResolvedCompression) => Buffer | string;
19
+ /** `kept`: the result is stored and served again, so it is worth compressing harder once. */
20
+ export declare const compressBody: (body: string, encoding: ContentEncoding, compression?: ResolvedCompression, kept?: boolean) => Buffer | string;
@@ -4,7 +4,8 @@ import { brotliCompressSync, constants, gzipSync } from "node:zlib";
4
4
  var DEFAULT_COMPRESSION = {
5
5
  encodings: ["br", "gzip"],
6
6
  threshold: 1024,
7
- brotliQuality: 4,
7
+ brotliQuality: 2,
8
+ keptBrotliQuality: 6,
8
9
  gzipLevel: 6
9
10
  };
10
11
  /**
@@ -43,10 +44,17 @@ var selectEncoding = (acceptEncoding, compression = DEFAULT_COMPRESSION) => {
43
44
  const accepted = acceptedEncodings(acceptEncoding);
44
45
  return compression.encodings.find((encoding) => accepted.has(encoding) || accepted.has("*")) ?? "identity";
45
46
  };
46
- var compressBody = (body, encoding, compression = DEFAULT_COMPRESSION) => {
47
+ /** `kept`: the result is stored and served again, so it is worth compressing harder once. */
48
+ var compressBody = (body, encoding, compression = DEFAULT_COMPRESSION, kept = false) => {
47
49
  if (encoding === "identity" || body.length < compression.threshold) return body;
48
50
  const buf = Buffer.from(body, "utf-8");
49
- if (encoding === "br") return brotliCompressSync(buf, { params: { [constants.BROTLI_PARAM_QUALITY]: compression.brotliQuality } });
51
+ if (encoding === "br") {
52
+ const quality = kept ? compression.keptBrotliQuality : compression.brotliQuality;
53
+ return brotliCompressSync(buf, { params: {
54
+ [constants.BROTLI_PARAM_QUALITY]: quality,
55
+ [constants.BROTLI_PARAM_SIZE_HINT]: buf.length
56
+ } });
57
+ }
50
58
  return gzipSync(buf, { level: compression.gzipLevel });
51
59
  };
52
60
  //#endregion
@@ -0,0 +1,11 @@
1
+ import { OfflineDataRaw } from '@plitzi/sdk-shared';
2
+ /**
3
+ * What the page's bootstrap reads: `{ offlineData, ...rest }` as escaped JSON, byte for byte what serializing the
4
+ * whole object would give. The escaping replaces characters one by one, so escaping the parts and joining them is
5
+ * the same as escaping the whole.
6
+ *
7
+ * The compiled stylesheet is left out when the page's own runtime stylesheet carries it in a form the browser reads
8
+ * back exactly (`styleCacheTravelsInDocument`), and `styleCacheInDocument` tells the bootstrap to. It is the largest
9
+ * part of the style document, and the page would otherwise ship it twice.
10
+ */
11
+ export declare const hydrationPayload: (offlineData: OfflineDataRaw | undefined, rest: Record<string, unknown>) => string;
@@ -0,0 +1,51 @@
1
+ import { escapeJson } from "./escapeJson.js";
2
+ import { styleCacheTravelsInDocument } from "@plitzi/sdk-shared/style";
3
+ //#region src/helpers/hydrationPayload.ts
4
+ var serializedSpaces = /* @__PURE__ */ new WeakMap();
5
+ /**
6
+ * The space as the page embeds it, serialized once per space object rather than once per render.
7
+ *
8
+ * It is most of the payload — tens of kilobytes of schema and style — and the adapters hand every request the same
9
+ * object until the space changes, so turning it into escaped JSON on each render was a tenth of a render's CPU spent
10
+ * producing the same string. Keyed weakly: a space the adapters let go of takes its string with it.
11
+ */
12
+ var serializeSpace = (offlineData, withoutStyleCache) => {
13
+ let serialized = serializedSpaces.get(offlineData);
14
+ if (!serialized) {
15
+ serialized = {};
16
+ serializedSpaces.set(offlineData, serialized);
17
+ }
18
+ if (withoutStyleCache) {
19
+ serialized.withoutStyleCache ??= escapeJson(JSON.stringify({
20
+ ...offlineData,
21
+ style: {
22
+ ...offlineData.style,
23
+ cache: ""
24
+ }
25
+ }));
26
+ return serialized.withoutStyleCache;
27
+ }
28
+ serialized.whole ??= escapeJson(JSON.stringify(offlineData));
29
+ return serialized.whole;
30
+ };
31
+ /**
32
+ * What the page's bootstrap reads: `{ offlineData, ...rest }` as escaped JSON, byte for byte what serializing the
33
+ * whole object would give. The escaping replaces characters one by one, so escaping the parts and joining them is
34
+ * the same as escaping the whole.
35
+ *
36
+ * The compiled stylesheet is left out when the page's own runtime stylesheet carries it in a form the browser reads
37
+ * back exactly (`styleCacheTravelsInDocument`), and `styleCacheInDocument` tells the bootstrap to. It is the largest
38
+ * part of the style document, and the page would otherwise ship it twice.
39
+ */
40
+ var hydrationPayload = (offlineData, rest) => {
41
+ const inDocument = offlineData !== void 0 && styleCacheTravelsInDocument(offlineData.style.cache);
42
+ const serializedRest = escapeJson(JSON.stringify(inDocument ? {
43
+ ...rest,
44
+ styleCacheInDocument: true
45
+ } : rest));
46
+ if (offlineData === void 0) return serializedRest;
47
+ const separator = serializedRest === "{}" ? "" : ",";
48
+ return `{"offlineData":${serializeSpace(offlineData, inDocument)}${separator}${serializedRest.slice(1)}`;
49
+ };
50
+ //#endregion
51
+ export { hydrationPayload };
@@ -0,0 +1 @@
1
+ export {};
@@ -6,6 +6,6 @@ export declare class RequestMetrics {
6
6
  /** Formats as Server-Timing header value. */
7
7
  toServerTimingHeader(): string;
8
8
  record(name: string, dur: number): void;
9
- /** Logs a one-line summary to stdout (dev mode only). */
9
+ /** Logs a one-line summary at `debug`. */
10
10
  log(label: string): void;
11
11
  }
@@ -1,3 +1,4 @@
1
+ import { serverLog } from "./serverLog.js";
1
2
  //#region src/helpers/metrics.ts
2
3
  var RequestMetrics = class {
3
4
  start;
@@ -25,11 +26,11 @@ var RequestMetrics = class {
25
26
  dur
26
27
  });
27
28
  }
28
- /** Logs a one-line summary to stdout (dev mode only). */
29
+ /** Logs a one-line summary at `debug`. */
29
30
  log(label) {
30
31
  const total = Math.round(performance.now() - this.start);
31
32
  const parts = this.phases.map((p) => `${p.name}=${p.dur}ms`).join(" ");
32
- console.log(`[SSR] ${label} — ${parts} | total=${total}ms`);
33
+ serverLog.debug("SSR", `${label} — ${parts} | total=${total}ms`);
33
34
  }
34
35
  };
35
36
  //#endregion
@@ -1,12 +1,9 @@
1
1
  /**
2
- * Where a customer-authored request may go.
2
+ * Whether a hostname — or an address a name resolved to — may never be the other end of an authored request.
3
3
  *
4
- * Two things reach the outside world on somebody else's say-so: the `http.request` task, whose URL is typed into
5
- * a flow, and the connector engine, whose `baseUrl` is typed into a manifest. Both run inside the cluster, from a
6
- * trusted network position, which is what turns "fetch this URL for me" into a way to read the instance's cloud
7
- * credentials at `169.254.169.254` or talk to Redis on `localhost`. One rule, in one place, for both of them.
4
+ * A NAME is only refused for what it says it is (`localhost`, `*.internal`); anything else about a name is decided by
5
+ * what it resolves to, in `assertOutboundAllowed`.
8
6
  */
9
- /** Literal forms an authored URL can hold, and the ones this refuses outright. */
10
7
  export declare const isBlockedHost: (hostname: string) => boolean;
11
8
  export type HostLookup = (hostname: string) => Promise<{
12
9
  address: string;
@@ -25,3 +22,14 @@ export type HostLookup = (hostname: string) => Promise<{
25
22
  * only by resolving and connecting to the same address, and that belongs in the transport rather than here.
26
23
  */
27
24
  export declare const assertOutboundAllowed: (url: URL, lookup?: HostLookup) => Promise<void>;
25
+ /**
26
+ * Sends an authored request, judging EVERY hop it takes by `assertOutboundAllowed`.
27
+ *
28
+ * Checking the URL and handing it to `fetch` judged only the first hop: `fetch` follows redirects on its own, so a
29
+ * public server answering `302 Location: http://169.254.169.254/` took the request wherever it liked. Redirects are
30
+ * therefore taken manually and each destination is checked before anything is sent to it.
31
+ *
32
+ * `base` is the caller's own transport — the action runner's is budgeted and capped — so every hop is also counted
33
+ * against whatever that transport enforces.
34
+ */
35
+ export declare const fetchOutbound: (base: typeof fetch, url: URL, init?: RequestInit, lookup?: HostLookup) => Promise<Response>;
@@ -1,4 +1,5 @@
1
1
  import { lookup } from "node:dns/promises";
2
+ import { BlockList, isIP } from "node:net";
2
3
  //#region src/helpers/outboundGuard.ts
3
4
  /**
4
5
  * Where a customer-authored request may go.
@@ -8,14 +9,64 @@ import { lookup } from "node:dns/promises";
8
9
  * trusted network position, which is what turns "fetch this URL for me" into a way to read the instance's cloud
9
10
  * credentials at `169.254.169.254` or talk to Redis on `localhost`. One rule, in one place, for both of them.
10
11
  */
11
- /** Literal forms an authored URL can hold, and the ones this refuses outright. */
12
+ /**
13
+ * Every range that is never a public peer, as ranges rather than string patterns.
14
+ *
15
+ * A `BlockList` also judges an IPv4-mapped IPv6 address (`::ffff:7f00:1`) against the IPv4 rules, which is the form a
16
+ * URL parser turns `[::ffff:127.0.0.1]` into and a dual-stack socket happily connects to loopback. The prefix
17
+ * patterns this replaced matched neither that nor `::`, and matched DNS names that merely began with `fc` or `fe8`.
18
+ */
19
+ var privateRanges = new BlockList();
20
+ var IPV4_RANGES = [
21
+ ["0.0.0.0", 8],
22
+ ["10.0.0.0", 8],
23
+ ["100.64.0.0", 10],
24
+ ["127.0.0.0", 8],
25
+ ["169.254.0.0", 16],
26
+ ["172.16.0.0", 12],
27
+ ["192.0.0.0", 24],
28
+ ["192.168.0.0", 16],
29
+ ["198.18.0.0", 15],
30
+ ["224.0.0.0", 4],
31
+ ["240.0.0.0", 4]
32
+ ];
33
+ var IPV6_RANGES = [
34
+ ["::", 96],
35
+ ["64:ff9b:1::", 48],
36
+ ["fc00::", 7],
37
+ ["fe80::", 10],
38
+ ["ff00::", 8]
39
+ ];
40
+ /** An IPv4 address as the two 16-bit groups it occupies inside an IPv6 one. */
41
+ var ipv4Groups = (address) => {
42
+ const [a = 0, b = 0, c = 0, d = 0] = address.split(".").map(Number);
43
+ return [(a << 8 | b).toString(16), (c << 8 | d).toString(16)];
44
+ };
45
+ /**
46
+ * The same private IPv4 ranges where IPv6 carries an IPv4 address inside it.
47
+ *
48
+ * NAT64 (`64:ff9b::/96`) is how an IPv6-only cluster reaches EVERY IPv4 host — DNS64 answers a public API with an address
49
+ * in it — so the prefix cannot be refused whole; what it carries is judged instead. 6to4 (`2002::/16`) carries its
50
+ * address in bits 16–47.
51
+ */
52
+ var EMBEDDED_IPV4_RANGES = IPV4_RANGES.flatMap(([network, prefix]) => {
53
+ const [high, low] = ipv4Groups(network);
54
+ return [[`64:ff9b::${high}:${low}`, 96 + prefix], [`2002:${high}:${low}::`, 16 + prefix]];
55
+ });
56
+ IPV4_RANGES.forEach(([network, prefix]) => privateRanges.addSubnet(network, prefix, "ipv4"));
57
+ [...IPV6_RANGES, ...EMBEDDED_IPV4_RANGES].forEach(([network, prefix]) => privateRanges.addSubnet(network, prefix, "ipv6"));
58
+ var isBlockedName = (host) => host === "localhost" || host.endsWith(".localhost") || host.endsWith(".internal");
59
+ /**
60
+ * Whether a hostname — or an address a name resolved to — may never be the other end of an authored request.
61
+ *
62
+ * A NAME is only refused for what it says it is (`localhost`, `*.internal`); anything else about a name is decided by
63
+ * what it resolves to, in `assertOutboundAllowed`.
64
+ */
12
65
  var isBlockedHost = (hostname) => {
13
- const host = hostname.toLowerCase().replace(/^\[|\]$/g, "");
14
- if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".internal") || host === "::1") return true;
15
- const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
16
- if (!ipv4) return /^f[cd]/.test(host) || host.startsWith("fe8") || host.startsWith("fe9") || /^fe[ab]/.test(host);
17
- const [a = 0, b = 0] = ipv4.slice(1).map((part) => Number.parseInt(part, 10));
18
- return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168;
66
+ const host = hostname.toLowerCase().replace(/^\[|\]$/g, "").replace(/%.*$/, "");
67
+ const family = isIP(host);
68
+ if (family === 0) return isBlockedName(host);
69
+ return privateRanges.check(host, family === 6 ? "ipv6" : "ipv4");
19
70
  };
20
71
  var resolveAll = (hostname) => lookup(hostname, {
21
72
  all: true,
@@ -45,5 +96,78 @@ var assertOutboundAllowed = async (url, lookup = resolveAll) => {
45
96
  }
46
97
  if (addresses.some(({ address }) => isBlockedHost(address))) throw new Error("Request host is not allowed");
47
98
  };
99
+ var MAX_REDIRECTS = 5;
100
+ var REDIRECT_STATUSES = [
101
+ 301,
102
+ 302,
103
+ 303,
104
+ 307,
105
+ 308
106
+ ];
107
+ /** What the Fetch standard strips when a redirect leaves the origin that was given them. */
108
+ var CROSS_ORIGIN_HEADERS = [
109
+ "authorization",
110
+ "cookie",
111
+ "proxy-authorization"
112
+ ];
113
+ var BODY_HEADERS = [
114
+ "content-type",
115
+ "content-length",
116
+ "content-encoding",
117
+ "content-language",
118
+ "content-location"
119
+ ];
120
+ /**
121
+ * The request a redirect continues as — the rules `fetch` itself follows, applied by hand because it no longer does.
122
+ *
123
+ * A 303, and a 301/302 answering a POST, become a bodiless GET; 307 and 308 repeat the request exactly. Leaving the
124
+ * origin drops the headers the Fetch standard drops, and only those: a key the author put in a header of its own
125
+ * travels on as it did before, and the author already decides where it is sent.
126
+ */
127
+ var redirectedInit = (init, status, crossOrigin) => {
128
+ const method = (init.method ?? "GET").toUpperCase();
129
+ const becomesGet = status === 303 && method !== "HEAD" || (status === 301 || status === 302) && method === "POST";
130
+ const headers = new Headers(init.headers);
131
+ if (crossOrigin) CROSS_ORIGIN_HEADERS.forEach((name) => headers.delete(name));
132
+ if (!becomesGet) return {
133
+ ...init,
134
+ headers
135
+ };
136
+ BODY_HEADERS.forEach((name) => headers.delete(name));
137
+ return {
138
+ ...init,
139
+ method: "GET",
140
+ body: void 0,
141
+ headers
142
+ };
143
+ };
144
+ /**
145
+ * Sends an authored request, judging EVERY hop it takes by `assertOutboundAllowed`.
146
+ *
147
+ * Checking the URL and handing it to `fetch` judged only the first hop: `fetch` follows redirects on its own, so a
148
+ * public server answering `302 Location: http://169.254.169.254/` took the request wherever it liked. Redirects are
149
+ * therefore taken manually and each destination is checked before anything is sent to it.
150
+ *
151
+ * `base` is the caller's own transport — the action runner's is budgeted and capped — so every hop is also counted
152
+ * against whatever that transport enforces.
153
+ */
154
+ var fetchOutbound = async (base, url, init = {}, lookup = resolveAll) => {
155
+ let target = url;
156
+ let request = init;
157
+ for (let hop = 0;; hop += 1) {
158
+ await assertOutboundAllowed(target, lookup);
159
+ const response = await base(target.toString(), {
160
+ ...request,
161
+ redirect: "manual"
162
+ });
163
+ const location = response.headers.get("location");
164
+ if (!REDIRECT_STATUSES.includes(response.status) || !location) return response;
165
+ await response.body?.cancel();
166
+ if (hop >= MAX_REDIRECTS) throw new Error(`Request followed more than ${MAX_REDIRECTS} redirects`);
167
+ const next = new URL(location, target);
168
+ request = redirectedInit(request, response.status, next.origin !== target.origin);
169
+ target = next;
170
+ }
171
+ };
48
172
  //#endregion
49
- export { assertOutboundAllowed, isBlockedHost };
173
+ export { assertOutboundAllowed, fetchOutbound, isBlockedHost };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The name the painted state of a server-rendered page is kept under, for this request's host.
3
+ *
4
+ * The page this server sends carries no `webKey`, so the SDK keeps its state under the web id an empty key decodes to —
5
+ * the same one its web storage key uses. One helper for both readers of the cookie, the render and the HTML cache key,
6
+ * so they cannot come to disagree about which cookie the page was drawn from.
7
+ */
8
+ export declare const ssrPaintedCookieName: (host: string | undefined) => string;
@@ -0,0 +1,13 @@
1
+ import { getKeyDecoded } from "@plitzi/sdk-shared/helpers/utils";
2
+ import { paintedStateCookieName } from "@plitzi/sdk-shared/state/paintedState";
3
+ //#region src/helpers/paintedCookie.ts
4
+ /**
5
+ * The name the painted state of a server-rendered page is kept under, for this request's host.
6
+ *
7
+ * The page this server sends carries no `webKey`, so the SDK keeps its state under the web id an empty key decodes to —
8
+ * the same one its web storage key uses. One helper for both readers of the cookie, the render and the HTML cache key,
9
+ * so they cannot come to disagree about which cookie the page was drawn from.
10
+ */
11
+ var ssrPaintedCookieName = (host) => paintedStateCookieName(getKeyDecoded("", true), host);
12
+ //#endregion
13
+ export { ssrPaintedCookieName };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Says, once per process, that a production server is rendering with React's development build.
3
+ *
4
+ * React picks its build from `NODE_ENV` when it is first imported, not from anything a server is configured with, so
5
+ * `devMode: false` without `NODE_ENV=production` renders every page with the development build — measured at about 40%
6
+ * fewer pages a second. An `error` because it is the only level a production server shows, and a deployment that
7
+ * never sees this line never learns why it needs twice the CPU.
8
+ */
9
+ export declare const reportReactBuild: (devMode: boolean | undefined, nodeEnv?: string | undefined) => void;
@@ -0,0 +1,18 @@
1
+ import { serverLog } from "./serverLog.js";
2
+ //#region src/helpers/reportReactBuild.ts
3
+ var reported = false;
4
+ /**
5
+ * Says, once per process, that a production server is rendering with React's development build.
6
+ *
7
+ * React picks its build from `NODE_ENV` when it is first imported, not from anything a server is configured with, so
8
+ * `devMode: false` without `NODE_ENV=production` renders every page with the development build — measured at about 40%
9
+ * fewer pages a second. An `error` because it is the only level a production server shows, and a deployment that
10
+ * never sees this line never learns why it needs twice the CPU.
11
+ */
12
+ var reportReactBuild = (devMode, nodeEnv = process.env.NODE_ENV) => {
13
+ if (reported || devMode || nodeEnv === "production") return;
14
+ reported = true;
15
+ serverLog.error("server", `NODE_ENV is ${nodeEnv === void 0 ? "unset" : `"${nodeEnv}"`}: React renders with its development build. Start the process with NODE_ENV=production.`);
16
+ };
17
+ //#endregion
18
+ export { reportReactBuild };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import { ServerLogEvent, ServerLogger } from '@plitzi/sdk-shared';
1
+ import { LogLevel, ServerLogEvent, ServerLogger } from '@plitzi/sdk-shared';
2
2
  /** One line for any {@link ServerLogEvent}: an HTTP request reads as an access-log line
3
3
  * (`[SSR] 203.0.113.7 GET /pricing 200 12ms ok`), the MCP events as what happened inside one
4
4
  * (`[MCP] tools/call plitzi_apply {operations:[3]} 41ms ok`), a server action as what its flow did
@@ -8,6 +8,30 @@ import { ServerLogEvent, ServerLogger } from '@plitzi/sdk-shared';
8
8
  * reduced a run to its steps; the client IP it does carry is personal data, so a sink that persists these lines
9
9
  * must say so. */
10
10
  export declare const renderLogEvent: (event: ServerLogEvent) => string;
11
+ /**
12
+ * How severe an event is: what it says it is, or — for a request, a tool call, a run — whether it went wrong. A
13
+ * refusal is never `ok` yet never the server's failure: the caller was turned away, which is a `warn`.
14
+ */
15
+ export declare const logLevelOf: (event: ServerLogEvent) => LogLevel;
16
+ /** Whether a server at `threshold` says something at `level`. */
17
+ export declare const isLogged: (threshold: LogLevel | "silent", level: LogLevel) => boolean;
18
+ /** What a server says when the deployment did not choose: what went wrong in production, more while developing. */
19
+ export declare const defaultLogLevel: (devMode?: boolean) => LogLevel;
11
20
  /** A drop-in `SSRServerConfig.logger` for consumers that just want the log on the console. Consumers with their
12
21
  * own logging stack should pass their own sink instead and read the structured event. */
13
22
  export declare const consoleLogger: ServerLogger;
23
+ export declare const configureServerLog: (options: {
24
+ level: LogLevel | "silent";
25
+ logger?: ServerLogger;
26
+ }) => void;
27
+ /** The server's own voice: `serverLog.error('RSC', 'element failed to resolve', error)`. Below the threshold, nothing. */
28
+ export declare const serverLog: {
29
+ error: (scope: string, message: string, cause?: unknown) => void;
30
+ warn: (scope: string, message: string, cause?: unknown) => void;
31
+ info: (scope: string, message: string, cause?: unknown) => void;
32
+ debug: (scope: string, message: string, cause?: unknown) => void;
33
+ /** Whether a level would be said — to skip building a costly message nobody will read. */
34
+ enabled: (level: LogLevel) => boolean;
35
+ /** Hands an event to a sink the consumer wired itself (`onRun`, `onReject`) only when the process would say it. */
36
+ emit: (logger: ServerLogger, event: ServerLogEvent) => void;
37
+ };
@@ -23,6 +23,7 @@ var renderReject = (event) => {
23
23
  const caller = event.callerId ? ` from ${event.callerId}` : "";
24
24
  return `[Action] ${event.name} via ${event.trigger} space=${event.spaceId} REFUSED ${event.reason}${caller} ${outcomeOf(event)}`;
25
25
  };
26
+ var renderMessage = (event) => `[${event.scope}] ${event.message}${event.error ? `: ${event.error}` : ""}`;
26
27
  /** One line for any {@link ServerLogEvent}: an HTTP request reads as an access-log line
27
28
  * (`[SSR] 203.0.113.7 GET /pricing 200 12ms ok`), the MCP events as what happened inside one
28
29
  * (`[MCP] tools/call plitzi_apply {operations:[3]} 41ms ok`), a server action as what its flow did
@@ -38,14 +39,78 @@ var renderLogEvent = (event) => {
38
39
  case "resource": return renderResource(event);
39
40
  case "run": return renderRun(event);
40
41
  case "reject": return renderReject(event);
42
+ case "message": return renderMessage(event);
41
43
  }
42
44
  };
45
+ var RANK = {
46
+ silent: 0,
47
+ error: 1,
48
+ warn: 2,
49
+ info: 3,
50
+ debug: 4
51
+ };
52
+ /**
53
+ * How severe an event is: what it says it is, or — for a request, a tool call, a run — whether it went wrong. A
54
+ * refusal is never `ok` yet never the server's failure: the caller was turned away, which is a `warn`.
55
+ */
56
+ var logLevelOf = (event) => {
57
+ if (event.kind === "message") return event.level;
58
+ if (event.kind === "reject") return "warn";
59
+ return event.ok ? "info" : "error";
60
+ };
61
+ /** Whether a server at `threshold` says something at `level`. */
62
+ var isLogged = (threshold, level) => RANK[level] <= RANK[threshold];
63
+ /** What a server says when the deployment did not choose: what went wrong in production, more while developing. */
64
+ var defaultLogLevel = (devMode = false) => devMode ? "info" : "error";
43
65
  /** A drop-in `SSRServerConfig.logger` for consumers that just want the log on the console. Consumers with their
44
66
  * own logging stack should pass their own sink instead and read the structured event. */
45
67
  var consoleLogger = (event) => {
46
68
  const line = renderLogEvent(event);
47
- if (event.ok) console.log(line);
48
- else console.error(line);
69
+ const level = logLevelOf(event);
70
+ if (level === "error") console.error(line);
71
+ else if (level === "warn") console.warn(line);
72
+ else console.log(line);
73
+ };
74
+ var errorText = (cause) => cause instanceof Error ? cause.stack ?? cause.message : typeof cause === "string" ? cause : JSON.stringify(cause);
75
+ /**
76
+ * Where everything the server says that is not a request goes — one threshold and one sink for the process.
77
+ *
78
+ * Process-wide because what speaks is often created before any server exists (an adapter, a job worker) and has no
79
+ * config to read. `createServer` sets it from its own `logLevel` and `logger`; until then it is `error` under
80
+ * `NODE_ENV=production` and `info` otherwise, to the console.
81
+ */
82
+ var state = {
83
+ level: process.env.NODE_ENV === "production" ? "error" : "info",
84
+ sink: consoleLogger
85
+ };
86
+ var configureServerLog = (options) => {
87
+ state.level = options.level;
88
+ state.sink = options.logger ?? consoleLogger;
89
+ };
90
+ var say = (level, scope, message, cause) => {
91
+ if (!isLogged(state.level, level)) return;
92
+ state.sink({
93
+ kind: "message",
94
+ level,
95
+ scope,
96
+ message,
97
+ ok: level !== "error",
98
+ ...cause === void 0 ? {} : { error: errorText(cause) },
99
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
100
+ });
101
+ };
102
+ /** The server's own voice: `serverLog.error('RSC', 'element failed to resolve', error)`. Below the threshold, nothing. */
103
+ var serverLog = {
104
+ error: (scope, message, cause) => say("error", scope, message, cause),
105
+ warn: (scope, message, cause) => say("warn", scope, message, cause),
106
+ info: (scope, message, cause) => say("info", scope, message, cause),
107
+ debug: (scope, message, cause) => say("debug", scope, message, cause),
108
+ /** Whether a level would be said — to skip building a costly message nobody will read. */
109
+ enabled: (level) => isLogged(state.level, level),
110
+ /** Hands an event to a sink the consumer wired itself (`onRun`, `onReject`) only when the process would say it. */
111
+ emit: (logger, event) => {
112
+ if (isLogged(state.level, logLevelOf(event))) logger(event);
113
+ }
49
114
  };
50
115
  //#endregion
51
- export { consoleLogger, renderLogEvent };
116
+ export { configureServerLog, consoleLogger, defaultLogLevel, isLogged, logLevelOf, renderLogEvent, serverLog };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { RawData } from 'ws';
2
+ /** A WebSocket frame as text, whichever of its three shapes `ws` delivered it in. */
3
+ export declare const frameText: (frame: RawData) => string;
@@ -0,0 +1,5 @@
1
+ //#region src/helpers/wsFrame.ts
2
+ /** A WebSocket frame as text, whichever of its three shapes `ws` delivered it in. */
3
+ var frameText = (frame) => Buffer.isBuffer(frame) ? frame.toString("utf8") : Array.isArray(frame) ? Buffer.concat(frame).toString("utf8") : Buffer.from(frame).toString("utf8");
4
+ //#endregion
5
+ export { frameText };