@hitch42/applet 0.1.0-beta.10

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 (193) hide show
  1. package/LICENSE +21 -0
  2. package/dist/_internal/client.d.ts +7 -0
  3. package/dist/_internal/client.js +169 -0
  4. package/dist/_internal/server.d.ts +92 -0
  5. package/dist/_internal/server.js +181 -0
  6. package/dist/applet.d.ts +1 -0
  7. package/dist/applet.js +184 -0
  8. package/dist/batch-Qa4B4-Yp.d.ts +30 -0
  9. package/dist/build-BlmLiKro.js +90 -0
  10. package/dist/build-CcT-wNN9.js +2 -0
  11. package/dist/ci-0Q2mUh53.js +68 -0
  12. package/dist/client.d.ts +48 -0
  13. package/dist/client.js +38 -0
  14. package/dist/contract-QUvEGiRI.d.ts +23 -0
  15. package/dist/deploy-BbL3GObV.js +2579 -0
  16. package/dist/email-DBTVhNB_.js +62 -0
  17. package/dist/generate-XB-72Fzj.js +537 -0
  18. package/dist/generate-wIN3IYqL.js +2 -0
  19. package/dist/index.d.ts +185 -0
  20. package/dist/index.js +251 -0
  21. package/dist/init-DwsXRUk-.js +244 -0
  22. package/dist/lazy-client-x0DCI98S.js +64 -0
  23. package/dist/load-config-OKZU3UKn.js +27 -0
  24. package/dist/login-BBO74hPC.js +12 -0
  25. package/dist/logs-XvK6Zs5Y.js +183 -0
  26. package/dist/manifest-4ZAc6HUk.js +191 -0
  27. package/dist/manifest-I-9s8R36.d.ts +88 -0
  28. package/dist/me-BW7Paary.js +32 -0
  29. package/dist/project-B0qNXnFu.js +536 -0
  30. package/dist/prompt-ChS-grYO.js +29 -0
  31. package/dist/query-cache-CXmAyLv3.js +82 -0
  32. package/dist/react.d.ts +37 -0
  33. package/dist/react.js +515 -0
  34. package/dist/schedules-BkWZsbSs.js +42 -0
  35. package/dist/server.d.ts +46 -0
  36. package/dist/server.js +104 -0
  37. package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
  38. package/dist/telephony.d.ts +77 -0
  39. package/dist/telephony.js +607 -0
  40. package/dist/upload-DKZzz5kW.d.ts +112 -0
  41. package/dist/upload-DRbVhuDm.js +114 -0
  42. package/dist/variables-0tjnAeut.js +108 -0
  43. package/dist/vite.d.ts +63 -0
  44. package/dist/vite.js +816 -0
  45. package/dist/workflow-CTo2kZF1.js +51 -0
  46. package/dist/workflow-DurM6Fwx.d.ts +28 -0
  47. package/dist/workflow-engine-MogejNfF.js +117 -0
  48. package/dist/workspace-DruAZPmV.js +390 -0
  49. package/package.json +112 -0
  50. package/scaffold/.agents/skills/hitch-applets/SKILL.md +163 -0
  51. package/scaffold/.agents/skills/hitch-applets/references/client.md +73 -0
  52. package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
  53. package/scaffold/.agents/skills/hitch-applets/references/files.md +109 -0
  54. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +198 -0
  55. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +37 -0
  56. package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
  57. package/scaffold/.agents/skills/hitch-applets/references/records.md +176 -0
  58. package/scaffold/.agents/skills/hitch-applets/references/schedules.md +91 -0
  59. package/scaffold/.agents/skills/hitch-applets/references/server.md +95 -0
  60. package/scaffold/.agents/skills/hitch-applets/references/workflows.md +81 -0
  61. package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
  62. package/scaffold/.agents/skills/hitch-data/SKILL.md +52 -0
  63. package/scaffold/.agents/skills/hitch-data/references/attributes.md +127 -0
  64. package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
  65. package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
  66. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  103. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  104. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  105. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  106. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  107. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  108. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  109. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  110. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  111. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  112. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  113. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  114. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  115. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  116. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  117. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  118. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  119. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  120. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  121. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  122. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  133. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  134. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  135. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  136. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  137. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  138. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  139. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  140. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  141. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  142. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  143. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  144. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  145. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  146. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  147. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  148. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  149. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  150. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  151. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  152. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  153. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  154. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  155. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  156. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  157. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  158. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  159. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  160. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  161. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  162. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  163. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  164. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  165. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  166. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  167. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  168. package/scaffold/.github/workflows/hitch.yml +15 -0
  169. package/scaffold/AGENTS.md +38 -0
  170. package/scaffold/applet/components.json +25 -0
  171. package/scaffold/applet/src/client/components/ui/alert.tsx +73 -0
  172. package/scaffold/applet/src/client/components/ui/avatar.tsx +100 -0
  173. package/scaffold/applet/src/client/components/ui/badge.tsx +55 -0
  174. package/scaffold/applet/src/client/components/ui/button.tsx +90 -0
  175. package/scaffold/applet/src/client/components/ui/card.tsx +85 -0
  176. package/scaffold/applet/src/client/components/ui/checkbox.tsx +35 -0
  177. package/scaffold/applet/src/client/components/ui/dialog.tsx +164 -0
  178. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +235 -0
  179. package/scaffold/applet/src/client/components/ui/input-group.tsx +144 -0
  180. package/scaffold/applet/src/client/components/ui/input.tsx +22 -0
  181. package/scaffold/applet/src/client/components/ui/label.tsx +29 -0
  182. package/scaffold/applet/src/client/components/ui/popover.tsx +74 -0
  183. package/scaffold/applet/src/client/components/ui/select.tsx +243 -0
  184. package/scaffold/applet/src/client/components/ui/separator.tsx +25 -0
  185. package/scaffold/applet/src/client/components/ui/sheet.tsx +177 -0
  186. package/scaffold/applet/src/client/components/ui/skeleton.tsx +13 -0
  187. package/scaffold/applet/src/client/components/ui/spinner.tsx +17 -0
  188. package/scaffold/applet/src/client/components/ui/switch.tsx +41 -0
  189. package/scaffold/applet/src/client/components/ui/table.tsx +114 -0
  190. package/scaffold/applet/src/client/components/ui/tabs.tsx +78 -0
  191. package/scaffold/applet/src/client/components/ui/textarea.tsx +23 -0
  192. package/scaffold/applet/src/client/lib/utils.ts +1 -0
  193. package/scaffold/applet/src/client/styles/globals.css +136 -0
