@hitch42/applet 0.1.1-beta.1 → 0.1.1-beta.11

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 (178) 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 +53 -29
  7. package/dist/build-Bh_S5BUv.js +2 -0
  8. package/dist/{build-Rk3xDZV7.js → build-CEfFoIZZ.js} +30 -14
  9. package/dist/ci-BKw14X2h.js +69 -0
  10. package/dist/client.d.ts +3 -3
  11. package/dist/{contract-DMrpbaLJ.d.ts → contract-CsUpTBe0.d.ts} +1 -1
  12. package/dist/{deploy-D3BGDqgC.js → deploy-wGYjwXrx.js} +249 -35
  13. package/dist/email-DBTVhNB_.js +62 -0
  14. package/dist/{generate-CH0VQmUi.js → generate-BEIYs17x.js} +79 -25
  15. package/dist/generate-Dox-OYt6.js +2 -0
  16. package/dist/headers-DkNr9MLJ.js +17 -0
  17. package/dist/index.d.ts +123 -11
  18. package/dist/index.js +142 -26
  19. package/dist/{init-BS1Y-eNL.js → init-6oGPM46R.js} +17 -18
  20. package/dist/load-config-xEdTXs0y.js +27 -0
  21. package/dist/log-context-zu8U9Sjm.js +73 -0
  22. package/dist/login-CUx3TmFQ.js +12 -0
  23. package/dist/{logs-m00pzr_O.js → logs-DSjuEN0Q.js} +20 -22
  24. package/dist/{manifest-BQCAxATS.d.ts → manifest-C6_F4bL8.d.ts} +9 -0
  25. package/dist/{manifest-DyoJ1MIL.js → manifest-CJSL9GuC.js} +25 -3
  26. package/dist/me-BW7Paary.js +32 -0
  27. package/dist/{project-CxN-Dtc7.js → project-BRZGd6xq.js} +227 -34
  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-CP7Ce3Np.js → variables-DT5RyHdH.js} +14 -39
  40. package/dist/vite.d.ts +3 -60
  41. package/dist/vite.js +503 -34
  42. package/dist/workflow-engine-BOTzURKM.js +111 -0
  43. package/dist/{auth-DaVK0k5R.js → workspace-D0dnnlxL.js} +75 -40
  44. package/package.json +19 -6
  45. package/scaffold/.agents/skills/hitch-applets/SKILL.md +60 -15
  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 +29 -2
  49. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
  50. package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
  51. package/scaffold/.agents/skills/hitch-applets/references/records.md +32 -8
  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 +129 -0
  56. package/scaffold/.agents/skills/hitch-data/SKILL.md +52 -0
  57. package/scaffold/.agents/skills/hitch-data/references/attributes.md +127 -0
  58. package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
  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 +15 -0
  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-CEku1lyb.js +0 -2
  177. package/dist/generate-6Nbt7uii.js +0 -2
  178. package/dist/load-config-BnxocLAQ.js +0 -56
@@ -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.
@@ -0,0 +1,74 @@
1
+ # Performance
2
+
3
+ Design the data access of a screen or a job before you write it. Decide the number of requests, the page size, and the largest dataset the code can touch.
4
+
5
+ ## Prefer the client SDK
6
+
7
+ - Read and write from the browser with the client SDK for normal user work. It uses the current user's access and needs no server.
8
+ - Add a server procedure only when the work needs a secret, an external service, a schedule, a workflow, or fan-in of many reads into one response.
9
+ - Do not proxy a single Hitch call through the server. The proxy adds a hop and adds nothing.
10
+
11
+ ## Consolidate on the server when needed
12
+
13
+ - When a report or dashboard needs many Hitch reads, make one applet server request, not hundreds of browser requests. A server call started by a user request keeps that user's identity.
14
+ - On the server, collect the pages, join the sets, and return one shaped result.
15
+ - Cache a server result in TanStack Query with a JSON-serializable value only. Do not put `Set`, `Map`, `Date`, or class instances in the cache.
16
+ - Version the query key when the result shape changes.
17
+
18
+ ## Do not write N+1 reads
19
+
20
+ Never call `get` once per row of a list. Use one of these instead:
21
+
22
+ - **Include.** Pass `include` on the query to hydrate owner reference and user attributes in the same page. Include a reference only when each record links to a small number of targets. Include has no cap on the number of linked records it returns.
23
+ - **One page plus one related query.** Collect the IDs from the page, then run one `query` on the related object with `$in` on its reference attribute, for example `{ company_id: { $in: companyIds } }`. Hitch has no filter on record `id`. The related query is a page too, so pass its cursor when it can exceed 200 records.
24
+ - **Aggregate.** Use `aggregate` for a count, sum, average, minimum, or maximum. Group by up to two dimensions. Give each aggregate its own `filter` to get several metrics in one call. Never load records to count them in JavaScript.
25
+ - **Promise.all.** Run a known, small, independent set of requests concurrently. Do not run one request per row this way.
26
+
27
+ ## Filter in Hitch, not in the browser
28
+
29
+ - Put every filter, sort, and search term in the query body.
30
+ - A search box that filters the loaded page hides every record on later pages. That is not search.
31
+ - Ordered, text-match, and sort operations need the `indexed` flag on the attribute. Read `../../hitch-data/references/limits.md`.
32
+
33
+ ## Page every list
34
+
35
+ - Every list request returns at most 200 records. A larger `limit` is clamped to 200 with no error.
36
+ - Show a page size or a bounded scroll area in the product, with a next-page control that passes the returned `next_cursor` as `cursor`.
37
+ - Loop the cursor only when the code must visit the whole dataset. Name the bound in a comment, and stop at it.
38
+ - Do not write a whole-dataset loop for a number that `aggregate` can return.
39
+
40
+ ## Batch writes
41
+
42
+ Send many writes as one batch. A batch runs in order, in one transaction, and holds at most 100 entries.
43
+
44
+ ```ts
45
+ import { hitch } from "@hitch42/applet/server";
46
+
47
+ const companyId = crypto.randomUUID();
48
+
49
+ await hitch.batch(
50
+ (tx) => {
51
+ tx.companies.create({ body: { id: companyId, name: "Acme" } });
52
+ tx.deals.create({ body: { name: "Acme renewal", company_id: companyId } });
53
+ tx.deals.update({ params: { id: dealId }, body: { stage: "won" } });
54
+ tx.deals.delete({ params: { id: staleDealId } });
55
+ tx.deals.updateWhere({ body: { filter: { stage: "open" }, set: { stage: "lost" } } });
56
+ },
57
+ { idempotencyKey: `import-${runId}-${chunkIndex}` },
58
+ );
59
+ ```
60
+
61
+ - Call `hitch.batch(...)` on the server. Call `hitch.batch.call(...)` on the client.
62
+ - The `tx` calls buffer. Nothing sends until the callback resolves.
63
+ - A failed entry rolls back the whole batch. The error names the entry index and the entry error.
64
+ - A later entry can reference a record that an earlier entry created. Supply the record `id` as a UUID on create.
65
+ - Pass `dryRun: true` to validate a batch with no write. The response carries `dry_run: true`.
66
+ - Pass `idempotencyKey` on every batch that a retry can send twice. After a successful batch, the same key replays the stored response with `replayed: true`. The same key with a different body fails with HTTP 422. A failed batch or a dry run does not consume the key.
67
+ - Chunk a large import into batches of 100. Give each chunk its own idempotency key.
68
+ - Single `create`, `update`, and `delete` calls have no idempotency key. Use a batch when a retry must be safe.
69
+
70
+ ## Bound background work
71
+
72
+ - Process an import, sync, or backfill in batches. Save a checkpoint after each durable batch.
73
+ - Resume from the last checkpoint. Do not restart from the beginning.
74
+ - Make a skipped or failed item visible in the run summary. Never convert a failure into an empty result.
@@ -16,13 +16,14 @@ Use the generated Hitch contract as the typed data plane.
16
16
  - `list` sends `GET /v1/<objectCode>` and lists records with query parameters.
