@hitch42/applet 0.1.1-beta.7 → 0.1.1-beta.9

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 (177) hide show
  1. package/dist/_internal/client.js +120 -11
  2. package/dist/_internal/log-context.d.ts +34 -0
  3. package/dist/_internal/log-context.js +2 -0
  4. package/dist/_internal/server.d.ts +14 -1
  5. package/dist/_internal/server.js +122 -118
  6. package/dist/applet.js +22 -11
  7. package/dist/build-Bh_S5BUv.js +2 -0
  8. package/dist/{build-DauirTcY.js → build-CEfFoIZZ.js} +1 -1
  9. package/dist/{ci-gPrwo6C0.js → ci-BKw14X2h.js} +14 -13
  10. package/dist/client.d.ts +3 -3
  11. package/dist/{contract-DB9QQhD9.d.ts → contract-CsUpTBe0.d.ts} +1 -1
  12. package/dist/{deploy-CqrtNJ1x.js → deploy-wGYjwXrx.js} +139 -88
  13. package/dist/email-DBTVhNB_.js +62 -0
  14. package/dist/{generate-BIifT6PK.js → generate-BEIYs17x.js} +70 -32
  15. package/dist/generate-Dox-OYt6.js +2 -0
  16. package/dist/headers-DkNr9MLJ.js +17 -0
  17. package/dist/index.d.ts +122 -10
  18. package/dist/index.js +142 -26
  19. package/dist/{init-Wu6XmyVc.js → init-6oGPM46R.js} +15 -17
  20. package/dist/{load-config-BknBkdRs.js → load-config-xEdTXs0y.js} +2 -2
  21. package/dist/log-context-zu8U9Sjm.js +73 -0
  22. package/dist/{login-iT9MRuaw.js → login-CUx3TmFQ.js} +1 -1
  23. package/dist/{logs-B-qZGXSK.js → logs-DSjuEN0Q.js} +20 -22
  24. package/dist/{manifest-C489nYUy.d.ts → manifest-C6_F4bL8.d.ts} +7 -0
  25. package/dist/{manifest-Bd04cWhu.js → manifest-CJSL9GuC.js} +22 -3
  26. package/dist/me-BW7Paary.js +32 -0
  27. package/dist/{project-C_rtpjp8.js → project-BRZGd6xq.js} +202 -20
  28. package/dist/prompt-ChS-grYO.js +29 -0
  29. package/dist/query-cache-CXmAyLv3.js +82 -0
  30. package/dist/react.d.ts +3 -2
  31. package/dist/react.js +98 -80
  32. package/dist/schedules-C8_Svr4h.js +42 -0
  33. package/dist/server.d.ts +9 -4
  34. package/dist/server.js +29 -8
  35. package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
  36. package/dist/telephony.d.ts +97 -0
  37. package/dist/telephony.js +726 -0
  38. package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
  39. package/dist/{variables-DcwMeIau.js → variables-DT5RyHdH.js} +14 -39
  40. package/dist/vite.d.ts +3 -60
  41. package/dist/vite.js +497 -27
  42. package/dist/workflow-engine-BOTzURKM.js +111 -0
  43. package/dist/{workspace-B6lenAS5.js → workspace-D0dnnlxL.js} +37 -39
  44. package/package.json +18 -5
  45. package/scaffold/.agents/skills/hitch-applets/SKILL.md +50 -18
  46. package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
  47. package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
  48. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +12 -2
  49. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
  50. package/scaffold/.agents/skills/hitch-applets/references/performance.md +1 -1
  51. package/scaffold/.agents/skills/hitch-applets/references/records.md +27 -4
  52. package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
  53. package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
  54. package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
  55. package/scaffold/.agents/skills/hitch-cli/SKILL.md +20 -20
  56. package/scaffold/.agents/skills/hitch-data/SKILL.md +3 -2
  57. package/scaffold/.agents/skills/hitch-data/references/attributes.md +7 -1
  58. package/scaffold/.agents/skills/hitch-data/references/limits.md +23 -23
  59. package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
  60. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  61. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  62. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  63. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  64. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  65. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  66. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  103. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  104. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  105. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  106. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  107. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  108. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  109. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  110. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  111. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  112. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  113. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  114. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  115. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  116. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  117. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  118. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  119. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  120. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  121. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  122. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  133. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  134. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  135. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  136. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  137. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  138. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  139. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  140. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  141. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  142. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  143. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  144. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  145. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  146. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  147. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  148. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  149. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  150. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  151. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  152. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  153. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  154. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  155. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  156. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  157. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  158. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  159. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  160. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  161. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  162. package/scaffold/.github/workflows/hitch.yml +1 -1
  163. package/scaffold/AGENTS.md +2 -2
  164. package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
  165. package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
  166. package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
  167. package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
  168. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
  169. package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
  170. package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
  171. package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
  172. package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
  173. package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
  174. package/scaffold/applet/src/client/lib/utils.ts +1 -6
  175. package/scaffold/applet/src/client/styles/globals.css +2 -0
  176. package/dist/build-DhNDp7ji.js +0 -2
  177. package/dist/generate-DSk8ihsC.js +0 -2
