@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,6 +1,6 @@
1
- import { i as APPLET_CODE_PATTERN } from "./manifest-Bp87Uz0a.js";
2
- import { i as readWorkspaceConfig, n as fetchWithOrgAuth, t as resolveWorkspaceOrg } from "./workspace-XVtM12ZC.js";
3
- import { t as loadAppletConfig } from "./load-config-BYSxDVAl.js";
1
+ import { i as APPLET_CODE_PATTERN } from "./manifest-CJSL9GuC.js";
2
+ import { i as readWorkspaceConfig, n as fetchWithOrgAuth, t as resolveWorkspaceOrg } from "./workspace-D0dnnlxL.js";
3
+ import { t as loadAppletConfig } from "./load-config-xEdTXs0y.js";
4
4
  import { z } from "zod";
5
5
  //#region src/logs.ts
6
6
  const LOG_LEVELS = [
@@ -19,6 +19,16 @@ const logSchema = z.object({
19
19
  timestamp: z.number(),
20
20
  level: z.string(),
21
21
  message: z.string(),
22
+ context: z.record(z.string(), z.unknown()).optional(),
23
+ error: z.object({
24
+ name: z.string(),
25
+ message: z.string(),
26
+ stack: z.string().optional(),
27
+ cause: z.object({
28
+ name: z.string(),
29
+ message: z.string()
30
+ }).optional()
31
+ }).optional(),
22
32
  releaseId: z.string(),
23
33
  type: z.string().optional(),
24
34
  runId: z.string().optional(),
@@ -93,20 +103,6 @@ async function readErrorDetails(response) {
93
103
  }
94
104
  return "";
95
105
  }
96
- function waitForPoll(signal) {
97
- if (signal?.aborted === true) return Promise.resolve(false);
98
- return new Promise((resolve) => {
99
- const timeout = setTimeout(() => {
100
- signal?.removeEventListener("abort", onAbort);
101
- resolve(true);
102
- }, POLL_INTERVAL_MS);
103
- const onAbort = () => {
104
- clearTimeout(timeout);
105
- resolve(false);
106
- };
107
- signal?.addEventListener("abort", onAbort, { once: true });
108
- });
109
- }
110
106
  function printLog(log) {
111
107
  const details = log.type === "invocation" ? [
112
108
  log.statusCode === void 0 ? void 0 : `status=${log.statusCode}`,
@@ -114,7 +110,9 @@ function printLog(log) {
114
110
  log.wallTimeMs === void 0 ? void 0 : `wall=${log.wallTimeMs}ms`,
115
111
  log.cpuTimeMs === void 0 ? void 0 : `cpu=${log.cpuTimeMs}ms`
116
112
  ].filter((value) => value !== void 0) : [];
117
- console.log(`${new Date(log.timestamp).toISOString()} ${log.level.toUpperCase().padEnd(5)} ${log.type === "workflow" ? `[${log.workflow} ${log.runId?.slice(0, 8)}#${log.invocation}] ` : ""}${log.message}${details.length === 0 ? "" : ` [${details.join(" ")}]`}`);
113
+ console.log(`${new Date(log.timestamp).toISOString()} ${log.level.toUpperCase().padEnd(5)} ${log.type === "workflow" ? `[${log.workflow} ${log.runId?.slice(0, 8)}#${log.invocation}] ` : ""}${log.message}${details.length === 0 ? "" : ` [${details.join(" ")}]`}${log.context === void 0 ? "" : ` ${JSON.stringify(log.context)}`}`);
114
+ if (log.error) console.log(log.error.stack ?? `${log.error.name}: ${log.error.message}`);
115
+ if (log.error?.cause) console.log(`Caused by ${log.error.cause.name}: ${log.error.cause.message}`);
118
116
  }
119
117
  async function runLogs(options) {
120
118
  const [{ code, org }, since, limit, level, userFilter, search] = await Promise.all([
@@ -127,13 +125,12 @@ async function runLogs(options) {
127
125
  ]);
128
126
  const runFilter = options.run === void 0 ? void 0 : { runId: { $eq: options.run } };
129
127
  const filter = runFilter === void 0 ? userFilter : userFilter === void 0 ? runFilter : { $and: [runFilter, userFilter] };
130
- const fetcher = options.fetch ?? globalThis.fetch;
131
128
  const startedAt = Date.now();
132
129
  const requestLogs = async (from, to) => {
133
- const response = await fetchWithOrgAuth(org, options, (instanceUrl, accessToken) => {
130
+ const response = await fetchWithOrgAuth(org, (instanceUrl, accessToken) => {
134
131
  const url = new URL(`/api/v1/applets/${encodeURIComponent(code)}/logs/query`, `${instanceUrl}/`);
135
132
  const levelFilter = level === void 0 ? void 0 : { level: { $in: LOG_LEVELS.slice(LOG_LEVELS.indexOf(level)) } };
136
- return fetcher(url, {
133
+ return fetch(url, {
137
134
  method: "POST",
138
135
  headers: {
139
136
  Authorization: `Bearer ${accessToken}`,
@@ -172,7 +169,8 @@ async function runLogs(options) {
172
169
  return;
173
170
  }
174
171
  let lastTimestamp = initialLogs.at(-1)?.timestamp ?? startedAt;
175
- while (await waitForPoll(options.signal)) {
172
+ for (;;) {
173
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
176
174
  const to = Date.now();
177
175
  const logs = await requestLogs(Math.max(startedAt - since, lastTimestamp - LIVE_OVERLAP_MS), to);
178
176
  printNewLogs(logs);
@@ -35,7 +35,9 @@ interface AppletManifest {
35
35
  /** This code needs the schema as of this changeset. */
36
36
  requiresChangeset?: string;
37
37
  telephony?: {
38
- events: readonly "message.received"[];
38
+ read?: boolean;
39
+ send?: boolean;
40
+ events?: readonly "message.received"[];
39
41
  };
40
42
  schedules?: readonly {
41
43
  code: string;
@@ -53,6 +55,8 @@ interface AppletManifest {
53
55
  canDelete: boolean;
54
56
  readOnly: readonly string[];
55
57
  readWrite: readonly string[];
58
+ optional?: boolean;
59
+ optionalAttributes?: readonly string[];
56
60
  }[];
57
61
  }
58
62
  type AppletDefinition = Omit<AppletManifest, "org">;
@@ -31,13 +31,22 @@ const appletManifestObjectSchema = z.strictObject({
31
31
  canCreate: z.boolean(),
32
32
  canDelete: z.boolean(),
33
33
  readOnly: attributeCodesSchema,
34
- readWrite: attributeCodesSchema
34
+ readWrite: attributeCodesSchema,
35
+ optional: z.boolean().optional(),
36
+ optionalAttributes: attributeCodesSchema.optional()
35
37
  }).superRefine((object, context) => {
36
38
  const overlap = object.readOnly.find((code) => object.readWrite.includes(code));
37
39
  if (overlap !== void 0) context.addIssue({
38
40
  code: "custom",
39
41
  message: `readOnly and readWrite both contain "${overlap}".`
40
42
  });
43
+ object.optionalAttributes?.forEach((code, index) => {
44
+ if (!object.readOnly.includes(code) && !object.readWrite.includes(code)) context.addIssue({
45
+ code: "custom",
46
+ message: `optionalAttributes contains undeclared attribute code "${code}".`,
47
+ path: ["optionalAttributes", index]
48
+ });
49
+ });
41
50
  });
42
51
  const appletManifestObjectsSchema = z.array(appletManifestObjectSchema).readonly();
43
52
  const appletManifestSchedulesSchema = z.array(z.strictObject({
@@ -64,7 +73,11 @@ const appletManifestShape = {
64
73
  name: nameSchema,
65
74
  code: codeSchema,
66
75
  requiresChangeset: z.uuid().optional(),
67
- telephony: z.strictObject({ events: z.array(z.literal("message.received")).min(1).refine((events) => new Set(events).size === events.length).readonly() }).optional(),
76
+ telephony: z.strictObject({
77
+ read: z.boolean().optional(),
78
+ send: z.boolean().optional(),
79
+ events: z.array(z.literal("message.received")).min(1).refine((events) => new Set(events).size === events.length).readonly().optional()
80
+ }).refine((telephony) => telephony.events === void 0 || telephony.read === true, "telephony.events needs telephony.read").optional(),
68
81
  schedules: appletManifestSchedulesSchema.optional(),
69
82
  variables: appletManifestVariablesSchema.optional(),
70
83
  objects: appletManifestObjectsSchema
@@ -104,6 +117,7 @@ const appletDefinitionSchema = z.strictObject({
104
117
  }).superRefine(checkDuplicateCodes);
105
118
  const packageVersionsSchema = z.record(z.string().min(1), z.string().min(1)).readonly();
106
119
  const RESERVED_APPLET_PACKAGES = /* @__PURE__ */ new Set([
120
+ "@fontsource-variable/geist-mono",
107
121
  "@fontsource-variable/inter",
108
122
  "@hitch42/applet",
109
123
  "@orpc/contract",
@@ -123,14 +137,16 @@ const RESERVED_APPLET_PACKAGES = /* @__PURE__ */ new Set([
123
137
  "@types/react",
124
138
  "@types/react-dom",
125
139
  "class-variance-authority",
126
- "clsx",
140
+ "cn",
127
141
  "lucide-react",
142
+ "oxfmt",
143
+ "oxlint",
144
+ "oxlint-tsgolint",
128
145
  "react",
129
146
  "react-aria-components",
130
147
  "react-dom",
131
148
  "shadcn",
132
149
  "streamdown",
133
- "tailwind-merge",
134
150
  "tailwindcss",
135
151
  "tw-animate-css",
136
152
  "typescript",
@@ -1,9 +1,72 @@
1
- import { i as readWorkspaceConfig } from "./workspace-XVtM12ZC.js";
2
- import { n as loadAppletSourceDefinition } from "./load-config-BYSxDVAl.js";
1
+ import { i as readWorkspaceConfig, l as organizationInstanceUrl, s as DEFAULT_INSTANCE_URL } from "./workspace-D0dnnlxL.js";
2
+ import { n as loadAppletSourceDefinition } from "./load-config-xEdTXs0y.js";
3
3
  import { z } from "zod";
4
4
  import { existsSync } from "node:fs";
5
- import { cp, mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises";
5
+ import { cp, mkdir, readFile, readdir, rm, symlink, writeFile } from "node:fs/promises";
6
6
  import { dirname, relative, resolve } from "node:path";
7
+ import { TomlError, parse, stringify } from "smol-toml";
8
+ //#region src/agent-support.ts
9
+ var AgentConfigError = class extends Error {};
10
+ async function ensureAgentConfigs(root, org) {
11
+ const servers = {
12
+ hitch: {
13
+ type: "http",
14
+ url: `${organizationInstanceUrl(DEFAULT_INSTANCE_URL, org)}/mcp`
15
+ },
16
+ "chrome-devtools": {
17
+ type: "stdio",
18
+ command: "pnpx",
19
+ args: ["chrome-devtools-mcp@latest", "--headless"]
20
+ }
21
+ };
22
+ const configs = await Promise.all([{
23
+ path: resolve(root, ".mcp.json"),
24
+ format: "json",
25
+ key: "mcpServers"
26
+ }, {
27
+ path: resolve(root, ".codex/config.toml"),
28
+ format: "toml",
29
+ key: "mcp_servers"
30
+ }].map(async ({ path, format, key }) => {
31
+ const current = existsSync(path) ? await readFile(path, "utf8") : void 0;
32
+ let config;
33
+ let additions;
34
+ try {
35
+ config = z.record(z.string(), z.unknown()).parse(current === void 0 ? {} : format === "json" ? JSON.parse(current) : parse(current, { integersAsBigInt: true }));
36
+ const existing = config[key] === void 0 ? {} : z.record(z.string(), z.unknown()).parse(config[key]);
37
+ const missing = Object.entries(servers).filter(([name]) => !Object.hasOwn(existing, name));
38
+ if (current !== void 0 && missing.length === 0) return void 0;
39
+ additions = Object.fromEntries(missing.map(([name, server]) => [name, format === "json" ? server : Object.fromEntries(Object.entries(server).filter(([field]) => field !== "type"))]));
40
+ config[key] = {
41
+ ...existing,
42
+ ...additions
43
+ };
44
+ } catch (cause) {
45
+ throw new AgentConfigError(`Invalid agent configuration in ${path}.`, { cause });
46
+ }
47
+ if (format === "json") return {
48
+ path,
49
+ contents: `${JSON.stringify(config, null, 2)}\n`
50
+ };
51
+ const contents = `${current ?? ""}${current ? "\n" : ""}${stringify({ [key]: additions })}`;
52
+ try {
53
+ parse(contents, { integersAsBigInt: true });
54
+ return {
55
+ path,
56
+ contents
57
+ };
58
+ } catch (error) {
59
+ if (!(error instanceof TomlError)) throw error;
60
+ return {
61
+ path,
62
+ contents: stringify(config, { numbersAsFloat: true })
63
+ };
64
+ }
65
+ }));
66
+ await mkdir(resolve(root, ".codex"), { recursive: true });
67
+ await Promise.all(configs.filter((config) => config !== void 0).map(({ path, contents }) => writeFile(path, contents)));
68
+ }
69
+ //#endregion
7
70
  //#region src/project.ts
8
71
  const PACKAGE_MANAGER = "pnpm@11.18.0+sha512.33d83c77da82f49fba836925c6f1b841181ec3132b670639bd012f7075f5c7cf634c5f870147c19aae7478fac01df09d8892e880454896edd23ee9b33757563c";
9
72
  const cliManifestValueSchema = z.string().min(1, "must be a non-empty string");
@@ -11,6 +74,7 @@ const cliManifestSchema = z.object({
11
74
  name: cliManifestValueSchema,
12
75
  version: cliManifestValueSchema,
13
76
  dependencies: z.object({
77
+ "@fontsource-variable/geist-mono": cliManifestValueSchema,
14
78
  "@fontsource-variable/inter": cliManifestValueSchema,
15
79
  "@orpc/contract": cliManifestValueSchema,
16
80
  "@tiptap/core": cliManifestValueSchema,
@@ -24,12 +88,14 @@ const cliManifestSchema = z.object({
24
88
  "@tailwindcss/vite": cliManifestValueSchema,
25
89
  "@tanstack/router-cli": cliManifestValueSchema,
26
90
  "class-variance-authority": cliManifestValueSchema,
27
- clsx: cliManifestValueSchema,
91
+ cn: cliManifestValueSchema,
28
92
  "lucide-react": cliManifestValueSchema,
93
+ oxfmt: cliManifestValueSchema,
94
+ oxlint: cliManifestValueSchema,
95
+ "oxlint-tsgolint": cliManifestValueSchema,
29
96
  "react-aria-components": cliManifestValueSchema,
30
97
  shadcn: cliManifestValueSchema,
31
98
  streamdown: cliManifestValueSchema,
32
- "tailwind-merge": cliManifestValueSchema,
33
99
  tailwindcss: cliManifestValueSchema,
34
100
  "tw-animate-css": cliManifestValueSchema,
35
101
  vite: cliManifestValueSchema,
@@ -71,8 +137,16 @@ function workspacePackage(org) {
71
137
  name: `${org.replaceAll("_", "-")}-applets`,
72
138
  private: true,
73
139
  type: "module",
140
+ scripts: {
141
+ lint: "oxlint",
142
+ fmt: "oxfmt",
143
+ check: "oxfmt --check && oxlint"
144
+ },
74
145
  devDependencies: {
75
146
  "@hitch42/applet": "catalog:",
147
+ oxfmt: "catalog:",
148
+ oxlint: "catalog:",
149
+ "oxlint-tsgolint": "catalog:",
76
150
  typescript: "catalog:",
77
151
  vite: "catalog:",
78
152
  vitest: "catalog:"
@@ -85,6 +159,7 @@ function workspaceFile(cli) {
85
159
  - applets/*
86
160
  catalog:
87
161
  "@hitch42/applet": npm:${cli.name}@${cli.version}
162
+ "@fontsource-variable/geist-mono": "${cli.dependencies["@fontsource-variable/geist-mono"]}"
88
163
  "@fontsource-variable/inter": "${cli.dependencies["@fontsource-variable/inter"]}"
89
164
  "@orpc/contract": "${cli.dependencies["@orpc/contract"]}"
90
165
  "@orpc/server": "${cli.peerDependencies["@orpc/server"]}"
@@ -104,14 +179,16 @@ catalog:
104
179
  "@types/react-dom": "${cli.peerDependencies["@types/react-dom"]}"
105
180
  "@tailwindcss/vite": "${cli.dependencies["@tailwindcss/vite"]}"
106
181
  class-variance-authority: "${cli.dependencies["class-variance-authority"]}"
107
- clsx: "${cli.dependencies.clsx}"
182
+ cn: "${cli.dependencies.cn}"
108
183
  lucide-react: "${cli.dependencies["lucide-react"]}"
184
+ oxfmt: "${cli.dependencies.oxfmt}"
185
+ oxlint: "${cli.dependencies.oxlint}"
186
+ oxlint-tsgolint: "${cli.dependencies["oxlint-tsgolint"]}"
109
187
  react: "${cli.peerDependencies.react}"
110
188
  react-aria-components: "${cli.dependencies["react-aria-components"]}"
111
189
  react-dom: "${cli.peerDependencies["react-dom"]}"
112
190
  shadcn: "${cli.dependencies.shadcn}"
113
191
  streamdown: "${cli.dependencies.streamdown}"
114
- tailwind-merge: "${cli.dependencies["tailwind-merge"]}"
115
192
  tailwindcss: "${cli.dependencies.tailwindcss}"
116
193
  tw-animate-css: "${cli.dependencies["tw-animate-css"]}"
117
194
  typescript: "${cli.peerDependencies.typescript}"
@@ -126,17 +203,91 @@ minimumReleaseAgeExclude:
126
203
  - "@hitch42/*"
127
204
  `;
128
205
  }
206
+ const OXLINT_CONFIG = `{
207
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
208
+ "plugins": ["eslint", "typescript", "unicorn", "import", "node"],
209
+ "options": { "typeAware": true, "typeCheck": true },
210
+ "env": { "node": true },
211
+ "overrides": [
212
+ {
213
+ "files": ["applets/*/src/client/**"],
214
+ "plugins": ["eslint", "typescript", "unicorn", "import", "react", "react-perf"],
215
+ "env": { "node": false, "browser": true },
216
+ "rules": {
217
+ "react/capitalized-calls": "error",
218
+ "react/error-boundaries": "error",
219
+ "react/exhaustive-effect-dependencies": "error",
220
+ "react/globals": "error",
221
+ "react/hooks": "error",
222
+ "react/immutability": "error",
223
+ "react/incompatible-library": "error",
224
+ "react/invariant": "error",
225
+ "react/memo-dependencies": "error",
226
+ "react/no-deriving-state-in-effects": "error",
227
+ "react/preserve-manual-memoization": "error",
228
+ "react/purity": "error",
229
+ "react/refs": "error",
230
+ "react/rule-suppression": "error",
231
+ "react/set-state-in-effect": "error",
232
+ "react/set-state-in-render": "error",
233
+ "react/static-components": "error",
234
+ "react/syntax": "error",
235
+ "react/todo": "error",
236
+ "react/unsupported-syntax": "error",
237
+ "react/use-memo": "error",
238
+ "react/void-use-memo": "error"
239
+ }
240
+ },
241
+ {
242
+ "files": ["applets/*/test/**"],
243
+ "plugins": ["eslint", "typescript", "unicorn", "import", "vitest"],
244
+ "rules": {
245
+ "vitest/expect-expect": [
246
+ "warn",
247
+ { "assertFunctionNames": ["expect", "expect*", "assert", "assert.*", "assert*"] }
248
+ ]
249
+ }
250
+ }
251
+ ]
252
+ }
253
+ `;
254
+ async function oxfmtConfig(root) {
255
+ const appletsRoot = resolve(root, "applets");
256
+ const overrides = (existsSync(appletsRoot) ? await readdir(appletsRoot) : []).filter((code) => existsSync(resolve(appletsRoot, code, "src/client/styles/globals.css"))).sort().map((code) => `
257
+ {
258
+ "files": ["applets/${code}/**"],
259
+ "options": {
260
+ "sortTailwindcss": {
261
+ "stylesheet": "applets/${code}/src/client/styles/globals.css",
262
+ "functions": ["cn", "cva"]
263
+ }
264
+ }
265
+ }`);
266
+ return `{
267
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
268
+ "sortImports": true,
269
+ "overrides": [${overrides.join(",")}${overrides.length > 0 ? "\n " : ""}]
270
+ }
271
+ `;
272
+ }
129
273
  const GITIGNORE_START = "# BEGIN Hitch generated files";
130
274
  const GITIGNORE_END = "# END Hitch generated files";
131
275
  const SKILLS = [
132
276
  "hitch-applets",
133
277
  "hitch-cli",
134
278
  "hitch-data",
135
- "hitch-theory"
279
+ "hitch-theory",
280
+ "react-best-practices",
281
+ "tanstack-query",
282
+ "tanstack-router",
283
+ "web-design-guidelines"
136
284
  ];
137
285
  const GITIGNORE = `${GITIGNORE_START}
138
286
  ${SKILLS.map((skill) => `/.agents/skills/${skill}/`).join("\n")}
287
+ ${SKILLS.map((skill) => `/.claude/skills/${skill}`).join("\n")}
139
288
  /.node-version
289
+ /.oxfmtrc.json
290
+ /.oxlintrc.json
140
291
  /AGENTS.md
141
292
  /CLAUDE.md
142
293
  /package.json
@@ -176,10 +327,12 @@ async function writeWorkspaceSupport(root, config, cli) {
176
327
  const resolvedCli = cli ?? await loadCliManifest();
177
328
  if (resolvedCli === void 0) throw new Error("The CLI package manifest is not published.");
178
329
  assertWorkspaceCli(config, resolvedCli);
330
+ await ensureAgentConfigs(root, config.org);
179
331
  const deployWorkflowTarget = resolve(root, ".github/workflows/hitch.yml");
180
332
  const agents = await readFile(new URL("../scaffold/AGENTS.md", import.meta.url), "utf8");
181
333
  await Promise.all([
182
334
  mkdir(resolve(root, ".agents/skills"), { recursive: true }),
335
+ mkdir(resolve(root, ".claude/skills"), { recursive: true }),
183
336
  config.githubWorkflow === false ? void 0 : mkdir(resolve(root, ".github/workflows"), { recursive: true }),
184
337
  ...SKILLS.map((skill) => rm(resolve(root, ".agents/skills", skill), {
185
338
  force: true,
@@ -192,11 +345,21 @@ async function writeWorkspaceSupport(root, config, cli) {
192
345
  writeFile(resolve(root, "pnpm-workspace.yaml"), workspaceFile(resolvedCli)),
193
346
  config.githubWorkflow === false || existsSync(deployWorkflowTarget) ? void 0 : cp(new URL("../scaffold/.github/workflows/hitch.yml", import.meta.url), deployWorkflowTarget),
194
347
  writeFile(resolve(root, ".node-version"), "24\n"),
348
+ writeFile(resolve(root, ".oxlintrc.json"), OXLINT_CONFIG),
349
+ oxfmtConfig(root).then((config) => writeFile(resolve(root, ".oxfmtrc.json"), config)),
195
350
  writeGitignore(root),
196
351
  writeFile(resolve(root, "AGENTS.md"), agents)
197
352
  ]);
198
353
  await rm(resolve(root, "CLAUDE.md"), { force: true });
199
354
  await symlink("AGENTS.md", resolve(root, "CLAUDE.md"));
355
+ await Promise.all(SKILLS.map(async (skill) => {
356
+ const target = resolve(root, ".claude/skills", skill);
357
+ await rm(target, { force: true });
358
+ await symlink(`../../.agents/skills/${skill}`, target, "dir");
359
+ }));
360
+ }
361
+ function sortedByName(packages) {
362
+ return Object.fromEntries(Object.entries(packages).sort(([a], [b]) => a < b ? -1 : 1));
200
363
  }
201
364
  function appletPackage(definition, options) {
202
365
  return {
@@ -209,14 +372,18 @@ function appletPackage(definition, options) {
209
372
  generate: "applet generate",
210
373
  deploy: "applet deploy",
211
374
  test: "vitest run",
212
- typecheck: "tsc --noEmit"
375
+ typecheck: "tsc --noEmit",
376
+ lint: "oxlint",
377
+ fmt: "oxfmt",
378
+ check: "oxfmt --check && oxlint"
213
379
  },
214
- dependencies: {
380
+ dependencies: sortedByName({
215
381
  "@hitch42/applet": "catalog:",
216
382
  "@orpc/contract": "catalog:",
217
383
  zod: "catalog:",
218
384
  ...options.hasServer ? { "@orpc/server": "catalog:" } : {},
219
385
  ...options.hasClient ? {
386
+ "@fontsource-variable/geist-mono": "catalog:",
220
387
  "@fontsource-variable/inter": "catalog:",
221
388
  "@tanstack/react-query": "catalog:",
222
389
  "@tanstack/react-router": "catalog:",
@@ -229,19 +396,18 @@ function appletPackage(definition, options) {
229
396
  "@tiptap/react": "catalog:",
230
397
  "@tiptap/starter-kit": "catalog:",
231
398
  "class-variance-authority": "catalog:",
232
- clsx: "catalog:",
399
+ cn: "catalog:",
233
400
  "lucide-react": "catalog:",
234
401
  react: "catalog:",
235
402
  "react-aria-components": "catalog:",
236
403
  "react-dom": "catalog:",
237
404
  shadcn: "catalog:",
238
405
  streamdown: "catalog:",
239
- "tailwind-merge": "catalog:",
240
406
  "tw-animate-css": "catalog:"
241
407
  } : {},
242
408
  ...definition.dependencies
243
- },
244
- devDependencies: {
409
+ }),
410
+ devDependencies: sortedByName({
245
411
  "@types/node": "catalog:",
246
412
  typescript: "catalog:",
247
413
  vite: "catalog:",
@@ -253,9 +419,21 @@ function appletPackage(definition, options) {
253
419
  tailwindcss: "catalog:"
254
420
  } : {},
255
421
  ...definition.devDependencies
256
- }
422
+ })
257
423
  };
258
424
  }
425
+ const LEGACY_CN_HELPER = `import { clsx, type ClassValue } from "clsx";
426
+ import { twMerge } from "tailwind-merge";
427
+
428
+ export function cn(...inputs: ClassValue[]) {
429
+ return twMerge(clsx(inputs));
430
+ }
431
+ `;
432
+ async function replaceLegacyCnHelper(root) {
433
+ const path = resolve(root, "src/client/lib/utils.ts");
434
+ if (!existsSync(path) || await readFile(path, "utf8") !== LEGACY_CN_HELPER) return;
435
+ await cp(new URL("../scaffold/applet/src/client/lib/utils.ts", import.meta.url), path);
436
+ }
259
437
  async function writeAppletSupport(root, definition) {
260
438
  const hasClient = existsSync(resolve(root, "src/client/routes"));
261
439
  const hasAuthorRoot = existsSync(resolve(root, "src/client/routes/__root.tsx"));
@@ -264,6 +442,7 @@ async function writeAppletSupport(root, definition) {
264
442
  if (!hasServer && (definition.schedules?.length ?? 0) > 0) throw new Error("An applet with schedules must have src/server/main.ts.");
265
443
  await Promise.all(hasClient ? [] : [rm(resolve(root, "index.html"), { force: true }), rm(resolve(root, "components.json"), { force: true })]);
266
444
  await Promise.all([
445
+ replaceLegacyCnHelper(root),
267
446
  writeFile(resolve(root, "package.json"), `${JSON.stringify(appletPackage(definition, {
268
447
  hasClient,
269
448
  hasServer
@@ -335,7 +514,9 @@ export default defineConfig({
335
514
  "./.hitch/*.ts",
336
515
  "./.hitch/*.tsx",
337
516
  "./src/**/*.ts",
338
- "./src/**/*.tsx"
517
+ "./src/**/*.tsx",
518
+ "./test/**/*.ts",
519
+ "./test/**/*.tsx"
339
520
  ]
340
521
  }
341
522
  `),
@@ -0,0 +1,82 @@
1
+ //#region src/internal/query-cache.ts
2
+ const STORE = "clients";
3
+ const TIMESTAMP_INDEX = "timestamp";
4
+ const TAB_KEY = "hitch:query-tab";
5
+ const SAVE_INTERVAL = 1e3;
6
+ function openQueryCacheDatabase() {
7
+ return new Promise((resolve, reject) => {
8
+ const open = indexedDB.open("hitch-query-cache", 1);
9
+ open.onupgradeneeded = () => {
10
+ open.result.createObjectStore("clients").createIndex("timestamp", "timestamp");
11
+ };
12
+ open.onblocked = () => reject(/* @__PURE__ */ new Error("The query cache is open in an older tab."));
13
+ open.onsuccess = () => {
14
+ open.result.onversionchange = () => open.result.close();
15
+ resolve(open.result);
16
+ };
17
+ open.onerror = () => reject(open.error);
18
+ });
19
+ }
20
+ let database;
21
+ function openCacheDatabase() {
22
+ database ??= window.__hitchCacheDatabase ?? openQueryCacheDatabase();
23
+ return database;
24
+ }
25
+ async function inStore(mode, run) {
26
+ const transaction = (await openCacheDatabase()).transaction(STORE, mode);
27
+ const request = run(transaction.objectStore(STORE));
28
+ return new Promise((resolve, reject) => {
29
+ transaction.oncomplete = () => resolve(request.result);
30
+ transaction.onabort = () => reject(transaction.error ?? request.error);
31
+ });
32
+ }
33
+ function cacheTabId() {
34
+ const existing = sessionStorage.getItem(TAB_KEY);
35
+ if (existing) return existing;
36
+ const id = crypto.randomUUID();
37
+ sessionStorage.setItem(TAB_KEY, id);
38
+ return id;
39
+ }
40
+ function indexedDbPersister(key) {
41
+ let pending;
42
+ let throttled = false;
43
+ const save = (client) => inStore("readwrite", (store) => store.put(client, key)).then(() => void 0, (error) => console.error("Could not persist the applet query cache.", error));
44
+ const release = () => {
45
+ if (!pending) {
46
+ throttled = false;
47
+ return;
48
+ }
49
+ save(pending);
50
+ pending = void 0;
51
+ setTimeout(release, SAVE_INTERVAL);
52
+ };
53
+ return {
54
+ persistClient: (client) => {
55
+ if (throttled) {
56
+ pending = client;
57
+ return;
58
+ }
59
+ throttled = true;
60
+ setTimeout(release, SAVE_INTERVAL);
61
+ return save(client);
62
+ },
63
+ restoreClient: () => inStore("readonly", (store) => store.get(key)),
64
+ removeClient: () => inStore("readwrite", (store) => store.delete(key))
65
+ };
66
+ }
67
+ function sweepExpiredQueryCaches(maxAge) {
68
+ return inStore("readwrite", (store) => {
69
+ const cursor = store.index(TIMESTAMP_INDEX).openKeyCursor(IDBKeyRange.upperBound(Date.now() - maxAge));
70
+ cursor.onsuccess = () => {
71
+ if (!cursor.result) return;
72
+ store.delete(cursor.result.primaryKey);
73
+ cursor.result.continue();
74
+ };
75
+ return cursor;
76
+ });
77
+ }
78
+ function clearAppletQueryCaches(appletCode) {
79
+ return inStore("readwrite", (store) => store.delete(IDBKeyRange.bound(`${appletCode}:`, `${appletCode}:\uffff`)));
80
+ }
81
+ //#endregion
82
+ export { openQueryCacheDatabase as a, openCacheDatabase as i, clearAppletQueryCaches as n, sweepExpiredQueryCaches as o, indexedDbPersister as r, cacheTabId as t };
package/dist/react.d.ts CHANGED
@@ -29,9 +29,10 @@ type MarkdownEditorProps = {
29
29
  minLength?: number;
30
30
  maxLength?: number;
31
31
  disabled?: boolean;
32
+ autoFocus?: boolean;
32
33
  className?: string;
33
34
  onUploadError?: (error: unknown, file: File) => void;
34
35
  };
35
- declare function MarkdownEditor({ value, onChange, files, id, placeholder, required, minLength, maxLength, disabled, className, onUploadError }: MarkdownEditorProps): ReactElement;
36
+ declare function MarkdownEditor({ value, onChange, files, id, placeholder, required, minLength, maxLength, disabled, autoFocus, className, onUploadError }: MarkdownEditorProps): ReactElement;
36
37
  //#endregion
37
38
  export { Markdown, MarkdownEditor, type MarkdownEditorFiles, type MarkdownEditorProps, type MarkdownFiles, type MarkdownProps };