@plitzi/sdk-server 0.33.2 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +283 -8
  3. package/dist/actions.d.ts +62 -0
  4. package/dist/actions.js +12 -0
  5. package/dist/adapters/cloudAdapters.d.ts +72 -0
  6. package/dist/adapters/cloudAdapters.js +303 -0
  7. package/dist/adapters/space/index.d.ts +93 -0
  8. package/dist/adapters/space/index.js +122 -0
  9. package/dist/adapters/space/resolvers.d.ts +69 -0
  10. package/dist/adapters/space/resolvers.js +79 -0
  11. package/dist/adapters/space/space.test.d.ts +1 -0
  12. package/dist/adapters/space/types.d.ts +64 -0
  13. package/dist/adapters/space/types.js +41 -0
  14. package/dist/auth.d.ts +50 -0
  15. package/dist/auth.js +19 -0
  16. package/dist/core/auth/api.d.ts +400 -11
  17. package/dist/core/auth/api.js +1044 -50
  18. package/dist/core/auth/createAuth.d.ts +135 -30
  19. package/dist/core/auth/createAuth.js +96 -24
  20. package/dist/core/auth/credentials.d.ts +9 -3
  21. package/dist/core/auth/credentials.js +10 -4
  22. package/dist/core/auth/csrf.d.ts +89 -0
  23. package/dist/core/auth/csrf.js +234 -0
  24. package/dist/core/auth/csrf.test.d.ts +1 -0
  25. package/dist/core/auth/identity.d.ts +6 -0
  26. package/dist/core/auth/identity.js +37 -8
  27. package/dist/core/auth/lifecycle.test.d.ts +1 -0
  28. package/dist/core/auth/oauth/index.d.ts +8 -0
  29. package/dist/core/auth/oauth/index.js +13 -15
  30. package/dist/core/auth/passwords.d.ts +15 -0
  31. package/dist/core/auth/passwords.js +32 -0
  32. package/dist/core/auth/redirects.d.ts +18 -0
  33. package/dist/core/auth/redirects.js +31 -0
  34. package/dist/core/auth/redirects.test.d.ts +1 -0
  35. package/dist/core/auth/routes.d.ts +4 -2
  36. package/dist/core/auth/routes.js +234 -9
  37. package/dist/core/auth/session.d.ts +11 -0
  38. package/dist/core/auth/session.js +1 -1
  39. package/dist/core/auth/spaceTokens.d.ts +38 -2
  40. package/dist/core/auth/spaceTokens.js +54 -2
  41. package/dist/core/auth/throttle.d.ts +5 -0
  42. package/dist/core/auth/throttle.js +81 -0
  43. package/dist/core/auth/throttle.test.d.ts +1 -0
  44. package/dist/core/auth/tokens.d.ts +83 -12
  45. package/dist/core/auth/tokens.js +51 -9
  46. package/dist/core/auth/totp.d.ts +48 -0
  47. package/dist/core/auth/totp.js +118 -0
  48. package/dist/core/auth/totp.test.d.ts +1 -0
  49. package/dist/core/createServer.d.ts +0 -6
  50. package/dist/core/createServer.js +35 -3
  51. package/dist/core/fontStore.d.ts +33 -0
  52. package/dist/core/fontStore.js +53 -0
  53. package/dist/core/fontStore.test.d.ts +1 -0
  54. package/dist/core/handlers/authMiddleware.js +1 -1
  55. package/dist/core/handlers/authRouteHandlers.d.ts +9 -1
  56. package/dist/core/handlers/authRouteHandlers.js +4 -3
  57. package/dist/core/handlers/csrfMiddleware.d.ts +20 -0
  58. package/dist/core/handlers/csrfMiddleware.js +32 -0
  59. package/dist/core/handlers/oauthRouteHandlers.d.ts +46 -0
  60. package/dist/core/handlers/oauthRouteHandlers.js +159 -0
  61. package/dist/core/handlers/socialRouteHandlers.d.ts +40 -0
  62. package/dist/core/handlers/socialRouteHandlers.js +129 -0
  63. package/dist/core/handlers/socialRouteHandlers.test.d.ts +1 -0
  64. package/dist/core/handlers/types.d.ts +22 -5
  65. package/dist/core/health.d.ts +11 -1
  66. package/dist/core/health.js +27 -3
  67. package/dist/core/http/dispatcher.d.ts +1 -1
  68. package/dist/core/http/dispatcher.js +20 -1
  69. package/dist/core/http/stages/authApi.js +1 -1
  70. package/dist/core/http/stages/fontAssets.d.ts +9 -0
  71. package/dist/core/http/stages/fontAssets.js +27 -0
  72. package/dist/core/http/stages/oauth.d.ts +19 -0
  73. package/dist/core/http/stages/oauth.js +118 -0
  74. package/dist/core/http/types.d.ts +12 -0
  75. package/dist/core/oauth/authorize.d.ts +22 -0
  76. package/dist/core/oauth/authorize.js +254 -0
  77. package/dist/core/oauth/authorize.test.d.ts +1 -0
  78. package/dist/core/oauth/challenge.d.ts +11 -0
  79. package/dist/core/oauth/challenge.js +33 -0
  80. package/dist/core/oauth/consentPage.d.ts +12 -0
  81. package/dist/core/oauth/consentPage.js +131 -0
  82. package/dist/core/oauth/metadata.d.ts +41 -0
  83. package/dist/core/oauth/metadata.js +71 -0
  84. package/dist/core/oauth/params.d.ts +5 -0
  85. package/dist/core/oauth/params.js +5 -0
  86. package/dist/core/oauth/pkce.d.ts +6 -0
  87. package/dist/core/oauth/pkce.js +15 -0
  88. package/dist/core/oauth/records.d.ts +72 -0
  89. package/dist/core/oauth/records.js +49 -0
  90. package/dist/core/oauth/register.d.ts +5 -0
  91. package/dist/core/oauth/register.js +55 -0
  92. package/dist/core/oauth/respond.d.ts +21 -0
  93. package/dist/core/oauth/respond.js +59 -0
  94. package/dist/core/oauth/token.d.ts +14 -0
  95. package/dist/core/oauth/token.js +130 -0
  96. package/dist/core/previewToken.d.ts +20 -0
  97. package/dist/core/previewToken.js +29 -1
  98. package/dist/core/server/pageServer.js +4 -1
  99. package/dist/core/services/action.d.ts +6 -4
  100. package/dist/core/services/action.js +103 -10
  101. package/dist/core/services/registry.js +2 -0
  102. package/dist/core/services/resolve.d.ts +8 -0
  103. package/dist/core/services/resolve.js +12 -1
  104. package/dist/core/staticFiles.js +1 -2
  105. package/dist/handlers.d.ts +7 -1
  106. package/dist/handlers.js +4 -1
  107. package/dist/helpers/buildResponseHelpers.d.ts +8 -0
  108. package/dist/helpers/buildResponseHelpers.js +9 -1
  109. package/dist/helpers/buildServerInfo.d.ts +8 -3
  110. package/dist/helpers/buildServerInfo.js +18 -14
  111. package/dist/helpers/cache/keys.d.ts +13 -0
  112. package/dist/helpers/cache/keys.js +12 -1
  113. package/dist/helpers/onAbort.d.ts +12 -0
  114. package/dist/helpers/onAbort.js +22 -0
  115. package/dist/helpers/onAbort.test.d.ts +1 -0
  116. package/dist/helpers/outboundGuard.d.ts +27 -0
  117. package/dist/helpers/outboundGuard.js +49 -0
  118. package/dist/helpers/outboundGuard.test.d.ts +1 -0
  119. package/dist/helpers/serverLog.d.ts +6 -3
  120. package/dist/helpers/serverLog.js +20 -3
  121. package/dist/index.d.ts +6 -0
  122. package/dist/index.js +9 -4
  123. package/dist/kernel.d.ts +5 -22
  124. package/dist/kernel.js +1 -14
  125. package/dist/modules/actions/{handler.js → connectorWrite.js} +5 -2
  126. package/dist/modules/actions/connectorWrite.test.d.ts +1 -0
  127. package/dist/modules/actions/index.d.ts +40 -0
  128. package/dist/modules/actions/index.js +46 -0
  129. package/dist/modules/actions/moduleFor.d.ts +3 -0
  130. package/dist/modules/actions/moduleFor.js +35 -0
  131. package/dist/modules/actions/moduleFor.test.d.ts +1 -0
  132. package/dist/modules/actions/runtime/check.d.ts +24 -0
  133. package/dist/modules/actions/runtime/check.js +111 -0
  134. package/dist/modules/actions/runtime/check.test.d.ts +1 -0
  135. package/dist/modules/actions/runtime/errors.d.ts +11 -0
  136. package/dist/modules/actions/runtime/errors.js +17 -0
  137. package/dist/modules/actions/runtime/guards.d.ts +126 -0
  138. package/dist/modules/actions/runtime/guards.js +246 -0
  139. package/dist/modules/actions/runtime/guards.test.d.ts +1 -0
  140. package/dist/modules/actions/runtime/kvStore.d.ts +27 -0
  141. package/dist/modules/actions/runtime/kvStore.js +41 -0
  142. package/dist/modules/actions/runtime/kvStore.test.d.ts +1 -0
  143. package/dist/modules/actions/runtime/limits.d.ts +11 -0
  144. package/dist/modules/actions/runtime/limits.js +35 -0
  145. package/dist/modules/actions/runtime/memoryKv.d.ts +2 -0
  146. package/dist/modules/actions/runtime/memoryKv.js +77 -0
  147. package/dist/modules/actions/runtime/namespaceKv.d.ts +9 -0
  148. package/dist/modules/actions/runtime/namespaceKv.js +19 -0
  149. package/dist/modules/actions/runtime/precheck.d.ts +27 -0
  150. package/dist/modules/actions/runtime/precheck.js +45 -0
  151. package/dist/modules/actions/runtime/renderResolver.d.ts +15 -0
  152. package/dist/modules/actions/runtime/renderResolver.js +130 -0
  153. package/dist/modules/actions/runtime/renderResolver.test.d.ts +1 -0
  154. package/dist/modules/actions/runtime/renderShare.d.ts +29 -0
  155. package/dist/modules/actions/runtime/renderShare.js +36 -0
  156. package/dist/modules/actions/runtime/renderShare.test.d.ts +1 -0
  157. package/dist/modules/actions/runtime/report.d.ts +14 -0
  158. package/dist/modules/actions/runtime/report.js +22 -0
  159. package/dist/modules/actions/runtime/runAction.d.ts +12 -0
  160. package/dist/modules/actions/runtime/runAction.js +405 -0
  161. package/dist/modules/actions/runtime/runAction.test.d.ts +1 -0
  162. package/dist/modules/actions/runtime/runLogger.d.ts +31 -0
  163. package/dist/modules/actions/runtime/runLogger.js +66 -0
  164. package/dist/modules/actions/runtime/runLogger.test.d.ts +1 -0
  165. package/dist/modules/actions/runtime/schedule.d.ts +35 -0
  166. package/dist/modules/actions/runtime/schedule.js +76 -0
  167. package/dist/modules/actions/runtime/schedule.test.d.ts +1 -0
  168. package/dist/modules/actions/runtime/scope.d.ts +40 -0
  169. package/dist/modules/actions/runtime/scope.js +103 -0
  170. package/dist/modules/actions/runtime/triggers.d.ts +12 -0
  171. package/dist/modules/actions/runtime/triggers.js +14 -0
  172. package/dist/modules/actions/taskCatalog.d.ts +26 -0
  173. package/dist/modules/actions/taskCatalog.js +24 -0
  174. package/dist/modules/actions/taskCatalog.test.d.ts +1 -0
  175. package/dist/modules/actions/tasks/auth.d.ts +2 -0
  176. package/dist/modules/actions/tasks/auth.js +40 -0
  177. package/dist/modules/actions/tasks/builtins.d.ts +8 -0
  178. package/dist/modules/actions/tasks/builtins.js +24 -0
  179. package/dist/modules/actions/tasks/connector.d.ts +2 -0
  180. package/dist/modules/actions/tasks/connector.js +141 -0
  181. package/dist/modules/actions/tasks/db.d.ts +2 -0
  182. package/dist/modules/actions/tasks/db.js +59 -0
  183. package/dist/modules/actions/tasks/db.test.d.ts +1 -0
  184. package/dist/modules/actions/tasks/flow.d.ts +3 -0
  185. package/dist/modules/actions/tasks/flow.js +109 -0
  186. package/dist/modules/actions/tasks/helpers.d.ts +16 -0
  187. package/dist/modules/actions/tasks/helpers.js +30 -0
  188. package/dist/modules/actions/tasks/http.d.ts +2 -0
  189. package/dist/modules/actions/tasks/http.js +111 -0
  190. package/dist/modules/actions/tasks/http.test.d.ts +1 -0
  191. package/dist/modules/actions/tasks/kv.d.ts +2 -0
  192. package/dist/modules/actions/tasks/kv.js +102 -0
  193. package/dist/modules/actions/tasks/kv.test.d.ts +1 -0
  194. package/dist/modules/actions/tasks/registry.d.ts +10 -0
  195. package/dist/modules/actions/tasks/registry.js +36 -0
  196. package/dist/modules/actions/tasks/transform.d.ts +2 -0
  197. package/dist/modules/actions/tasks/transform.js +34 -0
  198. package/dist/modules/actions/transport/callHandler.d.ts +24 -0
  199. package/dist/modules/actions/transport/callHandler.js +233 -0
  200. package/dist/modules/actions/transport/callHandler.test.d.ts +1 -0
  201. package/dist/modules/actions/transport/cancelHandler.d.ts +23 -0
  202. package/dist/modules/actions/transport/cancelHandler.js +23 -0
  203. package/dist/modules/actions/transport/catalogHandler.d.ts +18 -0
  204. package/dist/modules/actions/transport/catalogHandler.js +21 -0
  205. package/dist/modules/actions/transport/stream.d.ts +23 -0
  206. package/dist/modules/actions/transport/stream.js +100 -0
  207. package/dist/modules/actions/transport/verifySignature.d.ts +16 -0
  208. package/dist/modules/actions/transport/verifySignature.js +72 -0
  209. package/dist/modules/actions/transport/webhookHandler.d.ts +25 -0
  210. package/dist/modules/actions/transport/webhookHandler.js +206 -0
  211. package/dist/modules/actions/transport/webhookHandler.test.d.ts +1 -0
  212. package/dist/modules/actions/types.d.ts +234 -0
  213. package/dist/modules/connectors/engine.js +4 -1
  214. package/dist/modules/connectors/resolver.d.ts +10 -2
  215. package/dist/modules/connectors/resolver.js +8 -3
  216. package/dist/modules/mysql/accounts.d.ts +5 -0
  217. package/dist/modules/mysql/accounts.js +365 -0
  218. package/dist/modules/mysql/accounts.test.d.ts +1 -0
  219. package/dist/modules/mysql/admin.d.ts +58 -0
  220. package/dist/modules/mysql/admin.js +133 -0
  221. package/dist/modules/mysql/config.d.ts +82 -0
  222. package/dist/modules/mysql/config.js +30 -0
  223. package/dist/modules/mysql/identities.d.ts +35 -0
  224. package/dist/modules/mysql/identities.js +43 -0
  225. package/dist/modules/mysql/index.d.ts +67 -0
  226. package/dist/modules/mysql/index.js +62 -0
  227. package/dist/modules/mysql/pool.d.ts +3 -0
  228. package/dist/modules/mysql/pool.js +88 -0
  229. package/dist/modules/mysql/query.d.ts +12 -0
  230. package/dist/modules/mysql/query.js +21 -0
  231. package/dist/modules/mysql/schema.d.ts +43 -0
  232. package/dist/modules/mysql/schema.js +340 -0
  233. package/dist/modules/mysql/schema.test.d.ts +1 -0
  234. package/dist/modules/mysql/sessions.d.ts +33 -0
  235. package/dist/modules/mysql/sessions.js +46 -0
  236. package/dist/modules/mysql/spaceTokens.d.ts +11 -0
  237. package/dist/modules/mysql/spaceTokens.js +59 -0
  238. package/dist/modules/mysql/store.integration.test.d.ts +1 -0
  239. package/dist/modules/rsc/connectorRscData.d.ts +25 -0
  240. package/dist/modules/rsc/connectorRscData.js +51 -0
  241. package/dist/modules/rsc/connectorRscData.test.d.ts +1 -0
  242. package/dist/modules/rsc/handler.js +16 -3
  243. package/dist/modules/rsc/matchRscPage.d.ts +14 -0
  244. package/dist/modules/rsc/matchRscPage.js +26 -0
  245. package/dist/modules/rsc/resolveRscData.d.ts +14 -1
  246. package/dist/modules/rsc/resolveRscData.js +23 -34
  247. package/dist/modules/ssr/Component.d.ts +6 -2
  248. package/dist/modules/ssr/Component.js +3 -1
  249. package/dist/modules/ssr/draftSession.test.d.ts +1 -0
  250. package/dist/modules/ssr/handler.js +15 -2
  251. package/dist/modules/ssr/loadPluginComponents.js +22 -5
  252. package/dist/modules/ssr/prepareRender.js +87 -8
  253. package/dist/modules/ssr/prepareRender.test.d.ts +1 -0
  254. package/dist/modules/ssr/preview.d.ts +21 -8
  255. package/dist/modules/ssr/preview.js +38 -14
  256. package/dist/modules/ssr/registerExternalPlugins.js +2 -2
  257. package/dist/modules/ssr/resolvePageSeo.d.ts +18 -0
  258. package/dist/modules/ssr/resolvePageSeo.js +26 -0
  259. package/dist/modules/ssr/resolvePageSeo.test.d.ts +1 -0
  260. package/dist/modules/ssr/template.test.d.ts +1 -0
  261. package/dist/modules/ssr/views/template.ejs +71 -5
  262. package/dist/mysql.d.ts +26 -0
  263. package/dist/mysql.js +4 -0
  264. package/dist/oauth.d.ts +24 -0
  265. package/dist/oauth.js +10 -0
  266. package/dist/plugins/compile.d.ts +1 -0
  267. package/dist/plugins/compile.js +37 -3
  268. package/dist/plugins/manager.d.ts +40 -0
  269. package/dist/plugins/manager.js +113 -13
  270. package/dist/plugins/manager.test.d.ts +1 -0
  271. package/package.json +43 -14
  272. package/skills/plitzi-authoring/SKILL.md +252 -0
  273. /package/dist/{modules/actions/handler.test.d.ts → adapters/cloudAdapters.test.d.ts} +0 -0
  274. /package/dist/modules/actions/{handler.d.ts → connectorWrite.d.ts} +0 -0
