@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.
- package/dist/_internal/client.js +120 -11
- package/dist/_internal/log-context.d.ts +34 -0
- package/dist/_internal/log-context.js +2 -0
- package/dist/_internal/server.d.ts +14 -1
- package/dist/_internal/server.js +122 -118
- package/dist/applet.js +22 -11
- package/dist/build-Bh_S5BUv.js +2 -0
- package/dist/{build-DauirTcY.js → build-CEfFoIZZ.js} +1 -1
- package/dist/{ci-gPrwo6C0.js → ci-BKw14X2h.js} +14 -13
- package/dist/client.d.ts +3 -3
- package/dist/{contract-DB9QQhD9.d.ts → contract-CsUpTBe0.d.ts} +1 -1
- package/dist/{deploy-CqrtNJ1x.js → deploy-wGYjwXrx.js} +139 -88
- package/dist/email-DBTVhNB_.js +62 -0
- package/dist/{generate-BIifT6PK.js → generate-BEIYs17x.js} +70 -32
- package/dist/generate-Dox-OYt6.js +2 -0
- package/dist/headers-DkNr9MLJ.js +17 -0
- package/dist/index.d.ts +122 -10
- package/dist/index.js +142 -26
- package/dist/{init-Wu6XmyVc.js → init-6oGPM46R.js} +15 -17
- package/dist/{load-config-BknBkdRs.js → load-config-xEdTXs0y.js} +2 -2
- package/dist/log-context-zu8U9Sjm.js +73 -0
- package/dist/{login-iT9MRuaw.js → login-CUx3TmFQ.js} +1 -1
- package/dist/{logs-B-qZGXSK.js → logs-DSjuEN0Q.js} +20 -22
- package/dist/{manifest-C489nYUy.d.ts → manifest-C6_F4bL8.d.ts} +7 -0
- package/dist/{manifest-Bd04cWhu.js → manifest-CJSL9GuC.js} +22 -3
- package/dist/me-BW7Paary.js +32 -0
- package/dist/{project-C_rtpjp8.js → project-BRZGd6xq.js} +202 -20
- package/dist/prompt-ChS-grYO.js +29 -0
- package/dist/query-cache-CXmAyLv3.js +82 -0
- package/dist/react.d.ts +3 -2
- package/dist/react.js +98 -80
- package/dist/schedules-C8_Svr4h.js +42 -0
- package/dist/server.d.ts +9 -4
- package/dist/server.js +29 -8
- package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
- package/dist/telephony.d.ts +97 -0
- package/dist/telephony.js +726 -0
- package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
- package/dist/{variables-DcwMeIau.js → variables-DT5RyHdH.js} +14 -39
- package/dist/vite.d.ts +3 -60
- package/dist/vite.js +497 -27
- package/dist/workflow-engine-BOTzURKM.js +111 -0
- package/dist/{workspace-B6lenAS5.js → workspace-D0dnnlxL.js} +37 -39
- package/package.json +18 -5
- package/scaffold/.agents/skills/hitch-applets/SKILL.md +50 -18
- package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
- package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
- package/scaffold/.agents/skills/hitch-applets/references/manifest.md +12 -2
- package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
- package/scaffold/.agents/skills/hitch-applets/references/performance.md +1 -1
- package/scaffold/.agents/skills/hitch-applets/references/records.md +27 -4
- package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
- package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
- package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
- package/scaffold/.agents/skills/hitch-cli/SKILL.md +20 -20
- package/scaffold/.agents/skills/hitch-data/SKILL.md +3 -2
- package/scaffold/.agents/skills/hitch-data/references/attributes.md +7 -1
- package/scaffold/.agents/skills/hitch-data/references/limits.md +23 -23
- package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
- package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
- package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
- package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
- package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
- package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
- package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
- package/scaffold/.github/workflows/hitch.yml +1 -1
- package/scaffold/AGENTS.md +2 -2
- package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
- package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
- package/scaffold/applet/src/client/lib/utils.ts +1 -6
- package/scaffold/applet/src/client/styles/globals.css +2 -0
- package/dist/build-DhNDp7ji.js +0 -2
- package/dist/generate-DSk8ihsC.js +0 -2
|
@@ -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 a record 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`.
|
|
@@ -97,7 +117,7 @@ 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
|
|
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
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`.
|
|
@@ -106,11 +126,11 @@ const page = await hitch.records.deals.query({
|
|
|
106
126
|
- Pass a string to `$contains`, `$starts_with`, and `$ends_with`.
|
|
107
127
|
- Sort with `{ attribute, direction }`.
|
|
108
128
|
- Use `asc` or `desc` as the sort direction.
|
|
109
|
-
- Do not sort by multi-value, reference, user, or
|
|
129
|
+
- Do not sort by multi-value, reference, user, attachment, or rich text attributes.
|
|
110
130
|
- Omit `sort` to sort by `createdAt` in descending order.
|
|
111
131
|
- Use a positive integer for `limit`.
|
|
112
132
|
- The service caps `limit` at 200 and uses 50 by default.
|
|
113
|
-
- Pass the returned
|
|
133
|
+
- Pass the returned `next_cursor` back as `cursor`. A null `next_cursor` means the last page.
|
|
114
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.
|
|
115
135
|
|
|
116
136
|
## Aggregate records
|
|
@@ -122,6 +142,8 @@ Each aggregate can have its own `filter` with the same syntax as the top-level `
|
|
|
122
142
|
The array form returns `keys` and `labels` arrays per group.
|
|
123
143
|
The object form orders category groups by an aggregate value (top-N by value).
|
|
124
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.
|
|
125
147
|
|
|
126
148
|
```ts
|
|
127
149
|
const totals = await hitch.records.deals.aggregate({
|
|
@@ -148,6 +170,7 @@ The generated contract contains only objects declared in the manifest.
|
|
|
148
170
|
|
|
149
171
|
- The contract removes `create` when `canCreate` is false.
|
|
150
172
|
- The contract removes `delete` when `canDelete` is false.
|
|
173
|
+
- The contract removes `update` and `updateWhere` when `readWrite` is empty.
|
|
151
174
|
- Treat a missing method as a missing manifest grant, and edit the manifest when required.
|
|
152
175
|
- Run generation after a manifest or Hitch schema change.
|
|
153
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
|
-
-
|
|
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 `
|
|
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.
|
|
@@ -16,8 +16,8 @@ The `hitch` command talks to one organization on one Hitch instance. Its data an
|
|
|
16
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
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
18
|
- Run `hitch whoami` to print the current person, email, organization name, and organization id as JSON.
|
|
19
|
-
- Run `hitch logout` to remove
|
|
20
|
-
- Set `HITCH_API_URL`
|
|
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
21
|
|
|
22
22
|
## Select the organization
|
|
23
23
|
|
|
@@ -31,7 +31,7 @@ There is no remembered active organization. When `--org` and the workspace disag
|
|
|
31
31
|
|
|
32
32
|
## Refresh the commands
|
|
33
33
|
|
|
34
|
-
- Run `hitch sync` after a
|
|
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
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
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
37
|
|
|
@@ -41,11 +41,11 @@ Every generated command follows the API route.
|
|
|
41
41
|
|
|
42
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
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
|
|
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
45
|
- A required field is a required flag. The CLI does not prompt for a missing value. It prints the usage error.
|
|
46
46
|
- Run `hitch <object> <verb> --help` to see the flags for that organization's schema.
|
|
47
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
|
|
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
49
|
- An error prints `hitch: <message>` and exits with code 1.
|
|
50
50
|
|
|
51
51
|
## Work with records
|
|
@@ -60,8 +60,8 @@ hitch deals update --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --stage won
|
|
|
60
60
|
hitch deals delete --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --yes
|
|
61
61
|
hitch deals query --filter '{"stage":{"$in":["open","won"]}}' --sort '{"attribute":"createdAt","direction":"desc"}' --limit 50
|
|
62
62
|
hitch deals update-where --filter '{"stage":"open"}' --set '{"stage":"lost"}' --expect 3
|
|
63
|
-
hitch deals aggregate --aggregates '
|
|
64
|
-
hitch batch --requests '
|
|
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
65
|
```
|
|
66
66
|
|
|
67
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.
|
|
@@ -107,7 +107,7 @@ hitch admin changesets update --id <changesetId> --adopt <otherChangesetId>
|
|
|
107
107
|
|
|
108
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
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`
|
|
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
111
|
- Run `blockers list` before an apply. Any blocker stops the apply with HTTP 409.
|
|
112
112
|
- Run `hitch sync` after an apply, so the new objects and attributes appear as commands.
|
|
113
113
|
- Run `pnpm --dir applets/<code> run generate` after an apply, so applet types match.
|
|
@@ -115,15 +115,15 @@ hitch admin changesets update --id <changesetId> --adopt <otherChangesetId>
|
|
|
115
115
|
|
|
116
116
|
## Fix common errors
|
|
117
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 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.
|
|
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. |
|
|
@@ -24,8 +24,9 @@ An organization's data lives in Hitch objects. Each object has attributes. Each
|
|
|
24
24
|
## Attributes
|
|
25
25
|
|
|
26
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, attachment.
|
|
27
|
+
- Types: text, rich_text, integer, decimal, currency, checkbox, date, datetime, time, domain, url, email, phone, select, reference, user, organization, attachment.
|
|
28
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.
|
|
29
30
|
- Cardinality is configuration. Text, select, reference, user, and attachment can be multi-value.
|
|
30
31
|
- A required attribute rejects `null` and rejects an empty array.
|
|
31
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.
|
|
@@ -42,7 +43,7 @@ An organization's data lives in Hitch objects. Each object has attributes. Each
|
|
|
42
43
|
- Every attribute accepts `$eq` and `$not_empty`. Every attribute except checkbox accepts `$in`.
|
|
43
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.
|
|
44
45
|
- A filter can nest through a reference, through an inverse face, and can express an anti-join with `$not`.
|
|
45
|
-
-
|
|
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.
|
|
46
47
|
|
|
47
48
|
## Schema changes
|
|
48
49
|
|
|
@@ -13,7 +13,7 @@ Hitch validates and normalizes every attribute value on write. Read this before
|
|
|
13
13
|
- An `immutable` attribute rejects a change once it holds a non-null value. A null immutable attribute can still be set once.
|
|
14
14
|
- A `unique` attribute rejects a duplicate value with HTTP 409. Unique is never allowed on a multi-value attribute.
|
|
15
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 accepts `$in`. Other operators and `sort` need the `indexed` flag. Read `limits.md`.
|
|
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
17
|
|
|
18
18
|
## Text and rich text
|
|
19
19
|
|
|
@@ -91,6 +91,12 @@ Hitch validates and normalizes every attribute value on write. Read this before
|
|
|
91
91
|
- Read: the UUID. With `include`, the expansion is `{ id, firstName, lastName, email }`.
|
|
92
92
|
- Filters: identity operators only. Sort: never.
|
|
93
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
|
+
|
|
94
100
|
## Attachment
|
|
95
101
|
|
|
96
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`.
|
|
@@ -8,33 +8,33 @@ Some limits clamp with no error. Some reject the request. Design for both.
|
|
|
8
8
|
|
|
9
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
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 |
|
|
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 |
|
|
19
|
-
| IDs in one `$in` array | No cap | Only 200 records return per page
|
|
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
20
|
|
|
21
21
|
## Aggregates
|
|
22
22
|
|
|
23
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
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 |
|
|
31
|
-
| Distinct keys per dimension, max | 200 |
|
|
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
|
|
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
38
|
|
|
39
39
|
## Writes
|
|
40
40
|
|
|
@@ -56,7 +56,7 @@ Advanced filters and sorting are opt-in per attribute. This keeps each record ch
|
|
|
56
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
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
58
|
- No attribute allows `sort` when it is multi-value, rich text, reference, user, or attachment.
|
|
59
|
-
-
|
|
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
60
|
|
|
61
61
|
## Grant conditions
|
|
62
62
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hitch-theory
|
|
3
|
+
description: The mental model of Hitch. Read before you design, review, or explain anything that touches Hitch data, applets, access, or query cost. Use when a decision depends on what Hitch is for, what Hitch handles versus what an applet handles, how attributes and references model data, how changesets and releases move together, or who a call acts as. Pairs with hitch-applets, which holds the build procedure.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Theory of Hitch
|
|
7
|
+
|
|
8
|
+
This skill explains how Hitch works and why. For the build procedure, read the `hitch-applets` skill.
|
|
9
|
+
|
|
10
|
+
## 1. What Hitch is
|
|
11
|
+
|
|
12
|
+
Hitch is a platform for internal applications. It is not a CRM, a rostering product, a reporting product, or any other vertical. An organization builds its own rostering, job, or invoice applets on Hitch.
|
|
13
|
+
|
|
14
|
+
Hitch is two layers:
|
|
15
|
+
|
|
16
|
+
1. A durable data system that the organization owns: objects, attributes, records, files, identity, and grants.
|
|
17
|
+
2. A governed runtime for replaceable software: applets that Hitch builds, releases, hosts, and runs.
|
|
18
|
+
|
|
19
|
+
## 2. What an applet is
|
|
20
|
+
|
|
21
|
+
An applet is not an app. An app is the whole machine from the old days of software: its own authentication, its own database, its own permissions, a frontend, a server, caches, a deployment pipeline, and monitoring. Every app rebuilds that machine from scratch. Every app then spends most of its life on the machine, not on the work. The organization never wanted the machine. It wanted the business logic.
|
|
22
|
+
|
|
23
|
+
AI has changed the economics of software. Code is now cheap to write and cheap to change, so software can be personal to one organization, one team, and one workflow. Hitch is built for that world. Hitch flips the model. Instead of one monolithic app designed to serve many unrelated users, an organization gets a set of many applets, completely custom and personalised to its needs. An applet is the closest thing to pure business logic: one workflow, the screens it needs, and the rules that make it correct. Hitch handles everything underneath: the data and its schema, identity, permissions, files, immutable releases, hosting, schedules, and durable workflows. The author never touches the machine. The author builds only what matters to the user.
|
|
24
|
+
|
|
25
|
+
Hitch takes the machine away for correctness and trust as much as for speed. Authentication, access control, schema validation, and release safety are the parts an agent gets wrong, and the parts an organization cannot afford to have wrong. Hitch handles them, so your organisation has the confidence it needs to fully adopt vibe coded software and move off of legacy SaaS systems.
|
|
26
|
+
|
|
27
|
+
In practice an applet is a client-only, server-only, or full-stack application that Hitch builds, releases, hosts, and runs inside the platform trust boundary. It is not a third-party plugin.
|
|
28
|
+
|
|
29
|
+
Every organization has its own tenant on Hitch. Its data, its users, its grants, and its applets all live there. When Hitch runs an applet, it runs it inside that tenant, the way AWS runs a workload inside a customer's account: the provider operates the infrastructure, but the account, the identities, and the data are the customer's. An applet sees its organization's tenant and nothing else.
|
|
30
|
+
|
|
31
|
+
Applets can be used standalone via their `.app` URL or within the Hitch shell. Standalone and embedded operation use the same identity, release, routes, and theme.
|
|
32
|
+
|
|
33
|
+
## 3. What Hitch handles and what an applet handles
|
|
34
|
+
|
|
35
|
+
| Hitch handles | The applet handles |
|
|
36
|
+
| --------------------------------------------------------------- | --------------------------------------------- |
|
|
37
|
+
| Canonical organization records and files | One focused workflow and its presentation |
|
|
38
|
+
| Object and attribute schema, and references | Domain-specific UI composition |
|
|
39
|
+
| Validation, normalization, constraints, versions, audit | External API adapters and translation |
|
|
40
|
+
| Organization identity, authentication, grants | User interactions and app-specific validation |
|
|
41
|
+
| Immutable releases, schedules, the workflow engine, the runtime | Derived views and deliberate derived records |
|
|
42
|
+
| Theme delivery and platform navigation | Ephemeral client state |
|
|
43
|
+
|
|
44
|
+
## 4. Why code and data are separate
|
|
45
|
+
|
|
46
|
+
The organization's Hitch data is the single source of truth. An applet reads and writes that data. It does not keep its own copy.
|
|
47
|
+
|
|
48
|
+
The data layer stands on its own so that every way of using it is a client of the same thing. An applet, the `hitch` command line, an MCP server for an AI agent, an API key in a script, and an import all speak to the same objects, the same schema, and the same permissions. None of them is a bridge bolted on later. The CLI and applet doors are generated from the organization's own schema. The MCP door has a fixed set of record tools and reads each object's live schema at runtime with `get_object_schema`. Each one acts as the person or applet behind it, so it sees exactly what that caller may see. The result is that a person in a browser, an agent in a chat window, and a script in a terminal all work on the same records under the same rules, and each gets the interface that suits them. Software stops being one product with one screen and becomes your organizational data with many doors.
|
|
49
|
+
|
|
50
|
+
The boundary gives you:
|
|
51
|
+
|
|
52
|
+
- One source of truth that many applets share.
|
|
53
|
+
- One set of schema and integrity rules for every writer: applets, API keys, imports, and future code.
|
|
54
|
+
- Central permissions that applet UI code cannot weaken.
|
|
55
|
+
- Independent applet release and rollback with no data fork.
|
|
56
|
+
- Direct user-scoped reads from the browser, with no applet server proxy.
|
|
57
|
+
- One place for integrations: sync an external system into Hitch once, then let other applets use the same data.
|
|
58
|
+
|
|
59
|
+
Consequences:
|
|
60
|
+
|
|
61
|
+
- Do not add an applet-owned database, a parallel cache, or a copied object set. If an applet seems to need its own storage, Hitch has a gap. Raise the gap instead.
|
|
62
|
+
- Code rollback is not data recovery. A release switch never moves or restores records.
|
|
63
|
+
- When you install, upgrade, or remove an applet, the canonical data stays where it is.
|
|
64
|
+
- A domain rule can live in applet code when it controls one workflow. If the rule must hold for every writer, express it in the schema, so Hitch enforces it for every applet, API key, and import.
|
|
65
|
+
- Data normalization and UI hierarchy are separate decisions. Model distinct concepts as objects plus references when their lifecycle or permissions differ. Group them on one screen when that matches the user. Do not invent child objects to solve navigation.
|
|
66
|
+
|
|
67
|
+
## 5. Attributes are schema
|
|
68
|
+
|
|
69
|
+
An attribute is schema, not a free JSON field. Hitch validates each value against its type and configuration, normalizes it, stores it in the record, and indexes it when asked. A value that should be a select or a reference, but is stored as free text, cannot be grouped or filtered well later.
|
|
70
|
+
|
|
71
|
+
- Types: text, rich text, integer, decimal, currency, checkbox, date, datetime, time, domain, url, email, phone, select, reference, user, organisation, attachment.
|
|
72
|
+
- Cardinality is configuration, not a type. Text, select, reference, user, and attachment can be multi-value. A multi-value is an ordered array.
|
|
73
|
+
- An empty attribute reads as null. A required multi-value attribute rejects an empty array.
|
|
74
|
+
- Uniqueness is a real constraint, per attribute, per organization.
|
|
75
|
+
- Advanced filters and sorting are opt-in per attribute with the `indexed` flag. New attributes default to off. Every attribute still supports equality and not-empty filters. Aggregating or grouping by an attribute needs no flag. Type rules still apply: a reference, user, attachment, rich text, or multi-value attribute never sorts. Do not turn the flag on for every attribute. Turn it on for the attributes a query needs.
|
|
76
|
+
|
|
77
|
+
References model relationships:
|
|
78
|
+
|
|
79
|
+
- A reference attribute names a target object. Cardinality on each side comes from the single or multi setting of each face.
|
|
80
|
+
- A reference can expose an inverse face on the target object. One face owns and stores the edges. The other face is derived. Write the owner face only.
|
|
81
|
+
- Hitch keeps the links typed, clears inbound links when a target is deleted, applies grants through the inverse, and lets you filter through either face. Do not invent a join object for a plain relationship. Use a join object only when the relationship needs its own fields or lifecycle.
|
|
82
|
+
|
|
83
|
+
Special cases:
|
|
84
|
+
|
|
85
|
+
- A user attribute points at a membership in the same organization. Removal of the member sweeps those values.
|
|
86
|
+
- An organisation attribute points at a connected organisation; null means internal only; at most one exists per object.
|
|
87
|
+
- An attachment attribute holds file IDs. Files live outside records, in their own storage. Create the file, upload it, then store the ID on the record.
|
|
88
|
+
|
|
89
|
+
## 6. Changesets and releases move together
|
|
90
|
+
|
|
91
|
+
All schema mutations go through a changeset: an ordered list of object and attribute operations with a stored evaluation.
|
|
92
|
+
|
|
93
|
+
- A hard blocker means invalid schema or data. Examples: a unique constraint that finds duplicates, a required attribute with empty records, a tightened range with violators.
|
|
94
|
+
- A soft blocker means a dependency. Examples: applets, grant conditions, or delegations that depend on the object or attribute.
|
|
95
|
+
- Any blocker stops the apply. A submit with a blocker stages a pending changeset. The apply succeeds after the blocker is resolved.
|
|
96
|
+
|
|
97
|
+
An applet release can bind to a pending changeset. Hitch checks the manifest against the schema the changeset will produce. A mismatch is a deploy warning, not a denial. The unresolved warning becomes a hard blocker on the changeset. A bound release does not go live on deploy. When the changeset applies, the schema and the newest bound release of each applet become active together. Schema and code change together.
|
|
98
|
+
|
|
99
|
+
Adoption folds one pending changeset into another and abandons the source. The source must have no bound releases.
|
|
100
|
+
|
|
101
|
+
The applet manifest lists the objects the applet uses, with create and delete flags, read-only attributes, and read-write attributes. That list is the access declaration. It caps what the applet identity can do: the object actions, and the attributes it can update. It does not change what a user can see.
|
|
102
|
+
|
|
103
|
+
## 7. Identity and access
|
|
104
|
+
|
|
105
|
+
Hitch enforces access control on every Hitch API call. An applet never re-implements it. A UI filter or a hidden control is convenience, never a boundary. An applet's own server procedure is applet code, so it must check the business rules it introduces. Every call carries a principal and Hitch applies that principal's grants.
|
|
106
|
+
|
|
107
|
+
Principals:
|
|
108
|
+
|
|
109
|
+
- A person, through a session or a personal API key. A personal API key inherits the member's capabilities.
|
|
110
|
+
- An impersonated person. The target's capabilities must be a proper subset of the caller's.
|
|
111
|
+
- An applet. The applet identity has no role and no bypass. Its authority is exactly the grants projected from its manifest.
|
|
112
|
+
|
|
113
|
+
The organization roles are member and owner. There is no admin role. The owner holds every capability, bypasses every grant, and cannot be removed or impersonated. Capabilities are ten named org-wide powers: manage members, teams, objects, permissions, applets, and the organization; read all records; write all records; use applets; impersonate members. A capability grant goes to a team only, never to a person or to everyone. When one person needs a capability, put them in a one-person team. A caller can grant only a capability the caller holds.
|
|
114
|
+
|
|
115
|
+
Object grants are the matrix: one row per object and grantee with create, read, update, and delete flags. A grantee is a team, an applet, or the default row that every member holds. A member's access is the union of the default row and their team rows. The read-all and write-all capabilities bypass object grants. Object grants set the verbs. Conditions narrow rows. Attribute grants restrict updates to protected attributes and applet writes. Reads are never masked per attribute.
|
|
116
|
+
|
|
117
|
+
An object grant can delegate through one single-valued owner reference. The linked record then uses the same grantee's grant on the target object.
|
|
118
|
+
|
|
119
|
+
Grant conditions scope rows to the current user. A condition path is a user attribute on the object, or one reference followed by a user attribute on the target object. A record whose user attribute is empty is invisible to that grant. An invisible record behaves as missing. A condition applies to read, update, and delete, never to create. A condition cannot compare a value, such as "where status equals X".
|
|
120
|
+
|
|
121
|
+
Who a call acts as:
|
|
122
|
+
|
|
123
|
+
- A client applet call acts as the current user. The manifest grants do not apply.
|
|
124
|
+
- A schedule run acts as the applet, capped by the manifest.
|
|
125
|
+
- A workflow run acts as whoever started it: the invoking user, or the applet when a schedule started it.
|
|
126
|
+
|
|
127
|
+
Because Hitch enforces access, a client applet needs no server proxy for filtering or access control. Add a server only for secrets, external systems, schedules, workflows, or bounded fan-in.
|
|
128
|
+
|
|
129
|
+
## 8. What makes a good applet
|
|
130
|
+
|
|
131
|
+
A good applet:
|
|
132
|
+
|
|
133
|
+
- Solves one related workflow for one clear group of users.
|
|
134
|
+
- Uses the organization's existing Hitch data, not a private copy.
|
|
135
|
+
- Presents the smallest useful set of decisions and actions. It does not reproduce a legacy product screen for screen.
|
|
136
|
+
- Uses Hitch primitives before custom infrastructure.
|
|
137
|
+
- Feels native through semantic theme tokens, shared interaction patterns, useful empty, loading, and error states, and viewport-safe layouts.
|
|
138
|
+
- Has explicit access, performance, failure, and recovery behavior.
|
|
139
|
+
- Can be replaced or rolled back with no move or rewrite of the canonical data.
|
|
140
|
+
|
|
141
|
+
Split an applet when workflows, audiences, privileges, release cadence, or failure modes differ. Do not split because the UI has another page. A sync applet and a reporting applet are a good split. Ingestion has secrets, schedules, broad write grants, retries, and external failure modes. Reports read already-synced data with narrow grants.
|
|
142
|
+
|
|
143
|
+
## 9. Product-design principles
|
|
144
|
+
|
|
145
|
+
- Build to user outcomes, not to the screens or tables of an older system.
|
|
146
|
+
- Start with the important nouns, relationships, permissions, and failure cases. Design screens after that.
|
|
147
|
+
- Simplify critically. Defer speculative channels and infrastructure.
|
|
148
|
+
- Ship the smallest complete workflow. Add nothing unrequested.
|
|
149
|
+
- Treat thresholds, status rules, timing, channels, and recipients as product decisions for the user.
|
|
150
|
+
- Confirm destructive or external writes. Show the target, the intended change, the stale-source check, and the outcome. A Hitch mutation and a mutation of an external source are not equivalent.
|
|
151
|
+
- Record rejected designs and the reason. Do not rebuild what was deliberately cut.
|