@pikku/core 0.12.107 → 0.12.112

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 (594) hide show
  1. package/CHANGELOG.md +357 -0
  2. package/dist/analytics/analytics.d.ts +6 -0
  3. package/dist/analytics/analytics.js +58 -0
  4. package/dist/analytics/analytics.types.d.ts +156 -0
  5. package/dist/analytics/analytics.types.js +1 -0
  6. package/dist/analytics/anonymous-analytics-identity.d.ts +23 -0
  7. package/dist/analytics/anonymous-analytics-identity.js +27 -0
  8. package/dist/analytics/compose-analytics-identity.d.ts +14 -0
  9. package/dist/analytics/compose-analytics-identity.js +33 -0
  10. package/dist/analytics/cookie-analytics-identity.d.ts +24 -0
  11. package/dist/analytics/cookie-analytics-identity.js +37 -0
  12. package/dist/analytics/define-analytics-events.d.ts +22 -0
  13. package/dist/analytics/define-analytics-events.js +9 -0
  14. package/dist/analytics/fan-out-analytics.d.ts +15 -0
  15. package/dist/analytics/fan-out-analytics.js +36 -0
  16. package/dist/analytics/index.d.ts +13 -0
  17. package/dist/analytics/index.js +8 -0
  18. package/dist/analytics/logger-analytics-service.d.ts +14 -0
  19. package/dist/analytics/logger-analytics-service.js +23 -0
  20. package/dist/analytics/mint-cookie.d.ts +44 -0
  21. package/dist/analytics/mint-cookie.js +55 -0
  22. package/dist/dev/hot-reload.d.ts +0 -1
  23. package/dist/dev/hot-reload.js +4 -25
  24. package/dist/dev/module-runner.js +104 -25
  25. package/dist/errors/errors.d.ts +7 -0
  26. package/dist/errors/errors.js +17 -0
  27. package/dist/function/function-meta.types.d.ts +2 -0
  28. package/dist/function/function-runner.js +29 -0
  29. package/dist/function/functions.types.d.ts +22 -3
  30. package/dist/middleware/auth-apikey.d.ts +1 -1
  31. package/dist/pikku-state.d.ts +1 -1
  32. package/dist/scopes.d.ts +11 -0
  33. package/dist/scopes.js +17 -0
  34. package/dist/services/credential-wire-service.d.ts +1 -1
  35. package/dist/services/feature-flag-service.d.ts +100 -0
  36. package/dist/services/feature-flag-service.js +1 -0
  37. package/dist/services/index.d.ts +1 -0
  38. package/dist/services/meta-service.d.ts +16 -0
  39. package/dist/services/meta-service.js +19 -0
  40. package/dist/services/webhook-service.d.ts +7 -2
  41. package/dist/services/webhook-service.js +11 -6
  42. package/dist/testing/service-tests/agent-run-service-tests.d.ts +2 -2
  43. package/dist/testing/service-tests/agent-storage-service-tests.d.ts +1 -1
  44. package/dist/testing/service-tests/channel-store-tests.d.ts +1 -1
  45. package/dist/testing/service-tests/credential-service-tests.d.ts +1 -1
  46. package/dist/testing/service-tests/deployment-service-tests.d.ts +1 -1
  47. package/dist/testing/service-tests/event-hub-store-tests.d.ts +1 -1
  48. package/dist/testing/service-tests/secret-service-tests.d.ts +1 -1
  49. package/dist/testing/service-tests/session-store-tests.d.ts +1 -1
  50. package/dist/testing/service-tests/workflow-run-service-tests.d.ts +1 -1
  51. package/dist/testing/service-tests/workflow-service-tests.d.ts +1 -1
  52. package/dist/types/core.types.d.ts +23 -0
  53. package/dist/wirings/agent/agent-rpc.d.ts +1 -1
  54. package/dist/wirings/agent/agent-turn.d.ts +1 -1
  55. package/dist/wirings/agent-scorer/agent-scorer-live.d.ts +1 -1
  56. package/dist/wirings/channel/channel-common.d.ts +1 -1
  57. package/dist/wirings/channel/channel-rpc-validators.d.ts +2 -2
  58. package/dist/wirings/channel/channel-runner.d.ts +1 -1
  59. package/dist/wirings/channel/local/local-channel-runner.d.ts +1 -1
  60. package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +1 -1
  61. package/dist/wirings/cli/channel/cli-approval.d.ts +2 -2
  62. package/dist/wirings/flag/assert-feature-available.d.ts +15 -0
  63. package/dist/wirings/flag/assert-feature-available.js +23 -0
  64. package/dist/wirings/flag/cached-flag-source.d.ts +73 -0
  65. package/dist/wirings/flag/cached-flag-source.js +96 -0
  66. package/dist/wirings/flag/define-feature-flags.d.ts +33 -0
  67. package/dist/wirings/flag/define-feature-flags.js +32 -0
  68. package/dist/wirings/flag/flag.types.d.ts +97 -0
  69. package/dist/wirings/flag/flag.types.js +1 -0
  70. package/dist/wirings/flag/index.d.ts +7 -0
  71. package/dist/wirings/flag/index.js +5 -0
  72. package/dist/wirings/flag/resolve-flag.d.ts +52 -0
  73. package/dist/wirings/flag/resolve-flag.js +90 -0
  74. package/dist/wirings/flag/validate-flag-definitions.d.ts +26 -0
  75. package/dist/wirings/flag/validate-flag-definitions.js +103 -0
  76. package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +8 -8
  77. package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +1 -1
  78. package/dist/wirings/workflow/workflow-queue-routing.d.ts +1 -1
  79. package/knowledge/decisions/internals/a-reset-suite-makes-the-dev-seed-the-fixture.md +50 -0
  80. package/knowledge/decisions/internals/hot-reload-reads-the-changed-source-never-a-compiled-copy.md +38 -0
  81. package/knowledge/decisions/internals/index.md +2 -0
  82. package/package.json +11 -5
  83. package/run-tests.sh +17 -9
  84. package/src/public-surface.json +26 -0
  85. package/src/agent-naming.test.ts +0 -68
  86. package/src/api-report.test.ts +0 -138
  87. package/src/app-alias-surface.test.ts +0 -179
  88. package/src/app-leaf-surface.test.ts +0 -222
  89. package/src/bootstrap-compat/root.ts +0 -20
  90. package/src/classification/column-form.test.ts +0 -97
  91. package/src/classification/column-form.ts +0 -55
  92. package/src/classification/data-classification.ts +0 -73
  93. package/src/classification/index.ts +0 -40
  94. package/src/classification/key-ids.ts +0 -2
  95. package/src/classification/secret-value.test.ts +0 -204
  96. package/src/classification/secret-value.ts +0 -106
  97. package/src/crypto-utils.test.ts +0 -668
  98. package/src/crypto-utils.ts +0 -460
  99. package/src/dev/hot-reload.test.ts +0 -587
  100. package/src/dev/hot-reload.ts +0 -225
  101. package/src/dev/module-runner.test.ts +0 -235
  102. package/src/dev/module-runner.ts +0 -138
  103. package/src/dev/reload-meta.test.ts +0 -183
  104. package/src/dev/reload-meta.ts +0 -144
  105. package/src/ecosystem-tier-removed.test.ts +0 -72
  106. package/src/env.ts +0 -1
  107. package/src/errors/error-handler.ts +0 -77
  108. package/src/errors/error.test.ts +0 -229
  109. package/src/errors/errors.ts +0 -393
  110. package/src/errors/index.ts +0 -11
  111. package/src/errors/serialized-error.ts +0 -12
  112. package/src/factory-functions.test.ts +0 -111
  113. package/src/function/abort-scope.test.ts +0 -97
  114. package/src/function/abort-scope.ts +0 -80
  115. package/src/function/function-meta.types.ts +0 -151
  116. package/src/function/function-runner.test.ts +0 -1619
  117. package/src/function/function-runner.ts +0 -499
  118. package/src/function/functions.types.ts +0 -319
  119. package/src/function/index.ts +0 -40
  120. package/src/function/list.types.test.ts +0 -100
  121. package/src/function/list.types.ts +0 -69
  122. package/src/function/pikku-request.test.ts +0 -35
  123. package/src/function/pikku-request.ts +0 -17
  124. package/src/gopass-secrets-removed.test.ts +0 -51
  125. package/src/handle-error.test.ts +0 -532
  126. package/src/handle-error.ts +0 -64
  127. package/src/middleware/auth-apikey.test.ts +0 -362
  128. package/src/middleware/auth-apikey.ts +0 -39
  129. package/src/middleware/auth-bearer.test.ts +0 -566
  130. package/src/middleware/auth-bearer.ts +0 -83
  131. package/src/middleware/auth-cookie.test.ts +0 -572
  132. package/src/middleware/auth-cookie.ts +0 -107
  133. package/src/middleware/cors.test.ts +0 -579
  134. package/src/middleware/cors.ts +0 -103
  135. package/src/middleware/index.ts +0 -30
  136. package/src/middleware/middleware-factories.ts +0 -76
  137. package/src/middleware/middleware.types.ts +0 -61
  138. package/src/middleware/remote-auth.test.ts +0 -547
  139. package/src/middleware/remote-auth.ts +0 -95
  140. package/src/middleware/remote-jobs-secret.ts +0 -35
  141. package/src/middleware/require-origin.test.ts +0 -115
  142. package/src/middleware/require-origin.ts +0 -79
  143. package/src/middleware/telemetry.ts +0 -83
  144. package/src/middleware-runner.test.ts +0 -578
  145. package/src/middleware-runner.ts +0 -232
  146. package/src/no-any-casts.test.ts +0 -57
  147. package/src/no-root-barrel.test.ts +0 -122
  148. package/src/permissions.test.ts +0 -256
  149. package/src/permissions.ts +0 -217
  150. package/src/pikku-state.test.ts +0 -198
  151. package/src/pikku-state.ts +0 -206
  152. package/src/production-barrels-stay-lean.test.ts +0 -110
  153. package/src/public-surface.test.ts +0 -133
  154. package/src/remote.test.ts +0 -173
  155. package/src/remote.ts +0 -56
  156. package/src/removed-legacy-exports.test.ts +0 -66
  157. package/src/run-tests-script.test.ts +0 -67
  158. package/src/schema.test.ts +0 -430
  159. package/src/schema.ts +0 -204
  160. package/src/scopes.test.ts +0 -203
  161. package/src/scopes.ts +0 -67
  162. package/src/services/agent-run-state-service.ts +0 -37
  163. package/src/services/agent-runner-service.ts +0 -238
  164. package/src/services/agent-storage-service.ts +0 -39
  165. package/src/services/ai-embedding-service.ts +0 -9
  166. package/src/services/audit-service.test.ts +0 -44
  167. package/src/services/audit-service.ts +0 -280
  168. package/src/services/content-service.ts +0 -65
  169. package/src/services/credential-service.ts +0 -20
  170. package/src/services/credential-wire-service.test.ts +0 -235
  171. package/src/services/credential-wire-service.ts +0 -120
  172. package/src/services/deployment-service.ts +0 -32
  173. package/src/services/email-service.test.ts +0 -100
  174. package/src/services/email-service.ts +0 -60
  175. package/src/services/email-template.test.ts +0 -311
  176. package/src/services/email-template.ts +0 -254
  177. package/src/services/file-scenario-run-store.test.ts +0 -349
  178. package/src/services/file-scenario-run-store.ts +0 -282
  179. package/src/services/gateway-service.ts +0 -5
  180. package/src/services/http-personas-converse.test.ts +0 -274
  181. package/src/services/http-personas.test.ts +0 -379
  182. package/src/services/http-personas.ts +0 -508
  183. package/src/services/in-memory-agent-run-state-service.ts +0 -87
  184. package/src/services/in-memory-queue-service.test.ts +0 -162
  185. package/src/services/in-memory-queue-service.ts +0 -78
  186. package/src/services/in-memory-session-store.test.ts +0 -58
  187. package/src/services/in-memory-session-store.ts +0 -20
  188. package/src/services/in-memory-trigger-service.ts +0 -47
  189. package/src/services/in-memory-workflow-service.test.ts +0 -430
  190. package/src/services/in-memory-workflow-service.ts +0 -595
  191. package/src/services/index.ts +0 -192
  192. package/src/services/istanbul-coverage-service.test.ts +0 -66
  193. package/src/services/istanbul-coverage-service.ts +0 -59
  194. package/src/services/jwt-service.ts +0 -15
  195. package/src/services/local-content-request-handler.test.ts +0 -240
  196. package/src/services/local-content-request-handler.ts +0 -295
  197. package/src/services/local-content.test.ts +0 -186
  198. package/src/services/local-content.ts +0 -242
  199. package/src/services/local-credential-service.ts +0 -63
  200. package/src/services/local-email-service.test.ts +0 -182
  201. package/src/services/local-email-service.ts +0 -43
  202. package/src/services/local-gateway-service.ts +0 -51
  203. package/src/services/local-secrets.test.ts +0 -95
  204. package/src/services/local-secrets.ts +0 -71
  205. package/src/services/local-variables.test.ts +0 -61
  206. package/src/services/local-variables.ts +0 -45
  207. package/src/services/logger-console.test.ts +0 -117
  208. package/src/services/logger-console.ts +0 -174
  209. package/src/services/logger.ts +0 -48
  210. package/src/services/meta-service.test.ts +0 -75
  211. package/src/services/meta-service.ts +0 -767
  212. package/src/services/persona-actor-secret.test.ts +0 -68
  213. package/src/services/persona-actor-secret.ts +0 -70
  214. package/src/services/persona-sign-in.test.ts +0 -219
  215. package/src/services/persona-sign-in.ts +0 -238
  216. package/src/services/personas-service.ts +0 -191
  217. package/src/services/pikku-user-id.test.ts +0 -62
  218. package/src/services/pikku-user-id.ts +0 -13
  219. package/src/services/queue-webhook-service.test.ts +0 -409
  220. package/src/services/queue-webhook-service.ts +0 -150
  221. package/src/services/remote-jobs.test.ts +0 -106
  222. package/src/services/remote-jobs.ts +0 -92
  223. package/src/services/scheduler-service.ts +0 -37
  224. package/src/services/schema-service.ts +0 -10
  225. package/src/services/scope-service.ts +0 -91
  226. package/src/services/scoped-credential-service.test.ts +0 -86
  227. package/src/services/scoped-credential-service.ts +0 -63
  228. package/src/services/scoped-secret-service.test.ts +0 -93
  229. package/src/services/scoped-secret-service.ts +0 -40
  230. package/src/services/secret-host-binding.test.ts +0 -138
  231. package/src/services/secret-host-binding.ts +0 -51
  232. package/src/services/secret-service.ts +0 -25
  233. package/src/services/secretless.test.ts +0 -54
  234. package/src/services/secretless.ts +0 -29
  235. package/src/services/session-store.ts +0 -9
  236. package/src/services/stub-tracker.test.ts +0 -104
  237. package/src/services/stub-tracker.ts +0 -175
  238. package/src/services/system-role-guard.test.ts +0 -96
  239. package/src/services/system-role-guard.ts +0 -71
  240. package/src/services/temporary-file-service.ts +0 -76
  241. package/src/services/trigger-service.ts +0 -5
  242. package/src/services/typed-credential-service.ts +0 -83
  243. package/src/services/typed-secret-service.test.ts +0 -204
  244. package/src/services/typed-secret-service.ts +0 -132
  245. package/src/services/typed-variables-service.test.ts +0 -166
  246. package/src/services/typed-variables-service.ts +0 -166
  247. package/src/services/user-session-service.test.ts +0 -147
  248. package/src/services/user-session-service.ts +0 -85
  249. package/src/services/v8-coverage-service.test.ts +0 -69
  250. package/src/services/v8-coverage-service.ts +0 -163
  251. package/src/services/variables-service.ts +0 -13
  252. package/src/services/webhook-service.ts +0 -147
  253. package/src/services/workflow-service.ts +0 -120
  254. package/src/side-effects-are-declared.test.ts +0 -84
  255. package/src/source-files-stay-composable.test.ts +0 -41
  256. package/src/template-alias-surface.test.ts +0 -240
  257. package/src/test-utils.ts +0 -38
  258. package/src/testing/index.ts +0 -3
  259. package/src/testing/service-tests/agent-run-service-tests.ts +0 -98
  260. package/src/testing/service-tests/agent-storage-service-tests.ts +0 -379
  261. package/src/testing/service-tests/channel-store-tests.ts +0 -98
  262. package/src/testing/service-tests/credential-service-tests.ts +0 -143
  263. package/src/testing/service-tests/deployment-service-tests.ts +0 -33
  264. package/src/testing/service-tests/event-hub-store-tests.ts +0 -48
  265. package/src/testing/service-tests/secret-service-tests.ts +0 -105
  266. package/src/testing/service-tests/session-store-tests.ts +0 -62
  267. package/src/testing/service-tests/workflow-run-service-tests.ts +0 -59
  268. package/src/testing/service-tests/workflow-service-tests.ts +0 -308
  269. package/src/testing/service-tests.ts +0 -95
  270. package/src/time-utils.test.ts +0 -56
  271. package/src/time-utils.ts +0 -89
  272. package/src/types/core.types.ts +0 -446
  273. package/src/types/index.ts +0 -25
  274. package/src/types/state.types.ts +0 -226
  275. package/src/types/typed-credentials.test.ts +0 -72
  276. package/src/utils/hash.test.ts +0 -68
  277. package/src/utils/hash.ts +0 -26
  278. package/src/utils/hmac.ts +0 -21
  279. package/src/utils/node-host-resolver.ts +0 -20
  280. package/src/utils/safe-fetch.test.ts +0 -515
  281. package/src/utils/safe-fetch.ts +0 -347
  282. package/src/utils.test.ts +0 -359
  283. package/src/utils.ts +0 -165
  284. package/src/version.test.ts +0 -80
  285. package/src/version.ts +0 -25
  286. package/src/wirings/actor-flow/actor-flow.types.ts +0 -40
  287. package/src/wirings/actor-flow/index.ts +0 -10
  288. package/src/wirings/actor-flow/run-conversation.test.ts +0 -187
  289. package/src/wirings/actor-flow/run-conversation.ts +0 -280
  290. package/src/wirings/addon/addon-auth-tags.test.ts +0 -219
  291. package/src/wirings/addon/addon-runner.ts +0 -236
  292. package/src/wirings/addon/addon-scopes.test.ts +0 -225
  293. package/src/wirings/addon/addon-secrets.test.ts +0 -391
  294. package/src/wirings/addon/index.ts +0 -12
  295. package/src/wirings/addon/remote-addon-auth.ts +0 -65
  296. package/src/wirings/addon/wire-addon.test.ts +0 -102
  297. package/src/wirings/addon/wire-addon.ts +0 -202
  298. package/src/wirings/addon/wire-remote-addon.ts +0 -41
  299. package/src/wirings/agent/agent-agui.test.ts +0 -1213
  300. package/src/wirings/agent/agent-agui.ts +0 -405
  301. package/src/wirings/agent/agent-authorization.test.ts +0 -208
  302. package/src/wirings/agent/agent-finalize.test.ts +0 -187
  303. package/src/wirings/agent/agent-finalize.ts +0 -197
  304. package/src/wirings/agent/agent-helpers.test.ts +0 -215
  305. package/src/wirings/agent/agent-helpers.ts +0 -96
  306. package/src/wirings/agent/agent-interrupt.test.ts +0 -833
  307. package/src/wirings/agent/agent-interrupt.ts +0 -400
  308. package/src/wirings/agent/agent-memory.test.ts +0 -397
  309. package/src/wirings/agent/agent-memory.ts +0 -496
  310. package/src/wirings/agent/agent-middleware.types.test.ts +0 -41
  311. package/src/wirings/agent/agent-model-config.test.ts +0 -103
  312. package/src/wirings/agent/agent-model-config.ts +0 -59
  313. package/src/wirings/agent/agent-prepare.test.ts +0 -977
  314. package/src/wirings/agent/agent-prepare.ts +0 -1083
  315. package/src/wirings/agent/agent-registry.test.ts +0 -455
  316. package/src/wirings/agent/agent-registry.ts +0 -99
  317. package/src/wirings/agent/agent-resume-authorization.test.ts +0 -206
  318. package/src/wirings/agent/agent-rpc.ts +0 -116
  319. package/src/wirings/agent/agent-runner.test.ts +0 -1229
  320. package/src/wirings/agent/agent-runner.ts +0 -858
  321. package/src/wirings/agent/agent-stream-delegate.test.ts +0 -381
  322. package/src/wirings/agent/agent-stream-output-hooks.test.ts +0 -353
  323. package/src/wirings/agent/agent-stream.test.ts +0 -1800
  324. package/src/wirings/agent/agent-stream.ts +0 -1639
  325. package/src/wirings/agent/agent-thread-ownership.test.ts +0 -297
  326. package/src/wirings/agent/agent-turn.test.ts +0 -65
  327. package/src/wirings/agent/agent-turn.ts +0 -122
  328. package/src/wirings/agent/agent-utils.ts +0 -20
  329. package/src/wirings/agent/agent.types.ts +0 -676
  330. package/src/wirings/agent/index.ts +0 -58
  331. package/src/wirings/agent/voice-input.test.ts +0 -220
  332. package/src/wirings/agent/voice-input.ts +0 -180
  333. package/src/wirings/agent/voice-output.test.ts +0 -510
  334. package/src/wirings/agent/voice-output.ts +0 -311
  335. package/src/wirings/agent-scorer/agent-scorer-grade.test.ts +0 -106
  336. package/src/wirings/agent-scorer/agent-scorer-grade.ts +0 -55
  337. package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +0 -285
  338. package/src/wirings/agent-scorer/agent-scorer-judge.ts +0 -180
  339. package/src/wirings/agent-scorer/agent-scorer-live.test.ts +0 -174
  340. package/src/wirings/agent-scorer/agent-scorer-live.ts +0 -56
  341. package/src/wirings/agent-scorer/agent-scorer-registry.ts +0 -66
  342. package/src/wirings/agent-scorer/agent-scorer-sampling.test.ts +0 -34
  343. package/src/wirings/agent-scorer/agent-scorer-sampling.ts +0 -36
  344. package/src/wirings/agent-scorer/agent-scorer-snapshots.test.ts +0 -49
  345. package/src/wirings/agent-scorer/agent-scorer-snapshots.ts +0 -46
  346. package/src/wirings/agent-scorer/agent-scorer-worker.test.ts +0 -122
  347. package/src/wirings/agent-scorer/agent-scorer-worker.ts +0 -71
  348. package/src/wirings/agent-scorer/agent-scorer.ts +0 -102
  349. package/src/wirings/agent-scorer/agent-scorer.types.ts +0 -128
  350. package/src/wirings/agent-scorer/index.ts +0 -23
  351. package/src/wirings/channel/channel-common.ts +0 -102
  352. package/src/wirings/channel/channel-handler-shapes.test.ts +0 -72
  353. package/src/wirings/channel/channel-handler.test.ts +0 -322
  354. package/src/wirings/channel/channel-handler.ts +0 -259
  355. package/src/wirings/channel/channel-host-rpc.test.ts +0 -150
  356. package/src/wirings/channel/channel-host-rpc.ts +0 -69
  357. package/src/wirings/channel/channel-middleware-runner.test.ts +0 -188
  358. package/src/wirings/channel/channel-middleware-runner.ts +0 -126
  359. package/src/wirings/channel/channel-rpc-registry.ts +0 -116
  360. package/src/wirings/channel/channel-rpc-responder.ts +0 -117
  361. package/src/wirings/channel/channel-rpc-service.ts +0 -132
  362. package/src/wirings/channel/channel-rpc-validators.ts +0 -65
  363. package/src/wirings/channel/channel-rpc.test.ts +0 -841
  364. package/src/wirings/channel/channel-rpc.ts +0 -5
  365. package/src/wirings/channel/channel-rpc.types.ts +0 -160
  366. package/src/wirings/channel/channel-runner.ts +0 -164
  367. package/src/wirings/channel/channel-store.ts +0 -35
  368. package/src/wirings/channel/channel.types.ts +0 -193
  369. package/src/wirings/channel/define-channel-routes.ts +0 -5
  370. package/src/wirings/channel/eventhub-service.ts +0 -16
  371. package/src/wirings/channel/eventhub-store.ts +0 -13
  372. package/src/wirings/channel/index.ts +0 -40
  373. package/src/wirings/channel/local/index.ts +0 -3
  374. package/src/wirings/channel/local/local-channel-handler.ts +0 -98
  375. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -339
  376. package/src/wirings/channel/local/local-channel-runner.ts +0 -271
  377. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -77
  378. package/src/wirings/channel/local/local-eventhub-service.ts +0 -69
  379. package/src/wirings/channel/log-channels.ts +0 -16
  380. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -135
  381. package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -60
  382. package/src/wirings/channel/serverless/index.ts +0 -6
  383. package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -328
  384. package/src/wirings/cli/channel/cli-approval.test.ts +0 -177
  385. package/src/wirings/cli/channel/cli-approval.ts +0 -135
  386. package/src/wirings/cli/channel/cli-channel-runner.ts +0 -118
  387. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +0 -192
  388. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +0 -148
  389. package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +0 -480
  390. package/src/wirings/cli/channel/cli-raw-client-runner.ts +0 -188
  391. package/src/wirings/cli/channel/index.ts +0 -6
  392. package/src/wirings/cli/cli-runner.test.ts +0 -969
  393. package/src/wirings/cli/cli-runner.ts +0 -538
  394. package/src/wirings/cli/cli.types.ts +0 -388
  395. package/src/wirings/cli/command-parser.test.ts +0 -1120
  396. package/src/wirings/cli/command-parser.ts +0 -713
  397. package/src/wirings/cli/define-cli-commands.ts +0 -13
  398. package/src/wirings/cli/format-cli-error.test.ts +0 -91
  399. package/src/wirings/cli/format-cli-error.ts +0 -96
  400. package/src/wirings/cli/index.ts +0 -19
  401. package/src/wirings/credential/credential.types.ts +0 -37
  402. package/src/wirings/credential/define-credential.ts +0 -49
  403. package/src/wirings/credential/index.ts +0 -7
  404. package/src/wirings/credential/validate-credential-definitions.ts +0 -63
  405. package/src/wirings/gateway/gateway-authorization.test.ts +0 -575
  406. package/src/wirings/gateway/gateway-channel-meta.test.ts +0 -44
  407. package/src/wirings/gateway/gateway-runner.test.ts +0 -581
  408. package/src/wirings/gateway/gateway-runner.ts +0 -411
  409. package/src/wirings/gateway/gateway.types.ts +0 -137
  410. package/src/wirings/gateway/index.ts +0 -15
  411. package/src/wirings/http/http-routes.test.ts +0 -318
  412. package/src/wirings/http/http-routes.ts +0 -111
  413. package/src/wirings/http/http-runner-addon-ref.test.ts +0 -238
  414. package/src/wirings/http/http-runner.test.ts +0 -503
  415. package/src/wirings/http/http-runner.ts +0 -462
  416. package/src/wirings/http/http.types.ts +0 -307
  417. package/src/wirings/http/index.ts +0 -27
  418. package/src/wirings/http/log-http-routes.ts +0 -18
  419. package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -318
  420. package/src/wirings/http/pikku-fetch-http-request.ts +0 -260
  421. package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -123
  422. package/src/wirings/http/pikku-fetch-http-response.ts +0 -149
  423. package/src/wirings/http/routers/http-router.ts +0 -13
  424. package/src/wirings/http/routers/path-to-regex.test.ts +0 -306
  425. package/src/wirings/http/routers/path-to-regex.ts +0 -115
  426. package/src/wirings/http/web-request.test.ts +0 -382
  427. package/src/wirings/http/web-request.ts +0 -162
  428. package/src/wirings/mcp/index.ts +0 -26
  429. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -388
  430. package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
  431. package/src/wirings/mcp/mcp-runner.test.ts +0 -588
  432. package/src/wirings/mcp/mcp-runner.ts +0 -330
  433. package/src/wirings/mcp/mcp.types.ts +0 -199
  434. package/src/wirings/node/index.ts +0 -2
  435. package/src/wirings/node/node.types.ts +0 -23
  436. package/src/wirings/oauth2/index.ts +0 -1
  437. package/src/wirings/oauth2/oauth2.types.ts +0 -12
  438. package/src/wirings/persona/define-personas.ts +0 -33
  439. package/src/wirings/persona/index.ts +0 -75
  440. package/src/wirings/persona/persona-app-scopes.test.ts +0 -47
  441. package/src/wirings/persona/persona-app-scopes.ts +0 -74
  442. package/src/wirings/persona/persona-email.ts +0 -87
  443. package/src/wirings/persona/persona-environments.test.ts +0 -194
  444. package/src/wirings/persona/persona-environments.ts +0 -138
  445. package/src/wirings/persona/persona-mailbox.ts +0 -156
  446. package/src/wirings/persona/persona.test.ts +0 -230
  447. package/src/wirings/persona/persona.types.ts +0 -156
  448. package/src/wirings/persona/validate-personas.ts +0 -137
  449. package/src/wirings/queue/index.ts +0 -29
  450. package/src/wirings/queue/queue-identity.test.ts +0 -454
  451. package/src/wirings/queue/queue-identity.ts +0 -176
  452. package/src/wirings/queue/queue-runner.test.ts +0 -705
  453. package/src/wirings/queue/queue-runner.ts +0 -181
  454. package/src/wirings/queue/queue.types.ts +0 -161
  455. package/src/wirings/queue/register-queue-helper.ts +0 -46
  456. package/src/wirings/queue/signed-queue-service.ts +0 -60
  457. package/src/wirings/queue/validate-worker-config.test.ts +0 -108
  458. package/src/wirings/queue/validate-worker-config.ts +0 -90
  459. package/src/wirings/role/define-system-role.ts +0 -33
  460. package/src/wirings/role/index.ts +0 -13
  461. package/src/wirings/role/role.test.ts +0 -104
  462. package/src/wirings/role/role.types.ts +0 -47
  463. package/src/wirings/role/validate-role-definitions.ts +0 -93
  464. package/src/wirings/rpc/index.ts +0 -8
  465. package/src/wirings/rpc/rpc-runner.test.ts +0 -1130
  466. package/src/wirings/rpc/rpc-runner.ts +0 -569
  467. package/src/wirings/rpc/rpc-types.ts +0 -87
  468. package/src/wirings/scheduler/index.ts +0 -11
  469. package/src/wirings/scheduler/log-schedulers.ts +0 -16
  470. package/src/wirings/scheduler/scheduler-runner.test.ts +0 -835
  471. package/src/wirings/scheduler/scheduler-runner.ts +0 -138
  472. package/src/wirings/scheduler/scheduler.types.ts +0 -42
  473. package/src/wirings/scope/define-scope.ts +0 -33
  474. package/src/wirings/scope/index.ts +0 -15
  475. package/src/wirings/scope/scope.test.ts +0 -159
  476. package/src/wirings/scope/scope.types.ts +0 -42
  477. package/src/wirings/scope/validate-scope-definitions.ts +0 -107
  478. package/src/wirings/secret/index.ts +0 -9
  479. package/src/wirings/secret/secret.types.ts +0 -70
  480. package/src/wirings/secret/validate-secret-definitions.test.ts +0 -232
  481. package/src/wirings/secret/validate-secret-definitions.ts +0 -88
  482. package/src/wirings/trigger/index.ts +0 -9
  483. package/src/wirings/trigger/pikku-trigger-service.ts +0 -107
  484. package/src/wirings/trigger/trigger-runner.test.ts +0 -78
  485. package/src/wirings/trigger/trigger-runner.ts +0 -113
  486. package/src/wirings/trigger/trigger.types.ts +0 -100
  487. package/src/wirings/variable/index.ts +0 -8
  488. package/src/wirings/variable/validate-variable-definitions.test.ts +0 -115
  489. package/src/wirings/variable/validate-variable-definitions.ts +0 -71
  490. package/src/wirings/variable/variable.types.ts +0 -40
  491. package/src/wirings/virtual-user/index.ts +0 -107
  492. package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +0 -115
  493. package/src/wirings/virtual-user/prepare-virtual-user-run.ts +0 -95
  494. package/src/wirings/virtual-user/run-virtual-user.test.ts +0 -774
  495. package/src/wirings/virtual-user/run-virtual-user.ts +0 -684
  496. package/src/wirings/virtual-user/virtual-user-agents.test.ts +0 -72
  497. package/src/wirings/virtual-user/virtual-user-agents.ts +0 -62
  498. package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +0 -215
  499. package/src/wirings/virtual-user/virtual-user-catalogue.ts +0 -184
  500. package/src/wirings/virtual-user/virtual-user-derive.test.ts +0 -426
  501. package/src/wirings/virtual-user/virtual-user-derive.ts +0 -161
  502. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +0 -68
  503. package/src/wirings/virtual-user/virtual-user-dispositions.ts +0 -210
  504. package/src/wirings/virtual-user/virtual-user-intents.test.ts +0 -218
  505. package/src/wirings/virtual-user/virtual-user-intents.ts +0 -185
  506. package/src/wirings/virtual-user/virtual-user-rng.test.ts +0 -72
  507. package/src/wirings/virtual-user/virtual-user-rng.ts +0 -50
  508. package/src/wirings/virtual-user/virtual-user-run-store.ts +0 -131
  509. package/src/wirings/virtual-user/virtual-user-scaffold.test.ts +0 -854
  510. package/src/wirings/virtual-user/virtual-user-scaffold.ts +0 -664
  511. package/src/wirings/virtual-user/virtual-user-schedule-store.ts +0 -93
  512. package/src/wirings/virtual-user/virtual-user-schedule.test.ts +0 -280
  513. package/src/wirings/virtual-user/virtual-user-schedule.ts +0 -156
  514. package/src/wirings/virtual-user/virtual-user-target.ts +0 -47
  515. package/src/wirings/virtual-user/virtual-user.types.ts +0 -215
  516. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -709
  517. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -25
  518. package/src/wirings/workflow/feature.test.ts +0 -131
  519. package/src/wirings/workflow/feature.ts +0 -59
  520. package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +0 -569
  521. package/src/wirings/workflow/graph/graph-foreach.test.ts +0 -626
  522. package/src/wirings/workflow/graph/graph-node.ts +0 -115
  523. package/src/wirings/workflow/graph/graph-runner.test.ts +0 -1049
  524. package/src/wirings/workflow/graph/graph-runner.ts +0 -1376
  525. package/src/wirings/workflow/graph/graph-validation.test.ts +0 -27
  526. package/src/wirings/workflow/graph/graph-validation.ts +0 -41
  527. package/src/wirings/workflow/graph/template.test.ts +0 -49
  528. package/src/wirings/workflow/graph/template.ts +0 -42
  529. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -39
  530. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -75
  531. package/src/wirings/workflow/index.ts +0 -107
  532. package/src/wirings/workflow/pikku-scenario-service.ts +0 -1013
  533. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -963
  534. package/src/wirings/workflow/pikku-workflow-service.ts +0 -1993
  535. package/src/wirings/workflow/run-timeline.test.ts +0 -199
  536. package/src/wirings/workflow/run-timeline.ts +0 -185
  537. package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -107
  538. package/src/wirings/workflow/scenario-cookie-jar.ts +0 -57
  539. package/src/wirings/workflow/scenario-expectations.test.ts +0 -238
  540. package/src/wirings/workflow/scenario-hooks.test.ts +0 -259
  541. package/src/wirings/workflow/scenario-poll.test.ts +0 -64
  542. package/src/wirings/workflow/scenario-poll.ts +0 -27
  543. package/src/wirings/workflow/scenario-prose.test.ts +0 -275
  544. package/src/wirings/workflow/scenario-prose.ts +0 -86
  545. package/src/wirings/workflow/scenario-run.types.ts +0 -193
  546. package/src/wirings/workflow/scenario-service.test.ts +0 -147
  547. package/src/wirings/workflow/scenario-step-guards.ts +0 -23
  548. package/src/wirings/workflow/scenario-step.test.ts +0 -800
  549. package/src/wirings/workflow/scenario-step.types.ts +0 -262
  550. package/src/wirings/workflow/scenario-surface.test.ts +0 -146
  551. package/src/wirings/workflow/scenario-surface.ts +0 -71
  552. package/src/wirings/workflow/scenario.types.ts +0 -63
  553. package/src/wirings/workflow/workflow-approval-audit.ts +0 -47
  554. package/src/wirings/workflow/workflow-approval-policy.test.ts +0 -594
  555. package/src/wirings/workflow/workflow-approval-policy.ts +0 -68
  556. package/src/wirings/workflow/workflow-approval.ts +0 -290
  557. package/src/wirings/workflow/workflow-child-run-session.test.ts +0 -168
  558. package/src/wirings/workflow/workflow-constants.ts +0 -66
  559. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +0 -116
  560. package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -55
  561. package/src/wirings/workflow/workflow-dispatch-relay.test.ts +0 -128
  562. package/src/wirings/workflow/workflow-errors.ts +0 -132
  563. package/src/wirings/workflow/workflow-inline-authority.test.ts +0 -175
  564. package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -51
  565. package/src/wirings/workflow/workflow-invocation-id.ts +0 -28
  566. package/src/wirings/workflow/workflow-meta-resolver.ts +0 -45
  567. package/src/wirings/workflow/workflow-mirror.test.ts +0 -171
  568. package/src/wirings/workflow/workflow-missing-meta.test.ts +0 -92
  569. package/src/wirings/workflow/workflow-on-error.test.ts +0 -145
  570. package/src/wirings/workflow/workflow-queue-routing.ts +0 -129
  571. package/src/wirings/workflow/workflow-queue-wiring.ts +0 -149
  572. package/src/wirings/workflow/workflow-queue-workers.test.ts +0 -131
  573. package/src/wirings/workflow/workflow-queue-workers.ts +0 -66
  574. package/src/wirings/workflow/workflow-recovery.ts +0 -192
  575. package/src/wirings/workflow/workflow-replay-snapshot.test.ts +0 -140
  576. package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -113
  577. package/src/wirings/workflow/workflow-run-authority.test.ts +0 -212
  578. package/src/wirings/workflow/workflow-run-context.test.ts +0 -172
  579. package/src/wirings/workflow/workflow-run-engine.types.ts +0 -92
  580. package/src/wirings/workflow/workflow-run-ownership.ts +0 -37
  581. package/src/wirings/workflow/workflow-run-polling.test.ts +0 -127
  582. package/src/wirings/workflow/workflow-stalled-recovery.test.ts +0 -152
  583. package/src/wirings/workflow/workflow-status-stream.test.ts +0 -354
  584. package/src/wirings/workflow/workflow-status-stream.ts +0 -144
  585. package/src/wirings/workflow/workflow-step-claim.ts +0 -46
  586. package/src/wirings/workflow/workflow-step-ordinal.test.ts +0 -94
  587. package/src/wirings/workflow/workflow-step-session.test.ts +0 -87
  588. package/src/wirings/workflow/workflow-suspend.ts +0 -61
  589. package/src/wirings/workflow/workflow-terminal-run-guard.test.ts +0 -105
  590. package/src/wirings/workflow/workflow.types.ts +0 -268
  591. package/src/wirings-stay-decoupled.test.ts +0 -130
  592. package/tsconfig.json +0 -14
  593. package/tsconfig.tsbuildinfo +0 -1
  594. package/tsconfig.type-tests.json +0 -15