@@ -0,0 +1,31 @@
1
+ import { ActionRejectRecord, ActionRunRecord, ServerLogger } from '@plitzi/sdk-shared';
2
+ /**
3
+ * `onRun`, wired into the log stream the server already reports through.
4
+ *
5
+ * Without an `onRun` a deployment gets nothing at all about its flows: the request log says a call was answered,
6
+ * and a run started by a webhook or a schedule has no request to say anything about. This is the smallest useful
7
+ * answer to that, and it needs no store — whatever sink is already collecting `ServerLogEvent`s (a console, a
8
+ * file, a log shipper) starts carrying runs too, because a run is emitted as one more event on the same stream.
9
+ *
10
+ * What it reports is the SHAPE of the run — which steps ran and how each ended — and never what they held. A
11
+ * deployment that wants run HISTORY rather than run logs keeps its own `onRun` and writes rows; the two compose,
12
+ * since `onRun` is one function.
13
+ *
14
+ * Never throws: the runner treats the record as best-effort, and a logging outage must not take an action down.
15
+ */
16
+ export declare const createRunLogger: (logger: ServerLogger) => (record: ActionRunRecord) => void;
17
+ /**
18
+ * `onReject`, wired into the same log stream.
19
+ *
20
+ * The refusals are the half a deployment notices it is missing at the worst possible moment: an integration is
21
+ * being set up, every delivery is answered 401, and nothing anywhere says which check refused it. This puts them
22
+ * on the stream the server already reports through, as their own `kind` so a sink can route them somewhere
23
+ * louder than a run.
24
+ *
25
+ * It carries no body, no signature and no header — what went wrong is `reason`, and `detail` is the server's own
26
+ * words about it.
27
+ *
28
+ * Never throws, for the same reason the run logger does not: the caller was refused either way, and a sink's
29
+ * problem must not become the response's.
30
+ */
31
+ export declare const createRejectLogger: (logger: ServerLogger) => (record: ActionRejectRecord) => void;
@@ -0,0 +1,66 @@
1
+ //#region src/modules/actions/runtime/runLogger.ts
2
+ /**
3
+ * `onRun`, wired into the log stream the server already reports through.
4
+ *
5
+ * Without an `onRun` a deployment gets nothing at all about its flows: the request log says a call was answered,
6
+ * and a run started by a webhook or a schedule has no request to say anything about. This is the smallest useful
7
+ * answer to that, and it needs no store — whatever sink is already collecting `ServerLogEvent`s (a console, a
8
+ * file, a log shipper) starts carrying runs too, because a run is emitted as one more event on the same stream.
9
+ *
10
+ * What it reports is the SHAPE of the run — which steps ran and how each ended — and never what they held. A
11
+ * deployment that wants run HISTORY rather than run logs keeps its own `onRun` and writes rows; the two compose,
12
+ * since `onRun` is one function.
13
+ *
14
+ * Never throws: the runner treats the record as best-effort, and a logging outage must not take an action down.
15
+ */
16
+ var createRunLogger = (logger) => (record) => {
17
+ try {
18
+ logger({
19
+ kind: "run",
20
+ name: record.actionId,
21
+ spaceId: record.spaceId,
22
+ environment: record.environment,
23
+ trigger: record.trigger,
24
+ status: record.status,
25
+ ...record.userId === void 0 ? {} : { userId: record.userId },
26
+ steps: record.nodes.map((node) => `${node.action}:${node.status}`),
27
+ durationMs: record.durationMs,
28
+ ok: record.status === "completed",
29
+ ...record.error === void 0 ? {} : { error: record.error },
30
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
31
+ });
32
+ } catch {}
33
+ };
34
+ /**
35
+ * `onReject`, wired into the same log stream.
36
+ *
37
+ * The refusals are the half a deployment notices it is missing at the worst possible moment: an integration is
38
+ * being set up, every delivery is answered 401, and nothing anywhere says which check refused it. This puts them
39
+ * on the stream the server already reports through, as their own `kind` so a sink can route them somewhere
40
+ * louder than a run.
41
+ *
42
+ * It carries no body, no signature and no header — what went wrong is `reason`, and `detail` is the server's own
43
+ * words about it.
44
+ *
45
+ * Never throws, for the same reason the run logger does not: the caller was refused either way, and a sink's
46
+ * problem must not become the response's.
47
+ */
48
+ var createRejectLogger = (logger) => (record) => {
49
+ try {
50
+ logger({
51
+ kind: "reject",
52
+ name: record.actionId,
53
+ spaceId: record.spaceId,
54
+ environment: record.environment,
55
+ trigger: record.trigger,
56
+ reason: record.reason,
57
+ ...record.callerId === void 0 ? {} : { callerId: record.callerId },
58
+ durationMs: 0,
59
+ ok: false,
60
+ ...record.detail === void 0 ? {} : { error: record.detail },
61
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
62
+ });
63
+ } catch {}
64
+ };
65
+ //#endregion
66
+ export { createRejectLogger, createRunLogger };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ import { ActionsModule } from '../index';
2
+ import { ActionLookups } from '../types';
3
+ import { Environment } from '@plitzi/sdk-shared';
4
+ export type ScheduleTick = {
5
+ spaceId: number;
6
+ environment?: Environment;
7
+ /** The minute being asked about. Passed in rather than read here so a tick is testable and a missed one can be
8
+ * replayed deliberately — never guessed at. */
9
+ at?: Date;
10
+ };
11
+ export type ScheduleResult = {
12
+ ran: {
13
+ actionId: string;
14
+ runId: string;
15
+ status: string;
16
+ }[];
17
+ skipped: {
18
+ actionId: string;
19
+ reason: string;
20
+ }[];
21
+ };
22
+ export type ScheduleRunner = {
23
+ tick: (params: ScheduleTick) => Promise<ScheduleResult>;
24
+ };
25
+ /**
26
+ * Runs the actions a space has scheduled for this minute.
27
+ *
28
+ * It owns no timer, on purpose. What ticks it is the deployment's: a leader-locked interval in one role, a k8s
29
+ * CronJob hitting an endpoint, a queue consumer — all of which have to answer "which replica" and "what happens
30
+ * on restart", and none of which the SDK can answer for a deployment it cannot see.
31
+ *
32
+ * Missed ticks are NOT replayed. A scheduler that catches up fires an hour of digests at once after an outage,
33
+ * which is worse than the digest nobody got; the caller that wants a replay asks for that minute explicitly.
34
+ */
35
+ export declare const createScheduleRunner: (lookups: ActionLookups, module: ActionsModule) => ScheduleRunner;
@@ -0,0 +1,76 @@
1
+ import { ActionRunError } from "./errors.js";
2
+ import { findTriggerNode, triggerParams } from "./triggers.js";
3
+ import { cronMatches } from "@plitzi/sdk-shared/actions";
4
+ //#region src/modules/actions/runtime/schedule.ts
5
+ /**
6
+ * Runs the actions a space has scheduled for this minute.
7
+ *
8
+ * It owns no timer, on purpose. What ticks it is the deployment's: a leader-locked interval in one role, a k8s
9
+ * CronJob hitting an endpoint, a queue consumer — all of which have to answer "which replica" and "what happens
10
+ * on restart", and none of which the SDK can answer for a deployment it cannot see.
11
+ *
12
+ * Missed ticks are NOT replayed. A scheduler that catches up fires an hour of digests at once after an outage,
13
+ * which is worse than the digest nobody got; the caller that wants a replay asks for that minute explicitly.
14
+ */
15
+ var createScheduleRunner = (lookups, module) => {
16
+ const tick = async ({ spaceId, environment = "main", at = /* @__PURE__ */ new Date() }) => {
17
+ const result = {
18
+ ran: [],
19
+ skipped: []
20
+ };
21
+ const entries = await lookups.listActions?.(spaceId) ?? [];
22
+ for (const entry of entries) {
23
+ const schedule = findTriggerNode(entry.document.nodes, "schedule");
24
+ const params = schedule ? triggerParams(schedule) : void 0;
25
+ const cron = params?.cron;
26
+ if (!schedule?.enabled || !cron) continue;
27
+ if (!cronMatches(cron, at, typeof params.timezone === "string" ? params.timezone : void 0)) continue;
28
+ let run;
29
+ try {
30
+ run = await module.guards.begin({
31
+ spaceId,
32
+ actionId: entry.id,
33
+ callerId: "schedule",
34
+ input: {},
35
+ idempotencyKey: `schedule:${at.toISOString().slice(0, 16)}`,
36
+ sharedKey: true,
37
+ ttlMs: module.limitsFor(entry.document).timeoutMs
38
+ });
39
+ } catch (error) {
40
+ result.skipped.push({
41
+ actionId: entry.id,
42
+ reason: error instanceof ActionRunError ? error.reason : "failed"
43
+ });
44
+ continue;
45
+ }
46
+ try {
47
+ const run_ = await module.runAction({
48
+ entry,
49
+ input: {},
50
+ spaceId,
51
+ environment,
52
+ trigger: "schedule",
53
+ callerId: "schedule",
54
+ runId: run.runId,
55
+ signal: run.controller.signal
56
+ });
57
+ result.ran.push({
58
+ actionId: entry.id,
59
+ runId: run_.runId,
60
+ status: run_.status
61
+ });
62
+ } catch (error) {
63
+ result.skipped.push({
64
+ actionId: entry.id,
65
+ reason: error instanceof ActionRunError ? error.reason : "failed"
66
+ });
67
+ } finally {
68
+ await module.guards.end(run);
69
+ }
70
+ }
71
+ return result;
72
+ };
73
+ return { tick };
74
+ };
75
+ //#endregion
76
+ export { createScheduleRunner };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ import { ActionCredential } from '../types';
2
+ import { ActionField, SSRUser } from '@plitzi/sdk-shared';
3
+ export type FieldValidation = {
4
+ values: Record<string, unknown>;
5
+ missing: string[];
6
+ invalid: string[];
7
+ };
8
+ /**
9
+ * Coerces a value bag against declared fields.
10
+ *
11
+ * Undeclared keys are DROPPED rather than passed through, and this is what makes twig-in-params safe: every token
12
+ * a node interpolates resolves against values that survived a contract someone wrote down. A pass-through bag
13
+ * would let a caller inject whatever a template happens to reference.
14
+ */
15
+ export declare const applyFields: (fields: Record<string, ActionField>, raw: Record<string, unknown>) => FieldValidation;
16
+ /** The visitor as a flow sees them. The token is deliberately absent — see the `auth.currentUser` task. */
17
+ export declare const projectUser: (user?: SSRUser) => {
18
+ id: number;
19
+ username: string;
20
+ email: string;
21
+ verified: boolean;
22
+ permissions: string[];
23
+ roles: string[];
24
+ } | undefined;
25
+ /**
26
+ * Redacts every secret the run has actually resolved, wherever it appears, at any depth.
27
+ *
28
+ * Keyed on the VALUES rather than on field names, because a secret does not stay in the field it came from: it
29
+ * travels into a header a node built, into an error a provider echoed back, and from there into the trace and the
30
+ * log. Matching names would redact the one place it was already safe.
31
+ *
32
+ * It learns as the run goes rather than being built up front from a declared list. That is what makes dropping
33
+ * the declaration safe: a credential is registered the moment the task that needed it resolved one, which is
34
+ * always before that task's own result is redacted — and what was never resolved was never at risk.
35
+ */
36
+ export declare const createRedactor: () => {
37
+ /** Everything long enough to be a secret rather than a flag. Short values would redact ordinary text. */
38
+ add: (credential: ActionCredential) => void;
39
+ redact: <T>(value: T) => T;
40
+ };
@@ -0,0 +1,103 @@
1
+ //#region src/modules/actions/runtime/scope.ts
2
+ var coerce = (type, value) => {
3
+ switch (type) {
4
+ case "number": {
5
+ const parsed = typeof value === "number" ? value : Number(value);
6
+ return Number.isFinite(parsed) ? parsed : void 0;
7
+ }
8
+ case "boolean":
9
+ if (typeof value === "boolean") return value;
10
+ return value === "true" ? true : value === "false" ? false : void 0;
11
+ case "json":
12
+ if (typeof value !== "string") return value;
13
+ try {
14
+ return JSON.parse(value);
15
+ } catch {
16
+ return;
17
+ }
18
+ default: return typeof value === "object" && value !== null ? void 0 : String(value);
19
+ }
20
+ };
21
+ /**
22
+ * Coerces a value bag against declared fields.
23
+ *
24
+ * Undeclared keys are DROPPED rather than passed through, and this is what makes twig-in-params safe: every token
25
+ * a node interpolates resolves against values that survived a contract someone wrote down. A pass-through bag
26
+ * would let a caller inject whatever a template happens to reference.
27
+ */
28
+ var applyFields = (fields, raw) => {
29
+ const values = {};
30
+ const missing = [];
31
+ const invalid = [];
32
+ Object.entries(fields).forEach(([key, field]) => {
33
+ const provided = raw[key] ?? field.defaultValue;
34
+ if (provided === void 0 || provided === null || provided === "") {
35
+ if (field.required) missing.push(key);
36
+ return;
37
+ }
38
+ const coerced = coerce(field.type, provided);
39
+ if (coerced === void 0) {
40
+ invalid.push(key);
41
+ return;
42
+ }
43
+ values[key] = coerced;
44
+ });
45
+ return {
46
+ values,
47
+ missing,
48
+ invalid
49
+ };
50
+ };
51
+ /** The visitor as a flow sees them. The token is deliberately absent — see the `auth.currentUser` task. */
52
+ var projectUser = (user) => {
53
+ if (!user) return;
54
+ const { id, username, email, verified, permissions, roles } = user;
55
+ return {
56
+ id,
57
+ username,
58
+ email,
59
+ verified,
60
+ permissions,
61
+ roles
62
+ };
63
+ };
64
+ /**
65
+ * Redacts every secret the run has actually resolved, wherever it appears, at any depth.
66
+ *
67
+ * Keyed on the VALUES rather than on field names, because a secret does not stay in the field it came from: it
68
+ * travels into a header a node built, into an error a provider echoed back, and from there into the trace and the
69
+ * log. Matching names would redact the one place it was already safe.
70
+ *
71
+ * It learns as the run goes rather than being built up front from a declared list. That is what makes dropping
72
+ * the declaration safe: a credential is registered the moment the task that needed it resolved one, which is
73
+ * always before that task's own result is redacted — and what was never resolved was never at risk.
74
+ */
75
+ var createRedactor = () => {
76
+ const secrets = /* @__PURE__ */ new Set();
77
+ const redactString = (value) => {
78
+ let output = value;
79
+ for (const secret of secrets) output = output.split(secret).join("«redacted»");
80
+ return output;
81
+ };
82
+ const redact = (value) => {
83
+ if (secrets.size === 0) return value;
84
+ if (typeof value === "string") return redactString(value);
85
+ if (Array.isArray(value)) return value.map((item) => redact(item));
86
+ if (value !== null && typeof value === "object") return Object.entries(value).reduce((acum, [key, item]) => {
87
+ acum[key] = redact(item);
88
+ return acum;
89
+ }, {});
90
+ return value;
91
+ };
92
+ return {
93
+ /** Everything long enough to be a secret rather than a flag. Short values would redact ordinary text. */
94
+ add: (credential) => {
95
+ Object.values(credential).forEach((value) => {
96
+ if (typeof value === "string" && value.length >= 8) secrets.add(value);
97
+ });
98
+ },
99
+ redact
100
+ };
101
+ };
102
+ //#endregion
103
+ export { applyFields, createRedactor, projectUser };
@@ -0,0 +1,12 @@
1
+ import { ActionTriggerParams, ActionTriggerType, ElementInteraction } from '@plitzi/sdk-shared';
2
+ /**
3
+ * The step that starts a flow, for one way in.
4
+ *
5
+ * An action holds its ways in as trigger STEPS, exactly as an element holds `onClick` and `onSubmit` in one node
6
+ * map — so finding the entry point means finding the trigger whose kind matches what fired, not reading a list
7
+ * beside the flow. An action with no step for that kind cannot be started that way, and that is the whole of the
8
+ * "does it declare this trigger" check.
9
+ */
10
+ export declare const findTriggerNode: (nodes: Record<string, ElementInteraction>, kind: ActionTriggerType) => ElementInteraction | undefined;
11
+ /** What a trigger step carries: who may start it, what it takes, and whatever its kind needs. */
12
+ export declare const triggerParams: (node: ElementInteraction) => ActionTriggerParams;
@@ -0,0 +1,14 @@
1
+ //#region src/modules/actions/runtime/triggers.ts
2
+ /**
3
+ * The step that starts a flow, for one way in.
4
+ *
5
+ * An action holds its ways in as trigger STEPS, exactly as an element holds `onClick` and `onSubmit` in one node
6
+ * map — so finding the entry point means finding the trigger whose kind matches what fired, not reading a list
7
+ * beside the flow. An action with no step for that kind cannot be started that way, and that is the whole of the
8
+ * "does it declare this trigger" check.
9
+ */
10
+ var findTriggerNode = (nodes, kind) => Object.values(nodes).find((node) => node.type === "trigger" && node.action === kind);
11
+ /** What a trigger step carries: who may start it, what it takes, and whatever its kind needs. */
12
+ var triggerParams = (node) => node.params;
13
+ //#endregion
14
+ export { findTriggerNode, triggerParams };
@@ -0,0 +1,26 @@
1
+ import { ActionTaskRegistry, RegisteredTask } from './types';
2
+ /**
3
+ * A task as an editor renders it.
4
+ *
5
+ * `run` is dropped rather than left to JSON to discard silently — it is server code. `params` goes over as it is,
6
+ * because it is already the `InteractionCallbackParam` map the builder's WorkflowNode draws.
7
+ *
8
+ * What does not survive the trip is a FUNCTION-valued param: a `when`, or options computed from another param.
9
+ * Those are a task author's way of saying "this field depends on that one", and a catalog served over the wire
10
+ * keeps only the static half — worth knowing when writing a deployment task meant to be authored visually.
11
+ *
12
+ * They are STRIPPED here rather than left for the transport to choke on: a JSON scalar refuses a function outright,
13
+ * so `http.request` — whose `body` is hidden on GET — took the whole catalog down with it.
14
+ */
15
+ export type ActionTaskDescriptor = {
16
+ name: string;
17
+ namespace: string;
18
+ action: string;
19
+ title: string;
20
+ description?: string;
21
+ /** Serializable params only — see above. */
22
+ params: Record<string, unknown>;
23
+ };
24
+ export declare const describeTask: (task: RegisteredTask) => ActionTaskDescriptor;
25
+ /** The whole catalog, in the order the registry holds it. */
26
+ export declare const describeCatalog: (registry: ActionTaskRegistry) => ActionTaskDescriptor[];
@@ -0,0 +1,24 @@
1
+ //#region src/modules/actions/taskCatalog.ts
2
+ /** Drops anything JSON cannot carry, at any depth. Functions are the only such value a task declares today. */
3
+ var jsonSafe = (value) => {
4
+ if (typeof value === "function") return;
5
+ if (Array.isArray(value)) return value.map(jsonSafe).filter((item) => item !== void 0);
6
+ if (value !== null && typeof value === "object") return Object.entries(value).reduce((acum, [key, item]) => {
7
+ const safe = jsonSafe(item);
8
+ if (safe !== void 0) acum[key] = safe;
9
+ return acum;
10
+ }, {});
11
+ return value;
12
+ };
13
+ var describeTask = (task) => ({
14
+ name: task.name,
15
+ namespace: task.namespace,
16
+ action: task.action,
17
+ title: task.title,
18
+ description: task.description,
19
+ params: jsonSafe(task.params)
20
+ });
21
+ /** The whole catalog, in the order the registry holds it. */
22
+ var describeCatalog = (registry) => registry.list().map(describeTask);
23
+ //#endregion
24
+ export { describeCatalog, describeTask };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { ActionTask } from '../types';
2
+ export declare const authTasks: ActionTask<Record<string, unknown>>[];
@@ -0,0 +1,40 @@
1
+ var authTasks = [{
2
+ namespace: "auth",
3
+ action: "currentUser",
4
+ title: "Current User",
5
+ params: {},
6
+ run: (_params, ctx) => {
7
+ const { user } = ctx;
8
+ if (!user) return { signedIn: false };
9
+ const { id, username, email, verified, permissions, roles } = user;
10
+ return {
11
+ signedIn: true,
12
+ id,
13
+ username,
14
+ email,
15
+ verified,
16
+ permissions,
17
+ roles
18
+ };
19
+ }
20
+ }, {
21
+ namespace: "auth",
22
+ action: "requireRole",
23
+ title: "Require Permissions",
24
+ params: { permissions: {
25
+ type: "text",
26
+ canBind: true,
27
+ defaultValue: "",
28
+ label: "Permissions (comma separated)"
29
+ } },
30
+ run: ({ permissions }, ctx) => {
31
+ const required = permissions.split(",").map((permission) => permission.trim()).filter(Boolean);
32
+ if (required.length === 0) throw new Error("No permissions declared to require");
33
+ const held = new Set(ctx.user?.permissions ?? []);
34
+ const missing = required.filter((permission) => !held.has(permission));
35
+ if (missing.length > 0) throw new Error(`Missing permissions: ${missing.join(", ")}`);
36
+ return { granted: required };
37
+ }
38
+ }];
39
+ //#endregion
40
+ export { authTasks };
@@ -0,0 +1,8 @@
1
+ import { ActionTask } from '../types';
2
+ /**
3
+ * The tasks `sdk-server` itself ships: the ones that are mechanism rather than policy.
4
+ *
5
+ * Anything needing an account somewhere — email, AI, object storage — is the DEPLOYMENT's, registered through
6
+ * `actions.tasks`. A server with no mail transport must not offer a step whose only possible outcome is failure.
7
+ */
8
+ export declare const builtinTasks: ActionTask<Record<string, unknown>>[];
@@ -0,0 +1,24 @@
1
+ import { authTasks } from "./auth.js";
2
+ import { connectorTasks } from "./connector.js";
3
+ import { flowTasks, streamTasks } from "./flow.js";
4
+ import { httpTasks } from "./http.js";
5
+ import { kvTasks } from "./kv.js";
6
+ import { transformTasks } from "./transform.js";
7
+ //#region src/modules/actions/tasks/builtins.ts
8
+ /**
9
+ * The tasks `sdk-server` itself ships: the ones that are mechanism rather than policy.
10
+ *
11
+ * Anything needing an account somewhere — email, AI, object storage — is the DEPLOYMENT's, registered through
12
+ * `actions.tasks`. A server with no mail transport must not offer a step whose only possible outcome is failure.
13
+ */
14
+ var builtinTasks = [
15
+ ...flowTasks,
16
+ ...transformTasks,
17
+ ...httpTasks,
18
+ ...connectorTasks,
19
+ ...authTasks,
20
+ ...kvTasks,
21
+ ...streamTasks
22
+ ];
23
+ //#endregion
24
+ export { builtinTasks };
@@ -0,0 +1,2 @@
1
+ import { ActionTask } from '../types';
2
+ export declare const connectorTasks: ActionTask<Record<string, unknown>>[];
@@ -0,0 +1,141 @@
1
+ import { fetchConnectorRecords, writeConnectorRecord } from "../../connectors/engine.js";
2
+ //#region src/modules/actions/tasks/connector.ts
3
+ var parseFilters = (value) => {
4
+ if (Array.isArray(value)) return value;
5
+ if (typeof value !== "string" || value === "") return [];
6
+ try {
7
+ const parsed = JSON.parse(value);
8
+ return Array.isArray(parsed) ? parsed : [];
9
+ } catch {
10
+ throw new Error("Filters are not valid JSON");
11
+ }
12
+ };
13
+ var parseValues = (value) => {
14
+ if (value !== null && typeof value === "object") return value;
15
+ if (typeof value !== "string" || value === "") return {};
16
+ try {
17
+ const parsed = JSON.parse(value);
18
+ return parsed !== null && typeof parsed === "object" ? parsed : {};
19
+ } catch {
20
+ throw new Error("Values are not valid JSON");
21
+ }
22
+ };
23
+ var toNumber = (value) => {
24
+ if (value === void 0 || value === "") return;
25
+ const parsed = typeof value === "number" ? value : Number.parseInt(value, 10);
26
+ return Number.isFinite(parsed) ? parsed : void 0;
27
+ };
28
+ var connectorTasks = [{
29
+ namespace: "connector",
30
+ action: "read",
31
+ title: "Connector Read",
32
+ params: {
33
+ connector: {
34
+ type: "text",
35
+ canBind: true,
36
+ defaultValue: "",
37
+ label: "Connector"
38
+ },
39
+ endpoint: {
40
+ type: "text",
41
+ canBind: true,
42
+ defaultValue: "list",
43
+ label: "Endpoint"
44
+ },
45
+ resource: {
46
+ type: "text",
47
+ canBind: true,
48
+ defaultValue: "",
49
+ label: "Resource"
50
+ },
51
+ limit: {
52
+ type: "text",
53
+ canBind: true,
54
+ defaultValue: "10",
55
+ label: "Limit"
56
+ },
57
+ offset: {
58
+ type: "text",
59
+ canBind: true,
60
+ defaultValue: "0",
61
+ label: "Offset"
62
+ },
63
+ filters: {
64
+ type: "codemirror-json",
65
+ canBind: true,
66
+ defaultValue: "[]",
67
+ label: "Filters"
68
+ }
69
+ },
70
+ run: async (params, ctx) => {
71
+ const resolved = await ctx.connector(params.connector);
72
+ if (!resolved) throw new Error(`Connector "${params.connector}" is not available for this space`);
73
+ const result = await fetchConnectorRecords({
74
+ manifest: resolved.manifest,
75
+ endpoint: params.endpoint || void 0,
76
+ credential: resolved.credential,
77
+ query: {
78
+ resource: params.resource || void 0,
79
+ limit: toNumber(params.limit),
80
+ offset: toNumber(params.offset),
81
+ filters: parseFilters(params.filters)
82
+ },
83
+ fetchImpl: ctx.fetch
84
+ });
85
+ return {
86
+ records: result.records,
87
+ pageInfo: result.pageInfo
88
+ };
89
+ }
90
+ }, {
91
+ namespace: "connector",
92
+ action: "write",
93
+ title: "Connector Write",
94
+ params: {
95
+ connector: {
96
+ type: "text",
97
+ canBind: true,
98
+ defaultValue: "",
99
+ label: "Connector"
100
+ },
101
+ endpoint: {
102
+ type: "text",
103
+ canBind: true,
104
+ defaultValue: "create",
105
+ label: "Endpoint"
106
+ },
107
+ resource: {
108
+ type: "text",
109
+ canBind: true,
110
+ defaultValue: "",
111
+ label: "Resource"
112
+ },
113
+ recordId: {
114
+ type: "text",
115
+ canBind: true,
116
+ defaultValue: "",
117
+ label: "Record Id"
118
+ },
119
+ values: {
120
+ type: "codemirror-json",
121
+ canBind: true,
122
+ defaultValue: "{}",
123
+ label: "Values"
124
+ }
125
+ },
126
+ run: async (params, ctx) => {
127
+ const resolved = await ctx.connector(params.connector);
128
+ if (!resolved) throw new Error(`Connector "${params.connector}" is not available for this space`);
129
+ return { record: await writeConnectorRecord({
130
+ manifest: resolved.manifest,
131
+ credential: resolved.credential,
132
+ action: params.endpoint,
133
+ resource: params.resource || void 0,
134
+ recordId: params.recordId || void 0,
135
+ values: parseValues(params.values),
136
+ fetchImpl: ctx.fetch
137
+ }) ?? null };
138
+ }
139
+ }];
140
+ //#endregion
141
+ export { connectorTasks };
@@ -0,0 +1,2 @@
1
+ import { ActionTask } from '../types';
2
+ export declare const dbTasks: ActionTask<Record<string, unknown>>[];