package/dist/vite.js CHANGED
@@ -1,15 +1,301 @@
1
- import { i as APPLET_CODE_PATTERN, n as appletManifestSchema, r as appletSourceDefinitionSchema } from "./manifest-Bd04cWhu.js";
2
- import { l as resolveInstanceUrl, s as organizationInstanceUrl, t as resolveWorkspaceOrg } from "./workspace-B6lenAS5.js";
3
- import { c as writeClientEntry, i as writeRegisterDeclaration, l as writeManifestGen, r as syncContract, s as writeAppletRoot, u as writeServerEntry } from "./generate-BIifT6PK.js";
4
- import { existsSync } from "node:fs";
5
- import { appendFile, readFile, readdir, rm, writeFile } from "node:fs/promises";
6
- import { basename, relative, resolve } from "node:path";
1
+ import { n as meSchema } from "./me-BW7Paary.js";
2
+ import { i as APPLET_CODE_PATTERN, n as appletManifestSchema, r as appletSourceDefinitionSchema } from "./manifest-CJSL9GuC.js";
3
+ import { a as openQueryCacheDatabase } from "./query-cache-CXmAyLv3.js";
4
+ import { d as HITCH_USER_HEADER, u as HITCH_SESSION_HEADER } from "./headers-DkNr9MLJ.js";
5
+ import { a as workflowOutcomeSchema, n as runWorkflowJournal } from "./workflow-engine-BOTzURKM.js";
6
+ import { c as bearerToken, d as resolveAuthForOrg, f as resolveInstanceUrl, l as organizationInstanceUrl, t as resolveWorkspaceOrg, u as rejectEnvironmentKey } from "./workspace-D0dnnlxL.js";
7
+ import { c as writeClientEntry, i as writeRegisterDeclaration, l as writeManifestGen, r as syncContract, s as writeAppletRoot, u as writeServerEntry } from "./generate-BEIYs17x.js";
8
+ import { randomUUID } from "node:crypto";
9
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import { appendFile, readdir, rm, writeFile } from "node:fs/promises";
11
+ import { createServer } from "node:http";
12
+ import { basename, dirname, relative, resolve } from "node:path";
7
13
  import { cloudflare } from "@cloudflare/vite-plugin";
8
14
  import { tanstackRouter } from "@tanstack/router-plugin/vite";
9
15
  import react from "@vitejs/plugin-react";
