@jskit-ai/agent-docs 0.1.257 → 0.1.259

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 (230) hide show
  1. package/guide/agent/app-extras/assistant-conversation.md +88 -35
  2. package/guide/agent/app-extras/assistant.md +28 -28
  3. package/guide/agent/app-setup/authentication.md +36 -17
  4. package/guide/agent/app-setup/database-layer.md +116 -17
  5. package/guide/agent/app-setup/existing-application-migration.md +29 -25
  6. package/guide/agent/app-setup/multi-homing.md +94 -48
  7. package/guide/agent/app-setup/run-an-accounts-hub.md +6 -4
  8. package/guide/agent/framework/client-features.md +496 -0
  9. package/guide/agent/framework/crud-operations.md +5 -5
  10. package/guide/agent/framework/translation.md +4 -3
  11. package/guide/agent/framework/typed-features.md +1104 -0
  12. package/guide/agent/framework/ui-operations.md +2 -2
  13. package/guide/agent/index.md +6 -1
  14. package/package.json +1 -1
  15. package/patterns/feature-package/PATTERN.md +104 -10
  16. package/patterns/feature-package/example/booking-engine/package.json +9 -4
  17. package/patterns/feature-package/example/booking-engine/src/server/BookingEngineFeature.js +24 -0
  18. package/patterns/feature-package/example/booking-engine/src/server/bookingAvailability.js +10 -0
  19. package/patterns/feature-package/example/booking-engine/src/server/bookingEngine.js +29 -0
  20. package/patterns/feature-package/example/booking-engine/src/server/bookingEngineActions.js +26 -0
  21. package/patterns/feature-package/example/booking-engine/src/server/inputSchemas.js +1 -2
  22. package/patterns/feature-package/example/booking-engine/src/server/types.js +12 -0
  23. package/patterns/feature-package/example/variations/AvailabilityEngineFeature.js +19 -0
  24. package/patterns/feature-package/example/variations/InvoiceRollupFeature.js +23 -0
  25. package/patterns/feature-package/example/variations/availabilityEngineActions.js +26 -0
  26. package/patterns/feature-package/example/variations/customKnexRepository.js +32 -26
  27. package/patterns/feature-package/example/variations/invoiceRollupActions.js +26 -0
  28. package/patterns/feature-package/example/variations/orchestratorService.js +24 -19
  29. package/patterns/feature-package/example/variations/types.js +7 -0
  30. package/patterns/minimal-foundation/PATTERN.md +30 -1
  31. package/patterns/minimal-foundation/example/AGENTS.md +14 -0
  32. package/patterns/minimal-foundation/example/bin/develop.js +10 -5
  33. package/patterns/minimal-foundation/example/eslint.config.mjs +21 -2
  34. package/patterns/minimal-foundation/example/package.json +5 -4
  35. package/patterns/minimal-foundation/example/playwright.config.mjs +1 -1
  36. package/patterns/minimal-foundation/example/server/lib/envFile.js +19 -0
  37. package/patterns/minimal-foundation/example/server/lib/loggerOptions.js +15 -0
  38. package/patterns/minimal-foundation/example/server/lib/runtimeEnv.js +17 -17
  39. package/patterns/minimal-foundation/example/server.js +32 -29
  40. package/patterns/minimal-foundation/example/tests/server/smoke.test.js +2 -1
  41. package/patterns/shell-foundation/PATTERN.md +29 -0
  42. package/patterns/shell-foundation/example/AGENTS.md +14 -0
  43. package/patterns/shell-foundation/example/bin/develop.js +10 -5
  44. package/patterns/shell-foundation/example/eslint.config.mjs +21 -2
  45. package/patterns/shell-foundation/example/package.json +6 -5
  46. package/patterns/shell-foundation/example/packages/main/package.json +1 -1
  47. package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +1 -0
  48. package/patterns/shell-foundation/example/playwright.config.mjs +1 -1
  49. package/patterns/shell-foundation/example/server/lib/envFile.js +19 -0
  50. package/patterns/shell-foundation/example/server/lib/loggerOptions.js +15 -0
  51. package/patterns/shell-foundation/example/server/lib/runtimeEnv.js +17 -17
  52. package/patterns/shell-foundation/example/server.js +32 -29
  53. package/patterns/shell-foundation/example/tests/server/smoke.test.js +2 -1
  54. package/reference/autogen/KERNEL_MAP.md +14 -8
  55. package/reference/autogen/PATTERN_INDEX.md +37 -37
  56. package/reference/autogen/README.md +2 -0
  57. package/reference/autogen/packages/agent-docs.md +70 -26
  58. package/reference/autogen/packages/agent-runtime.md +129 -0
  59. package/reference/autogen/packages/agent.md +149 -0
  60. package/reference/autogen/packages/architecture-inspector.md +97 -44
  61. package/reference/autogen/packages/assistant-core.md +786 -292
  62. package/reference/autogen/packages/assistant-runtime.md +182 -57
  63. package/reference/autogen/packages/assistant-voice-sherpa.md +4 -0
  64. package/reference/autogen/packages/assistant-voice.md +54 -8
  65. package/reference/autogen/packages/auth-core.md +44 -13
  66. package/reference/autogen/packages/auth-provider-local-core.md +86 -34
  67. package/reference/autogen/packages/auth-provider-local-db-core.md +1 -2
  68. package/reference/autogen/packages/auth-provider-oidc-core.md +10 -4
  69. package/reference/autogen/packages/auth-provider-supabase-core.md +52 -66
  70. package/reference/autogen/packages/auth-web.md +19 -11
  71. package/reference/autogen/packages/connectors-catalog.md +71 -43
  72. package/reference/autogen/packages/connectors-core.md +42 -4
  73. package/reference/autogen/packages/console-core.md +27 -14
  74. package/reference/autogen/packages/crud-core.md +178 -170
  75. package/reference/autogen/packages/database-runtime-mysql.md +0 -2
  76. package/reference/autogen/packages/database-runtime-postgres.md +0 -2
  77. package/reference/autogen/packages/database-runtime.md +39 -10
  78. package/reference/autogen/packages/http-runtime.md +79 -57
  79. package/reference/autogen/packages/json-rest-api-core.md +11 -8
  80. package/reference/autogen/packages/kernel.md +144 -83
  81. package/reference/autogen/packages/oidc-issuer-core.md +49 -20
  82. package/reference/autogen/packages/payments-core.md +20 -3
  83. package/reference/autogen/packages/realtime.md +47 -22
  84. package/reference/autogen/packages/resource-core.md +1 -1
  85. package/reference/autogen/packages/resource-crud-core.md +64 -26
  86. package/reference/autogen/packages/rewarded-core.md +47 -36
  87. package/reference/autogen/packages/shell-web.md +7 -10
  88. package/reference/autogen/packages/storage-runtime.md +6 -2
  89. package/reference/autogen/packages/uploads-runtime.md +10 -2
  90. package/reference/autogen/packages/users-core.md +73 -71
  91. package/reference/autogen/packages/workspaces-core.md +121 -106
  92. package/reference/autogen/packages/workspaces-web.md +1 -2
  93. package/reference/autogen/tooling/config-eslint.md +302 -0
  94. package/reference/autogen/tooling/jskit-catalog.md +28 -18
  95. package/reference/autogen/tooling/jskit-release.md +35 -20
  96. package/reference/autogen/tooling/testUtils.md +3 -1
  97. package/skills/jskit/SKILL.md +9 -8
  98. package/skills/jskit/references/app-operations.md +1 -1
  99. package/skills/jskit/references/client-features.md +491 -0
  100. package/skills/jskit/references/crud-form-actions.md +1 -1
  101. package/skills/jskit/references/crud-operations.md +7 -7
  102. package/skills/jskit/references/existing-application-migration.md +31 -27
  103. package/skills/jskit/references/heuristics/INDEX.md +5 -1
  104. package/skills/jskit/references/heuristics/child-cruds.md +1 -1
  105. package/skills/jskit/references/heuristics/client-features.md +66 -0
  106. package/skills/jskit/references/heuristics/client-requests.md +1 -1
  107. package/skills/jskit/references/heuristics/crud-authoring.md +12 -2
  108. package/skills/jskit/references/heuristics/crud-links.md +1 -1
  109. package/skills/jskit/references/heuristics/crud-repository-mapping.md +3 -3
  110. package/skills/jskit/references/heuristics/filters.md +1 -1
  111. package/skills/jskit/references/heuristics/live-actions.md +1 -1
  112. package/skills/jskit/references/heuristics/page-redirects.md +1 -1
  113. package/skills/jskit/references/heuristics/page-scaffolding.md +1 -1
  114. package/skills/jskit/references/heuristics/placements.md +1 -1
  115. package/skills/jskit/references/heuristics/row-policies.md +1 -1
  116. package/skills/jskit/references/heuristics/server-search.md +1 -1
  117. package/skills/jskit/references/heuristics/surfaces.md +1 -1
  118. package/skills/jskit/references/heuristics/typed-features.md +100 -0
  119. package/skills/jskit/references/heuristics/ui-contract.md +1 -1
  120. package/skills/jskit/references/heuristics/ui-testing.md +4 -4
  121. package/skills/jskit/references/material-3.md +2 -2
  122. package/skills/jskit/references/multi-homing.md +96 -50
  123. package/skills/jskit/references/pattern-index.md +37 -37
  124. package/skills/jskit/references/patterns/app/minimal-foundation/PATTERN.md +30 -1
  125. package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +14 -0
  126. package/skills/jskit/references/patterns/app/minimal-foundation/example/bin/develop.js +10 -5
  127. package/skills/jskit/references/patterns/app/minimal-foundation/example/eslint.config.mjs +21 -2
  128. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +5 -4
  129. package/skills/jskit/references/patterns/app/minimal-foundation/example/playwright.config.mjs +1 -1
  130. package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/envFile.js +19 -0
  131. package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/loggerOptions.js +15 -0
  132. package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/runtimeEnv.js +17 -17
  133. package/skills/jskit/references/patterns/app/minimal-foundation/example/server.js +32 -29
  134. package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/server/smoke.test.js +2 -1
  135. package/skills/jskit/references/patterns/app/shell-foundation/PATTERN.md +29 -0
  136. package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +14 -0
  137. package/skills/jskit/references/patterns/app/shell-foundation/example/bin/develop.js +10 -5
  138. package/skills/jskit/references/patterns/app/shell-foundation/example/eslint.config.mjs +21 -2
  139. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +6 -5
  140. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
  141. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +1 -0
  142. package/skills/jskit/references/patterns/app/shell-foundation/example/playwright.config.mjs +1 -1
  143. package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/envFile.js +19 -0
  144. package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/loggerOptions.js +15 -0
  145. package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/runtimeEnv.js +17 -17
  146. package/skills/jskit/references/patterns/app/shell-foundation/example/server.js +32 -29
  147. package/skills/jskit/references/patterns/app/shell-foundation/example/tests/server/smoke.test.js +2 -1
  148. package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/ApplicationAiFeature.js +4 -2
  149. package/skills/jskit/references/patterns/auth/accounts-hub/PATTERN.md +2 -1
  150. package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +2 -2
  151. package/skills/jskit/references/patterns/auth/accounts-hub/example/server/hubPaths.js +9 -3
  152. package/skills/jskit/references/patterns/auth/auth-surface/PATTERN.md +25 -12
  153. package/skills/jskit/references/patterns/auth/auth-surface/example/tools/preview-identity +7 -1
  154. package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
  155. package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
  156. package/skills/jskit/references/patterns/connectors/ai-connections/PATTERN.md +16 -1
  157. package/skills/jskit/references/patterns/connectors/ai-connections/example/ai-model.js +12 -3
  158. package/skills/jskit/references/patterns/connectors/ai-connections/example/perplexity-answer.js +15 -4
  159. package/skills/jskit/references/patterns/connectors/api-key-connection/PATTERN.md +9 -1
  160. package/skills/jskit/references/patterns/connectors/api-key-connection/example/connections.js +5 -2
  161. package/skills/jskit/references/patterns/connectors/aws-storage-queries/PATTERN.md +13 -3
  162. package/skills/jskit/references/patterns/connectors/aws-storage-queries/example/s3-transfer.js +14 -3
  163. package/skills/jskit/references/patterns/connectors/calendar-cli/PATTERN.md +8 -0
  164. package/skills/jskit/references/patterns/connectors/calendar-cli/example/knexfile.js +1 -1
  165. package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
  166. package/skills/jskit/references/patterns/connectors/calendar-cli/example/scripts/calendar.js +8 -4
  167. package/skills/jskit/references/patterns/connectors/event-delivery/PATTERN.md +1 -1
  168. package/skills/jskit/references/patterns/connectors/firebase-messaging/PATTERN.md +10 -0
  169. package/skills/jskit/references/patterns/connectors/firebase-messaging/example/server/notifications.js +8 -0
  170. package/skills/jskit/references/patterns/connectors/google-ads-search/PATTERN.md +18 -7
  171. package/skills/jskit/references/patterns/connectors/google-ads-search/example/ads-setup.js +13 -5
  172. package/skills/jskit/references/patterns/connectors/oauth-connection/PATTERN.md +1 -1
  173. package/skills/jskit/references/patterns/connectors/paddle-catalogue/PATTERN.md +11 -2
  174. package/skills/jskit/references/patterns/connectors/paddle-catalogue/example/create-products.js +5 -2
  175. package/skills/jskit/references/patterns/connectors/public-image/PATTERN.md +10 -1
  176. package/skills/jskit/references/patterns/connectors/public-image/example/logo-url.js +4 -1
  177. package/skills/jskit/references/patterns/connectors/redshift-queries/PATTERN.md +1 -1
  178. package/skills/jskit/references/patterns/connectors/source-scanning/PATTERN.md +13 -2
  179. package/skills/jskit/references/patterns/connectors/source-scanning/example/source-scanner.js +9 -0
  180. package/skills/jskit/references/patterns/crud/json-api-resource-package/PATTERN.md +102 -23
  181. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +3 -2
  182. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/server/types.js +5 -0
  183. package/skills/jskit/references/patterns/crud/resource-contract/PATTERN.md +54 -1
  184. package/skills/jskit/references/patterns/database/mysql-application/PATTERN.md +16 -5
  185. package/skills/jskit/references/patterns/database/mysql-application/example/knexfile.js +2 -2
  186. package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
  187. package/skills/jskit/references/patterns/database/mysql-application/example/scripts/prepare-database.js +1 -1
  188. package/skills/jskit/references/patterns/database/postgres-application/PATTERN.md +16 -5
  189. package/skills/jskit/references/patterns/database/postgres-application/example/knexfile.js +2 -2
  190. package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
  191. package/skills/jskit/references/patterns/database/postgres-application/example/scripts/prepare-database.js +1 -1
  192. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
  193. package/skills/jskit/references/patterns/rewards/google-rewarded/example/GoogleRewardedDeliveryProvider.js +1 -1
  194. package/skills/jskit/references/patterns/rewards/google-rewarded/example/googlePublisherTag.js +5 -11
  195. package/skills/jskit/references/patterns/server/feature-package/PATTERN.md +104 -10
  196. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +9 -4
  197. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/BookingEngineFeature.js +24 -0
  198. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/bookingAvailability.js +10 -0
  199. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/bookingEngine.js +29 -0
  200. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/bookingEngineActions.js +26 -0
  201. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/inputSchemas.js +1 -2
  202. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/types.js +12 -0
  203. package/skills/jskit/references/patterns/server/feature-package/example/variations/AvailabilityEngineFeature.js +19 -0
  204. package/skills/jskit/references/patterns/server/feature-package/example/variations/InvoiceRollupFeature.js +23 -0
  205. package/skills/jskit/references/patterns/server/feature-package/example/variations/availabilityEngineActions.js +26 -0
  206. package/skills/jskit/references/patterns/server/feature-package/example/variations/customKnexRepository.js +32 -26
  207. package/skills/jskit/references/patterns/server/feature-package/example/variations/invoiceRollupActions.js +26 -0
  208. package/skills/jskit/references/patterns/server/feature-package/example/variations/orchestratorService.js +24 -19
  209. package/skills/jskit/references/patterns/server/feature-package/example/variations/types.js +7 -0
  210. package/skills/jskit/references/patterns/users/user-administration-server/PATTERN.md +16 -1
  211. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +3 -2
  212. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/server/types.js +5 -0
  213. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +4 -3
  214. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/UsersWorkspaceFeature.js +2 -9
  215. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/types.js +5 -0
  216. package/skills/jskit/references/patterns/workspaces/workspace-server/PATTERN.md +42 -17
  217. package/skills/jskit/references/patterns/workspaces/workspace-server/example/packages/main/src/server/WorkspaceInvitationsFeature.js +27 -0
  218. package/skills/jskit/references/patterns/workspaces/workspace-server/example/packages/main/src/server/email/workspaceInviteEmail.js +11 -10
  219. package/skills/jskit/references/patterns/workspaces/workspace-server/example/packages/main/src/server/email/workspaceInviteMailer.js +27 -0
  220. package/skills/jskit/references/translation.md +5 -4
  221. package/skills/jskit/references/typed-features.md +1099 -0
  222. package/skills/jskit/references/ui-operations.md +3 -3
  223. package/patterns/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +0 -33
  224. package/patterns/feature-package/example/booking-engine/src/server/actions.js +0 -24
  225. package/patterns/feature-package/example/variations/AvailabilityEngineProvider.js +0 -31
  226. package/patterns/feature-package/example/variations/InvoiceRollupProvider.js +0 -36
  227. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +0 -33
  228. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/actions.js +0 -24
  229. package/skills/jskit/references/patterns/server/feature-package/example/variations/AvailabilityEngineProvider.js +0 -31
  230. package/skills/jskit/references/patterns/server/feature-package/example/variations/InvoiceRollupProvider.js +0 -36
