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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/_internal/client.js +120 -11
  2. package/dist/_internal/log-context.d.ts +34 -0
  3. package/dist/_internal/log-context.js +2 -0
  4. package/dist/_internal/server.d.ts +14 -1
  5. package/dist/_internal/server.js +122 -118
  6. package/dist/applet.js +22 -11
  7. package/dist/build-Bh_S5BUv.js +2 -0
  8. package/dist/{build-DauirTcY.js → build-CEfFoIZZ.js} +1 -1
  9. package/dist/{ci-gPrwo6C0.js → ci-BKw14X2h.js} +14 -13
  10. package/dist/client.d.ts +3 -3
  11. package/dist/{contract-DB9QQhD9.d.ts → contract-CsUpTBe0.d.ts} +1 -1
  12. package/dist/{deploy-CqrtNJ1x.js → deploy-wGYjwXrx.js} +139 -88
  13. package/dist/email-DBTVhNB_.js +62 -0
  14. package/dist/{generate-BIifT6PK.js → generate-BEIYs17x.js} +70 -32
  15. package/dist/generate-Dox-OYt6.js +2 -0
  16. package/dist/headers-DkNr9MLJ.js +17 -0
  17. package/dist/index.d.ts +122 -10
  18. package/dist/index.js +142 -26
  19. package/dist/{init-Wu6XmyVc.js → init-6oGPM46R.js} +15 -17
  20. package/dist/{load-config-BknBkdRs.js → load-config-xEdTXs0y.js} +2 -2
  21. package/dist/log-context-zu8U9Sjm.js +73 -0
  22. package/dist/{login-iT9MRuaw.js → login-CUx3TmFQ.js} +1 -1
  23. package/dist/{logs-B-qZGXSK.js → logs-DSjuEN0Q.js} +20 -22
  24. package/dist/{manifest-C489nYUy.d.ts → manifest-C6_F4bL8.d.ts} +7 -0
  25. package/dist/{manifest-Bd04cWhu.js → manifest-CJSL9GuC.js} +22 -3
  26. package/dist/me-BW7Paary.js +32 -0
  27. package/dist/{project-C_rtpjp8.js → project-BRZGd6xq.js} +202 -20
  28. package/dist/prompt-ChS-grYO.js +29 -0
  29. package/dist/query-cache-CXmAyLv3.js +82 -0
  30. package/dist/react.d.ts +3 -2
  31. package/dist/react.js +98 -80
  32. package/dist/schedules-C8_Svr4h.js +42 -0
  33. package/dist/server.d.ts +9 -4
  34. package/dist/server.js +29 -8
  35. package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
  36. package/dist/telephony.d.ts +97 -0
  37. package/dist/telephony.js +726 -0
  38. package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
  39. package/dist/{variables-DcwMeIau.js → variables-DT5RyHdH.js} +14 -39
  40. package/dist/vite.d.ts +3 -60
  41. package/dist/vite.js +497 -27
  42. package/dist/workflow-engine-BOTzURKM.js +111 -0
  43. package/dist/{workspace-B6lenAS5.js → workspace-D0dnnlxL.js} +37 -39
  44. package/package.json +18 -5
  45. package/scaffold/.agents/skills/hitch-applets/SKILL.md +50 -18
  46. package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
  47. package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
  48. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +12 -2
  49. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
  50. package/scaffold/.agents/skills/hitch-applets/references/performance.md +1 -1
  51. package/scaffold/.agents/skills/hitch-applets/references/records.md +27 -4
  52. package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
  53. package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
  54. package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
  55. package/scaffold/.agents/skills/hitch-cli/SKILL.md +20 -20
  56. package/scaffold/.agents/skills/hitch-data/SKILL.md +3 -2
  57. package/scaffold/.agents/skills/hitch-data/references/attributes.md +7 -1
  58. package/scaffold/.agents/skills/hitch-data/references/limits.md +23 -23
  59. package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
  60. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  61. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  62. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  63. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  64. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  65. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  66. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  103. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  104. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  105. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  106. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  107. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  108. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  109. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  110. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  111. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  112. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  113. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  114. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  115. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  116. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  117. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  118. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  119. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  120. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  121. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  122. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  133. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  134. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  135. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  136. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  137. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  138. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  139. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  140. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  141. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  142. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  143. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  144. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  145. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  146. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  147. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  148. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  149. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  150. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  151. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  152. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  153. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  154. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  155. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  156. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  157. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  158. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  159. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  160. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  161. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  162. package/scaffold/.github/workflows/hitch.yml +1 -1
  163. package/scaffold/AGENTS.md +2 -2
  164. package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
  165. package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
  166. package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
  167. package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
  168. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
  169. package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
  170. package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
  171. package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
  172. package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
  173. package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
  174. package/scaffold/applet/src/client/lib/utils.ts +1 -6
  175. package/scaffold/applet/src/client/styles/globals.css +2 -0
  176. package/dist/build-DhNDp7ji.js +0 -2
  177. package/dist/generate-DSk8ihsC.js +0 -2