16
+ import { Readable } from "node:stream";
17
+ import { pipeline } from "node:stream/promises";
18
+ //#region src/dev-proxy.ts
19
+ const REQUEST_DROP = /* @__PURE__ */ new Set([
20
+ "host",
21
+ "connection",
22
+ "content-length",
23
+ "cookie",
24
+ "authorization",
25
+ "origin",
26
+ "referer",
27
+ "accept-encoding"
28
+ ]);
29
+ const RESPONSE_DROP = /* @__PURE__ */ new Set([
30
+ "content-encoding",
31
+ "content-length",
32
+ "transfer-encoding",
33
+ "connection",
34
+ "set-cookie"
35
+ ]);
36
+ function refusesClient(headers, clientAddress) {
37
+ if (![
38
+ "127.0.0.1",
39
+ "::1",
40
+ "::ffff:127.0.0.1"
41
+ ].includes(clientAddress ?? "")) return true;
42
+ const get = (name) => headers instanceof Headers ? headers.get(name) : headers[name]?.toString() ?? null;
43
+ const host = get("host") ?? get(":authority");
44
+ const origin = get("origin");
45
+ return origin !== null && (host === null || !URL.canParse(origin) || new URL(origin).host !== host) || get("sec-fetch-site") === "cross-site";
46
+ }
47
+ function createDevProxy(options) {
48
+ let warnedAboutUnauthorized = false;
49
+ return async (request, clientAddress) => {
50
+ if (refusesClient(request.headers, clientAddress)) return new Response(null, { status: 403 });
51
+ const intercepted = await options.intercept?.(request);
52
+ if (intercepted !== void 0) return intercepted;
53
+ const headers = new Headers(request.headers);
54
+ [...headers.keys()].filter((name) => REQUEST_DROP.has(name) || name.startsWith("x-hitch-")).forEach((name) => headers.delete(name));
55
+ headers.set("authorization", `Bearer ${options.token}`);
56
+ const url = new URL(request.url);
57
+ const upstream = await globalThis.fetch(`${options.orgUrl}${url.pathname}${url.search}`, {
58
+ method: request.method,
59
+ headers,
60
+ body: request.body,
61
+ signal: request.signal,
62
+ ...request.body === null ? {} : { duplex: "half" }
63
+ });
64
+ if (upstream.status === 401 && !warnedAboutUnauthorized) {
65
+ warnedAboutUnauthorized = true;
66
+ options.warn(options.source === "environment" ? "HITCH_API_KEY was rejected." : "Your login expired. Run applet login, then restart the dev server.");
67
+ }
68
+ const responseHeaders = new Headers(upstream.headers);
69
+ [...responseHeaders.keys()].filter((name) => RESPONSE_DROP.has(name)).forEach((name) => responseHeaders.delete(name));
70
+ return new Response(upstream.body, {
71
+ status: upstream.status,
72
+ statusText: upstream.statusText,
73
+ headers: responseHeaders
74
+ });
75
+ };
76
+ }
77
+ async function serveDevProxy(request, response, handler) {
78
+ const controller = new AbortController();
79
+ const onClose = () => {
80
+ if (!response.writableFinished) controller.abort();
81
+ };
82
+ response.on("close", onClose);
83
+ try {
84
+ const authority = request.headers[":authority"];
85
+ const host = request.headers.host ?? (Array.isArray(authority) ? authority[0] : authority);
86
+ const url = `http://${host ?? "localhost"}${request.url ?? "/"}`;
87
+ const result = await handler(new Request(url, {
88
+ method: request.method,
89
+ headers: {
90
+ ...Object.fromEntries(Object.entries(request.headers).filter(([name]) => !name.startsWith(":"))),
91
+ host: host ?? "localhost"
92
+ },
93
+ signal: controller.signal,
94
+ ...["GET", "HEAD"].includes(request.method ?? "GET") ? {} : {
95
+ body: Readable.toWeb(request),
96
+ duplex: "half"
97
+ }
98
+ }), request.socket.remoteAddress);
99
+ response.writeHead(result.status, result.statusText, Object.fromEntries(result.headers));
100
+ if (result.body === null) {
101
+ response.end();
102
+ return;
103
+ }
104
+ await pipeline(Readable.fromWeb(result.body), response);
105
+ } catch (error) {
106
+ if (!controller.signal.aborted || !(error instanceof Error) || error.name !== "AbortError" && error.code !== "ERR_STREAM_PREMATURE_CLOSE") throw error;
107
+ } finally {
108
+ response.off("close", onClose);
109
+ }
110
+ }
111
+ //#endregion
112
+ //#region src/dev-workflows.ts
113
+ const RUN_STATUSES = [
114
+ "in_progress",
115
+ "sleeping",
116
+ "success",
117
+ "error",
118
+ "terminated",
119
+ "open"
120
+ ];
121
+ const badRequest = (message) => Response.json({
122
+ code: "BAD_REQUEST",
123
+ message
124
+ }, { status: 400 });
125
+ const workflowCodeError = (code) => code.length < 1 || code.length > 64 ? badRequest("The workflow code must have 1 to 64 characters.") : void 0;
126
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
127
+ function createDevWorkflows(options) {
128
+ const runs = /* @__PURE__ */ new Map();
129
+ const controller = new AbortController();
130
+ const stopped = () => controller.signal.aborted;
131
+ const wait = (milliseconds) => {
132
+ if (controller.signal.aborted) return Promise.reject(/* @__PURE__ */ new Error("The dev server stopped."));
133
+ return new Promise((resolve, reject) => {
134
+ const deadline = Date.now() + Math.max(0, milliseconds);
135
+ let timer;
136
+ const schedule = () => {
137
+ const remaining = deadline - Date.now();
138
+ if (remaining <= 0) {
139
+ controller.signal.removeEventListener("abort", onAbort);
140
+ resolve();
141
+ return;
142
+ }
143
+ timer = setTimeout(schedule, Math.min(remaining, 2147483647));
144
+ };
145
+ const onAbort = () => {
146
+ clearTimeout(timer);
147
+ controller.signal.removeEventListener("abort", onAbort);
148
+ reject(/* @__PURE__ */ new Error("The dev server stopped."));
149
+ };
150
+ controller.signal.addEventListener("abort", onAbort, { once: true });
151
+ schedule();
152
+ });
153
+ };
154
+ const record = (run, outcome) => {
155
+ switch (outcome.kind) {
156
+ case "step":
157
+ case "step_error":
158
+ run.status = "in_progress";
159
+ run.sleep_until = null;
160
+ break;
161
+ case "sleep":
162
+ run.status = "sleeping";
163
+ run.sleep_until = new Date(outcome.until).toISOString();
164
+ break;
165
+ case "done":
166
+ run.status = "success";
167
+ run.result = outcome.result ?? null;
168
+ run.sleep_until = null;
169
+ run.finished_at = (/* @__PURE__ */ new Date()).toISOString();
170
+ break;
171
+ case "error":
172
+ run.status = "error";
173
+ run.error = outcome.message.slice(0, 2048);
174
+ run.sleep_until = null;
175
+ run.finished_at = (/* @__PURE__ */ new Date()).toISOString();
176
+ }
177
+ };
178
+ const start = (workflow, payload) => {
179
+ const run = {
180
+ id: randomUUID(),
181
+ workflow,
182
+ status: "in_progress",
183
+ result: null,
184
+ error: null,
185
+ sleep_until: null,
186
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
187
+ finished_at: null
188
+ };
189
+ runs.set(run.id, run);
190
+ options.logger.info(`ℹ Workflow ${workflow} run ${run.id} started.`);
191
+ runWorkflowJournal({
192
+ invoke: async (_name, fn) => {
193
+ if (controller.signal.aborted) throw new Error("The dev server stopped.");
194
+ for (let attempt = 0;; attempt += 1) try {
195
+ const outcome = await fn();
196
+ if (stopped()) throw new Error("The dev server stopped.");
197
+ return outcome;
198
+ } catch (error) {
199
+ if (stopped() || attempt === 5) throw error;
200
+ const message = error instanceof Error ? error.message : String(error);
201
+ options.logger.error(`Workflow ${workflow} run ${run.id} invoke failed, retrying: ${message}`);
202
+ await wait(1e3 * 2 ** attempt);
203
+ }
204
+ },
205
+ sleep: (_name, milliseconds) => options.skipSleep ? Promise.resolve() : wait(milliseconds),
206
+ sleepUntil: (_name, until) => options.skipSleep ? Promise.resolve() : wait(until.getTime() - Date.now())
207
+ }, async (journal) => {
208
+ const outcome = await options.invoke(workflow, {
209
+ payload,
210
+ journal
211
+ });
212
+ record(run, outcome);
213
+ return outcome;
214
+ }).then(() => options.logger.info(`ℹ Workflow ${workflow} run ${run.id} succeeded.`), (error) => {
215
+ if (controller.signal.aborted) return;
216
+ const message = (error instanceof Error ? error.message : String(error)).slice(0, 2048);
217
+ run.status = "error";
218
+ run.error = message;
219
+ run.sleep_until = null;
220
+ run.finished_at = (/* @__PURE__ */ new Date()).toISOString();
221
+ options.logger.error(`Workflow ${workflow} run ${run.id} failed: ${message}`);
222
+ });
223
+ return run;
224
+ };
225
+ const handle = (request) => {
226
+ const url = new URL(request.url);
227
+ const prefix = `/api/v1/applets/${encodeURIComponent(options.appletCode)}`;
228
+ if (!url.pathname.startsWith(`${prefix}/`)) return void 0;
229
+ const path = url.pathname.slice(prefix.length);
230
+ const startMatch = /^\/workflows\/([^/]+)\/runs$/.exec(path);
231
+ if (request.method === "POST" && startMatch !== null) return (async () => {
232
+ let body;
233
+ try {
234
+ const text = await request.text();
235
+ body = text === "" ? {} : JSON.parse(text);
236
+ } catch {
237
+ return badRequest("The body must be a JSON object with a payload key.");
238
+ }
239
+ if (body === null || typeof body !== "object" || Array.isArray(body) || Object.keys(body).some((key) => key !== "payload")) return badRequest("The body must be a JSON object with a payload key.");
240
+ let workflow;
241
+ try {
242
+ workflow = decodeURIComponent(startMatch[1]);
243
+ } catch (error) {
244
+ if (error instanceof URIError) return badRequest(error.message);
245
+ throw error;
246
+ }
247
+ const codeError = workflowCodeError(workflow);
248
+ if (codeError !== void 0) return codeError;
249
+ const run = start(workflow, "payload" in body ? body.payload : void 0);
250
+ return Response.json({ data: {
251
+ id: run.id,
252
+ workflow: run.workflow,
253
+ status: "in_progress"
254
+ } }, { status: 201 });
255
+ })();
256
+ const getMatch = /^\/workflows\/([^/]+)\/runs\/([^/]+)$/.exec(path);
257
+ if (request.method === "GET" && getMatch !== null) {
258
+ let workflow;
259
+ let id;
260
+ try {
261
+ workflow = decodeURIComponent(getMatch[1]);
262
+ id = decodeURIComponent(getMatch[2]);
263
+ } catch (error) {
264
+ if (error instanceof URIError) return Promise.resolve(badRequest(error.message));
265
+ throw error;
266
+ }
267
+ const codeError = workflowCodeError(workflow);
268
+ if (codeError !== void 0) return Promise.resolve(codeError);
269
+ if (!UUID_PATTERN.test(id)) return Promise.resolve(badRequest("The run id must be a uuid."));
270
+ const run = runs.get(id);
271
+ return Promise.resolve(run?.workflow === workflow ? Response.json({ data: run }) : Response.json({
272
+ code: "workflow_run_not_found",
273
+ message: "The workflow run does not exist."
274
+ }, { status: 404 }));
275
+ }
276
+ if (request.method === "GET" && path === "/workflow-runs") {
277
+ const status = url.searchParams.get("status");
278
+ const limitValue = url.searchParams.get("limit");
279
+ const limit = limitValue === null ? 50 : Number(limitValue);
280
+ if (status !== null && !RUN_STATUSES.some((value) => value === status)) return Promise.resolve(badRequest("Invalid workflow run status."));
281
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) return Promise.resolve(badRequest("The limit must be an integer from 1 to 100."));
282
+ const workflow = url.searchParams.get("workflow");
283
+ const codeError = workflow === null ? void 0 : workflowCodeError(workflow);
284
+ if (codeError !== void 0) return Promise.resolve(codeError);
285
+ const data = [...runs.values()].filter((run) => (workflow === null || run.workflow === workflow) && (status === null || status === run.status || status === "open" && ["in_progress", "sleeping"].includes(run.status))).reverse().slice(0, limit).map(({ result: _result, ...summary }) => summary);
286
+ return Promise.resolve(Response.json({ data }));
287
+ }
288
+ };
289
+ return {
290
+ handle,
291
+ stop: () => controller.abort()
292
+ };
293
+ }
294
+ //#endregion
10
295
  //#region src/vite.ts