package/dist/vite.js ADDED
@@ -0,0 +1,816 @@
1
+ import { n as meSchema } from "./me-BW7Paary.js";
2
+ import { i as APPLET_CODE_PATTERN, n as appletManifestSchema, r as appletSourceDefinitionSchema } from "./manifest-4ZAc6HUk.js";
3
+ import { a as openQueryCacheDatabase } from "./query-cache-CXmAyLv3.js";
4
+ import { a as workflowOutcomeSchema, c as HITCH_SESSION_HEADER, l as HITCH_USER_HEADER, n as runWorkflowJournal } from "./workflow-engine-MogejNfF.js";
5
+ import { c as bearerToken, d as resolveAuthForOrg, f as resolveInstanceUrl, l as organizationInstanceUrl, t as resolveWorkspaceOrg, u as rejectEnvironmentKey } from "./workspace-DruAZPmV.js";
6
+ import { c as writeClientEntry, i as writeRegisterDeclaration, l as writeManifestGen, r as syncContract, s as writeAppletRoot, u as writeServerEntry } from "./generate-XB-72Fzj.js";
7
+ import { randomUUID } from "node:crypto";
8
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
9
+ import { appendFile, readdir, rm, writeFile } from "node:fs/promises";
10
+ import { createServer } from "node:http";
11
+ import { basename, dirname, relative, resolve } from "node:path";
12
+ import { cloudflare } from "@cloudflare/vite-plugin";
13
+ import { tanstackRouter } from "@tanstack/router-plugin/vite";
14
+ import react from "@vitejs/plugin-react";
15
+ import { Readable } from "node:stream";
16
+ import { pipeline } from "node:stream/promises";
17
+ //#region src/dev-proxy.ts
18
+ const REQUEST_DROP = /* @__PURE__ */ new Set([
19
+ "host",
20
+ "connection",
21
+ "content-length",
22
+ "cookie",
23
+ "authorization",
24
+ "origin",
25
+ "referer",
26
+ "accept-encoding"
27
+ ]);
28
+ const RESPONSE_DROP = /* @__PURE__ */ new Set([
29
+ "content-encoding",
30
+ "content-length",
31
+ "transfer-encoding",
32
+ "connection",
33
+ "set-cookie"
34
+ ]);
35
+ function refusesClient(headers, clientAddress) {
36
+ if (![
37
+ "127.0.0.1",
38
+ "::1",
39
+ "::ffff:127.0.0.1"
40
+ ].includes(clientAddress ?? "")) return true;
41
+ const get = (name) => headers instanceof Headers ? headers.get(name) : headers[name]?.toString() ?? null;
42
+ const host = get("host") ?? get(":authority");
43
+ const origin = get("origin");
44
+ return origin !== null && (host === null || !URL.canParse(origin) || new URL(origin).host !== host) || get("sec-fetch-site") === "cross-site";
45
+ }
46
+ function createDevProxy(options) {
47
+ let warnedAboutUnauthorized = false;
48
+ return async (request, clientAddress) => {
49
+ if (refusesClient(request.headers, clientAddress)) return new Response(null, { status: 403 });
50
+ const intercepted = await options.intercept?.(request);
51
+ if (intercepted !== void 0) return intercepted;
52
+ const headers = new Headers(request.headers);
53
+ [...headers.keys()].filter((name) => REQUEST_DROP.has(name) || name.startsWith("x-hitch-")).forEach((name) => headers.delete(name));
54
+ headers.set("authorization", `Bearer ${options.token}`);
55
+ const url = new URL(request.url);
56
+ const upstream = await (options.fetch ?? globalThis.fetch)(`${options.orgUrl}${url.pathname}${url.search}`, {
57
+ method: request.method,
58
+ headers,
59
+ body: request.body,
60
+ signal: request.signal,
61
+ ...request.body === null ? {} : { duplex: "half" }
62
+ });
63
+ if (upstream.status === 401 && !warnedAboutUnauthorized) {
64
+ warnedAboutUnauthorized = true;
65
+ options.warn(options.source === "environment" ? "HITCH_API_KEY was rejected." : "Your login expired. Run applet login, then restart the dev server.");
66
+ }
67
+ const responseHeaders = new Headers(upstream.headers);
68
+ [...responseHeaders.keys()].filter((name) => RESPONSE_DROP.has(name)).forEach((name) => responseHeaders.delete(name));
69
+ return new Response(upstream.body, {
70
+ status: upstream.status,
71
+ statusText: upstream.statusText,
72
+ headers: responseHeaders
73
+ });
74
+ };
75
+ }
76
+ async function serveDevProxy(request, response, handler) {
77
+ const controller = new AbortController();
78
+ const onClose = () => {
79
+ if (!response.writableFinished) controller.abort();
80
+ };
81
+ response.on("close", onClose);
82
+ try {
83
+ const authority = request.headers[":authority"];
84
+ const host = request.headers.host ?? (Array.isArray(authority) ? authority[0] : authority);
85
+ const url = `http://${host ?? "localhost"}${request.url ?? "/"}`;
86
+ const result = await handler(new Request(url, {
87
+ method: request.method,
88
+ headers: {
89
+ ...Object.fromEntries(Object.entries(request.headers).filter(([name]) => !name.startsWith(":"))),
90
+ host: host ?? "localhost"
91
+ },
92
+ signal: controller.signal,
93
+ ...["GET", "HEAD"].includes(request.method ?? "GET") ? {} : {
94
+ body: Readable.toWeb(request),
95
+ duplex: "half"
96
+ }
97
+ }), request.socket.remoteAddress);
98
+ response.writeHead(result.status, result.statusText, Object.fromEntries(result.headers));
99
+ if (result.body === null) {
100
+ response.end();
101
+ return;
102
+ }
103
+ await pipeline(Readable.fromWeb(result.body), response);
104
+ } catch (error) {
105
+ if (!controller.signal.aborted || !(error instanceof Error) || error.name !== "AbortError" && error.code !== "ERR_STREAM_PREMATURE_CLOSE") throw error;
106
+ } finally {
107
+ response.off("close", onClose);
108
+ }
109
+ }
110
+ //#endregion
111
+ //#region src/dev-workflows.ts
112
+ const RUN_STATUSES = [
113
+ "in_progress",
114
+ "sleeping",
115
+ "success",
116
+ "error",
117
+ "terminated",
118
+ "open"
119
+ ];
120
+ const badRequest = (message) => Response.json({
121
+ code: "BAD_REQUEST",
122
+ message
123
+ }, { status: 400 });
124
+ const workflowCodeError = (code) => code.length < 1 || code.length > 64 ? badRequest("The workflow code must have 1 to 64 characters.") : void 0;
125
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
126
+ function createDevWorkflows(options) {
127
+ const runs = /* @__PURE__ */ new Map();
128
+ const controller = new AbortController();
129
+ const stopped = () => controller.signal.aborted;
130
+ const wait = (milliseconds) => {
131
+ if (controller.signal.aborted) return Promise.reject(/* @__PURE__ */ new Error("The dev server stopped."));
132
+ return new Promise((resolve, reject) => {
133
+ const deadline = Date.now() + Math.max(0, milliseconds);
134
+ let timer;
135
+ const schedule = () => {
136
+ const remaining = deadline - Date.now();
137
+ if (remaining <= 0) {
138
+ controller.signal.removeEventListener("abort", onAbort);
139
+ resolve();
140
+ return;
141
+ }
142
+ timer = setTimeout(schedule, Math.min(remaining, 2147483647));
143
+ };
144
+ const onAbort = () => {
145
+ clearTimeout(timer);
146
+ controller.signal.removeEventListener("abort", onAbort);
147
+ reject(/* @__PURE__ */ new Error("The dev server stopped."));
148
+ };
149
+ controller.signal.addEventListener("abort", onAbort, { once: true });
150
+ schedule();
151
+ });
152
+ };
153
+ const record = (run, outcome) => {
154
+ switch (outcome.kind) {
155
+ case "step":
156
+ case "step_error":
157
+ run.status = "in_progress";
158
+ run.sleep_until = null;
159
+ break;
160
+ case "sleep":
161
+ run.status = "sleeping";
162
+ run.sleep_until = new Date(outcome.until).toISOString();
163
+ break;
164
+ case "done":
165
+ run.status = "success";
166
+ run.result = outcome.result ?? null;
167
+ run.sleep_until = null;
168
+ run.finished_at = (/* @__PURE__ */ new Date()).toISOString();
169
+ break;
170
+ case "error":
171
+ run.status = "error";
172
+ run.error = outcome.message.slice(0, 2048);
173
+ run.sleep_until = null;
174
+ run.finished_at = (/* @__PURE__ */ new Date()).toISOString();
175
+ }
176
+ };
177
+ const start = (workflow, payload) => {
178
+ const run = {
179
+ id: randomUUID(),
180
+ workflow,
181
+ status: "in_progress",
182
+ result: null,
183
+ error: null,
184
+ sleep_until: null,
185
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
186
+ finished_at: null
187
+ };
188
+ runs.set(run.id, run);
189
+ options.logger.info(`ℹ Workflow ${workflow} run ${run.id} started.`);
190
+ runWorkflowJournal({
191
+ invoke: async (_name, fn) => {
192
+ if (controller.signal.aborted) throw new Error("The dev server stopped.");
193
+ for (let attempt = 0;; attempt += 1) try {
194
+ const outcome = await fn();
195
+ if (stopped()) throw new Error("The dev server stopped.");
196
+ return outcome;
197
+ } catch (error) {
198
+ if (stopped() || attempt === 5) throw error;
199
+ const message = error instanceof Error ? error.message : String(error);
200
+ options.logger.error(`Workflow ${workflow} run ${run.id} invoke failed, retrying: ${message}`);
201
+ await wait(1e3 * 2 ** attempt);
202
+ }
203
+ },
204
+ sleep: (_name, milliseconds) => options.skipSleep ? Promise.resolve() : wait(milliseconds),
205
+ sleepUntil: (_name, until) => options.skipSleep ? Promise.resolve() : wait(until.getTime() - Date.now())
206
+ }, async (journal) => {
207
+ const outcome = await options.invoke(workflow, {
208
+ payload,
209
+ journal
210
+ });
211
+ record(run, outcome);
212
+ return outcome;
213
+ }).then(() => options.logger.info(`ℹ Workflow ${workflow} run ${run.id} succeeded.`), (error) => {
214
+ if (controller.signal.aborted) return;
215
+ const message = (error instanceof Error ? error.message : String(error)).slice(0, 2048);
216
+ run.status = "error";
217
+ run.error = message;
218
+ run.sleep_until = null;
219
+ run.finished_at = (/* @__PURE__ */ new Date()).toISOString();
220
+ options.logger.error(`Workflow ${workflow} run ${run.id} failed: ${message}`);
221
+ });
222
+ return run;
223
+ };
224
+ const handle = (request) => {
225
+ const url = new URL(request.url);
226
+ const prefix = `/api/v1/applets/${encodeURIComponent(options.appletCode)}`;
227
+ if (!url.pathname.startsWith(`${prefix}/`)) return void 0;
228
+ const path = url.pathname.slice(prefix.length);
229
+ const startMatch = /^\/workflows\/([^/]+)\/runs$/.exec(path);
230
+ if (request.method === "POST" && startMatch !== null) return (async () => {
231
+ let body;
232
+ try {
233
+ const text = await request.text();
234
+ body = text === "" ? {} : JSON.parse(text);
235
+ } catch {
236
+ return badRequest("The body must be a JSON object with a payload key.");
237
+ }
238
+ 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.");
239
+ let workflow;
240
+ try {
241
+ workflow = decodeURIComponent(startMatch[1]);
242
+ } catch (error) {
243
+ if (error instanceof URIError) return badRequest(error.message);
244
+ throw error;
245
+ }
246
+ const codeError = workflowCodeError(workflow);
247
+ if (codeError !== void 0) return codeError;
248
+ const run = start(workflow, "payload" in body ? body.payload : void 0);
249
+ return Response.json({ data: {
250
+ id: run.id,
251
+ workflow: run.workflow,
252
+ status: "in_progress"
253
+ } }, { status: 201 });
254
+ })();
255
+ const getMatch = /^\/workflows\/([^/]+)\/runs\/([^/]+)$/.exec(path);
256
+ if (request.method === "GET" && getMatch !== null) {
257
+ let workflow;
258
+ let id;
259
+ try {
260
+ workflow = decodeURIComponent(getMatch[1]);
261
+ id = decodeURIComponent(getMatch[2]);
262
+ } catch (error) {
263
+ if (error instanceof URIError) return Promise.resolve(badRequest(error.message));
264
+ throw error;
265
+ }
266
+ const codeError = workflowCodeError(workflow);
267
+ if (codeError !== void 0) return Promise.resolve(codeError);
268
+ if (!UUID_PATTERN.test(id)) return Promise.resolve(badRequest("The run id must be a uuid."));
269
+ const run = runs.get(id);
270
+ return Promise.resolve(run?.workflow === workflow ? Response.json({ data: run }) : Response.json({
271
+ code: "workflow_run_not_found",
272
+ message: "The workflow run does not exist."
273
+ }, { status: 404 }));
274
+ }
275
+ if (request.method === "GET" && path === "/workflow-runs") {
276
+ const status = url.searchParams.get("status");
277
+ const limitValue = url.searchParams.get("limit");
278
+ const limit = limitValue === null ? 50 : Number(limitValue);
279
+ if (status !== null && !RUN_STATUSES.some((value) => value === status)) return Promise.resolve(badRequest("Invalid workflow run status."));
280
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) return Promise.resolve(badRequest("The limit must be an integer from 1 to 100."));
281
+ const workflow = url.searchParams.get("workflow");
282
+ const codeError = workflow === null ? void 0 : workflowCodeError(workflow);
283
+ if (codeError !== void 0) return Promise.resolve(codeError);
284
+ 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);
285
+ return Promise.resolve(Response.json({ data }));
286
+ }
287
+ };
288
+ return {
289
+ handle,
290
+ stop: () => controller.abort()
291
+ };
292
+ }
293
+ //#endregion
294
+ //#region src/vite.ts
295
+ const APPLET_COMPATIBILITY_DATE = "2026-07-14";
296
+ const APPLET_COMPATIBILITY_FLAGS = ["nodejs_compat"];
297
+ let activeDevListener;
298
+ function clientBootstrapScript(parentOrigin, appletCode) {
299
+ return `(function () {
300
+ var parentOrigin = ${parentOrigin === void 0 ? "null" : JSON.stringify(parentOrigin)};
301
+ var appletCode = ${JSON.stringify(appletCode)};
302
+ var appletCodePattern = ${APPLET_CODE_PATTERN.toString()};
303
+
304
+ Object.defineProperty(window, "__hitchCacheContext", {
305
+ configurable: false,
306
+ writable: false,
307
+ value: window.parent !== window && parentOrigin !== null
308
+ ? fetch("/__applet/context/" + encodeURIComponent(appletCode), {
309
+ credentials: "same-origin",
310
+ headers: { accept: "application/json" }
311
+ }).then(function (response) {
312
+ if (!response.ok) throw new Error("Applet cache context returned " + response.status + ".");
313
+ return response.json();
314
+ }).catch(function (error) {
315
+ console.error("Could not load the applet cache context.", error);
316
+ return null;
317
+ })
318
+ : null
319
+ });
320
+
321
+ // Opened here so it is ready by the time the applet module restores its
322
+ // query cache. A failure is reported when the module reads the promise.
323
+ Object.defineProperty(window, "__hitchCacheDatabase", {
324
+ configurable: false,
325
+ writable: false,
326
+ value: window.parent !== window && parentOrigin !== null
327
+ ? (function () {
328
+ var database = (${openQueryCacheDatabase.toString()})();
329
+ database.catch(function () {});
330
+ return database;
331
+ })()
332
+ : null
333
+ });
334
+
335
+ function applyTheme(theme) {
336
+ document.documentElement.classList.toggle("dark", theme === "dark");
337
+ }
338
+
339
+ function reportLocation() {
340
+ if (window.parent === window || parentOrigin === null) return;
341
+ window.parent.postMessage(
342
+ {
343
+ type: "hitch:location",
344
+ href: window.location.pathname + window.location.search + window.location.hash
345
+ },
346
+ parentOrigin
347
+ );
348
+ }
349
+
350
+ var params = new URLSearchParams(window.location.search);
351
+ var theme = params.get("_hitch_theme");
352
+ var dark = theme === "dark" ||
353
+ (theme !== "light" && window.matchMedia("(prefers-color-scheme: dark)").matches);
354
+ applyTheme(dark ? "dark" : "light");
355
+ if (params.has("_hitch_theme")) {
356
+ var cleanUrl = new URL(window.location.href);
357
+ cleanUrl.searchParams.delete("_hitch_theme");
358
+ window.history.replaceState(window.history.state, "", cleanUrl);
359
+ }
360
+
361
+ var pushState = window.history.pushState;
362
+ window.history.pushState = function () {
363
+ var result = pushState.apply(window.history, arguments);
364
+ reportLocation();
365
+ return result;
366
+ };
367
+ var replaceState = window.history.replaceState;
368
+ window.history.replaceState = function () {
369
+ var result = replaceState.apply(window.history, arguments);
370
+ reportLocation();
371
+ return result;
372
+ };
373
+ window.addEventListener("popstate", reportLocation);
374
+ window.addEventListener("hashchange", reportLocation);
375
+
376
+ window.addEventListener("message", function (event) {
377
+ if (
378
+ event.source !== window.parent ||
379
+ (parentOrigin !== null && event.origin !== parentOrigin) ||
380
+ !event.data
381
+ ) return;
382
+
383
+ if (
384
+ parentOrigin !== null &&
385
+ event.data.type === "hitch:location-request"
386
+ ) {
387
+ reportLocation();
388
+ return;
389
+ }
390
+
391
+ if (
392
+ event.data.type === "hitch:theme" &&
393
+ (event.data.theme === "light" || event.data.theme === "dark")
394
+ ) {
395
+ applyTheme(event.data.theme);
396
+ return;
397
+ }
398
+
399
+ if (
400
+ parentOrigin !== null &&
401
+ event.data.type === "hitch:navigation-probe" &&
402
+ typeof event.data.requestId === "string"
403
+ ) {
404
+ window.parent.postMessage(
405
+ { type: "hitch:navigation-ready", requestId: event.data.requestId },
406
+ parentOrigin
407
+ );
408
+ return;
409
+ }
410
+
411
+ if (
412
+ parentOrigin === null ||
413
+ event.data.type !== "hitch:navigate" ||
414
+ typeof event.data.appletCode !== "string" ||
415
+ !appletCodePattern.test(event.data.appletCode) ||
416
+ (event.data.theme !== "light" && event.data.theme !== "dark")
417
+ ) return;
418
+
419
+ var nextHref = typeof event.data.href === "string"
420
+ ? event.data.href
421
+ : "/" + event.data.appletCode;
422
+ var nextUrl = new URL(nextHref, window.location.origin);
423
+ var nextAppletPath = "/" + event.data.appletCode;
424
+ if (
425
+ nextUrl.origin !== window.location.origin ||
426
+ (nextUrl.pathname !== nextAppletPath && !nextUrl.pathname.startsWith(nextAppletPath + "/"))
427
+ ) return;
428
+
429
+ applyTheme(event.data.theme);
430
+ if (
431
+ nextUrl.pathname + nextUrl.search + nextUrl.hash ===
432
+ window.location.pathname + window.location.search + window.location.hash
433
+ ) return;
434
+ nextUrl.searchParams.set("_hitch_theme", event.data.theme);
435
+ window.location.replace(nextUrl);
436
+ });
437
+ })();`;
438
+ }
439
+ function applet(sourceDefinition, options = {}) {
440
+ const parsedSourceDefinition = appletSourceDefinitionSchema.safeParse(sourceDefinition);
441
+ if (!parsedSourceDefinition.success) {
442
+ const raw = sourceDefinition;
443
+ const orgMessage = typeof raw === "object" && raw !== null && "org" in raw ? " org now lives in hitch.config.ts, not in applet.manifest.ts." : "";
444
+ throw new Error(`applet(): ${parsedSourceDefinition.error.message}${orgMessage}`);
445
+ }
446
+ const { dependencies: _dependencies, devDependencies: _devDependencies, ...definition } = parsedSourceDefinition.data;
447
+ const parsedDefinition = { data: definition };
448
+ let appletConfig;
449
+ let buildRootOutDir;
450
+ let clearedOutDir = false;
451
+ let contractSync;
452
+ let themeStylesheetUrl;
453
+ let devApiUrl;
454
+ let devListener;
455
+ let devLogger;
456
+ let devWorkflows;
457
+ let devContext;
458
+ const virtualRouteConfig = {
459
+ type: "root",
460
+ file: "__root.tsx",
461
+ children: [{
462
+ type: "physical",
463
+ directory: ".",
464
+ pathPrefix: parsedDefinition.data.code
465
+ }]
466
+ };
467
+ const resolveAppletConfig = (root) => {
468
+ const resolvedRoot = resolve(root);
469
+ if (appletConfig?.root === resolvedRoot) return appletConfig;
470
+ const directoryCode = basename(resolvedRoot);
471
+ if (directoryCode !== parsedDefinition.data.code) throw new Error(`Applet directory "${directoryCode}" does not match code "${parsedDefinition.data.code}". Rename the directory or change the code.`);
472
+ const org = resolveWorkspaceOrg(resolvedRoot);
473
+ const routesDirectory = resolve(resolvedRoot, "src/client/routes");
474
+ const hasClient = existsSync(routesDirectory);
475
+ const hasServer = existsSync(resolve(resolvedRoot, "src/server/main.ts"));
476
+ if (!hasServer && parsedDefinition.data.telephony?.events.length) throw new Error("An applet with telephony events must have src/server/main.ts.");
477
+ if (!hasServer && (parsedDefinition.data.schedules?.length ?? 0) > 0) throw new Error("An applet with schedules must have src/server/main.ts.");
478
+ const legacyMain = resolve(resolvedRoot, "src/client/main.tsx");
479
+ const indexHtml = resolve(resolvedRoot, "index.html");
480
+ if (hasClient && existsSync(legacyMain)) throw new Error(`Applet client entrypoint must use file-based routes. Move your component into src/client/routes/index.tsx: ${legacyMain}`);
481
+ if (hasClient && !existsSync(indexHtml)) throw new Error(`Missing index.html at ${indexHtml}. Create it with <div id="root"></div> and <script type="module" src="/.hitch/entry.gen.tsx"><\/script>.`);
482
+ appletConfig = {
483
+ root: resolvedRoot,
484
+ manifest: appletManifestSchema.parse({
485
+ org,
486
+ ...parsedDefinition.data
487
+ }),
488
+ routesDirectory,
489
+ hasClient,
490
+ hasAuthorRoot: hasClient && existsSync(resolve(routesDirectory, "__root.tsx")),
491
+ hasServer,
492
+ hasRouter: existsSync(resolve(resolvedRoot, "src/client/router.tsx"))
493
+ };
494
+ if (hasClient) virtualRouteConfig.file = appletConfig.hasAuthorRoot ? "__root.tsx" : relative(routesDirectory, resolve(resolvedRoot, ".hitch/appletRoot.gen.tsx"));
495
+ return appletConfig;
496
+ };
497
+ const requireAppletConfig = () => {
498
+ if (appletConfig === void 0) throw new Error("Vite did not provide an applet project root.");
499
+ return appletConfig;
500
+ };
501
+ let apiManifest;
502
+ const callerRoot = resolve(".");
503
+ if (basename(callerRoot) === parsedDefinition.data.code && basename(resolve(callerRoot, "..")) === "applets") apiManifest = appletManifestSchema.parse({
504
+ org: resolveWorkspaceOrg(callerRoot),
505
+ ...parsedDefinition.data
506
+ });
507
+ const orchestrator = {
508
+ name: "hitch-applet",
509
+ enforce: "pre",
510
+ sharedDuringBuild: true,
511
+ api: { get manifest() {
512
+ if (apiManifest === void 0) throw new Error("Applet manifest is not available before Vite resolves the project.");
513
+ return apiManifest;
514
+ } },
515
+ async config(config, environment) {
516
+ const { root, manifest, hasClient, hasAuthorRoot, hasServer, hasRouter } = resolveAppletConfig(config.root ?? resolve("."));
517
+ apiManifest = manifest;
518
+ buildRootOutDir = resolve(root, config.build?.outDir ?? "dist");
519
+ if (environment.command === "build" && !clearedOutDir) {
520
+ await rm(buildRootOutDir, {
521
+ force: true,
522
+ recursive: true
523
+ });
524
+ clearedOutDir = true;
525
+ }
526
+ if (environment.command === "build") themeStylesheetUrl = `${organizationInstanceUrl(await resolveInstanceUrl(void 0, options.store), manifest.org)}/theme.css`;
527
+ if (environment.command === "build" && manifest.objects.length > 0) {
528
+ contractSync ??= syncContract(resolve(root, ".hitch"), manifest, options);
529
+ await contractSync;
530
+ }
531
+ const hasHitchGen = existsSync(resolve(root, ".hitch/index.ts"));
532
+ await Promise.all([
533
+ writeManifestGen(resolve(root, ".hitch"), manifest),
534
+ hasClient ? writeClientEntry(root, {
535
+ appletCode: manifest.code,
536
+ hasHitchGen,
537
+ hasRouter
538
+ }) : void 0,
539
+ hasClient && !hasAuthorRoot ? writeAppletRoot(root) : void 0,
540
+ hasServer ? writeServerEntry(root, hasHitchGen, manifest.code) : void 0,
541
+ hasHitchGen ? void 0 : writeRegisterDeclaration(resolve(root, ".hitch"))
542
+ ]);
543
+ if (environment.command === "serve" && environment.mode !== "test" && environment.isPreview !== true) {
544
+ const instanceUrl = await resolveInstanceUrl(void 0, options.store);
545
+ const orgUrl = organizationInstanceUrl(instanceUrl, manifest.org);
546
+ const auth = await resolveAuthForOrg(instanceUrl, manifest.org, options.store);
547
+ if (auth === void 0) throw new Error("Not logged in. Run applet login.");
548
+ const token = bearerToken(auth);
549
+ const meResponse = await (options.fetch ?? globalThis.fetch)(`${orgUrl}/api/v1/me`, { headers: { authorization: `Bearer ${token}` } });
550
+ rejectEnvironmentKey(meResponse, auth);
551
+ if (meResponse.status === 401) throw new Error("Your login expired. Run applet login.");
552
+ if (!meResponse.ok) throw new Error(`Hitch returned ${meResponse.status} for /api/v1/me.`);
553
+ const { person } = meSchema.parse(await meResponse.json());
554
+ const proxy = createDevProxy({
555
+ orgUrl,
556
+ token,
557
+ source: auth.source,
558
+ fetch: options.fetch,
559
+ warn: (message) => devLogger?.warn(message),
560
+ intercept: (request) => devWorkflows?.handle(request)
561
+ });
562
+ devContext = {
563
+ person,
564
+ proxy,
565
+ orgUrl,
566
+ cacheBuster: randomUUID()
567
+ };
568
+ if (hasServer && devListener === void 0) {
569
+ if (activeDevListener?.listening) activeDevListener.close();
570
+ const nonce = randomUUID();
571
+ devListener = createServer((request, response) => {
572
+ const url = request.url ?? "";
573
+ const prefix = `/${nonce}/api`;
574
+ const address = devListener?.address();
575
+ 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}`) {
576
+ response.writeHead(404).end();
577
+ return;
578
+ }
579
+ request.url = url.slice(nonce.length + 1);
580
+ serveDevProxy(request, response, proxy).catch((error) => {
581
+ const failure = error instanceof Error ? error : new Error(String(error));
582
+ devLogger?.error(`Local Hitch proxy failed: ${failure.message}`);
583
+ response.destroy(failure);
584
+ });
585
+ });
586
+ activeDevListener = devListener;
587
+ try {
588
+ await new Promise((resolvePromise, rejectPromise) => {
589
+ devListener?.once("error", rejectPromise);
590
+ devListener?.listen(0, "127.0.0.1", resolvePromise);
591
+ });
592
+ const address = devListener.address();
593
+ if (address === null || typeof address === "string") throw new Error("The local Hitch proxy has no port.");
594
+ devApiUrl = `http://127.0.0.1:${address.port}/${nonce}/api`;
595
+ } catch (error) {
596
+ if (devListener.listening) devListener.close();
597
+ if (activeDevListener === devListener) activeDevListener = void 0;
598
+ devListener = void 0;
599
+ throw error;
600
+ }
601
+ }
602
+ }
603
+ if (environment.mode === "test") {
604
+ const colocated = (await readdir(resolve(root, "src"), { recursive: true })).filter((path) => /\.(test|spec)\.[cm]?[jt]sx?$/.test(path));
605
+ if (colocated.length > 0) throw new Error(`Applet tests run only from the test folder. Move ${colocated.map((path) => `src/${path}`).join(", ")} to test/.`);
606
+ }
607
+ return {
608
+ base: `/${manifest.code}/`,
609
+ test: { include: ["test/**/*.test.{ts,tsx}"] },
610
+ resolve: { alias: {
611
+ clsx: "cn",
612
+ "tailwind-merge": "cn"
613
+ } },
614
+ ...hasServer && { environments: { worker: { build: { rollupOptions: { output: { codeSplitting: false } } } } } }
615
+ };
616
+ },
617
+ configResolved(config) {
618
+ apiManifest = resolveAppletConfig(config.root).manifest;
619
+ },
620
+ configureServer(server) {
621
+ if (devContext === void 0) return;
622
+ const { manifest } = requireAppletConfig();
623
+ const { person, proxy, orgUrl, cacheBuster } = devContext;
624
+ const orgOrigin = new URL(orgUrl).origin;
625
+ devLogger = server.config.logger;
626
+ const skipSleep = process.env.HITCH_DEV_SKIP_SLEEP === "1";
627
+ devWorkflows = createDevWorkflows({
628
+ appletCode: manifest.code,
629
+ skipSleep,
630
+ logger: server.config.logger,
631
+ invoke: async (workflow, body) => {
632
+ const address = server.httpServer?.address();
633
+ if (address === null || address === void 0 || typeof address === "string") throw new Error("The dev server has no port.");
634
+ const response = await fetch(`http://localhost:${address.port}/${manifest.code}/api/__hitch/workflows/${encodeURIComponent(workflow)}`, {
635
+ method: "POST",
636
+ headers: { "content-type": "application/json" },
637
+ body: JSON.stringify(body)
638
+ });
639
+ if (!response.ok) throw new Error(`Workflow worker returned HTTP ${response.status}.`);
640
+ return workflowOutcomeSchema.parse(await response.json());
641
+ }
642
+ });
643
+ server.config.logger.info(`ℹ Running as ${person.email} in production org "${manifest.org}". Reads and writes use real data with your access.`);
644
+ 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.`);
645
+ if (skipSleep) server.config.logger.info("ℹ HITCH_DEV_SKIP_SLEEP is set. Workflow sleeps and retry delays run at once.");
646
+ const devServerFile = resolve(requireAppletConfig().root, ".hitch/dev-server.json");
647
+ let devServerUrl;
648
+ const removeDevServerFile = () => {
649
+ if (devServerUrl === void 0) return;
650
+ try {
651
+ const file = JSON.parse(readFileSync(devServerFile, "utf8"));
652
+ if (file !== null && typeof file === "object" && "pid" in file && file.pid === process.pid && "url" in file && file.url === devServerUrl) rmSync(devServerFile, { force: true });
653
+ } catch (error) {
654
+ if (!(error instanceof SyntaxError) && !(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
655
+ }
656
+ };
657
+ server.httpServer?.once("listening", () => {
658
+ const address = server.httpServer?.address();
659
+ if (address === null || address === void 0 || typeof address === "string") return;
660
+ mkdirSync(dirname(devServerFile), { recursive: true });
661
+ devServerUrl = `http://localhost:${address.port}`;
662
+ writeFileSync(devServerFile, JSON.stringify({
663
+ url: devServerUrl,
664
+ pid: process.pid
665
+ }));
666
+ server.config.logger.info(`ℹ Preview in Hitch: ${orgUrl}/applets/${manifest.code}?_hitch_dev=${address.port}`);
667
+ process.once("exit", removeDevServerFile);
668
+ });
669
+ server.httpServer?.once("close", () => {
670
+ devWorkflows?.stop();
671
+ devListener?.close();
672
+ if (activeDevListener === devListener) activeDevListener = void 0;
673
+ process.off("exit", removeDevServerFile);
674
+ removeDevServerFile();
675
+ });
676
+ const mount = `/${manifest.code}`;
677
+ const apiPrefix = `${mount}/api`;
678
+ server.middlewares.use((request, response, next) => {
679
+ if (request.url === mount || request.url?.startsWith(`${mount}?`)) request.url = `${mount}/${request.url.slice(mount.length)}`;
680
+ const url = request.url ?? "";
681
+ const pathname = url.split("?", 1)[0] ?? "";
682
+ if (pathname.startsWith("/__applet/context/")) {
683
+ if (pathname !== `/__applet/context/${manifest.code}` || request.method !== "GET") {
684
+ response.writeHead(404).end();
685
+ return;
686
+ }
687
+ if (refusesClient(request.headers, request.socket.remoteAddress)) {
688
+ response.writeHead(403).end();
689
+ return;
690
+ }
691
+ response.setHeader("Cache-Control", "no-store");
692
+ response.setHeader("Content-Type", "application/json; charset=utf-8");
693
+ response.end(JSON.stringify({
694
+ cacheBuster,
695
+ cacheScope: person.id
696
+ }));
697
+ return;
698
+ }
699
+ if (url === "/_hitch/client.js" || url.startsWith("/_hitch/client.js?")) {
700
+ response.statusCode = 200;
701
+ response.setHeader("Cache-Control", "no-store");
702
+ response.setHeader("Content-Type", "application/javascript; charset=utf-8");
703
+ response.end("// PostHog is disabled in standalone applet development.\n");
704
+ return;
705
+ }
706
+ if (url === "/api" || url.startsWith("/api/") || url.startsWith("/api?") || url === "/theme.css" || url.startsWith("/theme.css?")) {
707
+ serveDevProxy(request, response, proxy).catch((error) => {
708
+ if (response.headersSent) response.destroy(error instanceof Error ? error : new Error(String(error)));
709
+ else next(error);
710
+ });
711
+ return;
712
+ }
713
+ if (url === apiPrefix || url.startsWith(`${apiPrefix}/`) || url.startsWith(`${apiPrefix}?`)) {
714
+ if (refusesClient(request.headers, request.socket.remoteAddress)) {
715
+ response.writeHead(403).end();
716
+ return;
717
+ }
718
+ request.url = url.slice(mount.length);
719
+ if (request.url.startsWith("/api/__hitch/") && request.headers.origin !== void 0) {
720
+ response.writeHead(404).end();
721
+ return;
722
+ }
723
+ request.originalUrl = request.url;
724
+ request.headers[HITCH_USER_HEADER] = person.id;
725
+ request.headers[HITCH_SESSION_HEADER] = "local-dev";
726
+ 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] ?? ""] : []);
727
+ request.rawHeaders.push(HITCH_USER_HEADER, person.id, HITCH_SESSION_HEADER, "local-dev");
728
+ }
729
+ response.setHeader("Content-Security-Policy", `frame-ancestors ${orgOrigin}`);
730
+ next();
731
+ });
732
+ },
733
+ transformIndexHtml: {
734
+ order: "post",
735
+ handler(html) {
736
+ const parentOrigin = devContext?.orgUrl === void 0 ? themeStylesheetUrl === void 0 ? void 0 : new URL(themeStylesheetUrl).origin : new URL(devContext.orgUrl).origin;
737
+ return {
738
+ html: themeStylesheetUrl === void 0 ? html : html.replace("href=\"/theme.css\"", `href="${themeStylesheetUrl}"`),
739
+ tags: [{
740
+ tag: "script",
741
+ attrs: {
742
+ async: true,
743
+ "vite-ignore": true,
744
+ src: "/_hitch/client.js"
745
+ },
746
+ injectTo: "head-prepend"
747
+ }, {
748
+ tag: "script",
749
+ children: clientBootstrapScript(parentOrigin, requireAppletConfig().manifest.code),
750
+ injectTo: "head-prepend"
751
+ }]
752
+ };
753
+ }
754
+ },
755
+ writeBundle: {
756
+ order: "post",
757
+ async handler(outputOptions, bundle) {
758
+ if (buildRootOutDir === void 0) throw new Error("Applet build output is not configured.");
759
+ if (this.environment.name === "client") {
760
+ await writeFile(resolve(buildRootOutDir, "manifest.json"), `${JSON.stringify(requireAppletConfig().manifest, null, 2)}\n`);
761
+ if (!requireAppletConfig().hasServer) await appendFile(resolve(buildRootOutDir, ".assetsignore"), "manifest.json\n");
762
+ console.log(`client → ${relative(requireAppletConfig().root, outputOptions.dir ?? buildRootOutDir)}/`);
763
+ return;
764
+ }
765
+ if (!requireAppletConfig().hasServer || this.environment.name !== "worker") return;
766
+ if (!requireAppletConfig().hasClient) await writeFile(resolve(buildRootOutDir, "manifest.json"), `${JSON.stringify(requireAppletConfig().manifest, null, 2)}\n`);
767
+ const workerDir = outputOptions.dir ?? resolve(buildRootOutDir, "worker");
768
+ const mainModule = Object.values(bundle).find((output) => output.type === "chunk" && output.isEntry)?.fileName;
769
+ const strayModules = (await readdir(workerDir, { recursive: true })).filter((path) => /\.(js|mjs)$/.test(path) && path !== mainModule);
770
+ if (strayModules.length > 0) throw new Error(`The applet server must bundle to a single worker/index.js module; found: ${strayModules.join(", ")}.`);
771
+ console.log(`server → ${relative(requireAppletConfig().root, resolve(workerDir, mainModule ?? ""))}`);
772
+ }
773
+ }
774
+ };
775
+ const cloudflarePlugins = cloudflare({
776
+ get assetsOnly() {
777
+ return !requireAppletConfig().hasServer;
778
+ },
779
+ config: (workerConfig) => {
780
+ const { root, manifest, hasClient, hasServer } = requireAppletConfig();
781
+ return {
782
+ ...workerConfig,
783
+ name: manifest.code.replaceAll("_", "-"),
784
+ compatibility_date: APPLET_COMPATIBILITY_DATE,
785
+ compatibility_flags: APPLET_COMPATIBILITY_FLAGS,
786
+ ...hasClient ? { assets: {
787
+ not_found_handling: "single-page-application",
788
+ run_worker_first: ["/api/*"]
789
+ } } : {},
790
+ ...hasServer ? { main: resolve(root, ".hitch/entry.server.gen.ts") } : {},
791
+ ...devApiUrl === void 0 ? {} : { vars: {
792
+ ...workerConfig.vars,
793
+ HITCH_API_URL: devApiUrl
794
+ } }
795
+ };
796
+ },
797
+ get viteEnvironment() {
798
+ return requireAppletConfig().hasServer ? { name: "worker" } : void 0;
799
+ }
800
+ }).map((plugin) => ({
801
+ ...plugin,
802
+ apply: ((_config, { mode }) => mode !== "test")
803
+ }));
804
+ return [
805
+ orchestrator,
806
+ ...existsSync(resolve("src/client/routes")) ? [tanstackRouter({
807
+ generatedRouteTree: ".hitch/routeTree.gen.ts",
808
+ routesDirectory: "src/client/routes",
809
+ routeFileIgnorePattern: "__root",
810
+ virtualRouteConfig
811
+ }), react({ compiler: true })] : [],
812
+ cloudflarePlugins
813
+ ];
814
+ }
815
+ //#endregion
816
+ export { applet };