@hitch42/applet 0.1.0-beta.9 → 0.1.1-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 (164) 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.js +112 -57
  5. package/dist/applet.js +12 -12
  6. package/dist/build-Bh_S5BUv.js +2 -0
  7. package/dist/{build-CAb26kMY.js → build-CEfFoIZZ.js} +1 -1
  8. package/dist/{ci-5qWl7lT3.js → ci-BKw14X2h.js} +14 -13
  9. package/dist/client.d.ts +2 -2
  10. package/dist/{contract-rP_eV9PK.d.ts → contract-CsUpTBe0.d.ts} +1 -1
  11. package/dist/{deploy-D_OFJ6-Q.js → deploy-wGYjwXrx.js} +139 -88
  12. package/dist/{generate-CBPnHoqX.js → generate-BEIYs17x.js} +54 -22
  13. package/dist/generate-Dox-OYt6.js +2 -0
  14. package/dist/headers-DkNr9MLJ.js +17 -0
  15. package/dist/index.d.ts +3 -2
  16. package/dist/index.js +2 -1
  17. package/dist/{init-tfzwuYnk.js → init-6oGPM46R.js} +10 -15
  18. package/dist/{load-config-BYSxDVAl.js → load-config-xEdTXs0y.js} +2 -2
  19. package/dist/log-context-zu8U9Sjm.js +73 -0
  20. package/dist/{login-C7RKmmTe.js → login-CUx3TmFQ.js} +1 -1
  21. package/dist/{logs-DgjgAxj2.js → logs-DSjuEN0Q.js} +20 -22
  22. package/dist/{manifest-DD_SGkS6.d.ts → manifest-C6_F4bL8.d.ts} +5 -1
  23. package/dist/{manifest-Bp87Uz0a.js → manifest-CJSL9GuC.js} +20 -4
  24. package/dist/{project-D6XHllmC.js → project-BRZGd6xq.js} +197 -16
  25. package/dist/query-cache-CXmAyLv3.js +82 -0
  26. package/dist/react.d.ts +2 -1
  27. package/dist/react.js +59 -39
  28. package/dist/{schedules-dhU1yyFr.js → schedules-C8_Svr4h.js} +1 -1
  29. package/dist/server.d.ts +8 -3
  30. package/dist/server.js +29 -8
  31. package/dist/telephony.d.ts +24 -4
  32. package/dist/telephony.js +135 -16
  33. package/dist/{variables-Bwnr7oxf.js → variables-DT5RyHdH.js} +13 -13
  34. package/dist/vite.d.ts +3 -60
  35. package/dist/vite.js +50 -15
  36. package/dist/{workflow-engine-MogejNfF.js → workflow-engine-BOTzURKM.js} +1 -7
  37. package/dist/{workspace-XVtM12ZC.js → workspace-D0dnnlxL.js} +33 -38
  38. package/package.json +13 -6
  39. package/scaffold/.agents/skills/hitch-applets/SKILL.md +29 -3
  40. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +8 -0
  41. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
  42. package/scaffold/.agents/skills/hitch-applets/references/records.md +2 -0
  43. package/scaffold/.agents/skills/hitch-data/SKILL.md +2 -1
  44. package/scaffold/.agents/skills/hitch-data/references/attributes.md +7 -1
  45. package/scaffold/.agents/skills/hitch-data/references/limits.md +13 -13
  46. package/scaffold/.agents/skills/hitch-theory/SKILL.md +3 -2
  47. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  48. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  49. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  50. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  51. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  52. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  53. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  54. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  55. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  56. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  57. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  58. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  59. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  60. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  61. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  62. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  63. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  64. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  65. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  66. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  103. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  104. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  105. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  106. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  107. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  108. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  109. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  110. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  111. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  112. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  113. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  114. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  115. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  116. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  117. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  118. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  119. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  120. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  121. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  122. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  133. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  134. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  135. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  136. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  137. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  138. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  139. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  140. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  141. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  142. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  143. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  144. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  145. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  146. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  147. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  148. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  149. package/scaffold/.github/workflows/hitch.yml +1 -1
  150. package/scaffold/AGENTS.md +2 -2
  151. package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
  152. package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
  153. package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
  154. package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
  155. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
  156. package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
  157. package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
  158. package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
  159. package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
  160. package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
  161. package/scaffold/applet/src/client/lib/utils.ts +1 -6
  162. package/scaffold/applet/src/client/styles/globals.css +2 -0
  163. package/dist/build-BfbTbt_r.js +0 -2
  164. package/dist/generate-BgepN4-b.js +0 -2
@@ -1,5 +1,5 @@
1
- import { n as fetchWithOrgAuth } from "./workspace-XVtM12ZC.js";
2
- import { t as loadAppletConfig } from "./load-config-BYSxDVAl.js";
1
+ import { n as fetchWithOrgAuth } from "./workspace-D0dnnlxL.js";
2
+ import { t as loadAppletConfig } from "./load-config-xEdTXs0y.js";
3
3
  import { t as question } from "./prompt-ChS-grYO.js";
4
4
  import { z } from "zod";
5
5
  //#region src/variables.ts
@@ -14,12 +14,12 @@ const variableResponseSchema = z.object({ data: variableRowSchema });
14
14
  const variablesResponseSchema = z.object({ data: z.array(variableRowSchema) });
15
15
  async function getValue(options) {
16
16
  if (options.value !== void 0) return options.value;
17
- if (!(options.stdinIsTTY ?? process.stdin.isTTY === true)) throw new Error("A value is required when stdin is not a TTY.");
17
+ if (process.stdin.isTTY !== true) throw new Error("A value is required when stdin is not a TTY.");
18
18
  return question("Value: ", true);
19
19
  }