11
296
  const APPLET_COMPATIBILITY_DATE = "2026-07-14";
12
297
  const APPLET_COMPATIBILITY_FLAGS = ["nodejs_compat"];
298
+ let activeDevListener;
13
299
  function clientBootstrapScript(parentOrigin, appletCode) {
14
300
  return `(function () {
15
301
  var parentOrigin = ${parentOrigin === void 0 ? "null" : JSON.stringify(parentOrigin)};
@@ -33,6 +319,20 @@ function clientBootstrapScript(parentOrigin, appletCode) {
33
319
  : null
34
320
  });
35
321
 
322
+ // Opened here so it is ready by the time the applet module restores its
323
+ // query cache. A failure is reported when the module reads the promise.
324
+ Object.defineProperty(window, "__hitchCacheDatabase", {
325
+ configurable: false,
326
+ writable: false,
327
+ value: window.parent !== window && parentOrigin !== null
328
+ ? (function () {
329
+ var database = (${openQueryCacheDatabase.toString()})();
330
+ database.catch(function () {});
331
+ return database;
332
+ })()
333
+ : null
334
+ });
335
+
36
336
  function applyTheme(theme) {
37
337
  document.documentElement.classList.toggle("dark", theme === "dark");
38
338
  }
@@ -137,7 +437,7 @@ function clientBootstrapScript(parentOrigin, appletCode) {
137
437
  });
138
438
  })();`;
139
439
  }