@@ -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,6 +73,11 @@ const appletManifestShape = {
64
73
  name: nameSchema,
65
74
  code: codeSchema,
66
75
  requiresChangeset: z.uuid().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(),
67
81
  schedules: appletManifestSchedulesSchema.optional(),
68
82
  variables: appletManifestVariablesSchema.optional(),
69
83
  objects: appletManifestObjectsSchema
@@ -96,12 +110,14 @@ const appletDefinitionSchema = z.strictObject({
96
110
  name: appletManifestShape.name,
97
111
  code: appletManifestShape.code,
98
112
  requiresChangeset: appletManifestShape.requiresChangeset,
113
+ telephony: appletManifestShape.telephony,
99
114
  schedules: appletManifestShape.schedules,
100
115
  variables: appletManifestShape.variables,
101
116
  objects: appletManifestShape.objects
102
117
  }).superRefine(checkDuplicateCodes);
103
118
  const packageVersionsSchema = z.record(z.string().min(1), z.string().min(1)).readonly();
104
119
  const RESERVED_APPLET_PACKAGES = /* @__PURE__ */ new Set([
120
+ "@fontsource-variable/geist-mono",
105
121
  "@fontsource-variable/inter",
106
122
  "@hitch42/applet",
107
123
  "@orpc/contract",
@@ -121,14 +137,16 @@ const RESERVED_APPLET_PACKAGES = /* @__PURE__ */ new Set([
121
137
  "@types/react",
122
138
  "@types/react-dom",
123
139
  "class-variance-authority",
124
- "clsx",
140
+ "cn",
125
141
  "lucide-react",
142
+ "oxfmt",
143
+ "oxlint",
144
+ "oxlint-tsgolint",
126
145
  "react",
127
146
  "react-aria-components",
128
147
  "react-dom",
129
148
  "shadcn",
130
149
  "streamdown",
131
- "tailwind-merge",
132
150
  "tailwindcss",
133
151
  "tw-animate-css",
134
152
  "typescript",
@@ -164,6 +182,7 @@ const appletSourceDefinitionSchema = z.strictObject({
164
182
  name: appletManifestShape.name,
165
183
  code: appletManifestShape.code,
166
184
  requiresChangeset: appletManifestShape.requiresChangeset,
185
+ telephony: appletManifestShape.telephony,
167
186
  schedules: appletManifestShape.schedules,
168
187
  variables: appletManifestShape.variables,
169
188
  objects: appletManifestShape.objects,
@@ -0,0 +1,32 @@
1
+ import { oc } from "@orpc/contract";
2
+ import { z } from "zod";
3
+ //#region src/hitch/me.ts
4
+ const personSchema = z.object({
5
+ id: z.uuid(),
6
+ firstName: z.string(),
7
+ lastName: z.string(),
8
+ email: z.email()
9
+ });
10
+ const membershipSchema = z.object({
11
+ orgId: z.uuid(),
12
+ code: z.string(),
13
+ name: z.string(),
14
+ iconFileId: z.uuid().nullable(),
15
+ role: z.enum(["member", "owner"]),
16
+ capabilities: z.array(z.string())
17
+ });
18
+ const meSchema = z.object({
19
+ person: personSchema,
20
+ memberships: z.array(membershipSchema),
21
+ organization: z.object({
22
+ id: z.uuid(),
23
+ code: z.string()
24
+ }).nullable(),
25
+ multiOrg: z.boolean()
26
+ });
27
+ const meContract = { get: oc.route({
28
+ method: "GET",
29
+ path: "/v1/me"
30
+ }).input(z.void()).output(meSchema) };
31
+ //#endregion
32
+ export { meSchema as n, meContract as t };
@@ -1,9 +1,72 @@
1
- import { i as readWorkspaceConfig } from "./workspace-B6lenAS5.js";
2
- import { n as loadAppletSourceDefinition } from "./load-config-BknBkdRs.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,
@@ -51,11 +117,11 @@ const cliManifestSchema = z.object({
51
117
  async function loadCliManifest(options) {
52
118
  const result = cliManifestSchema.safeParse(JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8")));
53
119
  if (!result.success) {
54
- const issue = result.error.issues[0];
120
+ const issue = result.error.issues.at(0);
55
121
  if (issue === void 0) throw new Error("Invalid CLI package manifest.");
56
122
  throw new Error(`Invalid CLI package manifest field "${issue.path.join(".")}": ${issue.message}.`);
57
123
  }
58
- const catalogEntry = [...Object.entries(result.data.dependencies).map(([name, value]) => [`dependencies.${name}`, value]), ...Object.entries(result.data.peerDependencies).map(([name, value]) => [`peerDependencies.${name}`, value])].find(([, value]) => value?.startsWith("catalog:"));
124
+ const catalogEntry = [...Object.entries(result.data.dependencies).map(([name, value]) => [`dependencies.${name}`, value]), ...Object.entries(result.data.peerDependencies).map(([name, value]) => [`peerDependencies.${name}`, value])].find(([, value]) => value.startsWith("catalog:"));
59
125
  if (catalogEntry !== void 0) {
60
126
  if (options?.allowDevelopment === true) return void 0;
61
127
  throw new Error(`Invalid CLI package manifest field "${catalogEntry[0]}": "catalog:" identifies an unpublished development build. Run the published @hitch42/applet CLI instead.`);
@@ -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,16 +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
- "hitch-data"
278
+ "hitch-data",
279
+ "hitch-theory",
280
+ "react-best-practices",
281
+ "tanstack-query",
282
+ "tanstack-router",
283
+ "web-design-guidelines"
135
284
  ];
136
285
  const GITIGNORE = `${GITIGNORE_START}
137
286
  ${SKILLS.map((skill) => `/.agents/skills/${skill}/`).join("\n")}
287
+ ${SKILLS.map((skill) => `/.claude/skills/${skill}`).join("\n")}
138
288
  /.node-version
289
+ /.oxfmtrc.json
290
+ /.oxlintrc.json
139
291
  /AGENTS.md
140
292
  /CLAUDE.md
141
293
  /package.json
@@ -175,11 +327,13 @@ async function writeWorkspaceSupport(root, config, cli) {
175
327
  const resolvedCli = cli ?? await loadCliManifest();
176
328
  if (resolvedCli === void 0) throw new Error("The CLI package manifest is not published.");
177
329
  assertWorkspaceCli(config, resolvedCli);
330
+ await ensureAgentConfigs(root, config.org);
178
331
  const deployWorkflowTarget = resolve(root, ".github/workflows/hitch.yml");
179
332
  const agents = await readFile(new URL("../scaffold/AGENTS.md", import.meta.url), "utf8");
180
333
  await Promise.all([
181
334
  mkdir(resolve(root, ".agents/skills"), { recursive: true }),
182
- mkdir(resolve(root, ".github/workflows"), { recursive: true }),
335
+ mkdir(resolve(root, ".claude/skills"), { recursive: true }),
336
+ config.githubWorkflow === false ? void 0 : mkdir(resolve(root, ".github/workflows"), { recursive: true }),
183
337
  ...SKILLS.map((skill) => rm(resolve(root, ".agents/skills", skill), {
184
338
  force: true,
185
339
  recursive: true
@@ -189,13 +343,23 @@ async function writeWorkspaceSupport(root, config, cli) {
189
343
  ...SKILLS.map((skill) => cp(new URL(`../scaffold/.agents/skills/${skill}`, import.meta.url), resolve(root, ".agents/skills", skill), { recursive: true })),
190
344
  writeFile(resolve(root, "package.json"), `${JSON.stringify(workspacePackage(config.org), null, 2)}\n`),
191
345
  writeFile(resolve(root, "pnpm-workspace.yaml"), workspaceFile(resolvedCli)),
192
- existsSync(deployWorkflowTarget) ? void 0 : cp(new URL("../scaffold/.github/workflows/hitch.yml", import.meta.url), deployWorkflowTarget),
346
+ config.githubWorkflow === false || existsSync(deployWorkflowTarget) ? void 0 : cp(new URL("../scaffold/.github/workflows/hitch.yml", import.meta.url), deployWorkflowTarget),
193
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)),
194
350
  writeGitignore(root),
195
351
  writeFile(resolve(root, "AGENTS.md"), agents)
196
352
  ]);
197
353
  await rm(resolve(root, "CLAUDE.md"), { force: true });
198
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));
199
363
  }
200
364
  function appletPackage(definition, options) {
201
365
  return {
@@ -208,14 +372,18 @@ function appletPackage(definition, options) {
208
372
  generate: "applet generate",
209
373
  deploy: "applet deploy",
210
374
  test: "vitest run",
211
- typecheck: "tsc --noEmit"
375
+ typecheck: "tsc --noEmit",
376
+ lint: "oxlint",
377
+ fmt: "oxfmt",
378
+ check: "oxfmt --check && oxlint"
212
379
  },
213
- dependencies: {
380
+ dependencies: sortedByName({
214
381
  "@hitch42/applet": "catalog:",
215
382
  "@orpc/contract": "catalog:",
216
383
  zod: "catalog:",
217
384
  ...options.hasServer ? { "@orpc/server": "catalog:" } : {},
218
385
  ...options.hasClient ? {
386
+ "@fontsource-variable/geist-mono": "catalog:",
219
387
  "@fontsource-variable/inter": "catalog:",
220
388
  "@tanstack/react-query": "catalog:",
221
389
  "@tanstack/react-router": "catalog:",
@@ -228,19 +396,18 @@ function appletPackage(definition, options) {
228
396
  "@tiptap/react": "catalog:",
229
397
  "@tiptap/starter-kit": "catalog:",
230
398
  "class-variance-authority": "catalog:",
231
- clsx: "catalog:",
399
+ cn: "catalog:",
232
400
  "lucide-react": "catalog:",
233
401
  react: "catalog:",
234
402
  "react-aria-components": "catalog:",
235
403
  "react-dom": "catalog:",
236
404
  shadcn: "catalog:",
237
405
  streamdown: "catalog:",
238
- "tailwind-merge": "catalog:",
239
406
  "tw-animate-css": "catalog:"
240
407
  } : {},
241
408
  ...definition.dependencies
242
- },
243
- devDependencies: {
409
+ }),
410
+ devDependencies: sortedByName({
244
411
  "@types/node": "catalog:",
245
412
  typescript: "catalog:",
246
413
  vite: "catalog:",
@@ -252,9 +419,21 @@ function appletPackage(definition, options) {
252
419
  tailwindcss: "catalog:"
253
420
  } : {},
254
421
  ...definition.devDependencies
255
- }
422
+ })
256
423
  };
257
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
+ }
258
437
  async function writeAppletSupport(root, definition) {
259
438
  const hasClient = existsSync(resolve(root, "src/client/routes"));
260
439
  const hasAuthorRoot = existsSync(resolve(root, "src/client/routes/__root.tsx"));
@@ -263,6 +442,7 @@ async function writeAppletSupport(root, definition) {
263
442
  if (!hasServer && (definition.schedules?.length ?? 0) > 0) throw new Error("An applet with schedules must have src/server/main.ts.");
264
443
  await Promise.all(hasClient ? [] : [rm(resolve(root, "index.html"), { force: true }), rm(resolve(root, "components.json"), { force: true })]);
265
444
  await Promise.all([
445
+ replaceLegacyCnHelper(root),
266
446
  writeFile(resolve(root, "package.json"), `${JSON.stringify(appletPackage(definition, {
267
447
  hasClient,
268
448
  hasServer
@@ -334,7 +514,9 @@ export default defineConfig({
334
514
  "./.hitch/*.ts",
335
515
  "./.hitch/*.tsx",
336
516
  "./src/**/*.ts",
337
- "./src/**/*.tsx"
517
+ "./src/**/*.tsx",
518
+ "./test/**/*.ts",
519
+ "./test/**/*.tsx"
338
520
  ]
339
521
  }
340
522
  `),
@@ -0,0 +1,29 @@
1
+ import { Writable } from "node:stream";
2
+ import { createInterface } from "node:readline";
3
+ //#region src/prompt.ts
4
+ function question(prompt, hidden = false) {
5
+ return new Promise((resolveAnswer, rejectAnswer) => {
6
+ let muted = false;
7
+ const output = hidden ? new Writable({ write(chunk, encoding, callback) {
8
+ if (!muted) process.stdout.write(chunk, encoding);
9
+ callback();
10
+ } }) : process.stdout;
11
+ const readline = createInterface({
12
+ input: process.stdin,
13
+ output,
14
+ terminal: true
15
+ });
16
+ readline.once("error", rejectAnswer);
17
+ if (hidden) {
18
+ process.stdout.write(prompt);
19
+ muted = true;
20
+ }
21
+ readline.question(hidden ? "" : prompt, (answer) => {
22
+ readline.close();
23
+ if (hidden) process.stdout.write("\n");
24
+ resolveAnswer(answer);
25
+ });
26
+ });
27
+ }
28
+ //#endregion
29
+ export { question as t };
@@ -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
@@ -1,4 +1,4 @@
1
- import { a as UploadedHitchFile } from "./upload-YbRKEqR6.js";
1
+ import { a as UploadedHitchFile } from "./upload-DKZzz5kW.js";
2
2
  import { ReactElement } from "react";
3
3
  import "@tiptap/core";
4
4
  //#region src/react/markdown.d.ts
@@ -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 };