20
20
  async function confirmLiveChange(code, options) {
21
21
  if (options.yes === true) return;
22
- if (!(options.stdinIsTTY ?? process.stdin.isTTY === true)) throw new Error("Confirmation requires a TTY. Pass -y to continue.");
22
+ if (process.stdin.isTTY !== true) throw new Error("Confirmation requires a TTY. Pass -y to continue.");
23
23
  const answer = await question(`This modifies the live applet "${code}". Continue? [y/N] `);
24
24
  if (!["y", "yes"].includes(answer.trim().toLowerCase())) throw new Error("Variable change cancelled.");
25
25
  }
@@ -45,19 +45,19 @@ function variablesError(status, code, message) {
45
45
  const details = [code, message].filter((value) => value !== void 0).join(": ");
46
46
  return /* @__PURE__ */ new Error(`Variables request failed (${status})${details ? `: ${details}` : ""}.`);
47
47
  }
48
- async function requestForApplet(options, request) {
48
+ async function requestForApplet(request) {
49
49
  const { code, org } = await loadAppletConfig(process.cwd());
50
50
  return {
51
51
  code,
52
- response: await fetchWithOrgAuth(org, options, (instanceUrl, accessToken) => request(instanceUrl, accessToken, code))
52
+ response: await fetchWithOrgAuth(org, (instanceUrl, accessToken) => request(instanceUrl, accessToken, code))
53
53
  };
54
54
  }
55
55
  async function runVariablesSet(options) {
56
56
  const value = await getValue(options);
57
57
  const manifest = await loadAppletConfig(process.cwd());
58
58
  await confirmLiveChange(manifest.code, options);
59
- const fetcher = options.fetch ?? globalThis.fetch;
60
- const response = await fetchWithOrgAuth(manifest.org, options, (instanceUrl, accessToken) => fetcher(`${instanceUrl}/api/v1/applets/${encodeURIComponent(manifest.code)}/variables/${encodeURIComponent(options.key)}`, {
59
+ const fetcher = globalThis.fetch;
60
+ const response = await fetchWithOrgAuth(manifest.org, (instanceUrl, accessToken) => fetcher(`${instanceUrl}/api/v1/applets/${encodeURIComponent(manifest.code)}/variables/${encodeURIComponent(options.key)}`, {
61
61
  method: "PUT",
62
62
  headers: {
63
63
  Authorization: `Bearer ${accessToken}`,
@@ -75,9 +75,9 @@ async function runVariablesSet(options) {
75
75
  const { data } = variableResponseSchema.parse(await response.json());
76
76
  console.log(`Set ${data.key} ${data.secret ? "(secret)" : data.value ?? value} for applet "${manifest.code}".`);
77
77
  }
78
- async function runVariablesList(options) {
79
- const fetcher = options.fetch ?? globalThis.fetch;
80
- const { code, response } = await requestForApplet(options, (instanceUrl, accessToken, appletCode) => fetcher(`${instanceUrl}/api/v1/applets/${encodeURIComponent(appletCode)}/variables`, { headers: { Authorization: `Bearer ${accessToken}` } }));
78
+ async function runVariablesList() {
79
+ const fetcher = globalThis.fetch;
80
+ const { code, response } = await requestForApplet((instanceUrl, accessToken, appletCode) => fetcher(`${instanceUrl}/api/v1/applets/${encodeURIComponent(appletCode)}/variables`, { headers: { Authorization: `Bearer ${accessToken}` } }));
81
81
  if (!response.ok) {
82
82
  const { code: errorCode, message } = await readErrorDetails(response);
83
83
  throw variablesError(response.status, errorCode, message);
@@ -92,8 +92,8 @@ async function runVariablesList(options) {
92
92
  async function runVariablesDelete(options) {
93
93
  const manifest = await loadAppletConfig(process.cwd());
94
94
  await confirmLiveChange(manifest.code, options);
95
- const fetcher = options.fetch ?? globalThis.fetch;
96
- const response = await fetchWithOrgAuth(manifest.org, options, (instanceUrl, accessToken) => fetcher(`${instanceUrl}/api/v1/applets/${encodeURIComponent(manifest.code)}/variables/${encodeURIComponent(options.key)}`, {
95
+ const fetcher = globalThis.fetch;
96
+ const response = await fetchWithOrgAuth(manifest.org, (instanceUrl, accessToken) => fetcher(`${instanceUrl}/api/v1/applets/${encodeURIComponent(manifest.code)}/variables/${encodeURIComponent(options.key)}`, {
97
97
  method: "DELETE",
98
98
  headers: { Authorization: `Bearer ${accessToken}` }
99
99
  }));
package/dist/vite.d.ts CHANGED
@@ -1,63 +1,6 @@
1
- import { o as RegisteredAppletSourceDefinition } from "./manifest-DD_SGkS6.js";
1
+ import { o as RegisteredAppletSourceDefinition } from "./manifest-C6_F4bL8.js";
2
2
  import { PluginOption } from "vite";
3
- //#region ../shared/auth/src/store.d.mts
4
- interface SessionCredentials {
5
- type: "session";
6
- accessToken: string;
7
- expiresAt?: number;
8
- organizationId: string;
9
- orgCode: string;
10
- }
11
- interface StoreOptions {
12
- env?: NodeJS.ProcessEnv;
13
- homeDir?: string;
14
- }
15
- //#endregion
16
- //#region ../shared/auth/src/device.d.mts
17
- interface MeResponse {
18
- memberships: Array<{
19
- code: string;
20
- name: string;
21
- orgId: string;
22
- }>;
23
- organization: {
24
- code: string;
25
- id: string;
26
- } | null;
27
- person: {
28
- email: string;
29
- firstName: string;
30
- id: string;
31
- lastName: string;
32
- };
33
- }
34
- interface DeviceLoginOptions {
35
- expectedOrg?: string;
36
- fetch?: typeof fetch;
37
- noBrowser?: boolean;
38
- now?: () => number;
39
- openUrl?: (url: string) => void | Promise<void>;
40
- print?: (message: string) => void;
41
- sleep?: (milliseconds: number) => Promise<void>;
42
- store?: StoreOptions;
43
- }
44
- interface DeviceLoginResult {
45
- credentials: SessionCredentials;
46
- me: MeResponse;
47
- membership: MeResponse["memberships"][number];
48
- }
49
- declare function loginWithDevice(instanceUrl: string, options?: DeviceLoginOptions): Promise<DeviceLoginResult>;
50
- //#endregion
51
- //#region src/auth.d.ts
52
- interface OrgAuthOptions {
53
- fetch?: typeof fetch;
54
- login?: typeof loginWithDevice;
55
- stdinIsTTY?: boolean;
56
- store?: StoreOptions;
57
- }
58
- //#endregion
59
3
  //#region src/vite.d.ts
60
- type AppletOptions = OrgAuthOptions;
61
- declare function applet<const M extends RegisteredAppletSourceDefinition>(sourceDefinition: M, options?: AppletOptions): PluginOption[];
4
+ declare function applet<const M extends RegisteredAppletSourceDefinition>(sourceDefinition: M): PluginOption[];
62
5
  //#endregion
63
- export { AppletOptions, applet };
6
+ export { applet };
package/dist/vite.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { n as meSchema } from "./me-BW7Paary.js";
2
- import { i as APPLET_CODE_PATTERN, n as appletManifestSchema, r as appletSourceDefinitionSchema } from "./manifest-Bp87Uz0a.js";
3
- import { a as workflowOutcomeSchema, c as HITCH_SESSION_HEADER, l as HITCH_USER_HEADER, n as runWorkflowJournal } from "./workflow-engine-MogejNfF.js";
4
- import { c as organizationInstanceUrl, d as resolveInstanceUrl, l as rejectEnvironmentKey, s as bearerToken, t as resolveWorkspaceOrg, u as resolveAuthForOrg } from "./workspace-XVtM12ZC.js";
5
- import { c as writeClientEntry, i as writeRegisterDeclaration, l as writeManifestGen, r as syncContract, s as writeAppletRoot, u as writeServerEntry } from "./generate-CBPnHoqX.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";
6
8
  import { randomUUID } from "node:crypto";
7
9
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
8
10
  import { appendFile, readdir, rm, writeFile } from "node:fs/promises";
@@ -52,7 +54,7 @@ function createDevProxy(options) {
52
54
  [...headers.keys()].filter((name) => REQUEST_DROP.has(name) || name.startsWith("x-hitch-")).forEach((name) => headers.delete(name));
53
55
  headers.set("authorization", `Bearer ${options.token}`);
54
56
  const url = new URL(request.url);
55
- const upstream = await (options.fetch ?? globalThis.fetch)(`${options.orgUrl}${url.pathname}${url.search}`, {
57
+ const upstream = await globalThis.fetch(`${options.orgUrl}${url.pathname}${url.search}`, {
56
58
  method: request.method,
57
59
  headers,
58
60
  body: request.body,
@@ -317,6 +319,20 @@ function clientBootstrapScript(parentOrigin, appletCode) {
317
319
  : null
318
320
  });
319
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
+
320
336
  function applyTheme(theme) {
321
337
  document.documentElement.classList.toggle("dark", theme === "dark");
322
338
  }
@@ -421,7 +437,7 @@ function clientBootstrapScript(parentOrigin, appletCode) {
421
437
  });
422
438
  })();`;
423
439
  }
424
- function applet(sourceDefinition, options = {}) {
440
+ function applet(sourceDefinition) {
425
441
  const parsedSourceDefinition = appletSourceDefinitionSchema.safeParse(sourceDefinition);
426
442
  if (!parsedSourceDefinition.success) {
427
443
  const raw = sourceDefinition;
@@ -458,7 +474,7 @@ function applet(sourceDefinition, options = {}) {
458
474
  const routesDirectory = resolve(resolvedRoot, "src/client/routes");
459
475
  const hasClient = existsSync(routesDirectory);
460
476
  const hasServer = existsSync(resolve(resolvedRoot, "src/server/main.ts"));
461
- 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.telephony?.events?.length) throw new Error("An applet with telephony events must have src/server/main.ts.");
462
478
  if (!hasServer && (parsedDefinition.data.schedules?.length ?? 0) > 0) throw new Error("An applet with schedules must have src/server/main.ts.");
463
479
  const legacyMain = resolve(resolvedRoot, "src/client/main.tsx");
464
480
  const indexHtml = resolve(resolvedRoot, "index.html");
@@ -489,6 +505,11 @@ function applet(sourceDefinition, options = {}) {
489
505
  org: resolveWorkspaceOrg(callerRoot),
490
506
  ...parsedDefinition.data
491
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
+ };
492
513
  const orchestrator = {
493
514
  name: "hitch-applet",
494
515
  enforce: "pre",
@@ -508,9 +529,9 @@ function applet(sourceDefinition, options = {}) {
508
529
  });
509
530
  clearedOutDir = true;
510
531
  }
511
- if (environment.command === "build") themeStylesheetUrl = `${organizationInstanceUrl(await resolveInstanceUrl(void 0, options.store), manifest.org)}/theme.css`;
512
- if (environment.command === "build" && manifest.objects.length > 0) {
513
- 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);
514
535
  await contractSync;
515
536
  }
516
537
  const hasHitchGen = existsSync(resolve(root, ".hitch/index.ts"));
@@ -526,12 +547,12 @@ function applet(sourceDefinition, options = {}) {
526
547
  hasHitchGen ? void 0 : writeRegisterDeclaration(resolve(root, ".hitch"))
527
548
  ]);
528
549
  if (environment.command === "serve" && environment.mode !== "test" && environment.isPreview !== true) {
529
- const instanceUrl = await resolveInstanceUrl(void 0, options.store);
550
+ const instanceUrl = await resolveInstanceUrl();
530
551
  const orgUrl = organizationInstanceUrl(instanceUrl, manifest.org);
531
- const auth = await resolveAuthForOrg(instanceUrl, manifest.org, options.store);
552
+ const auth = await resolveAuthForOrg(instanceUrl, manifest.org);
532
553
  if (auth === void 0) throw new Error("Not logged in. Run applet login.");
533
554
  const token = bearerToken(auth);
534
- const meResponse = await (options.fetch ?? globalThis.fetch)(`${orgUrl}/api/v1/me`, { headers: { authorization: `Bearer ${token}` } });
555
+ const meResponse = await globalThis.fetch(`${orgUrl}/api/v1/me`, { headers: { authorization: `Bearer ${token}` } });
535
556
  rejectEnvironmentKey(meResponse, auth);
536
557
  if (meResponse.status === 401) throw new Error("Your login expired. Run applet login.");
537
558
  if (!meResponse.ok) throw new Error(`Hitch returned ${meResponse.status} for /api/v1/me.`);
@@ -540,7 +561,6 @@ function applet(sourceDefinition, options = {}) {
540
561
  orgUrl,
541
562
  token,
542
563
  source: auth.source,
543
- fetch: options.fetch,
544
564
  warn: (message) => devLogger?.warn(message),
545
565
  intercept: (request) => devWorkflows?.handle(request)
546
566
  });
@@ -585,8 +605,18 @@ function applet(sourceDefinition, options = {}) {
585
605
  }
586
606
  }
587
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
+ }
588
612
  return {
589
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] } },
590
620
  ...hasServer && { environments: { worker: { build: { rollupOptions: { output: { codeSplitting: false } } } } } }
591
621
  };
592
622
  },
@@ -777,9 +807,14 @@ function applet(sourceDefinition, options = {}) {
777
807
  ...plugin,
778
808
  apply: ((_config, { mode }) => mode !== "test")
779
809
  }));
810
+ const hasClientSource = existsSync(resolve("src/client/routes"));
780
811
  return [
781
812
  orchestrator,
782
- ...existsSync(resolve("src/client/routes")) ? [tanstackRouter({
813
+ {
814
+ ...clsxFromCn,
815
+ enforce: "pre"
816
+ },
817
+ ...hasClientSource ? [tanstackRouter({
783
818
  generatedRouteTree: ".hitch/routeTree.gen.ts",
784
819
  routesDirectory: "src/client/routes",
785
820
  routeFileIgnorePattern: "__root",
@@ -1,10 +1,4 @@
1
1
  import { z } from "zod";
2
- //#region src/headers.ts
3
- const HITCH_USER_HEADER = "x-hitch-user";
4
- const HITCH_SESSION_HEADER = "x-hitch-session";
5
- const HITCH_APPLET_HEADER = "x-hitch-applet";
6
- const HITCH_APPLET_TOKEN_HEADER = "x-hitch-applet-token";
7
- //#endregion
8
2
  //#region src/server/workflow-protocol.ts
9
3
  const journalEntrySchema = z.discriminatedUnion("kind", [
10
4
  z.object({
@@ -114,4 +108,4 @@ async function runWorkflowJournal(engine, invoke) {
114
108
  }
115
109
  }
116
110
  //#endregion
117
- export { workflowOutcomeSchema as a, HITCH_SESSION_HEADER as c, workflowInvocationRequestSchema as i, HITCH_USER_HEADER as l, runWorkflowJournal as n, HITCH_APPLET_HEADER as o, journalEntrySchema as r, HITCH_APPLET_TOKEN_HEADER as s, WorkflowFailedError as t };
111
+ export { workflowOutcomeSchema as a, workflowInvocationRequestSchema as i, runWorkflowJournal as n, journalEntrySchema as r, WorkflowFailedError as t };
@@ -45,8 +45,8 @@ async function healCredentialStore(credentialsPath, source, store, unrecognizedC
45
45
  console.error(unrecognizedCount === void 0 ? `hitch: reset unreadable credentials file (backup: ${backupPath}).` : unrecognizedCount === 0 ? `hitch: migrated credentials to per-organization storage (backup: ${backupPath}).` : `hitch: removed ${unrecognizedCount} unrecognized credential ${unrecognizedCount === 1 ? "entry" : "entries"} (backup: ${backupPath}).`);
46
46
  return store;
47
47
  }
48
- async function readRawCredentialStore(options = {}) {
49
- const { credentialsPath } = storePaths(options);
48
+ async function readRawCredentialStore() {
49
+ const { credentialsPath } = storePaths();
50
50
  let source;
51
51
  try {
52
52
  source = await readFile(credentialsPath, "utf8");
@@ -113,9 +113,9 @@ function organizationInstanceUrl(instanceUrl, orgCode) {
113
113
  url.hostname = [orgCode, ...labels.slice(1)].join(".");
114
114
  return url.toString().replace(/\/$/, "");
115
115
  }
116
- function storePaths(options = {}) {
117
- const env = options.env ?? process.env;
118
- const home = options.homeDir ?? homedir();
116
+ function storePaths() {
117
+ const env = process.env;
118
+ const home = homedir();
119
119
  const configDir = join(env.XDG_CONFIG_HOME || join(home, ".config"), "hitch");
120
120
  return {
121
121
  configDir,
@@ -124,28 +124,28 @@ function storePaths(options = {}) {
124
124
  credentialsPath: join(configDir, "credentials.json")
125
125
  };
126
126
  }
127
- async function resolveInstanceUrl(override, options = {}) {
127
+ async function resolveInstanceUrl(override) {
128
128
  if (override !== void 0) return normalizeInstanceUrl(override);
129
- const envInstanceUrl = (options.env ?? process.env).HITCH_API_URL;
129
+ const envInstanceUrl = process.env.HITCH_API_URL;
130
130
  if (envInstanceUrl !== void 0 && envInstanceUrl.length > 0) return normalizeInstanceUrl(envInstanceUrl);
131
- const { configPath } = storePaths(options);
131
+ const { configPath } = storePaths();
132
132
  const config = await readJson(configPath);
133
133
  if (config === void 0) return DEFAULT_INSTANCE_URL;
134
134
  if (!isObject(config) || config.instanceUrl !== void 0 && typeof config.instanceUrl !== "string") throw new Error(`${configPath}: expected { instanceUrl?: string }.`);
135
135
  return normalizeInstanceUrl(config.instanceUrl ?? "https://app.hitch42.com");
136
136
  }
137
- async function readCredentialStore(options = {}) {
138
- const store = await readRawCredentialStore(options);
137
+ async function readCredentialStore() {
138
+ const store = await readRawCredentialStore();
139
139
  return { instances: Object.fromEntries(Object.entries(store.instances).map(([instanceUrl, credentialsByOrg]) => [instanceUrl, Object.fromEntries(Object.entries(credentialsByOrg).flatMap(([orgCode, value]) => {
140
140
  const credentials = parseCredentials(value);
141
141
  return credentials === void 0 ? [] : [[orgCode, credentials]];
142
142
  }))])) };
143
143
  }
144
- async function getCredentialsForOrg(instanceUrl, orgCode, options = {}) {
145
- return (await readCredentialStore(options)).instances[normalizeInstanceUrl(instanceUrl)]?.[orgCode];
144
+ async function getCredentialsForOrg(instanceUrl, orgCode) {
145
+ return (await readCredentialStore()).instances[normalizeInstanceUrl(instanceUrl)]?.[orgCode];
146
146
  }
147
- function environmentAuth(options) {
148
- const apiKey = (options.env ?? process.env).HITCH_API_KEY;
147
+ function environmentAuth() {
148
+ const apiKey = process.env.HITCH_API_KEY;
149
149
  return apiKey !== void 0 && apiKey.length > 0 ? {
150
150
  accessToken: apiKey,
151
151
  source: "environment"
@@ -169,11 +169,11 @@ function resolveStoredAuthForOrg(credentialsByOrg, orgCode) {
169
169
  const apiKeyOrgs = Object.values(credentialsByOrg).filter((credentials) => credentials.type === "apiKey").map(({ orgCode: storedOrg }) => storedOrg).sort();
170
170
  if (apiKeyOrgs.length > 0) throw new AuthenticationError(`Stored API ${apiKeyOrgs.length === 1 ? "key is" : "keys are"} scoped to ${apiKeyOrgs.map((storedOrg) => `"${storedOrg}"`).join(", ")}, not "${orgCode}".`);
171
171
  }
172
- async function resolveAuthForOrg(instanceUrl, orgCode, options = {}) {
173
- const auth = environmentAuth(options);
172
+ async function resolveAuthForOrg(instanceUrl, orgCode) {
173
+ const auth = environmentAuth();
174
174
  if (auth !== void 0) return auth;
175
175
  const normalizedUrl = normalizeInstanceUrl(instanceUrl);
176
- const credentialsByOrg = Object.entries((await readCredentialStore(options)).instances).find(([instance]) => instance === normalizedUrl)?.[1];
176
+ const credentialsByOrg = Object.entries((await readCredentialStore()).instances).find(([instance]) => instance === normalizedUrl)?.[1];
177
177
  return resolveStoredAuthForOrg(credentialsByOrg ?? {}, orgCode);
178
178
  }
179
179
  function rejectEnvironmentKey(response, auth) {
@@ -182,9 +182,9 @@ function rejectEnvironmentKey(response, auth) {
182
182
  function bearerToken(credentials) {
183
183
  return "apiKey" in credentials ? credentials.apiKey : credentials.accessToken;
184
184
  }
185
- async function setCredentials(instanceUrl, credentials, options = {}) {
186
- const paths = storePaths(options);
187
- const store = await readRawCredentialStore(options);
185
+ async function setCredentials(instanceUrl, credentials) {
186
+ const paths = storePaths();
187
+ const store = await readRawCredentialStore();
188
188
  const normalizedUrl = normalizeInstanceUrl(instanceUrl);
189
189
  const credentialsByOrg = store.instances[normalizedUrl];
190
190
  store.instances[normalizedUrl] = {
@@ -291,17 +291,17 @@ async function pollForToken(fetcher, instanceUrl, device, now, sleep) {
291
291
  throw new AuthenticationError("The sign-in request expired. Run hitch login again.");
292
292
  }
293
293
  async function loginWithDevice(instanceUrl, options = {}) {
294
- const fetcher = options.fetch ?? globalThis.fetch;
295
- const now = options.now ?? Date.now;
294
+ const fetcher = globalThis.fetch;
295
+ const now = Date.now;
296
296
  const { body, response } = await postJson(fetcher, `${instanceUrl}/api/auth/device/code`, { client_id: CLIENT_ID });
297
297
  if (!response.ok) throw new AuthenticationError(typeof body.error_description === "string" ? body.error_description : `Could not start sign-in (${response.status}).`);
298
298
  const device = parseDeviceCode(body);
299
299
  const verificationUrl = options.expectedOrg === void 0 ? device.verification_uri_complete : `${device.verification_uri_complete}&org=${encodeURIComponent(options.expectedOrg)}`;
300
- (options.print ?? console.log)(`Open ${verificationUrl} and confirm code ${device.user_code}.`);
300
+ console.log(`Open ${verificationUrl} and confirm code ${device.user_code}.`);
301
301
  if (!options.noBrowser) try {
302
- await (options.openUrl ?? defaultOpenUrl)(verificationUrl);
302
+ defaultOpenUrl(verificationUrl);
303
303
  } catch {}
304
- const token = await pollForToken(fetcher, instanceUrl, device, now, options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))));
304
+ const token = await pollForToken(fetcher, instanceUrl, device, now, (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
305
305
  const { body: organizationBody, response: organizationResponse } = await postJson(fetcher, `${instanceUrl}/api/auth/device/organization`, { device_code: device.device_code });
306
306
  if (!organizationResponse.ok) throw new AuthenticationError("The sign-in did not resolve an organization.");
307
307
  const organization = parseDeviceOrganization(organizationBody);
@@ -319,7 +319,7 @@ async function loginWithDevice(instanceUrl, options = {}) {
319
319
  organizationId: organization.organizationId,
320
320
  orgCode: organization.orgCode
321
321
  };
322
- await setCredentials(instanceUrl, credentials, options.store);
322
+ await setCredentials(instanceUrl, credentials);
323
323
  if (options.expectedOrg !== void 0 && organization.orgCode !== options.expectedOrg) throw new AuthenticationError(`logged in to "${organization.orgCode}" but this applet targets "${options.expectedOrg}" — confirm the right organization on the device page`);
324
324
  return {
325
325
  credentials,
@@ -356,23 +356,18 @@ function findWorkspaceConfig(cwd) {
356
356
  }
357
357
  //#endregion
358
358
  //#region src/auth.ts
359
- async function fetchWithOrgAuth(orgCode, options, request) {
360
- const fetcher = options.fetch ?? globalThis.fetch;
361
- const instanceUrl = await resolveInstanceUrl(void 0, options.store);
359
+ async function fetchWithOrgAuth(orgCode, request) {
360
+ const instanceUrl = await resolveInstanceUrl();
362
361
  const organizationUrl = organizationInstanceUrl(instanceUrl, orgCode);
363
- const auth = await resolveAuthForOrg(instanceUrl, orgCode, options.store);
362
+ const auth = await resolveAuthForOrg(instanceUrl, orgCode);
364
363
  if (auth !== void 0) {
365
364
  const response = await request(organizationUrl, bearerToken(auth));
366
365
  rejectEnvironmentKey(response, auth);
367
366
  if (response.status !== 401) return response;
368
367
  }
369
- if (!(options.stdinIsTTY ?? process.stdin.isTTY === true)) throw new AuthenticationError("Not logged in. Run applet login, or set HITCH_API_KEY.");
370
- await (options.login ?? loginWithDevice)(instanceUrl, {
371
- expectedOrg: orgCode,
372
- fetch: fetcher,
373
- store: options.store
374
- });
375
- const credentials = await getCredentialsForOrg(instanceUrl, orgCode, options.store);
368
+ if (process.stdin.isTTY !== true) throw new AuthenticationError("Not logged in. Run applet login, or set HITCH_API_KEY.");
369
+ await loginWithDevice(instanceUrl, { expectedOrg: orgCode });
370
+ const credentials = await getCredentialsForOrg(instanceUrl, orgCode);
376
371
  if (credentials === void 0) throw new Error(`login did not provide credentials for organization "${orgCode}"`);
377
372
  return request(organizationUrl, bearerToken(credentials));
378
373
  }
@@ -387,4 +382,4 @@ function resolveWorkspaceOrg(appletRoot) {
387
382
  throw new Error(`No Hitch workspace found for ${appletRoot}. An applet must live at <workspace>/applets/<code> with hitch.config.ts in the workspace root.`);
388
383
  }
389
384
  //#endregion
390
- export { loginWithDevice as a, organizationInstanceUrl as c, resolveInstanceUrl as d, readWorkspaceConfig as i, rejectEnvironmentKey as l, fetchWithOrgAuth as n, AuthenticationError as o, findWorkspaceConfig as r, bearerToken as s, resolveWorkspaceOrg as t, resolveAuthForOrg as u };
385
+ export { loginWithDevice as a, bearerToken as c, resolveAuthForOrg as d, resolveInstanceUrl as f, readWorkspaceConfig as i, organizationInstanceUrl as l, fetchWithOrgAuth as n, AuthenticationError as o, findWorkspaceConfig as r, DEFAULT_INSTANCE_URL as s, resolveWorkspaceOrg as t, rejectEnvironmentKey as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hitch42/applet",
3
- "version": "0.1.0-beta.9",
3
+ "version": "0.1.1-beta.10",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "applet": "./dist/applet.js"
@@ -42,10 +42,15 @@
42
42
  "./_internal/server": {
43
43
  "source": "./src/internal/server.ts",
44
44
  "default": "./dist/_internal/server.js"
45
+ },
46
+ "./_internal/log-context": {
47
+ "source": "./src/server/log-context.ts",
48
+ "default": "./dist/_internal/log-context.js"
45
49
  }
46
50
  },
47
51
  "dependencies": {
48
52
  "@cloudflare/vite-plugin": "^1.51.1",
53
+ "@fontsource-variable/geist-mono": "^5.3.0",
49
54
  "@fontsource-variable/inter": "^5.3.0",
50
55
  "@hey-api/openapi-ts": "0.0.0-next-20260824173136",
51
56
  "@orpc/client": "^1.15.0",
@@ -53,7 +58,6 @@
53
58
  "@orpc/openapi-client": "^1.15.0",
54
59
  "@orpc/tanstack-query": "^1.15.0",
55
60
  "@tailwindcss/vite": "^4.3.0",
56
- "@tanstack/query-async-storage-persister": "5.101.4",
57
61
  "@tanstack/react-query-persist-client": "5.101.4",
58
62
  "@tanstack/router-cli": "^1.167.24",
59
63
  "@tanstack/router-plugin": "^1.168.23",
@@ -64,23 +68,26 @@
64
68
  "@tiptap/extension-table": "^3.30.3",
65
69
  "@tiptap/extensions": "^3.30.3",
66
70
  "@tiptap/markdown": "^3.30.3",
71
+ "@tiptap/pm": "^3.30.3",
67
72
  "@tiptap/react": "^3.30.3",
68
73
  "@tiptap/starter-kit": "^3.30.3",
69
74
  "@vitejs/plugin-react": "^6.0.3",
70
75
  "class-variance-authority": "^0.7.1",
71
- "clsx": "^2.1.1",
76
+ "cn": "^0.4.0",
72
77
  "croner": "^10.0.1",
73
78
  "lucide-react": "^1.33.0",
74
79
  "oxc-transform-react": "^0.149.0",
80
+ "oxfmt": "^0.72.0",
81
+ "oxlint": "^1.87.0",
82
+ "oxlint-tsgolint": "^7.0.2003",
75
83
  "react-aria-components": "^1.20.0",
76
84
  "shadcn": "^4.16.1",
85
+ "smol-toml": "^1.9.0",
77
86
  "streamdown": "^2.6.0",
78
- "tailwind-merge": "^3.6.0",
79
87
  "tailwindcss": "^4.3.0",
80
88
  "tw-animate-css": "^1.4.0",
81
89
  "vite": "8.3.0",
82
90
  "vitest": "4.1.11",
83
- "wrangler": "^4.120.0",
84
91
  "zod": "^4.4.3"
85
92
  },
86
93
  "peerDependencies": {
@@ -105,6 +112,6 @@
105
112
  "publishConfig": {
106
113
  "access": "public",
107
114
  "registry": "https://registry.npmjs.org",
108
- "tag": "beta"
115
+ "tag": "latest"
109
116
  }
110
117
  }
@@ -28,6 +28,10 @@ This file holds the common rules. Read each matching reference before you edit a
28
28
 
29
29
  For attribute types, filters, and API limits, read `../hitch-data/SKILL.md`. To inspect or change Hitch data and settings from the shell, read `../hitch-cli/SKILL.md`.
30
30
 
31
+ Read `../tanstack-router/SKILL.md` for route work and `../tanstack-query/SKILL.md` for query and cache work.
32
+ Read `../react-best-practices/SKILL.md` for React performance and `../web-design-guidelines/SKILL.md` for requested interface reviews.
33
+ Follow the Hitch applet rules for SDK-owned configuration, providers, and generated files.
34
+
31
35
  ## Work with the workspace
32
36
 
33
37
  - Run package commands with `pnpm`.
@@ -53,14 +57,15 @@ For attribute types, filters, and API limits, read `../hitch-data/SKILL.md`. To
53
57
  - The preview lasts until you select Exit or close the tab.
54
58
  - A pending `requiresChangeset` schema is not available in dev. Deploy to test it.
55
59
  - Run `pnpm --dir applets/<code> run deploy` only when the user requests a deployment.
56
- - Edit only `hitch.config.ts`, `applet.manifest.ts`, files in `src`, and `.gitignore` rules below the generated block.
57
- - Treat all other workspace files as generated output. The applet CLI replaces them. It creates `.github/workflows/hitch.yml` only when the file is absent. To stop it, set `githubWorkflow: false` in `hitch.config.ts` and delete the file.
60
+ - The CLI creates missing MCP files and adds missing Hitch and Chrome DevTools entries. Existing entries and other settings belong to the user.
61
+ - The applet CLI replaces its generated support files. It creates `.github/workflows/hitch.yml` only when the file is absent. To stop it, set `githubWorkflow: false` in `hitch.config.ts` and delete the file.
58
62
  - Remember that generation refreshes the local theme copy. Deployed applets load the public org theme from its Hitch `.com` URL.
59
63
 
60
64
  ## Use Vite and Vitest
61
65
 
62
66
  - Import configuration APIs from `vite`.
63
67
  - Import test APIs from `vitest`.
68
+ - Put tests in the applet's `test` folder and name them `*.test.ts` or `*.test.tsx`. Vitest does not run test files in other folders.
64
69
  - Do not edit `vite.config.ts`. The applet CLI rewrites it. Do not add another Vite config.
65
70
  - Add applet packages to `dependencies` or `devDependencies` in `applet.manifest.ts`.
66
71
 
@@ -132,6 +137,25 @@ Read `references/files.md` before download links, attachment attributes, or serv
132
137
  - Include the applet type filter during diagnosis. Without it, Cloudflare invocation entries can hide the useful applet entries. For a workflow run, use `--run <id>` or `--filter '{"type":"workflow"}'` instead.
133
138
  - Do not log secrets, tokens, or personal data.
134
139
 
140
+ ## Add request context with `log.set()`
141
+
142
+ Each server request gets one request entry with the route, status, outcome, CPU time, and wall time. Add facts about the request to that entry:
143
+
144
+ ```ts
145
+ import { log } from "@hitch42/applet/server";
146
+
147
+ log.set({ invoiceId, processedCount });
148
+ ```
149
+
150
+ - Set the IDs, counts, and decisions that explain what the request did. One entry with many fields is easier to filter than many `console.log` lines.
151
+ - A later call replaces an earlier value with the same top-level key.
152
+ - To fail a request, throw. The request entry records the error and its stack.
153
+ - To record a problem that the code recovered from, set a field, for example `log.set({ fallbackUsed: true })`.
154
+ - Use `console.<level>` for a separate line with its own level.
155
+ - Outside a request, for example at module scope, `log.set()` warns and drops the fields.
156
+ - A single call larger than 16 KB is dropped. The entry then shows `noteDropped`.
157
+ - Run `pnpm --dir applets/<code> exec applet logs --filter '{"type":"invocation"}'` to read request entries. The CLI prints the fields and any error stack.
158
+
135
159
  ## Verify changes
136
160
 
137
161
  Run these commands from the workspace root before deployment:
@@ -139,6 +163,8 @@ Run these commands from the workspace root before deployment:
139
163
  ```sh
140
164
  pnpm --dir applets/<code> run generate
141
165
  pnpm --dir applets/<code> run typecheck
166
+ pnpm --dir applets/<code> run fmt
167
+ pnpm --dir applets/<code> run lint
142
168
  pnpm --dir applets/<code> run test # Only when the applet contains tests.
143
169
  pnpm --dir applets/<code> run build
144
170
  ```
@@ -150,7 +176,7 @@ If a command fails, report the exact blocker. Do not deploy as a verification st
150
176
  ## Deploy from GitHub
151
177
 
152
178
  - A push to the production branch runs `applet deploy` in the workspace root through GitHub Actions.
153
- - An applet deploys when its own folder changed since its current CI release. Every applet deploys when a file outside `applets/` changed. An applet without a CI release always deploys. An applet also deploys when CI cannot load the commit of its current release.
179
+ - An applet deploys when its folder or a file outside `applets/` differs from its current release, including a local `applet deploy`. Uncommitted and untracked files count. Gitignored files do not count. An applet whose current release has no source hash always deploys.
154
180
  - The deploy skips other applets and logs the reason. A skip is expected, not a failure.
155
181
  - A schema change in Hitch alone does not trigger CI.
156
182
  - Do not import from a sibling applet folder. The change check does not follow such imports.
@@ -83,6 +83,14 @@ Each object entry has five required fields.
83
83
  - `readOnly` lists attribute codes that the applet can read but cannot write.
84
84
  - `readWrite` lists attribute codes that the applet can read and write.
85
85
 
86
+ Set `optional: true` when the applet works for a viewer without access to this object.
87
+ List codes from `readOnly` or `readWrite` in `optionalAttributes` when some viewers cannot see their values.
88
+ Typegen makes these record fields nullable for every viewer.
89
+ These fields never grant or remove access.
90
+ They matter when an org shares the applet with a connected organisation.
91
+ Hitch refuses a share if the connected org lacks access to a required object.
92
+ Hitch also refuses a share if a required object lists a required user attribute outside `optionalAttributes`.
93
+
86
94
  - Use the applet code format for each object code.
87
95
  - Do not declare the same object code more than once.
88
96
  - Use the applet code format for each attribute code.
@@ -15,6 +15,8 @@ import { MarkdownEditor } from "@hitch42/applet/react";
15
15
  <MarkdownEditor value={value} onChange={setValue} files={hitch.files} />;
16
16
  ```
17
17
 
18
+ Pass `autoFocus` to put the cursor at the end of the text when the editor mounts. It is off by default, so the editor never takes focus from another field.
19
+
18
20
  The editor uploads pasted or dropped images. It stores each image as `![name](hitch-file://<file-id>)`.
19
21
 
20
22
  The `files` value must supply both `upload(file)` and `download(id)`. The editor uses `download(id)` to show stored Hitch images.
@@ -142,6 +142,8 @@ Each aggregate can have its own `filter` with the same syntax as the top-level `
142
142
  The array form returns `keys` and `labels` arrays per group.
143
143
  The object form orders category groups by an aggregate value (top-N by value).
144
144
  The array form orders each dimension by key; `limit` selects the first N distinct keys in that order. It does not support top-N by value.
145
+ Grouped results include `truncated`. When it is `true`, the limit omitted groups.
146
+ Narrow the filter or split the query.
145
147
 
146
148
  ```ts
147
149
  const totals = await hitch.records.deals.aggregate({