140
- function applet(sourceDefinition, options = {}) {
440
+ function applet(sourceDefinition) {
141
441
  const parsedSourceDefinition = appletSourceDefinitionSchema.safeParse(sourceDefinition);
142
442
  if (!parsedSourceDefinition.success) {
143
443
  const raw = sourceDefinition;
@@ -151,6 +451,11 @@ function applet(sourceDefinition, options = {}) {
151
451
  let clearedOutDir = false;
152
452
  let contractSync;
153
453
  let themeStylesheetUrl;
454
+ let devApiUrl;
455
+ let devListener;
456
+ let devLogger;
457
+ let devWorkflows;
458
+ let devContext;
154
459
  const virtualRouteConfig = {
155
460
  type: "root",
156
461
  file: "__root.tsx",
@@ -169,6 +474,7 @@ function applet(sourceDefinition, options = {}) {
169
474
  const routesDirectory = resolve(resolvedRoot, "src/client/routes");
170
475
  const hasClient = existsSync(routesDirectory);
171
476
  const hasServer = existsSync(resolve(resolvedRoot, "src/server/main.ts"));
477
+ if (!hasServer && parsedDefinition.data.telephony?.events?.length) throw new Error("An applet with telephony events must have src/server/main.ts.");
172
478
  if (!hasServer && (parsedDefinition.data.schedules?.length ?? 0) > 0) throw new Error("An applet with schedules must have src/server/main.ts.");
173
479
  const legacyMain = resolve(resolvedRoot, "src/client/main.tsx");
174
480
  const indexHtml = resolve(resolvedRoot, "index.html");
@@ -199,6 +505,11 @@ function applet(sourceDefinition, options = {}) {
199
505
  org: resolveWorkspaceOrg(callerRoot),
200
506
  ...parsedDefinition.data
201
507
  });
508
+ const clsxFromCn = {
509
+ name: "hitch-clsx",
510
+ resolveId: (id) => id === "clsx" ? "\0hitch:clsx" : void 0,
511
+ load: (id) => id === "\0hitch:clsx" ? "export { clsx, clsx as default } from \"cn\";" : void 0
512
+ };
202
513
  const orchestrator = {
203
514
  name: "hitch-applet",
204
515
  enforce: "pre",
@@ -218,9 +529,9 @@ function applet(sourceDefinition, options = {}) {
218
529
  });
219
530
  clearedOutDir = true;
220
531
  }
221
- if (environment.command === "build") themeStylesheetUrl = `${organizationInstanceUrl(await resolveInstanceUrl(void 0, options.store), manifest.org)}/theme.css`;
222
- if (environment.command === "build" && manifest.objects.length > 0) {
223
- contractSync ??= syncContract(resolve(root, ".hitch"), manifest, options);
532
+ if (environment.command === "build") themeStylesheetUrl = `${organizationInstanceUrl(await resolveInstanceUrl(), manifest.org)}/theme.css`;
533
+ if (environment.command === "build") {
534
+ contractSync ??= syncContract(resolve(root, ".hitch"), manifest);
224
535
  await contractSync;
225
536
  }
226
537
  const hasHitchGen = existsSync(resolve(root, ".hitch/index.ts"));
@@ -235,8 +546,77 @@ function applet(sourceDefinition, options = {}) {
235
546
  hasServer ? writeServerEntry(root, hasHitchGen, manifest.code) : void 0,
236
547
  hasHitchGen ? void 0 : writeRegisterDeclaration(resolve(root, ".hitch"))
237
548
  ]);
549
+ if (environment.command === "serve" && environment.mode !== "test" && environment.isPreview !== true) {
550
+ const instanceUrl = await resolveInstanceUrl();
551
+ const orgUrl = organizationInstanceUrl(instanceUrl, manifest.org);
552
+ const auth = await resolveAuthForOrg(instanceUrl, manifest.org);
553
+ if (auth === void 0) throw new Error("Not logged in. Run applet login.");
554
+ const token = bearerToken(auth);
555
+ const meResponse = await globalThis.fetch(`${orgUrl}/api/v1/me`, { headers: { authorization: `Bearer ${token}` } });
556
+ rejectEnvironmentKey(meResponse, auth);
557
+ if (meResponse.status === 401) throw new Error("Your login expired. Run applet login.");
558
+ if (!meResponse.ok) throw new Error(`Hitch returned ${meResponse.status} for /api/v1/me.`);
559
+ const { person } = meSchema.parse(await meResponse.json());
560
+ const proxy = createDevProxy({
561
+ orgUrl,
562
+ token,
563
+ source: auth.source,
564
+ warn: (message) => devLogger?.warn(message),
565
+ intercept: (request) => devWorkflows?.handle(request)
566
+ });
567
+ devContext = {
568
+ person,
569
+ proxy,
570
+ orgUrl,
571
+ cacheBuster: randomUUID()
572
+ };
573
+ if (hasServer && devListener === void 0) {
574
+ if (activeDevListener?.listening) activeDevListener.close();
575
+ const nonce = randomUUID();
576
+ devListener = createServer((request, response) => {
577
+ const url = request.url ?? "";
578
+ const prefix = `/${nonce}/api`;
579
+ const address = devListener?.address();
580
+ if (!(url === prefix || url.startsWith(`${prefix}/`) || url.startsWith(`${prefix}?`)) || address === null || address === void 0 || typeof address === "string" || request.headers.host !== `127.0.0.1:${address.port}`) {
581
+ response.writeHead(404).end();
582
+ return;
583
+ }
584
+ request.url = url.slice(nonce.length + 1);
585
+ serveDevProxy(request, response, proxy).catch((error) => {
586
+ const failure = error instanceof Error ? error : new Error(String(error));
587
+ devLogger?.error(`Local Hitch proxy failed: ${failure.message}`);
588
+ response.destroy(failure);
589
+ });
590
+ });
591
+ activeDevListener = devListener;
592
+ try {
593
+ await new Promise((resolvePromise, rejectPromise) => {
594
+ devListener?.once("error", rejectPromise);
595
+ devListener?.listen(0, "127.0.0.1", resolvePromise);
596
+ });
597
+ const address = devListener.address();
598
+ if (address === null || typeof address === "string") throw new Error("The local Hitch proxy has no port.");
599
+ devApiUrl = `http://127.0.0.1:${address.port}/${nonce}/api`;
600
+ } catch (error) {
601
+ if (devListener.listening) devListener.close();
602
+ if (activeDevListener === devListener) activeDevListener = void 0;
603
+ devListener = void 0;
604
+ throw error;
605
+ }
606
+ }
607
+ }
608
+ if (environment.mode === "test") {
609
+ const colocated = (await readdir(resolve(root, "src"), { recursive: true })).filter((path) => /\.(test|spec)\.[cm]?[jt]sx?$/.test(path));
610
+ if (colocated.length > 0) throw new Error(`Applet tests run only from the test folder. Move ${colocated.map((path) => `src/${path}`).join(", ")} to test/.`);
611
+ }
238
612
  return {
239
613
  base: `/${manifest.code}/`,
614
+ test: { include: ["test/**/*.test.{ts,tsx}"] },
615
+ resolve: { alias: {
616
+ "clsx/lite": "cn/lite",
617
+ "tailwind-merge": "cn"
618
+ } },
619
+ optimizeDeps: { rolldownOptions: { plugins: [clsxFromCn] } },
240
620
  ...hasServer && { environments: { worker: { build: { rollupOptions: { output: { codeSplitting: false } } } } } }
241
621
  };
242
622
  },
@@ -244,11 +624,84 @@ function applet(sourceDefinition, options = {}) {
244
624
  apiManifest = resolveAppletConfig(config.root).manifest;
245
625
  },
246
626
  configureServer(server) {
247
- const { manifest, root } = requireAppletConfig();
627
+ if (devContext === void 0) return;
628
+ const { manifest } = requireAppletConfig();
629
+ const { person, proxy, orgUrl, cacheBuster } = devContext;
630
+ const orgOrigin = new URL(orgUrl).origin;
631
+ devLogger = server.config.logger;
632
+ const skipSleep = process.env.HITCH_DEV_SKIP_SLEEP === "1";
633
+ devWorkflows = createDevWorkflows({
634
+ appletCode: manifest.code,
635
+ skipSleep,
636
+ logger: server.config.logger,
637
+ invoke: async (workflow, body) => {
638
+ const address = server.httpServer?.address();
639
+ if (address === null || address === void 0 || typeof address === "string") throw new Error("The dev server has no port.");
640
+ const response = await fetch(`http://localhost:${address.port}/${manifest.code}/api/__hitch/workflows/${encodeURIComponent(workflow)}`, {
641
+ method: "POST",
642
+ headers: { "content-type": "application/json" },
643
+ body: JSON.stringify(body)
644
+ });
645
+ if (!response.ok) throw new Error(`Workflow worker returned HTTP ${response.status}.`);
646
+ return workflowOutcomeSchema.parse(await response.json());
647
+ }
648
+ });
649
+ server.config.logger.info(`ℹ Running as ${person.email} in production org "${manifest.org}". Reads and writes use real data with your access.`);
650
+ if (manifest.requiresChangeset !== void 0) server.config.logger.info(`ℹ This applet requires changeset ${manifest.requiresChangeset}. Local dev uses the live schema, so a pending changeset's schema is not available until it applies.`);
651
+ if (skipSleep) server.config.logger.info("ℹ HITCH_DEV_SKIP_SLEEP is set. Workflow sleeps and retry delays run at once.");
652
+ const devServerFile = resolve(requireAppletConfig().root, ".hitch/dev-server.json");
653
+ let devServerUrl;
654
+ const removeDevServerFile = () => {
655
+ if (devServerUrl === void 0) return;
656
+ try {
657
+ const file = JSON.parse(readFileSync(devServerFile, "utf8"));
658
+ if (file !== null && typeof file === "object" && "pid" in file && file.pid === process.pid && "url" in file && file.url === devServerUrl) rmSync(devServerFile, { force: true });
659
+ } catch (error) {
660
+ if (!(error instanceof SyntaxError) && !(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
661
+ }
662
+ };
663
+ server.httpServer?.once("listening", () => {
664
+ const address = server.httpServer?.address();
665
+ if (address === null || address === void 0 || typeof address === "string") return;
666
+ mkdirSync(dirname(devServerFile), { recursive: true });
667
+ devServerUrl = `http://localhost:${address.port}`;
668
+ writeFileSync(devServerFile, JSON.stringify({
669
+ url: devServerUrl,
670
+ pid: process.pid
671
+ }));
672
+ server.config.logger.info(`ℹ Preview in Hitch: ${orgUrl}/applets/${manifest.code}?_hitch_dev=${address.port}`);
673
+ process.once("exit", removeDevServerFile);
674
+ });
675
+ server.httpServer?.once("close", () => {
676
+ devWorkflows?.stop();
677
+ devListener?.close();
678
+ if (activeDevListener === devListener) activeDevListener = void 0;
679
+ process.off("exit", removeDevServerFile);
680
+ removeDevServerFile();
681
+ });
248
682
  const mount = `/${manifest.code}`;
249
683
  const apiPrefix = `${mount}/api`;
250
684
  server.middlewares.use((request, response, next) => {
685
+ if (request.url === mount || request.url?.startsWith(`${mount}?`)) request.url = `${mount}/${request.url.slice(mount.length)}`;
251
686
  const url = request.url ?? "";
687
+ const pathname = url.split("?", 1)[0] ?? "";
688
+ if (pathname.startsWith("/__applet/context/")) {
689
+ if (pathname !== `/__applet/context/${manifest.code}` || request.method !== "GET") {
690
+ response.writeHead(404).end();
691
+ return;
692
+ }
693
+ if (refusesClient(request.headers, request.socket.remoteAddress)) {
694
+ response.writeHead(403).end();
695
+ return;
696
+ }
697
+ response.setHeader("Cache-Control", "no-store");
698
+ response.setHeader("Content-Type", "application/json; charset=utf-8");
699
+ response.end(JSON.stringify({
700
+ cacheBuster,
701
+ cacheScope: person.id
702
+ }));
703
+ return;
704
+ }
252
705
  if (url === "/_hitch/client.js" || url.startsWith("/_hitch/client.js?")) {
253
706
  response.statusCode = 200;
254
707
  response.setHeader("Cache-Control", "no-store");
@@ -256,29 +709,37 @@ function applet(sourceDefinition, options = {}) {
256
709
  response.end("// PostHog is disabled in standalone applet development.\n");
257
710
  return;
258
711
  }
259
- if (url === "/theme.css" || url.startsWith("/theme.css?")) {
260
- readFile(resolve(root, ".hitch/theme.css")).then((theme) => {
261
- response.statusCode = 200;
262
- response.setHeader("Cache-Control", "no-cache");
263
- response.setHeader("Content-Type", "text/css; charset=utf-8");
264
- response.end(theme);
265
- }, (error) => {
266
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
267
- next();
268
- return;
269
- }
270
- next(error);
712
+ if (url === "/api" || url.startsWith("/api/") || url.startsWith("/api?") || url === "/theme.css" || url.startsWith("/theme.css?")) {
713
+ serveDevProxy(request, response, proxy).catch((error) => {
714
+ if (response.headersSent) response.destroy(error instanceof Error ? error : new Error(String(error)));
715
+ else next(error);
271
716
  });
272
717
  return;
273
718
  }
274
- if (url === apiPrefix || url.startsWith(`${apiPrefix}/`) || url.startsWith(`${apiPrefix}?`)) request.url = url.slice(mount.length);
719
+ if (url === apiPrefix || url.startsWith(`${apiPrefix}/`) || url.startsWith(`${apiPrefix}?`)) {
720
+ if (refusesClient(request.headers, request.socket.remoteAddress)) {
721
+ response.writeHead(403).end();
722
+ return;
723
+ }
724
+ request.url = url.slice(mount.length);
725
+ if (request.url.startsWith("/api/__hitch/") && request.headers.origin !== void 0) {
726
+ response.writeHead(404).end();
727
+ return;
728
+ }
729
+ request.originalUrl = request.url;
730
+ request.headers[HITCH_USER_HEADER] = person.id;
731
+ request.headers[HITCH_SESSION_HEADER] = "local-dev";
732
+ request.rawHeaders = request.rawHeaders.flatMap((name, index, headers) => index % 2 === 0 && name.toLowerCase() !== "x-hitch-user" && name.toLowerCase() !== "x-hitch-session" ? [name, headers[index + 1] ?? ""] : []);
733
+ request.rawHeaders.push(HITCH_USER_HEADER, person.id, HITCH_SESSION_HEADER, "local-dev");
734
+ }
735
+ response.setHeader("Content-Security-Policy", `frame-ancestors ${orgOrigin}`);
275
736
  next();
276
737
  });
277
738
  },
278
739
  transformIndexHtml: {
279
740
  order: "post",
280
741
  handler(html) {
281
- const parentOrigin = themeStylesheetUrl === void 0 ? void 0 : new URL(themeStylesheetUrl).origin;
742
+ const parentOrigin = devContext?.orgUrl === void 0 ? themeStylesheetUrl === void 0 ? void 0 : new URL(themeStylesheetUrl).origin : new URL(devContext.orgUrl).origin;
282
743
  return {
283
744
  html: themeStylesheetUrl === void 0 ? html : html.replace("href=\"/theme.css\"", `href="${themeStylesheetUrl}"`),
284
745
  tags: [{
@@ -332,7 +793,11 @@ function applet(sourceDefinition, options = {}) {
332
793
  not_found_handling: "single-page-application",
333
794
  run_worker_first: ["/api/*"]
334
795
  } } : {},
335
- ...hasServer ? { main: resolve(root, ".hitch/entry.server.gen.ts") } : {}
796
+ ...hasServer ? { main: resolve(root, ".hitch/entry.server.gen.ts") } : {},
797
+ ...devApiUrl === void 0 ? {} : { vars: {
798
+ ...workerConfig.vars,
799
+ HITCH_API_URL: devApiUrl
800
+ } }
336
801
  };
337
802
  },
338
803
  get viteEnvironment() {
@@ -342,9 +807,14 @@ function applet(sourceDefinition, options = {}) {
342
807
  ...plugin,
343
808
  apply: ((_config, { mode }) => mode !== "test")
344
809
  }));
810
+ const hasClientSource = existsSync(resolve("src/client/routes"));
345
811
  return [
346
812
  orchestrator,
347
- ...existsSync(resolve("src/client/routes")) ? [tanstackRouter({
813
+ {
814
+ ...clsxFromCn,
815
+ enforce: "pre"
816
+ },
817
+ ...hasClientSource ? [tanstackRouter({
348
818
  generatedRouteTree: ".hitch/routeTree.gen.ts",
349
819
  routesDirectory: "src/client/routes",
350
820
  routeFileIgnorePattern: "__root",