@@ -1,11 +1,16 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { startServer } from "../server.js";
3
+ import { DEFAULT_PORT } from "../server/lib/runtimeEnv.js";
3
4
 
4
5
  const LOOPBACK_HOST = "127.0.0.1";
5
6
 
6
- function requiredPort(value, fallback = 3000) {
7
- const normalized = String(value || "").trim();
8
- const parsed = Number.parseInt(normalized || String(fallback), 10);
7
+ /**
8
+ * The development port a value names; anything that is not a TCP port stops the launcher.
9
+ * @param {string} value
10
+ */
11
+ function requiredPort(value) {
12
+ const normalized = value.trim();
13
+ const parsed = Number.parseInt(normalized, 10);
9
14
  if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65_535) {
10
15
  throw new Error(`Invalid development port: ${normalized || "(empty)"}.`);
11
16
  }
@@ -20,7 +25,7 @@ function waitForChild(child) {
20
25
  }
21
26
 
22
27
  const previewHost = String(process.env.HOST || "").trim() || "0.0.0.0";
23
- const previewPort = requiredPort(process.env.PORT);
28
+ const previewPort = requiredPort(process.env.PORT || String(DEFAULT_PORT));
24
29
  let apiServer = null;
25
30
  let viteProcess = null;
26
31
  let requestedSignal = "";
@@ -37,7 +42,7 @@ process.once("SIGTERM", () => requestShutdown("SIGTERM"));
37
42
 
38
43
  try {
39
44
  apiServer = await startServer({ host: LOOPBACK_HOST, port: 0 });
40
- const apiPort = requiredPort(apiServer.server.address()?.port);
45
+ const apiPort = /** @type {import("node:net").AddressInfo} */ (apiServer.server.address()).port;
41
46
  const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
42
47
  viteProcess = spawn(
43
48
  npmCommand,
@@ -1,4 +1,19 @@
1
- import { baseConfig, nodeConfig, vueConfig, webConfig } from "@jskit-ai/config-eslint/server";
1
+ import {
2
+ baseConfig,
3
+ clientFeaturesConfig,
4
+ formatterConfig,
5
+ nodeConfig,
6
+ sizeLimitsConfig,
7
+ typedFeaturesConfig,
8
+ vueConfig,
9
+ webConfig
10
+ } from "@jskit-ai/config-eslint/server";
11
+
12
+ // The checks of https://mobily-enterprises.github.io/jskit-ai/guide/framework/typed-features and
13
+ // https://mobily-enterprises.github.io/jskit-ai/guide/framework/client-features, as errors.
14
+ const SERVER_SOURCES = ["packages/*/src/server/**", "packages/*/src/shared/**", "server/**", "server.js"];
15
+ const CLIENT_SOURCES = ["packages/*/src/client/**", "src/**"];
16
+ const HAND_WRITTEN_SOURCES = ["packages/*/src/**", "src/**", "config/**", "server/**", "server.js", "bin/**"];
2
17
 
3
18
  export default [
4
19
  {
@@ -13,7 +28,11 @@ export default [
13
28
  }
14
29
  },
15
30
  ...baseConfig,
31
+ ...typedFeaturesConfig(SERVER_SOURCES),
32
+ ...sizeLimitsConfig(HAND_WRITTEN_SOURCES),
16
33
  ...vueConfig,
34
+ ...clientFeaturesConfig(CLIENT_SOURCES),
17
35
  ...webConfig,
18
- ...nodeConfig
36
+ ...nodeConfig,
37
+ ...formatterConfig([...SERVER_SOURCES, "config/**", "bin/**"])
19
38
  ];
@@ -34,21 +34,22 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.2.3",
37
+ "@jskit-ai/kernel": "0.2.4",
38
38
  "@tanstack/vue-query": "^5.101.0",
39
39
  "fastify": "^5.8.5",
40
+ "pino": "^10.3.1",
40
41
  "json-rest-schema": "^1.0.17",
41
42
  "pinia": "^4.0.3",
42
43
  "vue": "^3.5.44",
43
44
  "vue-router": "^5.3.1",
44
45
  "vuetify": "^4.1.2",
45
- "@jskit-ai/http-runtime": "0.2.3",
46
+ "@jskit-ai/http-runtime": "0.2.4",
46
47
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
47
48
  "@vue/devtools-api": "^8.2.1"
48
49
  },
49
50
  "devDependencies": {
50
- "@jskit-ai/config-eslint": "0.1.241",
51
- "@jskit-ai/jskit-catalog": "0.2.9",
51
+ "@jskit-ai/config-eslint": "0.1.242",
52
+ "@jskit-ai/jskit-catalog": "0.2.11",
52
53
  "@playwright/test": "1.61.1",
53
54
  "@vitejs/plugin-vue": "^6.0.7",
54
55
  "eslint": "^10.8.0",
@@ -4,7 +4,7 @@ const managedBaseUrl = String(process.env.PLAYWRIGHT_BASE_URL || "")
4
4
  .trim()
5
5
  .replace(/\/+$/u, "");
6
6
  const baseURL = managedBaseUrl || "http://127.0.0.1:4173";
7
- const storageState = String(process.env.VIBE64_PLAYWRIGHT_STORAGE_STATE || "").trim();
7
+ const storageState = String(process.env.PLAYWRIGHT_STORAGE_STATE || "").trim();
8
8
 
9
9
  export default defineConfig({
10
10
  testDir: "./tests/e2e",
@@ -0,0 +1,19 @@
1
+ // .env is loaded into process.env, which every caller shares, at most once per process.
2
+ let loaded = false;
3
+
4
+ /** Loads .env into process.env once per process; a missing file is not an error. */
5
+ function loadEnvFile() {
6
+ if (loaded) {
7
+ return;
8
+ }
9
+ try {
10
+ process.loadEnvFile(".env");
11
+ } catch (error) {
12
+ if (error.code !== "ENOENT") {
13
+ throw error;
14
+ }
15
+ }
16
+ loaded = true;
17
+ }
18
+
19
+ export { loadEnvFile };
@@ -0,0 +1,15 @@
1
+ import pino from "pino";
2
+
3
+ /**
4
+ * The Fastify logger's options. Fastify's logger serializes only the `err` key, so the same serializer under `error`
5
+ * keeps the message and stack of `logger.error({ error }, "...")`.
6
+ */
7
+ function buildLoggerOptions() {
8
+ return {
9
+ serializers: {
10
+ error: pino.stdSerializers.err
11
+ }
12
+ };
13
+ }
14
+
15
+ export { buildLoggerOptions };
@@ -1,7 +1,14 @@
1
- import { existsSync } from "node:fs";
1
+ import { loadEnvFile } from "./envFile.js";
2
2
  import { surfaceRuntime } from "./surfaceRuntime.js";
3
3
 
4
- function toPort(value, fallback = 3000) {
4
+ const DEFAULT_PORT = 3000;
5
+
6
+ /**
7
+ * The port a value names, else the fallback.
8
+ * @param {string | undefined} value
9
+ * @param {number} fallback
10
+ */
11
+ function toPort(value, fallback) {
5
12
  const parsed = Number.parseInt(String(value || "").trim(), 10);
6
13
  if (Number.isInteger(parsed) && parsed > 0) {
7
14
  return parsed;
@@ -9,29 +16,22 @@ function toPort(value, fallback = 3000) {
9
16
  return fallback;
10
17
  }
11
18
 
12
- let envLoaded = false;
13
-
14
- function ensureRuntimeEnvLoaded() {
15
- if (envLoaded) {
16
- return;
17
- }
18
- if (existsSync(".env")) {
19
- process.loadEnvFile(".env");
20
- }
21
- envLoaded = true;
22
- }
23
-
19
+ /**
20
+ * The process environment, with .env loaded and SERVER_SURFACE, PORT and HOST resolved. Every value stays a string,
21
+ * as the kernel's RuntimeEnv type says.
22
+ * @returns {import("@jskit-ai/kernel/server/types").RuntimeEnv}
23
+ */
24
24
  function resolveRuntimeEnv() {
25
- ensureRuntimeEnvLoaded();
25
+ loadEnvFile();
26
26
  const serverSurface = surfaceRuntime.normalizeSurfaceMode(
27
27
  process.env.JSKIT_SERVER_SURFACE || process.env.SERVER_SURFACE
28
28
  );
29
29
  return {
30
30
  ...process.env,
31
31
  SERVER_SURFACE: serverSurface,
32
- PORT: toPort(process.env.PORT, 3000),
32
+ PORT: String(toPort(process.env.PORT, DEFAULT_PORT)),
33
33
  HOST: String(process.env.HOST || "").trim() || "0.0.0.0"
34
34
  };
35
35
  }
36
36
 
37
- export { resolveRuntimeEnv };
37
+ export { DEFAULT_PORT, resolveRuntimeEnv };
@@ -2,6 +2,7 @@ import Fastify from "fastify";
2
2
  import SerializerSelector from "@fastify/fast-json-stringify-compiler";
3
3
  import { createCachedResponseSerializerFactory } from "@jskit-ai/kernel/server/http";
4
4
  import fastifyStatic from "@fastify/static";
5
+ import { buildLoggerOptions } from "./server/lib/loggerOptions.js";
5
6
  import { resolveRuntimeEnv } from "./server/lib/runtimeEnv.js";
6
7
  import { existsSync, readFileSync } from "node:fs";
7
8
  import path from "node:path";
@@ -11,11 +12,17 @@ import {
11
12
  resolveRuntimeProfileFromSurface
12
13
  } from "@jskit-ai/kernel/server/platform";
13
14
  import { loadAppConfigFromAppRoot } from "@jskit-ai/kernel/server/support";
14
- import { matchesPathPrefix, normalizePathname } from "@jskit-ai/kernel/shared/surface/paths";
15
+ import {
16
+ API_BASE_PATH,
17
+ matchesPathPrefix,
18
+ normalizePathname,
19
+ toRequestPathname
20
+ } from "@jskit-ai/kernel/shared/surface/paths";
15
21
  import { surfaceRuntime } from "./server/lib/surfaceRuntime.js";
16
22
 
23
+ /** @typedef {import("@jskit-ai/kernel/server/types").RuntimeEnv} RuntimeEnv */
24
+
17
25
  const SPA_INDEX_FILE = "index.html";
18
- const API_BASE_PATH = "/api";
19
26
  const STATIC_GLOBAL_UI_PATHS = Object.freeze([
20
27
  "/assets",
21
28
  "/favicon.svg",
@@ -24,15 +31,6 @@ const STATIC_GLOBAL_UI_PATHS = Object.freeze([
24
31
  "/manifest.webmanifest"
25
32
  ]);
26
33
 
27
- function toRequestPathname(urlValue) {
28
- const rawUrl = String(urlValue || "").trim() || "/";
29
- try {
30
- return normalizePathname(new URL(rawUrl, "http://localhost").pathname || "/");
31
- } catch {
32
- return normalizePathname(rawUrl.split("?")[0] || "/");
33
- }
34
- }
35
-
36
34
  function isApiPath(pathname) {
37
35
  return matchesPathPrefix(pathname, API_BASE_PATH);
38
36
  }
@@ -41,15 +39,15 @@ function hasFileExtension(pathname) {
41
39
  return path.extname(normalizePathname(pathname)) !== "";
42
40
  }
43
41
 
44
- function resolveGlobalUiPaths(runtimeGlobalUiPaths = []) {
45
- const paths = new Set(Array.isArray(runtimeGlobalUiPaths) ? runtimeGlobalUiPaths : []);
42
+ function buildGlobalUiPaths(runtimeGlobalUiPaths) {
43
+ const paths = new Set(runtimeGlobalUiPaths);
46
44
  for (const staticPath of STATIC_GLOBAL_UI_PATHS) {
47
45
  paths.add(staticPath);
48
46
  }
49
47
  return [...paths];
50
48
  }
51
49
 
52
- function resolveStaticFilePath(pathname) {
50
+ function toStaticFilePath(pathname) {
53
51
  const normalizedPathname = normalizePathname(pathname);
54
52
 
55
53
  const relativePath = normalizedPathname.replace(/^\/+/, "");
@@ -85,9 +83,13 @@ function canServeStaticFile(distRoot, relativePath) {
85
83
  return existsSync(resolvedPath);
86
84
  }
87
85
 
88
- async function createServer({ runtimeEnv = resolveRuntimeEnv() } = {}) {
86
+ /**
87
+ * The application's Fastify server with its installed JSKIT runtime, for the environment given.
88
+ * @param {{ runtimeEnv: RuntimeEnv }} settings
89
+ */
90
+ async function createServer({ runtimeEnv }) {
89
91
  const app = Fastify({
90
- logger: true,
92
+ logger: buildLoggerOptions(),
91
93
  schemaController: {
92
94
  compilersFactory: {
93
95
  buildSerializer: createCachedResponseSerializerFactory(SerializerSelector())
@@ -126,7 +128,7 @@ async function createServer({ runtimeEnv = resolveRuntimeEnv() } = {}) {
126
128
  fastify: app,
127
129
  surfaceRuntime,
128
130
  serverSurface: runtimeEnv.SERVER_SURFACE,
129
- globalUiPaths: resolveGlobalUiPaths(runtime?.globalUiPaths || [])
131
+ globalUiPaths: buildGlobalUiPaths(runtime.globalUiPaths)
130
132
  });
131
133
 
132
134
  if (hasWebBuild) {
@@ -140,10 +142,8 @@ async function createServer({ runtimeEnv = resolveRuntimeEnv() } = {}) {
140
142
  }
141
143
 
142
144
  app.setNotFoundHandler(async (request, reply) => {
143
- const pathname = toRequestPathname(request?.url);
144
- const method = String(request?.method || "GET")
145
- .trim()
146
- .toUpperCase();
145
+ const pathname = toRequestPathname(request.url);
146
+ const method = request.method;
147
147
  if (isApiPath(pathname) || (method !== "GET" && method !== "HEAD")) {
148
148
  return reply.code(404).send({
149
149
  message: `Route ${method}:${pathname} not found`,
@@ -152,7 +152,7 @@ async function createServer({ runtimeEnv = resolveRuntimeEnv() } = {}) {
152
152
  });
153
153
  }
154
154
  if (hasFileExtension(pathname)) {
155
- const staticFilePath = resolveStaticFilePath(pathname);
155
+ const staticFilePath = toStaticFilePath(pathname);
156
156
  if (hasWebBuild && staticFilePath && canServeStaticFile(distRoot, staticFilePath)) {
157
157
  return reply.sendFile(staticFilePath);
158
158
  }
@@ -183,19 +183,22 @@ async function createServer({ runtimeEnv = resolveRuntimeEnv() } = {}) {
183
183
  return app;
184
184
  }
185
185
 
186
- async function startServer(options = {}) {
186
+ /**
187
+ * Starts the server on the host and port given, else on the environment's HOST and PORT.
188
+ * @param {{ host?: string, port?: number }} [options]
189
+ */
190
+ async function startServer({ host, port } = {}) {
187
191
  const runtimeEnv = resolveRuntimeEnv();
188
- const configuredPort = options?.port === undefined ? runtimeEnv.PORT : Number(options.port);
189
- const port = Number.isInteger(configuredPort) && configuredPort >= 0 ? configuredPort : runtimeEnv.PORT;
190
- const host = String(options?.host || "").trim() || runtimeEnv.HOST;
192
+ const listenHost = host || runtimeEnv.HOST;
193
+ const listenPort = port ?? Number(runtimeEnv.PORT);
191
194
  const app = await createServer({
192
195
  runtimeEnv: {
193
196
  ...runtimeEnv,
194
- HOST: host,
195
- PORT: port
197
+ HOST: listenHost,
198
+ PORT: String(listenPort)
196
199
  }
197
200
  });
198
- await app.listen({ port, host });
201
+ await app.listen({ port: listenPort, host: listenHost });
199
202
  return app;
200
203
  }
201
204
 
@@ -1,9 +1,10 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
  import { createServer } from "../../server.js";
4
+ import { resolveRuntimeEnv } from "../../server/lib/runtimeEnv.js";
4
5
 
5
6
  test("GET /api/health returns built-in health response", async () => {
6
- const app = await createServer();
7
+ const app = await createServer({ runtimeEnv: resolveRuntimeEnv() });
7
8
  const response = await app.inject({
8
9
  method: "GET",
9
10
  url: "/api/health"
@@ -59,6 +59,33 @@ not convert those choices into a generator questionnaire.
59
59
  scrims or coordinates.
60
60
  - Do not attach generator provenance, receipts, completion state, or replay
61
61
  history to copied source.
62
+ - Server code the application adds follows
63
+ [typed features](https://github.com/mobily-enterprises/jskit-ai/blob/main/packages/agent-docs/site/guide/framework/typed-features.md):
64
+ a provided capability exports its type from `types.js`; collaborators arrive
65
+ through declared capabilities, typed in a JSDoc `@param` and required, never
66
+ probed or defaulted to nothing; slots are `optional` tokens with a named
67
+ default; one `create<Name>Actions({ <api> })` per feature returns frozen
68
+ actions whose `execute` is one call; every `create*` returns a frozen object
69
+ of named functions; one copy of each helper per package; files stay under
70
+ 750 lines and functions under 600. `createServer({ runtimeEnv })` requires
71
+ the environment it runs with, typed as the kernel's `RuntimeEnv` (the process
72
+ environment, string values: `resolveRuntimeEnv()` keeps `PORT` a string), and
73
+ `server.js` takes `API_BASE_PATH` and `toRequestPathname` from the kernel's
74
+ `shared/surface/paths`. `server/lib/envFile.js` loads `.env` once per process
75
+ for every entry point. `server/lib/loggerOptions.js` gives Fastify's logger
76
+ pino's error serializer under `error` as well as `err`, so
77
+ `logger.error({ error }, "...")` keeps the error's message and stack. `eslint.config.mjs` holds
78
+ server and shared code to every typed-features rule (`typedFeaturesConfig`),
79
+ client code (`src/**` and `packages/*/src/client/**`, Vue components
80
+ included) to the client rules (`clientFeaturesConfig`), all hand-written
81
+ code to the size limits (`sizeLimitsConfig`), and server, config and script
82
+ code to the formatter (`formatterConfig`), all as errors, so `npm run lint`
83
+ reports code that breaks the
84
+ [typed features guide](https://mobily-enterprises.github.io/jskit-ai/guide/framework/typed-features)
85
+ or the
86
+ [client features guide](https://mobily-enterprises.github.io/jskit-ai/guide/framework/client-features).
87
+ Client code the application adds keeps simple, direct code first, and types
88
+ each provider hook with `@jskit-ai/kernel/client/types`.
62
89
 
63
90
  ## Framework APIs
64
91
 
@@ -126,3 +153,5 @@ horizontal overflow.
126
153
  - Do not push content down with persistent transient-error banners; use the
127
154
  standard toast/error presentation.
128
155
  - Do not preserve historical template ownership after copying.
156
+ - Do not probe a collaborator, default one to `null`, `{}` or a no-op, fill a
157
+ slot with a setter after construction, or keep a module-level registry.
@@ -8,6 +8,20 @@ Framework references:
8
8
  - `https://mobily-enterprises.github.io/jskit-ai/guide/`
9
9
  - `https://mobily-enterprises.github.io/jskit-ai/patterns/`
10
10
 
11
+ Before writing or reviewing server code (a feature, provider, action, service,
12
+ repository, route, error or test), read the typed features guide at
13
+ `https://mobily-enterprises.github.io/jskit-ai/guide/framework/typed-features`.
14
+ `npm run lint` checks its rules, and each finding links the section that
15
+ explains it; `npm run verify` adds the package graph, tests and build. Fix a
16
+ finding in the code; excuse one only with the guide's `readable-check: allow`
17
+ comment and reason prefix.
18
+
19
+ Before writing or reviewing client code (a composable, component, client
20
+ factory or client provider), read the client features guide at
21
+ `https://mobily-enterprises.github.io/jskit-ai/guide/framework/client-features`.
22
+ Simple, direct code comes first: never add a layer, wrapper, registry or tiny
23
+ module only to satisfy a rule. `npm run lint` checks its rules too.
24
+
11
25
  Before implementation, read the narrow relevant source pattern completely. If
12
26
  its owner package is installed, prefer that version-matched package-owned copy.
13
27
  Do not install an unrelated runtime package only to read documentation.
@@ -1,11 +1,16 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { startServer } from "../server.js";
3
+ import { DEFAULT_PORT } from "../server/lib/runtimeEnv.js";
3
4
 
4
5
  const LOOPBACK_HOST = "127.0.0.1";
5
6
 
6
- function requiredPort(value, fallback = 3000) {
7
- const normalized = String(value || "").trim();
8
- const parsed = Number.parseInt(normalized || String(fallback), 10);
7
+ /**
8
+ * The development port a value names; anything that is not a TCP port stops the launcher.
9
+ * @param {string} value
10
+ */
11
+ function requiredPort(value) {
12
+ const normalized = value.trim();
13
+ const parsed = Number.parseInt(normalized, 10);
9
14
  if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65_535) {
10
15
  throw new Error(`Invalid development port: ${normalized || "(empty)"}.`);
11
16
  }
@@ -20,7 +25,7 @@ function waitForChild(child) {
20
25
  }
21
26
 
22
27
  const previewHost = String(process.env.HOST || "").trim() || "0.0.0.0";
23
- const previewPort = requiredPort(process.env.PORT);
28
+ const previewPort = requiredPort(process.env.PORT || String(DEFAULT_PORT));
24
29
  let apiServer = null;
25
30
  let viteProcess = null;
26
31
  let requestedSignal = "";
@@ -37,7 +42,7 @@ process.once("SIGTERM", () => requestShutdown("SIGTERM"));
37
42
 
38
43
  try {
39
44
  apiServer = await startServer({ host: LOOPBACK_HOST, port: 0 });
40
- const apiPort = requiredPort(apiServer.server.address()?.port);
45
+ const apiPort = /** @type {import("node:net").AddressInfo} */ (apiServer.server.address()).port;
41
46
  const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
42
47
  viteProcess = spawn(
43
48
  npmCommand,
@@ -1,4 +1,19 @@
1
- import { baseConfig, nodeConfig, vueConfig, webConfig } from "@jskit-ai/config-eslint/server";
1
+ import {
2
+ baseConfig,
3
+ clientFeaturesConfig,
4
+ formatterConfig,
5
+ nodeConfig,
6
+ sizeLimitsConfig,
7
+ typedFeaturesConfig,
8
+ vueConfig,
9
+ webConfig
10
+ } from "@jskit-ai/config-eslint/server";
11
+
12
+ // The checks of https://mobily-enterprises.github.io/jskit-ai/guide/framework/typed-features and
13
+ // https://mobily-enterprises.github.io/jskit-ai/guide/framework/client-features, as errors.
14
+ const SERVER_SOURCES = ["packages/*/src/server/**", "packages/*/src/shared/**", "server/**", "server.js"];
15
+ const CLIENT_SOURCES = ["packages/*/src/client/**", "src/**"];
16
+ const HAND_WRITTEN_SOURCES = ["packages/*/src/**", "src/**", "config/**", "server/**", "server.js", "bin/**"];
2
17
 
3
18
  export default [
4
19
  {
@@ -13,7 +28,11 @@ export default [
13
28
  }
14
29
  },
15
30
  ...baseConfig,
31
+ ...typedFeaturesConfig(SERVER_SOURCES),
32
+ ...sizeLimitsConfig(HAND_WRITTEN_SOURCES),
16
33
  ...vueConfig,
34
+ ...clientFeaturesConfig(CLIENT_SOURCES),
17
35
  ...webConfig,
18
- ...nodeConfig
36
+ ...nodeConfig,
37
+ ...formatterConfig([...SERVER_SOURCES, "config/**", "bin/**"])
19
38
  ];
@@ -34,23 +34,24 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.2.3",
37
+ "@jskit-ai/kernel": "0.2.4",
38
38
  "@tanstack/vue-query": "^5.101.0",
39
39
  "fastify": "^5.8.5",
40
+ "pino": "^10.3.1",
40
41
  "json-rest-schema": "^1.0.17",
41
42
  "pinia": "^4.0.3",
42
43
  "vue": "^3.5.44",
43
44
  "vue-router": "^5.3.1",
44
45
  "vuetify": "^4.1.2",
45
- "@jskit-ai/http-runtime": "0.2.3",
46
+ "@jskit-ai/http-runtime": "0.2.4",
46
47
  "@mdi/js": "^7.4.47",
47
- "@jskit-ai/shell-web": "0.2.3",
48
+ "@jskit-ai/shell-web": "0.2.4",
48
49
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
49
50
  "@vue/devtools-api": "^8.2.1"
50
51
  },
51
52
  "devDependencies": {
52
- "@jskit-ai/config-eslint": "0.1.241",
53
- "@jskit-ai/jskit-catalog": "0.2.9",
53
+ "@jskit-ai/config-eslint": "0.1.242",
54
+ "@jskit-ai/jskit-catalog": "0.2.11",
54
55
  "@playwright/test": "1.61.1",
55
56
  "@vitejs/plugin-vue": "^6.0.7",
56
57
  "eslint": "^10.8.0",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "description": "App-local runtime composition and lightweight glue.",
11
11
  "dependencies": {
12
- "@jskit-ai/kernel": "0.2.3"
12
+ "@jskit-ai/kernel": "0.2.4"
13
13
  },
14
14
  "jskit": {
15
15
  "kind": "runtime",
@@ -8,6 +8,7 @@ const MainClientProvider = defineProvider({
8
8
  requires: {
9
9
  components: "client.components"
10
10
  },
11
+ /** @param {{ components: import("@jskit-ai/kernel/client/types").ClientComponents }} capabilities */
11
12
  setup({ components }) {
12
13
  components.register("local.main.ui.menu-link-item", MenuLinkItem);
13
14
  components.register("local.main.ui.surface-aware-menu-link-item", SurfaceAwareMenuLinkItem);
@@ -4,7 +4,7 @@ const managedBaseUrl = String(process.env.PLAYWRIGHT_BASE_URL || "")
4
4
  .trim()
5
5
  .replace(/\/+$/u, "");
6
6
  const baseURL = managedBaseUrl || "http://127.0.0.1:4173";
7
- const storageState = String(process.env.VIBE64_PLAYWRIGHT_STORAGE_STATE || "").trim();
7
+ const storageState = String(process.env.PLAYWRIGHT_STORAGE_STATE || "").trim();
8
8
 
9
9
  export default defineConfig({
10
10
  testDir: "./tests/e2e",
@@ -0,0 +1,19 @@
1
+ // .env is loaded into process.env, which every caller shares, at most once per process.
2
+ let loaded = false;
3
+
4
+ /** Loads .env into process.env once per process; a missing file is not an error. */
5
+ function loadEnvFile() {
6
+ if (loaded) {
7
+ return;
8
+ }
9
+ try {
10
+ process.loadEnvFile(".env");
11
+ } catch (error) {
12
+ if (error.code !== "ENOENT") {
13
+ throw error;
14
+ }
15
+ }
16
+ loaded = true;
17
+ }
18
+
19
+ export { loadEnvFile };
@@ -0,0 +1,15 @@
1
+ import pino from "pino";
2
+
3
+ /**
4
+ * The Fastify logger's options. Fastify's logger serializes only the `err` key, so the same serializer under `error`
5
+ * keeps the message and stack of `logger.error({ error }, "...")`.
6
+ */
7
+ function buildLoggerOptions() {
8
+ return {
9
+ serializers: {
10
+ error: pino.stdSerializers.err
11
+ }
12
+ };
13
+ }
14
+
15
+ export { buildLoggerOptions };
@@ -1,7 +1,14 @@
1
- import { existsSync } from "node:fs";
1
+ import { loadEnvFile } from "./envFile.js";
2
2
  import { surfaceRuntime } from "./surfaceRuntime.js";
3
3
 
4
- function toPort(value, fallback = 3000) {
4
+ const DEFAULT_PORT = 3000;
5
+
6
+ /**
7
+ * The port a value names, else the fallback.
8
+ * @param {string | undefined} value
9
+ * @param {number} fallback
10
+ */
11
+ function toPort(value, fallback) {
5
12
  const parsed = Number.parseInt(String(value || "").trim(), 10);
6
13
  if (Number.isInteger(parsed) && parsed > 0) {
7
14
  return parsed;
@@ -9,29 +16,22 @@ function toPort(value, fallback = 3000) {
9
16
  return fallback;
10
17
  }
11
18
 
12
- let envLoaded = false;
13
-
14
- function ensureRuntimeEnvLoaded() {
15
- if (envLoaded) {
16
- return;
17
- }
18
- if (existsSync(".env")) {
19
- process.loadEnvFile(".env");
20
- }
21
- envLoaded = true;
22
- }
23
-
19
+ /**
20
+ * The process environment, with .env loaded and SERVER_SURFACE, PORT and HOST resolved. Every value stays a string,
21
+ * as the kernel's RuntimeEnv type says.
22
+ * @returns {import("@jskit-ai/kernel/server/types").RuntimeEnv}
23
+ */
24
24
  function resolveRuntimeEnv() {
25
- ensureRuntimeEnvLoaded();
25
+ loadEnvFile();
26
26
  const serverSurface = surfaceRuntime.normalizeSurfaceMode(
27
27
  process.env.JSKIT_SERVER_SURFACE || process.env.SERVER_SURFACE
28
28
  );
29
29
  return {
30
30
  ...process.env,
31
31
  SERVER_SURFACE: serverSurface,
32
- PORT: toPort(process.env.PORT, 3000),
32
+ PORT: String(toPort(process.env.PORT, DEFAULT_PORT)),
33
33
  HOST: String(process.env.HOST || "").trim() || "0.0.0.0"
34
34
  };
35
35
  }
36
36
 
37
- export { resolveRuntimeEnv };
37
+ export { DEFAULT_PORT, resolveRuntimeEnv };