17
17
  - `create` sends `POST /v1/<objectCode>` and creates one record.
18
18
  - `get` sends `GET /v1/<objectCode>/{id}` and gets one record.
19
+ - `history` sends `GET /v1/<objectCode>/{id}/history` and gets record changes.
19
20
  - `update` sends `PATCH /v1/<objectCode>/{id}` and updates one record.
20
21
  - `delete` sends `DELETE /v1/<objectCode>/{id}` and deletes one record.
21
22
  - `query` sends `POST /v1/<objectCode>/query` and lists records with a structured body.
22
23
  - `updateWhere` sends `POST /v1/<objectCode>/update` and updates matching records.
23
24
  - `aggregate` sends `POST /v1/<objectCode>/aggregate` and calculates aggregate values.
24
25
 
25
- - Pass an object ID in `params.id` for `get`, `update`, and `delete`.
26
+ - Pass a record ID in `params.id` for `get`, `history`, `update`, and `delete`.
26
27
  - Pass record fields in `body` for `create` and `update`.
27
28
  - Pass structured query data in `body` for `query`.
28
29
 
@@ -73,6 +74,25 @@ const deal = await hitch.records.deals.get({
73
74
  - Do not use `.queryOptions(...)` or `.mutationOptions(...)` on the server.
74
75
  - Use the server SDK for imperative data access, secrets, or external services.
75
76
 
77
+ ## Read history
78
+
79
+ ```ts
80
+ const page = await hitch.records.deals.history({
81
+ params: { id },
82
+ query: { limit: 25, cursor },
83
+ });
84
+
85
+ for (const entry of page.data) {
86
+ for (const change of entry.changes) {
87
+ if (change.attributeCode === "stage") console.log(change.to);
88
+ }
89
+ }
90
+ ```
91
+
92
+ You can read record history if and only if you can read the record.
93
+ The history uses the current schema. `attributeId` tracks an attribute across renames. `attributeCode` is its current code. Older history cannot follow a rename, so some changes do not appear.
94
+ Use `next_cursor` as the next `cursor`.
95
+
76
96
  ## Build a query
77
97
 
78
98
  The `query` body accepts `filter`, `sort`, `limit`, `cursor`, and `include`.
@@ -86,7 +106,7 @@ const page = await hitch.records.deals.query({
86
106
  sort: { attribute: "amount", direction: "desc" },
87
107
  limit: 50,
88
108
  cursor,
89
- include: ["company"],
109
+ include: "company",
90
110
  },
91
111
  });
92
112
  ```
@@ -97,30 +117,33 @@ const page = await hitch.records.deals.query({
97
117
  - Use a direct field value as shorthand for `$eq`.
98
118
  - Use `$eq`, `$in`, or `$not_empty` for identity fields.
99
119
  - Use `$eq` or `$not_empty` for checkbox fields.
100
- - Use only `$not_empty` for attachment fields.
120
+ - Use `$eq`, `$in`, or `$not_empty` for attachment fields. Pass file IDs.
101
121
  - Use `$eq`, `$in`, `$not_empty`, `$contains`, `$starts_with`, or `$ends_with` for text fields.
102
122
  - Use `$eq`, `$in`, `$not_empty`, `$lt`, `$lte`, `$gt`, or `$gte` for ordered fields.
103
- - Pass `true` to `$not_empty`.
123
+ - Use `$contains`, `$starts_with`, `$ends_with`, `$lt`, `$lte`, `$gt`, `$gte`, and `sort` only on an attribute with the `indexed` flag. Read `../../hitch-data/references/limits.md`.
124
+ - Pass `true` to `$not_empty` for set values, or `false` for empty values.
104
125
  - Pass an array to `$in`.
105
126
  - Pass a string to `$contains`, `$starts_with`, and `$ends_with`.
106
127
  - Sort with `{ attribute, direction }`.
107
128
  - Use `asc` or `desc` as the sort direction.
108
- - Do not sort by multi-value, reference, user, or attachment attributes.
129
+ - Do not sort by multi-value, reference, user, attachment, or rich text attributes.
109
130
  - Omit `sort` to sort by `createdAt` in descending order.
110
131
  - Use a positive integer for `limit`.
111
132
  - The service caps `limit` at 200 and uses 50 by default.
112
- - Pass the returned pagination cursor back as `cursor`.
113
- - Pass one attribute code or an array of attribute codes as `include`.
133
+ - Pass the returned `next_cursor` back as `cursor`. A null `next_cursor` means the last page.
134
+ - Pass `include` as one string. Use the include name, which is the code without its `_id` or `_ids` suffix. Separate several names with commas.
114
135
 
115
136
  ## Aggregate records
116
137
 
117
138
  Pass `filter`, `aggregates`, and `groupBy` in the aggregate body.
118
- An aggregate or group attribute can name one reference step and a target attribute, for example `company_id.industry`.
139
+ An aggregate or group attribute can name one single-value owner reference and a target attribute, for example `company_id.industry`.
119
140
  Each aggregate can have its own `filter` with the same syntax as the top-level `filter`, including nested reference filters.
120
141
  `groupBy` accepts an array of up to two dimensions.
121
142
  The array form returns `keys` and `labels` arrays per group.
122
143
  The object form orders category groups by an aggregate value (top-N by value).
123
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.
124
147
 
125
148
  ```ts
126
149
  const totals = await hitch.records.deals.aggregate({
@@ -147,6 +170,7 @@ The generated contract contains only objects declared in the manifest.
147
170
 
148
171
  - The contract removes `create` when `canCreate` is false.
149
172
  - The contract removes `delete` when `canDelete` is false.
173
+ - The contract removes `update` and `updateWhere` when `readWrite` is empty.
150
174
  - Treat a missing method as a missing manifest grant, and edit the manifest when required.
151
175
  - Run generation after a manifest or Hitch schema change.
152
176
  - Do not bypass the contract with a cast.
@@ -2,6 +2,12 @@
2
2
 
3
3
  Use a schedule to call an applet server procedure from a cron expression.
4
4
 
5
+ ## Local dev
6
+
7
+ The dev server does not run cron.
8
+ Run `pnpm --dir applets/<code> exec applet schedules run <schedule>` from the workspace root while `pnpm --dir applets/<code> run dev` runs.
9
+ The run acts as you, not as the applet. The run writes real data.
10
+
5
11
  ## Define the target procedure
6
12
 
7
13
  - Add the target to the applet's server router.
@@ -44,7 +50,7 @@ schedules: [
44
50
  - The five fields are minute, hour, day of month, month, and day of week.
45
51
  - The registered router type checks the path after type generation.
46
52
  - A nested procedure uses its nested router path.
47
- - The scheduler invokes the named router procedure with no input.
53
+ - The scheduler invokes the named router procedure with the input `{}`. Accept an empty object or declare no input.
48
54
 
49
55
  ## Understand the run identity
50
56
 
@@ -82,5 +88,4 @@ const uploaded = await hitch.files.upload(file);
82
88
  - Read the stored status, path, HTTP status, and error details.
83
89
  - Check the applet logs for procedure details.
84
90
  - Follow the log commands in the main Hitch applet skill.
85
- - Include `--level log` during diagnosis.
86
91
  - Do not log secrets, tokens, or personal data.
@@ -29,9 +29,6 @@ export default { router };
29
29
  ```
30
30
 
31
31
  - Nest procedure objects when the applet needs nested paths.
32
- - Do not name a top-level procedure `hitch`.
33
- - The client reserves `hitch` for the Hitch data client.
34
- - The generated API client type rejects a router with that reserved key.
35
32
 
36
33
  ## Call the router from the client
37
34
 
@@ -2,6 +2,15 @@
2
2
 
3
3
  Use a workflow for server work that runs in steps, retries a step, or waits between steps.
4
4
 
5
+ ## Local dev
6
+
7
+ In `pnpm --dir applets/<code> run dev`, workflows run in the dev server as you. The server keeps runs in memory.
8
+ Runs disappear when the dev server stops.
9
+ In local dev, a step fails when it runs longer than 5 minutes. A deployed step can run for 15 minutes.
10
+ Set `HITCH_DEV_SKIP_SLEEP=1` in the shell that runs `pnpm --dir applets/<code> run dev` to skip sleeps and step retry delays.
11
+ A `.env` file does not set this value for the dev server.
12
+ With this value, a polling loop that sleeps between checks calls Hitch without pause.
13
+
5
14
  ## Define a workflow
6
15
 
7
16
  - Import `createWorkflow` from `@hitch42/applet/server`.
@@ -63,11 +72,10 @@ export default { router, workflows };
63
72
  - Each step has the same limits as a scheduled run: 30,000 milliseconds of CPU and 1,000 subrequests.
64
73
  - A step that still fails after its retries throws inside the workflow function. Catch it to run a compensating step. An error you do not catch fails the run.
65
74
  - A run keeps the release it started on. A new release does not stop or change a run that is in progress or asleep.
66
- - An owner or admin can terminate an open run from the applet settings page. A terminated run has the status `terminated` and does not resume.
75
+ - The owner or a member with the `applets:manage` capability can terminate an open run from the applet settings page. A terminated run has the status `terminated` and does not resume.
67
76
 
68
77
  ## Diagnose a failed run
69
78
 
70
79
  - Call `get(id)` and read `error`.
71
80
  - Workflow step output is in the applet logs with the type `workflow`, the run id, and the workflow code.
72
- - Run `applet logs <code> --run <id>` to see one run.
73
- - Include `--level log` during diagnosis.
81
+ - Run `pnpm --dir applets/<code> exec applet logs --run <id>` from the workspace root to see one run.
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: hitch-cli
3
+ description: Use the `hitch` command line to sign in, select an organization, and read or write Hitch data and administration from the shell. Use when a task needs to inspect records, try a filter or aggregate, create test data, manage teams, grants, or capabilities, apply a changeset, or check applet access without writing applet code.
4
+ ---
5
+
6
+ # Hitch CLI
7
+
8
+ The `hitch` command talks to one organization on one Hitch instance. Its data and admin commands are generated from that organization's API, so the command set matches the organization's objects.
9
+
10
+ `hitch` is separate from `applet`. Use `applet` to create, generate, build, and deploy applets. Use `hitch` to work with the organization's data and settings. Read the `hitch-applets` skill for `applet`.
11
+
12
+ ## Sign in
13
+
14
+ - Run `hitch login` to sign in with the device flow. The command prints a URL and a code. Open the URL and confirm the code. Add `--no-browser` when the shell cannot open a browser.
15
+ - `applet login` uses this same device flow and credential store. Both commands preselect the organization from `hitch.config.ts` at the workspace root or in an applet directory. Use `hitch login --org <code>` to select it explicitly.
16
+ - Run `hitch login --api-key` to store an API key instead. The command prompts for the key. Pass `--api-key <key>` to skip the prompt.
17
+ - Set `HITCH_API_KEY` to use a key for one shell session. It overrides every stored login. While it is set, `hitch login`, `applet login`, and `hitch logout` refuse to run.
18
+ - Run `hitch whoami` to print the current person, email, organization name, and organization id as JSON.
19
+ - Run `hitch logout` to remove the stored logins and cached command sets for the current instance.
20
+ - Set `HITCH_API_URL` for an instance other than the default. `hitch login --instance <url>` affects only that login. Set `HITCH_API_URL` too, so later commands use the same instance.
21
+
22
+ ## Select the organization
23
+
24
+ The CLI resolves the organization in this order. The first match wins.
25
+
26
+ 1. `--org <code>` on any command.
27
+ 2. The workspace organization: the `org` field in `hitch.config.ts` in the current directory, or in the workspace root when the current directory is an applet under `applets/`.
28
+ 3. `HITCH_DEFAULT_ORG`.
29
+
30
+ There is no remembered active organization. When `--org` and the workspace disagree, the command fails with `--org "x" does not match workspace organization "y"`. When several organizations are stored and none is selected, the command fails and names the three ways to choose.
31
+
32
+ ## Refresh the commands
33
+
34
+ - `hitch login` syncs the commands. Run `hitch sync` after a schema change, and whenever `hitch --help` lacks an object. The command downloads the organization's API and caches the generated commands. It prints `Synced 3 objects.`
35
+ - If the CLI prints `the cached commands ... no longer load — run hitch sync`, run `hitch sync`. Until then only the built-in commands work.
36
+ - The `admin` commands appear only when the signed-in identity holds an admin capability. A missing `admin` command means no capability, not a broken CLI.
37
+
38
+ ## Read the command names
39
+
40
+ Every generated command follows the API route.
41
+
42
+ - Data commands: `hitch <object> <verb>`. The object is its code, for example `deals`. The verbs are `list`, `get`, `create`, `update`, `delete`, `query`, `update-where`, and `aggregate`. `hitch batch` sends a batch.
43
+ - Admin commands: `hitch admin <route segments> <verb>`. The verb comes from the HTTP method: `list` or `get` for GET, `create` for POST, `update` for PATCH, `set` for PUT, `delete` for DELETE. For example `PUT /admin/v1/teams/{id}/members/{userId}` is `hitch admin teams members set`.
44
+ - Every path, query, and body field becomes a flag in kebab case: `--id`, `--team-id`, `--can-read`. An object field takes JSON: `--filter '{"stage":"won"}'`. An array field takes one JSON value per item: `--aggregates '{"op":"sum","attribute":"amount","as":"total"}' '{"op":"count","as":"n"}'`.
45
+ - A required field is a required flag. The CLI does not prompt for a missing value. It prints the usage error.
46
+ - Run `hitch <object> <verb> --help` to see the flags for that organization's schema.
47
+ - Every `delete` command asks for confirmation. Pass `--yes` to skip it. A script without a terminal must pass `--yes`.
48
+ - A command prints its result as JSON. A list prints one JSON object with `data` and `next_cursor`. A command with no result prints nothing.
49
+ - An error prints `hitch: <message>` and exits with code 1.
50
+
51
+ ## Work with records
52
+
53
+ Examples use an object called `deals` with attributes `amount`, `stage`, and `owner_id`. Ids are UUIDs.
54
+
55
+ ```sh
56
+ hitch deals list --limit 20 --include owner
57
+ hitch deals get --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --include owner
58
+ hitch deals create --name "Acme renewal" --amount 5000 --stage open
59
+ hitch deals update --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --stage won
60
+ hitch deals delete --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --yes
61
+ hitch deals query --filter '{"stage":{"$in":["open","won"]}}' --sort '{"attribute":"createdAt","direction":"desc"}' --limit 50
62
+ hitch deals update-where --filter '{"stage":"open"}' --set '{"stage":"lost"}' --expect 3
63
+ hitch deals aggregate --aggregates '{"op":"sum","attribute":"amount","as":"total"}' --group-by '[{"attribute":"stage"}]'
64
+ hitch batch --requests '{"method":"POST","path":"/v1/deals","body":{"name":"A","stage":"open"}}'
65
+ ```
66
+
67
+ - The filter, sort, include, aggregate, and batch bodies use the same shapes as the SDK. Read the `hitch-data` skill for the rules and limits.
68
+ - A list returns one page of at most 200 records and a cursor. Pass `--cursor <value>` for the next page.
69
+ - Data commands act as the signed-in person with that person's grants. An API key acts as the member who made it.
70
+
71
+ ## Administer the organization
72
+
73
+ Admin commands need the matching capability. Examples use fake ids.
74
+
75
+ ```sh
76
+ hitch admin teams list
77
+ hitch admin teams create --name Sales
78
+ hitch admin teams members set --id <teamId> --user-id <userId>
79
+ hitch admin capabilities list
80
+ hitch admin capabilities grants set --capability objects:manage --team-id <teamId>
81
+ hitch admin objects list
82
+ hitch admin objects access list --object-id <objectId>
83
+ hitch admin teams object-grants set --team-id <teamId> --object-id <objectId> --can-create --can-read --can-update --no-can-delete
84
+ hitch admin teams object-grants conditions create --team-id <teamId> --object-id <objectId> --operator equals --operand current_user --path <attributeId>
85
+ hitch admin objects default-grant set --object-id <objectId> --can-read --no-can-create --no-can-update --no-can-delete
86
+ hitch admin applets access list --code sales_portal
87
+ hitch admin applets grants set --code sales_portal --team-id <teamId>
88
+ ```
89
+
90
+ - Object grants for a team live under `admin teams object-grants`. The default grant that every member holds lives under `admin objects default-grant`. Each has `conditions` and `attributes` subcommands.
91
+ - Add `--delegate-attribute-id <attributeId>` to an object grant to delegate through a reference attribute.
92
+ - `admin objects access list` shows which grantees cannot read an object. `admin applets access list` shows which grantees cannot use what an applet reads.
93
+
94
+ ## Change the schema
95
+
96
+ Schema changes go through a changeset. The CLI names the lifecycle as state, not as actions.
97
+
98
+ ```sh
99
+ hitch admin changesets create
100
+ hitch admin objects create --name Deal --changeset-id <changesetId>
101
+ hitch admin objects attributes create --code deals --name Amount --type currency --changeset-id <changesetId>
102
+ hitch admin changesets blockers list --id <changesetId>
103
+ hitch admin changesets update --id <changesetId> --status applied
104
+ hitch admin changesets update --id <changesetId> --status abandoned
105
+ hitch admin changesets update --id <changesetId> --adopt <otherChangesetId>
106
+ ```
107
+
108
+ - A schema command without `--changeset-id` applies at once when it has no blockers. With a blocker, Hitch stages it in a new pending changeset and names it in the error.
109
+ - Add `--dry-run` to evaluate a schema command without a change.
110
+ - Add `--indexed` to an attribute that a query must sort or filter with ordered or text operators. Add `--is-multi` for a multi-value attribute. Pass `--type-config` as JSON. Pass `--options` as one JSON value per option.
111
+ - Run `blockers list` before an apply. Any blocker stops the apply with HTTP 409.
112
+ - Run `hitch sync` after an apply, so the new objects and attributes appear as commands.
113
+ - Run `pnpm --dir applets/<code> run generate` after an apply, so applet types match.
114
+ - To build applet code before the apply, set `requiresChangeset` in `applet.manifest.ts` to the changeset ID. Generate, build, and deploy then use the changeset schema, and the release goes live when the changeset applies.
115
+
116
+ ## Fix common errors
117
+
118
+ | Message | Cause and fix |
119
+ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
120
+ | `Not logged in. Run hitch login.` | No stored login and no `HITCH_API_KEY`. Sign in. |
121
+ | `HITCH_API_KEY was rejected.` | The key in the environment is invalid or revoked. Unset it or replace it. |
122
+ | `API key is scoped to organization "x", not "y".` | The key belongs to another organization. Select `x` or use another key. |
123
+ | `--org "x" does not match workspace organization` | Run the command outside the workspace, or drop `--org`. |
124
+ | `Multiple organizations are stored ...` | Pass `--org`, use the workspace `hitch.config.ts`, or set `HITCH_DEFAULT_ORG`. |
125
+ | `the cached commands ... no longer load` | Run `hitch sync`. |
126
+ | `Refusing to delete without --yes` | Add `--yes` in a script. |
127
+ | `hitch <object>` is an unknown command | Run `hitch whoami`. If you are signed in, run `hitch sync`. The object is new or the cache is stale. |
128
+ | `User's teams lack the `read` grant on this object.` | The identity has no grant on that object. Ask for a team grant, or use another identity. |
129
+ | `hitch admin` is missing | The identity holds no admin capability. |
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: hitch-data
3
+ description: The Hitch data model and data API rules. Use when a task defines or changes objects and attributes, models a relationship, writes or validates record values, builds a filter or sort, or hits an API limit. Covers attribute types, references, required and null rules, the indexed flag, and the enforced read, write, and aggregate limits.
4
+ ---
5
+
6
+ # Hitch data
7
+
8
+ An organization's data lives in Hitch objects. Each object has attributes. Each record holds one value per attribute. Hitch validates, normalizes, and stores every value. Applets read and write records through the SDK and never keep a private copy.
9
+
10
+ ## Go deeper
11
+
12
+ | Read this | When the task involves |
13
+ | -------------------------- | ------------------------------------------------------------------------------------------------ |
14
+ | `references/attributes.md` | The behavior of one attribute type: accepted values, null and empty rules, normalization, config |
15
+ | `references/limits.md` | A page size, a batch size, an aggregate limit, a filter that fails, or a sort that fails |
16
+
17
+ ## Objects and records
18
+
19
+ - An object has a code, a name, and attributes. A record belongs to one object.
20
+ - Every record has an `id`, a required `name`, `createdAt`, `updatedAt`, and `deletedAt`.
21
+ - A record is JSON. Every owner attribute key is present on read. An empty attribute reads as `null`. An inverse reference face has no key.
22
+ - A record can be created with a client-supplied UUID `id`. Use this to link records inside one batch.
23
+
24
+ ## Attributes
25
+
26
+ - An attribute is schema, not a free JSON field. Choose a type. Hitch validates the value against the type and its configuration.
27
+ - Types: text, rich_text, integer, decimal, currency, checkbox, date, datetime, time, domain, url, email, phone, select, reference, user, organization, attachment.
28
+ - A value is a non-empty value or `null`. No type accepts an empty string.
29
+ - An organisation attribute points at a connected organisation; null means internal only; at most one exists per object.
30
+ - Cardinality is configuration. Text, select, reference, user, and attachment can be multi-value.
31
+ - A required attribute rejects `null` and rejects an empty array.
32
+ - Store a fixed set of values as a select, not as text. Store a link to another record as a reference, not as text or as an id in a text field. Text cannot be a group dimension, and text matching needs the `indexed` flag.
33
+
34
+ ## References
35
+
36
+ - A reference attribute names a target object. Write the target record id.
37
+ - A reference can expose an inverse face on the target object. The owner face stores the link. The inverse face is read-only and is absent from the record JSON.
38
+ - One-to-many is a single reference on one side and a multi inverse on the other. Many-to-many is multi on both sides. `targetSingle` limits each target record to one link through the attribute.
39
+ - Do not model a plain relationship as a separate join object. A reference gives typed links, link cleanup when a target is deleted, filters through either face, and access through grants. Use a join object only when the relationship needs its own fields or lifecycle.
40
+
41
+ ## Filters and sorting
42
+
43
+ - Every attribute accepts `$eq` and `$not_empty`. Every attribute except checkbox accepts `$in`.
44
+ - Ordered operators, text-match operators, and `sort` need the `indexed` flag on the attribute. A new attribute starts with the flag off. Ask for the flag on the attributes a query needs, not on every attribute.
45
+ - A filter can nest through a reference, through an inverse face, and can express an anti-join with `$not`.
46
+ - Aggregating or grouping by an attribute does not need the flag. An aggregate filter follows the same flag rules as a query filter. Sum, average, minimum, and maximum need a single-value numeric attribute. A group dimension is a checkbox, select, reference, user, or a bucketed date or datetime.
47
+
48
+ ## Schema changes
49
+
50
+ - Every object or attribute change goes through a changeset. Hitch evaluates the change against existing records and dependent applets.
51
+ - A change that would break existing data, such as a unique constraint with duplicates, is blocked until the data is fixed.
52
+ - An applet release can bind to a pending changeset. Set `requiresChangeset` in `applet.manifest.ts` to bind it. The changeset apply switches the schema and promotes the release together.
@@ -0,0 +1,127 @@
1
+ # Attribute types
2
+
3
+ Hitch validates and normalizes every attribute value on write. Read this before you build a form, an import, or a write path.
4
+
5
+ ## Rules for every type
6
+
7
+ - A value is a non-empty value or `null`. Every type rejects an empty string.
8
+ - Send `null` to clear a value. A required attribute rejects `null` on create and on update.
9
+ - A single-value attribute rejects an array. A multi-value attribute rejects a scalar.
10
+ - A multi-value attribute holds an ordered array. An empty array is accepted when the attribute is not required, and reads back as `null`.
11
+ - Only text, select, reference, user, and attachment can be multi-value. Text allows duplicate entries. The other four reject duplicates.
12
+ - A `default` in the attribute configuration applies on create only, when the body omits the attribute.
13
+ - An `immutable` attribute rejects a change once it holds a non-null value. A null immutable attribute can still be set once.
14
+ - A `unique` attribute rejects a duplicate value with HTTP 409. Unique is never allowed on a multi-value attribute.
15
+ - Every owner attribute key is present on read. An empty attribute reads as `null`. An inverse reference face has no key.
16
+ - Every attribute accepts the `$eq` and `$not_empty` filters. Every attribute except checkbox and organization accepts `$in`. Other operators and `sort` need the `indexed` flag. Read `limits.md`.
17
+
18
+ ## Text and rich text
19
+
20
+ | Type | Write | Normalization | Config | Unique | Multi |
21
+ | --------- | ------------------------------------------- | ------------- | --------------------------------------------------------- | ------ | ----- |
22
+ | text | Non-empty string, at most 256 characters | None | default, immutable, unique, minLength, maxLength, pattern | Yes | Yes |
23
+ | rich_text | Non-empty string, no cap unless `maxLength` | None | default, immutable, minLength, maxLength | No | No |
24
+
25
+ - `pattern` is a JavaScript regular expression string. It is not anchored.
26
+ - Rich text is stored and returned as written. Hitch does not sanitize or parse it. Rich text supports identity filters only and never sorts.
27
+
28
+ ## Numbers
29
+
30
+ | Type | Write | Read shape | Config | Unique |
31
+ | -------- | ------------------------------------------------------- | ---------- | ------------------------------------------- | ------ |
32
+ | integer | A JavaScript number that is a safe integer | number | default, immutable, unique, min, max | Yes |
33
+ | decimal | A decimal string or a number, at most 12 integer digits | string | default, immutable, unique, min, max, scale | Yes |
34
+ | currency | Same as decimal, scale defaults to 2 | string | decimal config plus `code` | Yes |
35
+
36
+ - Integer rejects a numeric string on write.
37
+ - Decimal returns a canonical string. Leading zeros are removed. With `scale`, the value is padded to exactly that many places. Without `scale`, trailing zeros are removed. Excess precision is rejected, never rounded.
38
+ - Decimal `min` and `max` are decimal strings. `scale` is 0 to 8 for decimal and 0 to 4 for currency.
39
+ - Currency `code` is three uppercase letters. It is metadata on the attribute, not part of the value. Hitch does no conversion.
40
+
41
+ ## Checkbox
42
+
43
+ - Write a boolean. Hitch does not coerce strings on write.
44
+ - Config: default, immutable. No unique, no multi.
45
+ - Filters: `$eq` and `$not_empty` only.
46
+
47
+ ## Date and time
48
+
49
+ | Type | Write | Read shape | Timezone | Config |
50
+ | -------- | ---------------------------------- | ----------------------- | --------------------- | ------------------------------------ |
51
+ | date | `YYYY-MM-DD`, a real calendar date | `YYYY-MM-DD` | None | default, immutable, unique, min, max |
52
+ | datetime | Any string `Date.parse` accepts | ISO 8601 in UTC, `...Z` | Converted to UTC | default, immutable, unique, min, max |
53
+ | time | `HH:MM` or `HH:MM:SS`, 24-hour | `HH:MM:SS` | None, wall clock only | default, immutable, unique |
54
+
55
+ - A datetime with an offset reads back in UTC. Store the organization timezone elsewhere when the display needs it.
56
+ - Date `min` and `max` are `YYYY-MM-DD` strings. Datetime `min` and `max` are datetime strings. Time has no `min` or `max`.
57
+
58
+ ## Domain, URL, email, phone
59
+
60
+ | Type | Write | Normalization | Config | Unique |
61
+ | ------ | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------- | ------ |
62
+ | domain | Host name with at least two labels, no scheme, no path | Trimmed, lowercased, one trailing dot removed | default, immutable, unique | Yes |
63
+ | url | `http` or `https` URL, at most 2048 bytes | Canonical WHATWG form: lowercased host, trailing slash on a bare origin | default, immutable, unique | Yes |
64
+ | email | One `@`, a dot in the domain part, at most 256 characters | Trimmed, lowercased | default, immutable, unique | Yes |
65
+ | phone | 7 to 14 digits, optional leading `+`, with space, `(`, `)`, `.`, `-` separators | Separators removed, leading `+` kept | default, immutable | No |
66
+
67
+ - Email has no deliverability check.
68
+ - Phone has no region parsing. Hitch adds no country code. Send the full international form when the organization needs it.
69
+
70
+ ## Select
71
+
72
+ - Write the option label, not the option id. Only an active option label matches.
73
+ - Hitch stores the option id and returns the label.
74
+ - A deleted option reads as `null` for a single select. It is dropped from the array for a multi select.
75
+ - Config: default, immutable. No unique. Multi-value allowed, duplicates rejected.
76
+ - Filters: identity operators only. Sort: allowed on a single select with `indexed`.
77
+
78
+ ## Reference
79
+
80
+ - Write the target record UUID, or an array of UUIDs for a multi reference. The target must be a live record of the declared target object.
81
+ - A reference has two faces. The owner face stores the link. The inverse face on the target object is read-only and is absent from the record JSON. A write to it fails with `read_only_field`.
82
+ - `targetSingle` means each target record can be linked at most once through this attribute. A second link fails with HTTP 409 `reference_conflict`.
83
+ - Config: none. A reference accepts no default, immutable, or unique.
84
+ - Read: the UUID or array of UUIDs under the attribute code. With `include`, the expanded record appears under the include name, which is the code without its `_id` or `_ids` suffix. Include is one level deep.
85
+ - Filters: identity operators on the id, plus a nested filter on the target object's attributes. Sort: never.
86
+
87
+ ## User
88
+
89
+ - Write the user UUID. The user must be a member of the organization.
90
+ - Config: default, immutable, unique. `default` is a UUID. Multi-value allowed, duplicates rejected.
91
+ - Read: the UUID. With `include`, the expansion is `{ id, firstName, lastName, email }`.
92
+ - Filters: identity operators only. Sort: never.
93
+
94
+ ## Organisation
95
+
96
+ - An organisation attribute points at a connected organisation; null means internal only; at most one exists per object.
97
+ - Write an accepted peer organization UUID. The value stays visible after the connection ends.
98
+ - With `include`, the expansion is `{ id, name }`. Filters are `$eq` and `$not_empty` only.
99
+
100
+ ## Attachment
101
+
102
+ - Write the file id from a completed upload. An unknown id fails with `file not found`. A file with no uploaded bytes fails with `file has not arrived`. Read `../../hitch-applets/references/files.md`.
103
+ - Config: immutable only. No default, no unique. Multi-value allowed, duplicates rejected.
104
+ - Read: the file id. `include` does not expand attachments. Call the files API for a download link.
105
+ - Filters: identity operators only. Sort: never.
106
+
107
+ ## Errors
108
+
109
+ | Situation | Status and code |
110
+ | -------------------------------------- | -------------------------------------------------------- |
111
+ | Value fails validation | 400 `invalid_field` |
112
+ | Required attribute missing on create | 400 `invalid_field` |
113
+ | Unknown attribute code in the body | 400 `unknown_field` |
114
+ | Write to an inverse reference face | 400 `read_only_field` |
115
+ | Change to a set immutable value | 400 `immutable_field` |
116
+ | Missing or blank record name | 400 `invalid_name` |
117
+ | Client-supplied `id` already exists | 409 `id_conflict` |
118
+ | Reference target missing | 400 `invalid_field` |
119
+ | User not a member | 400 `invalid_field` |
120
+ | Unique value already used | 409 `unique_conflict` |
121
+ | Single-target reference already linked | 409 `reference_conflict` |
122
+ | Operator not allowed for the attribute | 400 `invalid_filter` |
123
+ | Attribute cannot sort | 400 `invalid_sort` |
124
+ | Bad `limit`, `cursor`, or `include` | 400 `invalid_limit`, `invalid_cursor`, `invalid_include` |
125
+ | Bad aggregate or group | 400 `invalid_aggregate`, `invalid_group_by` |
126
+
127
+ Every record also has a `name`. It is a required non-empty string on create.
@@ -0,0 +1,64 @@
1
+ # Hitch limits
2
+
3
+ Hitch limits the data API on purpose. The limits force good software design. No screen accidentally loads 500,000 records. No report runs one request per row. No import writes half of its rows and then fails. The limits also protect the organization: one applet or one user cannot ruin the experience for everyone else, and the cost of running the data stays sensible. When a limit stops you, the design is wrong, not the limit. Change the design: page the list, aggregate in Hitch, batch the writes, or narrow the filter.
4
+
5
+ Some limits clamp with no error. Some reject the request. Design for both.
6
+
7
+ ## Reads
8
+
9
+ A page is the unit of a read. A screen shows one page and asks for the next one. A page holds at most 200 root records. An included reference has no cap on linked records, so include only small links.
10
+
11
+ | Limit | Value | Past the limit |
12
+ | ---------------------------------- | ----------- | ----------------------------------------------------- |
13
+ | `limit` on `list` and `query` | 50 default | Clamped to 200, no error |
14
+ | `limit` on `list` and `query`, max | 200 | `list` clamps to 200; the `query` schema rejects more |
15
+ | Filter nesting depth | 20 | HTTP 400 |
16
+ | `include` depth | 1 | HTTP 400 for a dotted path |
17
+ | `include` on an inverse reference | Not allowed | HTTP 400 |
18
+ | Records per included reference | No cap | Every linked record the caller can read returns |
19
+ | IDs in one `$in` array | No cap | Only 200 records return per page |
20
+
21
+ ## Aggregates
22
+
23
+ An aggregate answers a question about many records with a small result. The caps keep the result small enough to render. A question that needs more cells is two questions.
24
+
25
+ | Limit | Value | Past the limit |
26
+ | --------------------------------------- | ------------------------------------------------------------ | --------------------------------------- |
27
+ | Aggregates per call | 20 | HTTP 400 |
28
+ | Group dimensions | 2 | HTTP 400 |
29
+ | Multi-value group dimensions | 1 | HTTP 400 |
30
+ | Distinct keys per dimension, default | 50 | Extra groups omitted, `truncated: true` |
31
+ | Distinct keys per dimension, max | 200 | HTTP 400 |
32
+ | Cells, the product of dimension limits | 2500 | HTTP 400 |
33
+ | Reference steps in an aggregate path | 1 | HTTP 400 |
34
+ | Sum, average, min, max on a multi-value | Not allowed | HTTP 400 |
35
+ | Group dimension type | checkbox, select, reference, user, bucketed date or datetime | HTTP 400 |
36
+ | Aggregate alias `as` | 64 characters | HTTP 400 |
37
+ | Sum, average, min, max on a non-numeric | Not allowed | HTTP 400 |
38
+
39
+ ## Writes
40
+
41
+ A write is bounded and atomic. A batch either commits every entry or none. An `updateWhere` refuses a filter that matches more than it can hold in one page, so a loose filter cannot rewrite a whole object.
42
+
43
+ | Limit | Value | Past the limit |
44
+ | ------------------------------------- | ----------- | --------------------------------------- |
45
+ | Entries per batch | 100 | HTTP 400 before any write |
46
+ | Records per `updateWhere` | 200 | HTTP 400 `update_too_broad`, no write |
47
+ | `expect` on `updateWhere` | Exact count | HTTP 409 `expectation_failed`, no write |
48
+ | Idempotency key with a different body | Not allowed | HTTP 422 `idempotency_key_reuse` |
49
+ | Idempotency key retention | 24 hours | The key is forgotten |
50
+
51
+ ## Filters and sorting
52
+
53
+ Advanced filters and sorting are opt-in per attribute. This keeps each record cheap to write, and makes the attributes a query depends on an explicit decision.
54
+
55
+ - Every attribute accepts `$eq` and `$not_empty`. Every attribute except checkbox accepts `$in`.
56
+ - Ordered operators, text-match operators, and `sort` need the `indexed` flag on the attribute. Without it the request fails with HTTP 400 and a message that starts with `Enable advanced filters and sort for`.
57
+ - New attributes start with `indexed` off. Ask the organization admin to turn it on for the attributes a query needs. Do not ask for it on every attribute.
58
+ - No attribute allows `sort` when it is multi-value, rich text, reference, user, or attachment.
59
+ - Aggregating or grouping by an attribute does not need `indexed`. An aggregate filter follows the same `indexed` rules as a query filter. Sum, average, minimum, and maximum need a single-value numeric attribute.
60
+
61
+ ## Grant conditions
62
+
63
+ - A condition path has at most two hops.
64
+ - A record whose condition target is empty is invisible to that grant. A `get` on it returns HTTP 404.