@@ -1,1619 +0,0 @@
1
- import { describe, test, beforeEach } from 'node:test'
2
- import * as assert from 'node:assert'
3
- import {
4
- addFunction,
5
- getAllFunctionNames,
6
- getFunctionNames,
7
- runPikkuFunc,
8
- } from './function-runner.js'
9
- import { addGlobalPermission } from '../permissions.js'
10
- import { addTagMiddleware } from '../middleware-runner.js'
11
- import { addSchema } from '../schema.js'
12
- import { resetPikkuState, pikkuState } from '../pikku-state.js'
13
- import type { CoreServices } from '../types/core.types.js'
14
- import type { CorePikkuMiddleware } from '../middleware/middleware.types.js'
15
- import type { CorePermissionGroup } from './functions.types.js'
16
- import { PikkuSessionService } from '../services/user-session-service.js'
17
- import { MissingScopeError, ReadonlySessionError } from '../errors/errors.js'
18
- import { createInvocationAudit } from '../services/audit-service.js'
19
- import { SecretAccessDeniedError } from '../services/secretless.js'
20
-
21
- beforeEach(() => {
22
- resetPikkuState()
23
- })
24
-
25
- const addTestFunction = (funcName: string, funcConfig: any) => {
26
- addFunction(funcName, funcConfig)
27
- // Convert tags to middleware metadata (tags are organizational only; they no
28
- // longer carry permissions in the session+function authorization model).
29
- const middleware = funcConfig.tags
30
- ? funcConfig.tags.map((tag: string) => ({ type: 'tag' as const, tag }))
31
- : undefined
32
- pikkuState(null, 'function', 'meta')[funcName] = {
33
- pikkuFuncId: funcName,
34
- inputSchemaName: null,
35
- outputSchemaName: null,
36
- sessionless: true,
37
- middleware,
38
- }
39
- }
40
-
41
- const mockSingletonServices = {
42
- logger: {
43
- info: () => {},
44
- warn: () => {},
45
- error: () => {},
46
- debug: () => {},
47
- },
48
- } as any
49
-
50
- const mockServices: CoreServices = {
51
- ...mockSingletonServices,
52
- } as any
53
-
54
- describe('runPikkuFunc - Integration Tests', () => {
55
- test('should execute middleware in correct order: wiringTags → wiringMiddleware → funcTags → funcMiddleware', async () => {
56
- const executionOrder: string[] = []
57
- const createMiddleware = (name: string): CorePikkuMiddleware => {
58
- return async (services, wire, next) => {
59
- executionOrder.push(name)
60
- await next()
61
- }
62
- }
63
-
64
- addTagMiddleware('wiringTag', [createMiddleware('wiringTag')])
65
- addTagMiddleware('funcTag', [createMiddleware('funcTag')])
66
-
67
- addTestFunction('testFunc', {
68
- func: async () => {
69
- executionOrder.push('main')
70
- return 'success'
71
- },
72
- middleware: [createMiddleware('funcMiddleware')],
73
- tags: ['funcTag'],
74
- })
75
-
76
- const result = await runPikkuFunc(
77
- 'rpc',
78
- Math.random().toString(),
79
- 'testFunc',
80
- {
81
- singletonServices: mockSingletonServices,
82
- getAllServices: () => mockServices,
83
- data: () => ({}),
84
- wireMiddleware: [createMiddleware('wiringMiddleware')],
85
- inheritedMiddleware: [{ type: 'tag', tag: 'wiringTag' }],
86
- auth: false,
87
- wire: {},
88
- }
89
- )
90
-
91
- assert.equal(result, 'success')
92
- // Order: wireInheritedMiddleware (tags), wireMiddleware, funcInheritedMiddleware (tags), funcMiddleware
93
- assert.deepEqual(executionOrder, [
94
- 'wiringTag',
95
- 'wiringMiddleware',
96
- 'funcTag',
97
- 'funcMiddleware',
98
- 'main',
99
- ])
100
- })
101
-
102
- test('runs global (AND) then function (OR) permissions before the handler', async () => {
103
- const executionOrder: string[] = []
104
-
105
- addGlobalPermission([
106
- async () => {
107
- executionOrder.push('globalPermission')
108
- return true
109
- },
110
- ])
111
-
112
- const funcPermissions: CorePermissionGroup = {
113
- permissions: [
114
- async () => {
115
- executionOrder.push('funcPermission')
116
- return true
117
- },
118
- ],
119
- }
120
-
121
- addTestFunction('testFunc', {
122
- func: async () => {
123
- executionOrder.push('main')
124
- return 'success'
125
- },
126
- permissions: funcPermissions,
127
- })
128
-
129
- const result = await runPikkuFunc(
130
- 'rpc',
131
- Math.random().toString(),
132
- 'testFunc',
133
- {
134
- singletonServices: mockSingletonServices,
135
- getAllServices: () => mockServices,
136
- data: () => ({}),
137
- auth: false,
138
- wire: {},
139
- }
140
- )
141
-
142
- assert.equal(result, 'success')
143
- assert.deepEqual(executionOrder, [
144
- 'globalPermission',
145
- 'funcPermission',
146
- 'main',
147
- ])
148
- })
149
-
150
- test('a failing global permission denies before the function permission runs', async () => {
151
- let funcPermissionRan = false
152
- addGlobalPermission([async () => false])
153
-
154
- addTestFunction('testFunc', {
155
- func: async () => 'success',
156
- permissions: {
157
- allow: [
158
- async () => {
159
- funcPermissionRan = true
160
- return true
161
- },
162
- ],
163
- },
164
- })
165
-
166
- await assert.rejects(
167
- runPikkuFunc('rpc', Math.random().toString(), 'testFunc', {
168
- singletonServices: mockSingletonServices,
169
- getAllServices: () => mockServices,
170
- data: () => ({}),
171
- auth: false,
172
- wire: {},
173
- }),
174
- {
175
- message: 'Permission denied',
176
- }
177
- )
178
- assert.equal(funcPermissionRan, false)
179
- })
180
-
181
- test('a passing global permission does not satisfy a failing function permission', async () => {
182
- addGlobalPermission([async () => true])
183
-
184
- addTestFunction('testFunc', {
185
- func: async () => 'success',
186
- permissions: { admin: [async () => false] },
187
- })
188
-
189
- await assert.rejects(
190
- runPikkuFunc('rpc', Math.random().toString(), 'testFunc', {
191
- singletonServices: mockSingletonServices,
192
- getAllServices: () => mockServices,
193
- data: () => ({}),
194
- auth: false,
195
- wire: {},
196
- }),
197
- {
198
- message: 'Permission denied',
199
- }
200
- )
201
- })
202
-
203
- test('should throw specific error for function permission failures', async () => {
204
- const funcPermissions: CorePermissionGroup = {
205
- permissions: [async () => false],
206
- }
207
-
208
- addTestFunction('testFunc', {
209
- func: async () => 'success',
210
- permissions: funcPermissions,
211
- })
212
-
213
- await assert.rejects(
214
- runPikkuFunc('rpc', Math.random().toString(), 'testFunc', {
215
- singletonServices: mockSingletonServices,
216
- getAllServices: () => mockServices,
217
- data: () => ({}),
218
- auth: false,
219
- wire: {},
220
- }),
221
- {
222
- message: 'Permission denied',
223
- }
224
- )
225
- })
226
-
227
- test('should deduplicate middleware when same middleware appears in tags and direct', async () => {
228
- let executionCount = 0
229
-
230
- const duplicatedMiddleware: CorePikkuMiddleware = async (
231
- services,
232
- wire,
233
- next
234
- ) => {
235
- executionCount++
236
- await next()
237
- }
238
-
239
- addTagMiddleware('testTag', [duplicatedMiddleware])
240
-
241
- addTestFunction('testFunc', {
242
- func: async () => 'success',
243
- middleware: [duplicatedMiddleware], // Same middleware directly
244
- tags: ['testTag'], // Same middleware via tag
245
- })
246
-
247
- await runPikkuFunc('rpc', Math.random().toString(), 'testFunc', {
248
- singletonServices: mockSingletonServices,
249
- getAllServices: () => mockServices,
250
- data: () => ({}),
251
- auth: false,
252
- wire: {},
253
- })
254
-
255
- assert.equal(executionCount, 1)
256
- })
257
-
258
- test('should OR function permission groups and short-circuit on the first pass', async () => {
259
- const executionOrder: string[] = []
260
-
261
- // Two groups ORed together: the first (owner) fails, the second (admin) passes.
262
- const permissions: CorePermissionGroup = {
263
- owner: [
264
- async () => {
265
- executionOrder.push('owner')
266
- return false
267
- },
268
- ],
269
- admin: [
270
- async () => {
271
- executionOrder.push('admin')
272
- return true
273
- },
274
- ],
275
- }
276
-
277
- addTestFunction('testFunc', {
278
- func: async () => {
279
- executionOrder.push('main')
280
- return 'success'
281
- },
282
- permissions,
283
- })
284
-
285
- const result = await runPikkuFunc(
286
- 'rpc',
287
- Math.random().toString(),
288
- 'testFunc',
289
- {
290
- singletonServices: mockSingletonServices,
291
- getAllServices: () => mockServices,
292
- data: () => ({}),
293
- auth: false,
294
- wire: {},
295
- }
296
- )
297
-
298
- assert.equal(result, 'success')
299
- assert.deepEqual(executionOrder, ['owner', 'admin', 'main'])
300
- })
301
-
302
- test('should work without any middleware or permissions', async () => {
303
- addTestFunction('simpleFunc', {
304
- func: async () => 'simple success',
305
- })
306
-
307
- const result = await runPikkuFunc(
308
- 'rpc',
309
- Math.random().toString(),
310
- 'simpleFunc',
311
- {
312
- singletonServices: mockSingletonServices,
313
- getAllServices: () => mockServices,
314
- data: () => ({}),
315
- auth: false,
316
- wire: {},
317
- }
318
- )
319
-
320
- assert.equal(result, 'simple success')
321
- })
322
-
323
- test('should work with only wiring-level middleware', async () => {
324
- const executionOrder: string[] = []
325
-
326
- const wiringMiddleware: CorePikkuMiddleware = async (
327
- services,
328
- wire,
329
- next
330
- ) => {
331
- executionOrder.push('wiringMiddleware')
332
- await next()
333
- }
334
-
335
- addTestFunction('testFunc', {
336
- func: async () => {
337
- executionOrder.push('main')
338
- return 'success'
339
- },
340
- })
341
-
342
- const result = await runPikkuFunc(
343
- 'rpc',
344
- Math.random().toString(),
345
- 'testFunc',
346
- {
347
- singletonServices: mockSingletonServices,
348
- getAllServices: () => mockServices,
349
- data: () => ({}),
350
- wireMiddleware: [wiringMiddleware],
351
- auth: false,
352
- wire: {},
353
- }
354
- )
355
-
356
- assert.equal(result, 'success')
357
- assert.deepEqual(executionOrder, ['wiringMiddleware', 'main'])
358
- })
359
-
360
- test('should work with only function-level middleware and permissions', async () => {
361
- const executionOrder: string[] = []
362
-
363
- const funcMiddleware: CorePikkuMiddleware = async (
364
- services,
365
- wire,
366
- next
367
- ) => {
368
- executionOrder.push('funcMiddleware')
369
- await next()
370
- }
371
-
372
- const funcPermissions: CorePermissionGroup = {
373
- permissions: [
374
- async () => {
375
- executionOrder.push('funcPermission')
376
- return true
377
- },
378
- ],
379
- }
380
-
381
- addTestFunction('testFunc', {
382
- func: async () => {
383
- executionOrder.push('main')
384
- return 'success'
385
- },
386
- middleware: [funcMiddleware],
387
- permissions: funcPermissions,
388
- })
389
-
390
- const result = await runPikkuFunc(
391
- 'rpc',
392
- Math.random().toString(),
393
- 'testFunc',
394
- {
395
- singletonServices: mockSingletonServices,
396
- getAllServices: () => mockServices,
397
- data: () => ({}),
398
- auth: false,
399
- wire: {},
400
- }
401
- )
402
-
403
- assert.equal(result, 'success')
404
- assert.deepEqual(executionOrder, [
405
- 'funcMiddleware',
406
- 'funcPermission',
407
- 'main',
408
- ])
409
- })
410
-
411
- test('should pass correct parameters to function', async () => {
412
- let receivedServices: any
413
- let receivedData: any
414
- let receivedWire: any
415
-
416
- const testData = { test: 'data' }
417
-
418
- addTestFunction('testFunc', {
419
- func: async (services, data, wire) => {
420
- receivedServices = services
421
- receivedData = data
422
- receivedWire = wire
423
- return 'success'
424
- },
425
- })
426
-
427
- await runPikkuFunc('rpc', Math.random().toString(), 'testFunc', {
428
- singletonServices: mockServices,
429
- data: () => testData,
430
- auth: false,
431
- wire: { rpc: {} },
432
- })
433
-
434
- assert.deepEqual(receivedServices, mockServices)
435
- assert.equal(receivedData, testData)
436
- assert.ok(receivedWire.rpc)
437
- assert.equal(receivedWire.session, undefined)
438
- })
439
-
440
- test('should handle async createWireServices function', async () => {
441
- let servicesProvided: any
442
- const wireServices = { customService: 'value' }
443
-
444
- addTestFunction('testFunc', {
445
- func: async (services, data, wire) => {
446
- servicesProvided = services
447
- return 'success'
448
- },
449
- })
450
-
451
- const asyncCreateWireServices = async () => {
452
- await new Promise((resolve) => setTimeout(resolve, 1))
453
- return wireServices
454
- }
455
-
456
- const result = await runPikkuFunc(
457
- 'rpc',
458
- Math.random().toString(),
459
- 'testFunc',
460
- {
461
- singletonServices: mockSingletonServices,
462
- createWireServices: asyncCreateWireServices,
463
- data: () => ({}),
464
- auth: false,
465
- wire: {},
466
- }
467
- )
468
-
469
- assert.equal(result, 'success')
470
- assert.deepEqual(servicesProvided, {
471
- ...mockSingletonServices,
472
- ...wireServices,
473
- })
474
- })
475
-
476
- test('should resolve versioned function ids to the base function and warn once', async () => {
477
- const warnings: string[] = []
478
- const singletonServices = {
479
- ...mockSingletonServices,
480
- logger: {
481
- ...mockSingletonServices.logger,
482
- warn: (message: string) => {
483
- warnings.push(message)
484
- },
485
- },
486
- } as any
487
-
488
- addTestFunction('versionedBase', {
489
- func: async () => 'resolved',
490
- })
491
-
492
- const result = await runPikkuFunc(
493
- 'rpc',
494
- Math.random().toString(),
495
- 'versionedBase@v2',
496
- {
497
- singletonServices,
498
- data: () => ({}),
499
- auth: false,
500
- wire: {},
501
- }
502
- )
503
-
504
- assert.equal(result, 'resolved')
505
- assert.equal(warnings.length, 1)
506
- assert.match(
507
- warnings[0]!,
508
- /Version 'versionedBase@v2' not registered, resolved to 'versionedBase'/
509
- )
510
- })
511
-
512
- test('should throw when function metadata is missing', async () => {
513
- addFunction('metaMissing', {
514
- func: async () => 'ok',
515
- } as any)
516
-
517
- await assert.rejects(
518
- () =>
519
- runPikkuFunc('rpc', Math.random().toString(), 'metaMissing', {
520
- singletonServices: mockSingletonServices,
521
- data: () => ({}),
522
- auth: false,
523
- wire: {},
524
- }),
525
- {
526
- message: 'Function meta not found: metaMissing',
527
- }
528
- )
529
- })
530
-
531
- test('should throw when function config is missing', async () => {
532
- pikkuState(null, 'function', 'meta').missingConfig = {
533
- pikkuFuncId: 'missingConfig',
534
- sessionless: true,
535
- permissions: [],
536
- } as any
537
-
538
- await assert.rejects(
539
- () =>
540
- runPikkuFunc('rpc', Math.random().toString(), 'missingConfig', {
541
- singletonServices: mockSingletonServices,
542
- data: () => ({}),
543
- auth: false,
544
- wire: {},
545
- }),
546
- {
547
- message: 'Function not found: missingConfig',
548
- }
549
- )
550
- })
551
-
552
- test('should require a session when sessionless metadata is false', async () => {
553
- addTestFunction('sessionRequired', {
554
- func: async () => 'ok',
555
- })
556
- pikkuState(null, 'function', 'meta').sessionRequired.sessionless = false
557
-
558
- await assert.rejects(
559
- () =>
560
- runPikkuFunc('rpc', Math.random().toString(), 'sessionRequired', {
561
- singletonServices: mockSingletonServices,
562
- data: () => ({}),
563
- auth: true,
564
- wire: {},
565
- }),
566
- {
567
- message: 'Authentication required',
568
- }
569
- )
570
- })
571
-
572
- test('should warn when auth is disabled for a session-required function but still enforce session', async () => {
573
- const warnings: string[] = []
574
- const singletonServices = {
575
- ...mockSingletonServices,
576
- logger: {
577
- ...mockSingletonServices.logger,
578
- warn: (message: string) => {
579
- warnings.push(message)
580
- },
581
- },
582
- } as any
583
-
584
- addTestFunction('sessionRequired', {
585
- func: async () => 'ok',
586
- auth: false,
587
- })
588
- pikkuState(null, 'function', 'meta').sessionRequired.sessionless = false
589
-
590
- await assert.rejects(
591
- () =>
592
- runPikkuFunc('rpc', Math.random().toString(), 'sessionRequired', {
593
- singletonServices,
594
- data: () => ({}),
595
- auth: false,
596
- wire: {},
597
- }),
598
- {
599
- message: 'Authentication required',
600
- }
601
- )
602
-
603
- assert.equal(warnings.length, 1)
604
- assert.match(
605
- warnings[0]!,
606
- /requires a session but auth was explicitly disabled/
607
- )
608
- })
609
-
610
- test('should require auth when sessionless metadata is absent (undefined defaults to session-required)', async () => {
611
- addTestFunction('legacyAuth', {
612
- func: async () => 'ok',
613
- auth: true,
614
- })
615
- delete pikkuState(null, 'function', 'meta').legacyAuth.sessionless
616
-
617
- await assert.rejects(
618
- () =>
619
- runPikkuFunc('rpc', Math.random().toString(), 'legacyAuth', {
620
- singletonServices: mockSingletonServices,
621
- data: () => ({}),
622
- auth: false,
623
- wire: {},
624
- }),
625
- {
626
- message: 'Authentication required',
627
- }
628
- )
629
- })
630
-
631
- test('should reject readonly sessions for non-readonly functions', async () => {
632
- addTestFunction('readonlyBlocked', {
633
- func: async () => 'ok',
634
- })
635
- pikkuState(null, 'function', 'meta').readonlyBlocked.sessionless = true
636
-
637
- await assert.rejects(
638
- () =>
639
- runPikkuFunc('rpc', Math.random().toString(), 'readonlyBlocked', {
640
- singletonServices: mockSingletonServices,
641
- data: () => ({}),
642
- auth: false,
643
- wire: { session: { readonly: true } },
644
- }),
645
- ReadonlySessionError
646
- )
647
- })
648
-
649
- test('should set session helpers on the wire when a session service is provided', async () => {
650
- const sessionStore = {
651
- get: async () => undefined,
652
- }
653
- const sessionService = new PikkuSessionService(sessionStore as any)
654
- const wire: any = {}
655
- let receivedWire: any
656
-
657
- addTestFunction('sessionHelpers', {
658
- func: async (_services: any, _data: any, innerWire: any) => {
659
- receivedWire = innerWire
660
- innerWire.setSession({ userId: 'u1' })
661
- return innerWire.getSession()
662
- },
663
- })
664
-
665
- const result = await runPikkuFunc(
666
- 'rpc',
667
- Math.random().toString(),
668
- 'sessionHelpers',
669
- {
670
- singletonServices: {
671
- ...mockSingletonServices,
672
- sessionStore,
673
- } as any,
674
- data: () => ({}),
675
- auth: false,
676
- wire,
677
- sessionService,
678
- }
679
- )
680
-
681
- assert.deepEqual(result, { userId: 'u1' })
682
- assert.equal(typeof receivedWire.setSession, 'function')
683
- assert.equal(typeof receivedWire.clearSession, 'function')
684
- assert.equal(typeof receivedWire.getSession, 'function')
685
- assert.equal(typeof receivedWire.hasSessionChanged, 'function')
686
- })
687
-
688
- test('should load session from sessionStore using pikkuUserId from the wire', async () => {
689
- let sessionLookups = 0
690
- let receivedSession: any
691
-
692
- addTestFunction('loadStoredSession', {
693
- func: async (_services: any, _data: any, wire: any) => {
694
- receivedSession = wire.session
695
- return 'ok'
696
- },
697
- })
698
-
699
- await runPikkuFunc('rpc', Math.random().toString(), 'loadStoredSession', {
700
- singletonServices: {
701
- ...mockSingletonServices,
702
- sessionStore: {
703
- get: async (userId: string) => {
704
- sessionLookups++
705
- assert.equal(userId, 'user-1')
706
- return { userId, loaded: true }
707
- },
708
- },
709
- } as any,
710
- data: () => ({}),
711
- auth: false,
712
- wire: { pikkuUserId: 'user-1' },
713
- })
714
-
715
- assert.equal(sessionLookups, 1)
716
- assert.deepEqual(receivedSession, { userId: 'user-1', loaded: true })
717
- })
718
-
719
- test('should expose middleware-set session to createWireServices credential lookups', async () => {
720
- let credentialLookups = 0
721
- let receivedCredential: any
722
- const sessionService = new PikkuSessionService<any>()
723
-
724
- addTestFunction('credentialWire', {
725
- middleware: [
726
- async (_services: any, wire: any, next: any) => {
727
- wire.setSession({ userId: 'user-1' })
728
- await next()
729
- },
730
- ],
731
- func: async (_services: any, _data: any, wire: any) => {
732
- return wire.getSession()
733
- },
734
- })
735
-
736
- await runPikkuFunc('rpc', Math.random().toString(), 'credentialWire', {
737
- singletonServices: {
738
- ...mockSingletonServices,
739
- credentialService: {
740
- getAll: async (userId: string) => {
741
- credentialLookups++
742
- assert.equal(userId, 'user-1')
743
- return {
744
- 'user-oauth': { accessToken: 'token-1' },
745
- }
746
- },
747
- },
748
- } as any,
749
- createWireServices: async (_services: any, wire: any) => {
750
- receivedCredential = await wire.getCredential('user-oauth')
751
- return {}
752
- },
753
- data: () => ({}),
754
- auth: false,
755
- wire: {
756
- session: undefined,
757
- setSession: (session: any) => sessionService.setInitial(session),
758
- getSession: () => sessionService.get(),
759
- hasSessionChanged: () => sessionService.sessionChanged,
760
- } as any,
761
- sessionService,
762
- })
763
-
764
- assert.equal(credentialLookups, 1)
765
- assert.deepEqual(receivedCredential, { accessToken: 'token-1' })
766
- })
767
-
768
- test('should pass addon-scoped singleton services and wrap bare workflow names', async () => {
769
- pikkuState(null, 'addons', 'packages').set('stripe', {
770
- package: '@addon/stripe',
771
- } as any)
772
-
773
- const workflowCalls: unknown[][] = []
774
- let singletonFactoryCalls = 0
775
- let configFactoryCalls = 0
776
-
777
- pikkuState('@addon/stripe', 'package', 'factories', {
778
- createConfig: async () => {
779
- configFactoryCalls++
780
- return { name: 'addon-config' }
781
- },
782
- createSingletonServices: async () => {
783
- singletonFactoryCalls++
784
- return {
785
- logger: mockSingletonServices.logger,
786
- workflowService: {
787
- startWorkflow: async (...args: unknown[]) => {
788
- workflowCalls.push(args)
789
- return { runId: 'wf-1' }
790
- },
791
- },
792
- }
793
- },
794
- } as any)
795
-
796
- addFunction(
797
- 'addonFunc',
798
- {
799
- func: async (services: any, _data: any) => {
800
- await services.workflowService.startWorkflow('chargeCustomer', {
801
- amount: 10,
802
- })
803
- await services.workflowService.startWorkflow('stripe:alreadyScoped', {
804
- amount: 20,
805
- })
806
- return 'ok'
807
- },
808
- } as any,
809
- '@addon/stripe'
810
- )
811
- pikkuState('@addon/stripe', 'function', 'meta').addonFunc = {
812
- pikkuFuncId: 'addonFunc',
813
- sessionless: true,
814
- permissions: [],
815
- } as any
816
-
817
- await runPikkuFunc('rpc', Math.random().toString(), 'addonFunc', {
818
- singletonServices: {
819
- ...mockSingletonServices,
820
- config: { parent: true },
821
- variables: {},
822
- } as any,
823
- data: () => ({}),
824
- auth: false,
825
- wire: {},
826
- packageName: '@addon/stripe',
827
- })
828
-
829
- assert.equal(configFactoryCalls, 1)
830
- assert.equal(singletonFactoryCalls, 1)
831
- assert.deepEqual(workflowCalls, [
832
- ['stripe:chargeCustomer', { amount: 10 }],
833
- ['stripe:alreadyScoped', { amount: 20 }],
834
- ])
835
- })
836
-
837
- test('should reuse cached addon singleton services on subsequent calls', async () => {
838
- let singletonFactoryCalls = 0
839
- pikkuState('@addon/cache', 'package', 'factories', {
840
- createSingletonServices: async () => {
841
- singletonFactoryCalls++
842
- return {
843
- logger: mockSingletonServices.logger,
844
- }
845
- },
846
- } as any)
847
-
848
- addFunction(
849
- 'cachedFunc',
850
- {
851
- func: async () => 'ok',
852
- } as any,
853
- '@addon/cache'
854
- )
855
- pikkuState('@addon/cache', 'function', 'meta').cachedFunc = {
856
- pikkuFuncId: 'cachedFunc',
857
- sessionless: true,
858
- permissions: [],
859
- } as any
860
-
861
- await runPikkuFunc('rpc', '1', 'cachedFunc', {
862
- singletonServices: mockSingletonServices,
863
- data: () => ({}),
864
- auth: false,
865
- wire: {},
866
- packageName: '@addon/cache',
867
- })
868
- await runPikkuFunc('rpc', '2', 'cachedFunc', {
869
- singletonServices: mockSingletonServices,
870
- data: () => ({}),
871
- auth: false,
872
- wire: {},
873
- packageName: '@addon/cache',
874
- })
875
-
876
- assert.equal(singletonFactoryCalls, 1)
877
- })
878
-
879
- test('should use addon createWireServices instead of the caller createWireServices', async () => {
880
- let callerCreateWireServicesUsed = false
881
- let addonCreateWireServicesUsed = false
882
- let receivedServices: any
883
-
884
- pikkuState('@addon/wires', 'package', 'factories', {
885
- createSingletonServices: async () => ({
886
- logger: mockSingletonServices.logger,
887
- }),
888
- createWireServices: async () => {
889
- addonCreateWireServicesUsed = true
890
- return { addonWire: true }
891
- },
892
- } as any)
893
-
894
- addFunction(
895
- 'wireFunc',
896
- {
897
- func: async (services: any) => {
898
- receivedServices = services
899
- return 'ok'
900
- },
901
- } as any,
902
- '@addon/wires'
903
- )
904
- pikkuState('@addon/wires', 'function', 'meta').wireFunc = {
905
- pikkuFuncId: 'wireFunc',
906
- sessionless: true,
907
- permissions: [],
908
- } as any
909
-
910
- await runPikkuFunc('rpc', 'wire', 'wireFunc', {
911
- singletonServices: mockSingletonServices,
912
- createWireServices: async () => {
913
- callerCreateWireServicesUsed = true
914
- return { callerWire: true }
915
- },
916
- data: () => ({}),
917
- auth: false,
918
- wire: {},
919
- packageName: '@addon/wires',
920
- })
921
-
922
- assert.equal(callerCreateWireServicesUsed, false)
923
- assert.equal(addonCreateWireServicesUsed, true)
924
- assert.deepEqual(receivedServices, {
925
- logger: mockSingletonServices.logger,
926
- addonWire: true,
927
- })
928
- })
929
-
930
- test('should lazily memoize the rpc getter on the wire', async () => {
931
- let receivedWire: any
932
-
933
- addTestFunction('rpcGetter', {
934
- func: async (_services: any, _data: any, wire: any) => {
935
- receivedWire = wire
936
- const rpcA = wire.rpc
937
- const rpcB = wire.rpc
938
- return rpcA === rpcB
939
- },
940
- })
941
-
942
- const result = await runPikkuFunc('rpc', 'rpc-getter', 'rpcGetter', {
943
- singletonServices: mockSingletonServices,
944
- data: () => ({}),
945
- auth: false,
946
- wire: {},
947
- })
948
-
949
- assert.equal(result, true)
950
- assert.ok(receivedWire.rpc)
951
- })
952
-
953
- test('should expose resolved audit metadata on the wire for audited functions', async () => {
954
- let receivedAudit: any
955
-
956
- addTestFunction('auditedWire', {
957
- audit: { durability: 'transactional' },
958
- func: async (_services: any, _data: any, wire: any) => {
959
- receivedAudit = wire.audit
960
- return 'ok'
961
- },
962
- })
963
-
964
- const result = await runPikkuFunc('rpc', 'wire-audit-on', 'auditedWire', {
965
- singletonServices: mockSingletonServices,
966
- data: () => ({}),
967
- auth: false,
968
- wire: {},
969
- })
970
-
971
- assert.equal(result, 'ok')
972
- assert.deepEqual(receivedAudit, { durability: 'transactional' })
973
- })
974
-
975
- test('should leave wire audit metadata undefined when audit is disabled', async () => {
976
- let receivedWire: any
977
-
978
- addTestFunction('plainWire', {
979
- func: async (_services: any, _data: any, wire: any) => {
980
- receivedWire = wire
981
- return 'ok'
982
- },
983
- })
984
-
985
- const result = await runPikkuFunc('rpc', 'wire-audit-off', 'plainWire', {
986
- singletonServices: mockSingletonServices,
987
- data: () => ({}),
988
- auth: false,
989
- wire: {},
990
- })
991
-
992
- assert.equal(result, 'ok')
993
- assert.equal(receivedWire.audit, undefined)
994
- })
995
-
996
- test('should restore parent audit metadata after nested non-audited calls', async () => {
997
- let auditBeforeChild: any
998
- let auditAfterChild: any
999
-
1000
- addTestFunction('childPlain', {
1001
- func: async () => 'child-ok',
1002
- })
1003
-
1004
- addTestFunction('parentAudited', {
1005
- audit: true,
1006
- func: async (_services: any, _data: any, wire: any) => {
1007
- auditBeforeChild = wire.audit
1008
- await runPikkuFunc('rpc', 'nested-child-wire', 'childPlain', {
1009
- singletonServices: mockSingletonServices,
1010
- data: () => ({}),
1011
- auth: false,
1012
- wire,
1013
- })
1014
- auditAfterChild = wire.audit
1015
- return 'parent-ok'
1016
- },
1017
- })
1018
-
1019
- const result = await runPikkuFunc(
1020
- 'rpc',
1021
- 'nested-parent-wire',
1022
- 'parentAudited',
1023
- {
1024
- singletonServices: mockSingletonServices,
1025
- data: () => ({}),
1026
- auth: false,
1027
- wire: {},
1028
- }
1029
- )
1030
-
1031
- assert.equal(result, 'parent-ok')
1032
- assert.deepEqual(auditBeforeChild, { durability: 'best-effort' })
1033
- assert.deepEqual(auditAfterChild, { durability: 'best-effort' })
1034
- })
1035
-
1036
- test('should preserve parent audit metadata across rpc sub-calls to non-audited functions', async () => {
1037
- let parentAuditBeforeChild: any
1038
- let parentAuditAfterChild: any
1039
- let parentFunctionIdBeforeChild: any
1040
- let parentFunctionIdAfterChild: any
1041
- let childAudit: any
1042
- let childFunctionId: any
1043
-
1044
- addTestFunction('rpcChildPlain', {
1045
- func: async (_services: any, _data: any, wire: any) => {
1046
- childAudit = wire.audit
1047
- childFunctionId = wire.functionId
1048
- return 'child-ok'
1049
- },
1050
- })
1051
-
1052
- addTestFunction('rpcParentAudited', {
1053
- audit: true,
1054
- func: async (_services: any, _data: any, wire: any) => {
1055
- parentAuditBeforeChild = wire.audit
1056
- parentFunctionIdBeforeChild = wire.functionId
1057
- const childResult = await wire.rpc.invoke('rpcChildPlain', {})
1058
- parentAuditAfterChild = wire.audit
1059
- parentFunctionIdAfterChild = wire.functionId
1060
- return childResult
1061
- },
1062
- })
1063
-
1064
- const result = await runPikkuFunc(
1065
- 'rpc',
1066
- 'rpc-parent-wire',
1067
- 'rpcParentAudited',
1068
- {
1069
- singletonServices: mockSingletonServices,
1070
- data: () => ({}),
1071
- auth: false,
1072
- wire: {},
1073
- }
1074
- )
1075
-
1076
- assert.equal(result, 'child-ok')
1077
- assert.deepEqual(parentAuditBeforeChild, { durability: 'best-effort' })
1078
- assert.deepEqual(parentAuditAfterChild, { durability: 'best-effort' })
1079
- assert.equal(parentFunctionIdBeforeChild, 'rpcParentAudited')
1080
- assert.equal(parentFunctionIdAfterChild, 'rpcParentAudited')
1081
- assert.equal(childAudit, undefined)
1082
- assert.equal(childFunctionId, 'rpcChildPlain')
1083
- })
1084
-
1085
- test('should bind audited rpc sub-calls to the child invocation context', async () => {
1086
- let parentAuditBeforeChild: any
1087
- let parentAuditAfterChild: any
1088
- let parentFunctionIdAfterChild: any
1089
- let childAudit: any
1090
- let childFunctionId: any
1091
-
1092
- addTestFunction('rpcChildAudited', {
1093
- audit: { durability: 'transactional' },
1094
- func: async (_services: any, _data: any, wire: any) => {
1095
- childAudit = wire.audit
1096
- childFunctionId = wire.functionId
1097
- return 'child-audited-ok'
1098
- },
1099
- })
1100
-
1101
- addTestFunction('rpcParentAuditedChild', {
1102
- audit: true,
1103
- func: async (_services: any, _data: any, wire: any) => {
1104
- parentAuditBeforeChild = wire.audit
1105
- const childResult = await wire.rpc.invoke('rpcChildAudited', {})
1106
- parentAuditAfterChild = wire.audit
1107
- parentFunctionIdAfterChild = wire.functionId
1108
- return childResult
1109
- },
1110
- })
1111
-
1112
- const result = await runPikkuFunc(
1113
- 'rpc',
1114
- 'rpc-parent-child-audit-wire',
1115
- 'rpcParentAuditedChild',
1116
- {
1117
- singletonServices: mockSingletonServices,
1118
- data: () => ({}),
1119
- auth: false,
1120
- wire: {},
1121
- }
1122
- )
1123
-
1124
- assert.equal(result, 'child-audited-ok')
1125
- assert.deepEqual(parentAuditBeforeChild, { durability: 'best-effort' })
1126
- assert.deepEqual(parentAuditAfterChild, { durability: 'best-effort' })
1127
- assert.equal(parentFunctionIdAfterChild, 'rpcParentAuditedChild')
1128
- assert.deepEqual(childAudit, { durability: 'transactional' })
1129
- assert.equal(childFunctionId, 'rpcChildAudited')
1130
- })
1131
-
1132
- test('should audit writes from an audited function invoked via exposed rpc (stale outer auditLog)', async () => {
1133
- // Repro of the exposed-RPC HTTP path: the generated rpcCaller has no
1134
- // audit config, and createWireServices builds auditLog on ITS wire —
1135
- // the audited inner function must not inherit that disabled instance.
1136
- const events: any[] = []
1137
- const singletonServices = {
1138
- ...mockSingletonServices,
1139
- audit: { audit: async (event: any) => events.push(event) },
1140
- }
1141
-
1142
- addTestFunction('auditedInner', {
1143
- audit: true,
1144
- func: async (services: any) => {
1145
- await services.auditLog.write({
1146
- type: 'booking.update',
1147
- source: 'explicit',
1148
- })
1149
- return 'inner-ok'
1150
- },
1151
- })
1152
-
1153
- addTestFunction('rpcCallerLike', {
1154
- func: async (_services: any, _data: any, wire: any) => {
1155
- return wire.rpc.invoke('auditedInner', {})
1156
- },
1157
- })
1158
-
1159
- const result = await runPikkuFunc(
1160
- 'http',
1161
- 'exposed-rpc-route',
1162
- 'rpcCallerLike',
1163
- {
1164
- singletonServices,
1165
- data: () => ({}),
1166
- auth: false,
1167
- wire: {},
1168
- createWireServices: async (services: any, wire: any) => ({
1169
- auditLog: createInvocationAudit(services.audit, wire),
1170
- }),
1171
- }
1172
- )
1173
-
1174
- assert.equal(result, 'inner-ok')
1175
- assert.equal(events.length, 1)
1176
- assert.equal(events[0].type, 'booking.update')
1177
- assert.equal(events[0].functionId, 'auditedInner')
1178
- })
1179
-
1180
- test('should give an audited rpc sub-call its own audit buffer with child attribution', async () => {
1181
- const events: any[] = []
1182
- const singletonServices = {
1183
- ...mockSingletonServices,
1184
- audit: { audit: async (event: any) => events.push(event) },
1185
- }
1186
-
1187
- addTestFunction('auditedChildWriter', {
1188
- audit: true,
1189
- func: async (services: any) => {
1190
- await services.auditLog.write({
1191
- type: 'child.event',
1192
- source: 'explicit',
1193
- })
1194
- return 'child-ok'
1195
- },
1196
- })
1197
-
1198
- addTestFunction('auditedParentWriter', {
1199
- audit: true,
1200
- func: async (services: any, _data: any, wire: any) => {
1201
- await services.auditLog.write({
1202
- type: 'parent.event',
1203
- source: 'explicit',
1204
- })
1205
- return wire.rpc.invoke('auditedChildWriter', {})
1206
- },
1207
- })
1208
-
1209
- const result = await runPikkuFunc(
1210
- 'rpc',
1211
- 'audited-parent-child',
1212
- 'auditedParentWriter',
1213
- {
1214
- singletonServices,
1215
- data: () => ({}),
1216
- auth: false,
1217
- wire: {},
1218
- }
1219
- )
1220
-
1221
- assert.equal(result, 'child-ok')
1222
- const byType = Object.fromEntries(events.map((e) => [e.type, e.functionId]))
1223
- assert.equal(byType['parent.event'], 'auditedParentWriter')
1224
- assert.equal(byType['child.event'], 'auditedChildWriter')
1225
- })
1226
-
1227
- test('should warn via the singleton logger when audit writes are dropped', async () => {
1228
- const warnings: any[] = []
1229
- const singletonServices = {
1230
- ...mockSingletonServices,
1231
- logger: {
1232
- ...mockSingletonServices.logger,
1233
- warn: (msg: any) => warnings.push(msg),
1234
- },
1235
- audit: { audit: async () => {} },
1236
- }
1237
-
1238
- addTestFunction('unauditedWriter', {
1239
- func: async (services: any) => {
1240
- await services.auditLog.write({
1241
- type: 'dropped.event',
1242
- source: 'explicit',
1243
- })
1244
- return 'ok'
1245
- },
1246
- })
1247
-
1248
- const result = await runPikkuFunc(
1249
- 'rpc',
1250
- 'unaudited-writer',
1251
- 'unauditedWriter',
1252
- {
1253
- singletonServices,
1254
- data: () => ({}),
1255
- auth: false,
1256
- wire: {},
1257
- createWireServices: async (services: any, wire: any) => ({
1258
- auditLog: createInvocationAudit(
1259
- services.audit,
1260
- wire,
1261
- services.logger
1262
- ),
1263
- }),
1264
- }
1265
- )
1266
-
1267
- assert.equal(result, 'ok')
1268
- assert.equal(warnings.length, 1)
1269
- assert.match(String(warnings[0]), /dropped/)
1270
- assert.match(String(warnings[0]), /unauditedWriter/)
1271
- })
1272
-
1273
- test('should expose the resolved base function id after version fallback', async () => {
1274
- let receivedFunctionId: any
1275
-
1276
- addTestFunction('versionedBase', {
1277
- audit: true,
1278
- func: async (_services: any, _data: any, wire: any) => {
1279
- receivedFunctionId = wire.functionId
1280
- return 'ok'
1281
- },
1282
- })
1283
-
1284
- const result = await runPikkuFunc(
1285
- 'rpc',
1286
- 'wire-version-fallback',
1287
- 'versionedBase@v2',
1288
- {
1289
- singletonServices: mockSingletonServices,
1290
- data: () => ({}),
1291
- auth: false,
1292
- wire: {},
1293
- }
1294
- )
1295
-
1296
- assert.equal(result, 'ok')
1297
- assert.equal(receivedFunctionId, 'versionedBase')
1298
- })
1299
- })
1300
-
1301
- describe('function-runner helpers', () => {
1302
- test('getFunctionNames and getAllFunctionNames should include addon namespaces', () => {
1303
- addTestFunction('rootFunc', { func: async () => 'ok' })
1304
- pikkuState(null, 'addons', 'packages').set('stripe', {
1305
- package: '@addon/stripe',
1306
- } as any)
1307
- addFunction(
1308
- 'addonFunc',
1309
- {
1310
- func: async () => 'ok',
1311
- } as any,
1312
- '@addon/stripe'
1313
- )
1314
- pikkuState('@addon/stripe', 'function', 'meta').addonFunc = {
1315
- pikkuFuncId: 'addonFunc',
1316
- sessionless: true,
1317
- permissions: [],
1318
- } as any
1319
-
1320
- assert.deepEqual(getFunctionNames().sort(), ['rootFunc'])
1321
- assert.deepEqual(getFunctionNames('@addon/stripe').sort(), ['addonFunc'])
1322
- assert.deepEqual(getAllFunctionNames().sort(), [
1323
- 'rootFunc',
1324
- 'stripe:addonFunc',
1325
- ])
1326
- })
1327
- })
1328
-
1329
- describe('runPikkuFunc - scopes', () => {
1330
- const runWithScopes = (
1331
- funcName: string,
1332
- {
1333
- scopes,
1334
- sessionScopes,
1335
- data = () => ({}),
1336
- }: {
1337
- scopes?: string[]
1338
- sessionScopes?: string[]
1339
- data?: () => any
1340
- }
1341
- ) =>
1342
- runPikkuFunc('rpc', Math.random().toString(), funcName, {
1343
- singletonServices: mockSingletonServices,
1344
- data,
1345
- auth: false,
1346
- wire: { session: { userId: 'u1', scopes: sessionScopes } as any },
1347
- })
1348
-
1349
- test('runs the function when the session holds the required scope', async () => {
1350
- addTestFunction('scoped', {
1351
- func: async () => 'ok',
1352
- scopes: ['invoices:create'],
1353
- })
1354
-
1355
- const result = await runWithScopes('scoped', {
1356
- sessionScopes: ['invoices:create'],
1357
- })
1358
-
1359
- assert.equal(result, 'ok')
1360
- })
1361
-
1362
- test('throws MissingScopeError when the session lacks the scope', async () => {
1363
- addTestFunction('scoped', {
1364
- func: async () => 'ok',
1365
- scopes: ['invoices:create'],
1366
- })
1367
-
1368
- await assert.rejects(
1369
- () => runWithScopes('scoped', { sessionScopes: ['billing:read'] }),
1370
- MissingScopeError
1371
- )
1372
- })
1373
-
1374
- test('honours wildcards held by the session', async () => {
1375
- addTestFunction('scoped', {
1376
- func: async () => 'ok',
1377
- scopes: ['admin:invoices:create'],
1378
- })
1379
-
1380
- const result = await runWithScopes('scoped', { sessionScopes: ['admin:*'] })
1381
-
1382
- assert.equal(result, 'ok')
1383
- })
1384
-
1385
- test('does not gate a function that declares no scopes', async () => {
1386
- addTestFunction('unscoped', { func: async () => 'ok' })
1387
-
1388
- const result = await runWithScopes('unscoped', { sessionScopes: [] })
1389
-
1390
- assert.equal(result, 'ok')
1391
- })
1392
-
1393
- test('reads scopes from function meta when the config omits them', async () => {
1394
- addTestFunction('metaScoped', { func: async () => 'ok' })
1395
- pikkuState(null, 'function', 'meta').metaScoped.scopes = ['invoices:create']
1396
-
1397
- await assert.rejects(
1398
- () => runWithScopes('metaScoped', { sessionScopes: [] }),
1399
- MissingScopeError
1400
- )
1401
- })
1402
-
1403
- // The load-bearing test. Scopes are an AND gate separate from permissions, so
1404
- // a passing global (or function) permission must never satisfy a scope.
1405
- test('a passing global permission does not satisfy a scope', async () => {
1406
- addGlobalPermission([async () => true])
1407
- addTestFunction('scoped', { func: async () => 'ok', scopes: ['admin'] })
1408
-
1409
- await assert.rejects(
1410
- () => runWithScopes('scoped', { sessionScopes: [] }),
1411
- MissingScopeError
1412
- )
1413
- })
1414
-
1415
- test('a passing function permission does not satisfy a scope', async () => {
1416
- addTestFunction('scoped', {
1417
- func: async () => 'ok',
1418
- scopes: ['admin'],
1419
- permissions: { allow: [async () => true] },
1420
- })
1421
-
1422
- await assert.rejects(
1423
- () => runWithScopes('scoped', { sessionScopes: [] }),
1424
- MissingScopeError
1425
- )
1426
- })
1427
-
1428
- test('a held scope does not bypass a failing permission', async () => {
1429
- addTestFunction('scoped', {
1430
- func: async () => 'ok',
1431
- scopes: ['admin'],
1432
- permissions: { deny: [async () => false] },
1433
- })
1434
-
1435
- await assert.rejects(
1436
- () => runWithScopes('scoped', { sessionScopes: ['admin'] }),
1437
- { message: 'Permission denied' }
1438
- )
1439
- })
1440
-
1441
- test('a failing scope short-circuits before permissions run', async () => {
1442
- let permissionRan = false
1443
- addTestFunction('scoped', {
1444
- func: async () => 'ok',
1445
- scopes: ['admin'],
1446
- permissions: {
1447
- allow: [
1448
- async () => {
1449
- permissionRan = true
1450
- return true
1451
- },
1452
- ],
1453
- },
1454
- })
1455
-
1456
- await assert.rejects(
1457
- () => runWithScopes('scoped', { sessionScopes: [] }),
1458
- MissingScopeError
1459
- )
1460
- assert.equal(permissionRan, false)
1461
- })
1462
-
1463
- // Scopes depend only on the session, so a denied request must never pay to
1464
- // parse or validate its body.
1465
- test('a failing scope short-circuits before the data is evaluated', async () => {
1466
- addTestFunction('scoped', { func: async () => 'ok', scopes: ['admin'] })
1467
-
1468
- await assert.rejects(
1469
- () =>
1470
- runWithScopes('scoped', {
1471
- sessionScopes: [],
1472
- data: () => {
1473
- throw new Error('data should not have been evaluated')
1474
- },
1475
- }),
1476
- MissingScopeError
1477
- )
1478
- })
1479
-
1480
- test('fails closed when there is no session at all', async () => {
1481
- addTestFunction('scoped', { func: async () => 'ok', scopes: ['admin'] })
1482
-
1483
- await assert.rejects(
1484
- () =>
1485
- runPikkuFunc('rpc', Math.random().toString(), 'scoped', {
1486
- singletonServices: mockSingletonServices,
1487
- data: () => ({}),
1488
- auth: false,
1489
- wire: {},
1490
- }),
1491
- MissingScopeError
1492
- )
1493
- })
1494
-
1495
- describe('schema defaults', () => {
1496
- const addPagedFunction = (funcName: string, seen: { data?: any }) => {
1497
- addSchema('PagedInput', {
1498
- properties: {
1499
- page: { type: 'number', default: 1 },
1500
- limit: { type: 'number', default: 50 },
1501
- },
1502
- })
1503
- addTestFunction(funcName, {
1504
- func: async (_services: any, data: any) => {
1505
- seen.data = data
1506
- return 'ok'
1507
- },
1508
- })
1509
- pikkuState(null, 'function', 'meta')[funcName]!.inputSchemaName =
1510
- 'PagedInput'
1511
- }
1512
-
1513
- // The whole point of the fix: `{}` validates because a defaulted property
1514
- // is not `required`, so without this the body reads `undefined` for a value
1515
- // its generated type declares as a number.
1516
- test('a function called with {} receives its schema defaults', async () => {
1517
- const seen: { data?: any } = {}
1518
- addPagedFunction('paged', seen)
1519
-
1520
- await runPikkuFunc('rpc', Math.random().toString(), 'paged', {
1521
- singletonServices: mockSingletonServices,
1522
- getAllServices: () => mockServices,
1523
- data: () => ({}),
1524
- auth: false,
1525
- wire: {},
1526
- })
1527
-
1528
- assert.equal(seen.data.page, 1)
1529
- assert.equal(seen.data.limit, 50)
1530
- })
1531
-
1532
- // `coerceDataFromSchema` is not passed here, exactly as a direct RPC
1533
- // invocation leaves it. Defaults belong to the schema rather than to the
1534
- // transport, so they must not ride on that flag — and a call made with no
1535
- // arguments at all still has to get them.
1536
- test('defaults apply with coercion off and no data', async () => {
1537
- const seen: { data?: any } = {}
1538
- addPagedFunction('pagedNoCoerce', seen)
1539
-
1540
- await runPikkuFunc('rpc', Math.random().toString(), 'pagedNoCoerce', {
1541
- singletonServices: mockSingletonServices,
1542
- getAllServices: () => mockServices,
1543
- data: () => undefined,
1544
- auth: false,
1545
- wire: {},
1546
- })
1547
-
1548
- assert.equal(seen.data.page, 1)
1549
- assert.equal(seen.data.limit, 50)
1550
- })
1551
-
1552
- test('a supplied value survives', async () => {
1553
- const seen: { data?: any } = {}
1554
- addPagedFunction('pagedSupplied', seen)
1555
-
1556
- await runPikkuFunc('rpc', Math.random().toString(), 'pagedSupplied', {
1557
- singletonServices: mockSingletonServices,
1558
- getAllServices: () => mockServices,
1559
- data: () => ({ page: 3 }),
1560
- auth: false,
1561
- wire: {},
1562
- })
1563
-
1564
- assert.equal(seen.data.page, 3)
1565
- assert.equal(seen.data.limit, 50)
1566
- })
1567
- })
1568
- })
1569
-
1570
- describe('runPikkuFunc - secrets', () => {
1571
- const singletonServicesWithSecrets = {
1572
- ...mockSingletonServices,
1573
- secrets: { getSecret: async () => 'super-secret' },
1574
- } as any
1575
-
1576
- const addSecretReadingFunction = (
1577
- funcName: string,
1578
- seen: { error?: unknown }
1579
- ) => {
1580
- addTestFunction(funcName, {
1581
- func: async (services: any) => {
1582
- try {
1583
- return services.secrets
1584
- } catch (error) {
1585
- seen.error = error
1586
- return 'denied'
1587
- }
1588
- },
1589
- })
1590
- }
1591
-
1592
- const run = (funcName: string) =>
1593
- runPikkuFunc('rpc', Math.random().toString(), funcName, {
1594
- singletonServices: singletonServicesWithSecrets,
1595
- getAllServices: () => singletonServicesWithSecrets,
1596
- data: () => ({}),
1597
- auth: false,
1598
- wire: {},
1599
- })
1600
-
1601
- test('a function never receives the secrets service', async () => {
1602
- const seen: { error?: unknown } = {}
1603
- addSecretReadingFunction('readsSecrets', seen)
1604
-
1605
- assert.equal(await run('readsSecrets'), 'denied')
1606
- assert.ok(seen.error instanceof SecretAccessDeniedError)
1607
- })
1608
-
1609
- test('no metadata flag can restore the secrets service', async () => {
1610
- const seen: { error?: unknown } = {}
1611
- addSecretReadingFunction('claimsAnExemption', seen)
1612
- Object.assign(pikkuState(null, 'function', 'meta')['claimsAnExemption']!, {
1613
- secretBroker: true,
1614
- })
1615
-
1616
- assert.equal(await run('claimsAnExemption'), 'denied')
1617
- assert.ok(seen.error instanceof SecretAccessDeniedError)
1618
- })
1619
- })