@hitch42/applet 0.1.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/_internal/client.d.ts +7 -0
- package/dist/_internal/client.js +169 -0
- package/dist/_internal/server.d.ts +92 -0
- package/dist/_internal/server.js +181 -0
- package/dist/applet.d.ts +1 -0
- package/dist/applet.js +184 -0
- package/dist/batch-Qa4B4-Yp.d.ts +30 -0
- package/dist/build-BlmLiKro.js +90 -0
- package/dist/build-CcT-wNN9.js +2 -0
- package/dist/ci-0Q2mUh53.js +68 -0
- package/dist/client.d.ts +48 -0
- package/dist/client.js +38 -0
- package/dist/contract-QUvEGiRI.d.ts +23 -0
- package/dist/deploy-BbL3GObV.js +2579 -0
- package/dist/email-DBTVhNB_.js +62 -0
- package/dist/generate-XB-72Fzj.js +537 -0
- package/dist/generate-wIN3IYqL.js +2 -0
- package/dist/index.d.ts +185 -0
- package/dist/index.js +251 -0
- package/dist/init-DwsXRUk-.js +244 -0
- package/dist/lazy-client-x0DCI98S.js +64 -0
- package/dist/load-config-OKZU3UKn.js +27 -0
- package/dist/login-BBO74hPC.js +12 -0
- package/dist/logs-XvK6Zs5Y.js +183 -0
- package/dist/manifest-4ZAc6HUk.js +191 -0
- package/dist/manifest-I-9s8R36.d.ts +88 -0
- package/dist/me-BW7Paary.js +32 -0
- package/dist/project-B0qNXnFu.js +536 -0
- package/dist/prompt-ChS-grYO.js +29 -0
- package/dist/query-cache-CXmAyLv3.js +82 -0
- package/dist/react.d.ts +37 -0
- package/dist/react.js +515 -0
- package/dist/schedules-BkWZsbSs.js +42 -0
- package/dist/server.d.ts +46 -0
- package/dist/server.js +104 -0
- package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
- package/dist/telephony.d.ts +77 -0
- package/dist/telephony.js +607 -0
- package/dist/upload-DKZzz5kW.d.ts +112 -0
- package/dist/upload-DRbVhuDm.js +114 -0
- package/dist/variables-0tjnAeut.js +108 -0
- package/dist/vite.d.ts +63 -0
- package/dist/vite.js +816 -0
- package/dist/workflow-CTo2kZF1.js +51 -0
- package/dist/workflow-DurM6Fwx.d.ts +28 -0
- package/dist/workflow-engine-MogejNfF.js +117 -0
- package/dist/workspace-DruAZPmV.js +390 -0
- package/package.json +112 -0
- package/scaffold/.agents/skills/hitch-applets/SKILL.md +163 -0
- package/scaffold/.agents/skills/hitch-applets/references/client.md +73 -0
- package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
- package/scaffold/.agents/skills/hitch-applets/references/files.md +109 -0
- package/scaffold/.agents/skills/hitch-applets/references/manifest.md +198 -0
- package/scaffold/.agents/skills/hitch-applets/references/markdown.md +37 -0
- package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
- package/scaffold/.agents/skills/hitch-applets/references/records.md +176 -0
- package/scaffold/.agents/skills/hitch-applets/references/schedules.md +91 -0
- package/scaffold/.agents/skills/hitch-applets/references/server.md +95 -0
- package/scaffold/.agents/skills/hitch-applets/references/workflows.md +81 -0
- package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
- package/scaffold/.agents/skills/hitch-data/SKILL.md +52 -0
- package/scaffold/.agents/skills/hitch-data/references/attributes.md +127 -0
- package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
- 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 +15 -0
- package/scaffold/AGENTS.md +38 -0
- package/scaffold/applet/components.json +25 -0
- package/scaffold/applet/src/client/components/ui/alert.tsx +73 -0
- package/scaffold/applet/src/client/components/ui/avatar.tsx +100 -0
- package/scaffold/applet/src/client/components/ui/badge.tsx +55 -0
- package/scaffold/applet/src/client/components/ui/button.tsx +90 -0
- package/scaffold/applet/src/client/components/ui/card.tsx +85 -0
- package/scaffold/applet/src/client/components/ui/checkbox.tsx +35 -0
- package/scaffold/applet/src/client/components/ui/dialog.tsx +164 -0
- package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +235 -0
- package/scaffold/applet/src/client/components/ui/input-group.tsx +144 -0
- package/scaffold/applet/src/client/components/ui/input.tsx +22 -0
- package/scaffold/applet/src/client/components/ui/label.tsx +29 -0
- package/scaffold/applet/src/client/components/ui/popover.tsx +74 -0
- package/scaffold/applet/src/client/components/ui/select.tsx +243 -0
- package/scaffold/applet/src/client/components/ui/separator.tsx +25 -0
- package/scaffold/applet/src/client/components/ui/sheet.tsx +177 -0
- package/scaffold/applet/src/client/components/ui/skeleton.tsx +13 -0
- package/scaffold/applet/src/client/components/ui/spinner.tsx +17 -0
- package/scaffold/applet/src/client/components/ui/switch.tsx +41 -0
- package/scaffold/applet/src/client/components/ui/table.tsx +114 -0
- package/scaffold/applet/src/client/components/ui/tabs.tsx +78 -0
- package/scaffold/applet/src/client/components/ui/textarea.tsx +23 -0
- package/scaffold/applet/src/client/lib/utils.ts +1 -0
- package/scaffold/applet/src/client/styles/globals.css +136 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Applet manifest
|
|
2
|
+
|
|
3
|
+
Define the applet manifest in `applet.manifest.ts`.
|
|
4
|
+
|
|
5
|
+
The authored definition omits `org`.
|
|
6
|
+
The applet tool reads `org` from `hitch.config.ts` in the workspace root.
|
|
7
|
+
|
|
8
|
+
## Use top-level fields
|
|
9
|
+
|
|
10
|
+
- `org` identifies the Hitch organization.
|
|
11
|
+
- `name` gives the applet a display name.
|
|
12
|
+
- `code` gives the applet its stable code and URL prefix.
|
|
13
|
+
- `objects` declares record objects and attribute access.
|
|
14
|
+
- `requiresChangeset` optionally names the schema changeset that the code needs.
|
|
15
|
+
- `variables` optionally declares runtime variables.
|
|
16
|
+
- `schedules` optionally declares scheduled server calls.
|
|
17
|
+
- `dependencies` optionally declares applet runtime packages.
|
|
18
|
+
- `devDependencies` optionally declares applet build packages.
|
|
19
|
+
|
|
20
|
+
The manifest and applet definition reject unknown fields.
|
|
21
|
+
|
|
22
|
+
## Set `org`
|
|
23
|
+
|
|
24
|
+
- Use a non-empty organization code.
|
|
25
|
+
- Start the code with a lowercase letter.
|
|
26
|
+
- End the code with a lowercase letter or number.
|
|
27
|
+
- Use only lowercase letters, numbers, and non-consecutive hyphens.
|
|
28
|
+
- Do not put `org` in `applet.manifest.ts`.
|
|
29
|
+
- Set it in the root `hitch.config.ts` file.
|
|
30
|
+
|
|
31
|
+
## Set `name`
|
|
32
|
+
|
|
33
|
+
- Use a string with 1 through 120 characters after trimming.
|
|
34
|
+
- Use the name as the applet display name.
|
|
35
|
+
|
|
36
|
+
## Set `code`
|
|
37
|
+
|
|
38
|
+
- Start the code with a lowercase letter.
|
|
39
|
+
- End the code with a lowercase letter or number.
|
|
40
|
+
- Use only lowercase letters, numbers, and non-consecutive underscores.
|
|
41
|
+
- Make the applet directory name equal to the code.
|
|
42
|
+
- Rename the directory or change the code when they differ.
|
|
43
|
+
- Expect the Vite base path to be `/<code>/`.
|
|
44
|
+
- Include the code in each client route path.
|
|
45
|
+
|
|
46
|
+
## Set `requiresChangeset`
|
|
47
|
+
|
|
48
|
+
Set `requiresChangeset` when the code needs a schema change that is not live yet.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
requiresChangeset: "0192a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- Use the ID of a changeset in the same organization.
|
|
55
|
+
- Generate, build, and deploy all use this field. The CLI has no changeset flag.
|
|
56
|
+
- While the changeset is pending, types and the build use the schema that the changeset will produce.
|
|
57
|
+
- While the changeset is pending, a deploy binds the release to the changeset. The release goes live when a person applies the changeset in Hitch.
|
|
58
|
+
- After the changeset applies, the field uses the live schema. A deploy then promotes as usual. Remove the field when it is convenient.
|
|
59
|
+
- A deploy fails when the changeset was abandoned. Remove the field or name a pending changeset.
|
|
60
|
+
- A git push uses the same rules. The push never applies the changeset.
|
|
61
|
+
|
|
62
|
+
## Declare objects
|
|
63
|
+
|
|
64
|
+
Add one entry for each object that the applet needs.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
objects: [
|
|
68
|
+
{
|
|
69
|
+
code: "deals",
|
|
70
|
+
canCreate: true,
|
|
71
|
+
canDelete: false,
|
|
72
|
+
readOnly: ["created_by"],
|
|
73
|
+
readWrite: ["stage", "amount"],
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Each object entry has five required fields.
|
|
79
|
+
|
|
80
|
+
- `code` identifies the Hitch object.
|
|
81
|
+
- `canCreate` is a boolean that controls the generated `create` method.
|
|
82
|
+
- `canDelete` is a boolean that controls the generated `delete` method.
|
|
83
|
+
- `readOnly` lists attribute codes that the applet can read but cannot write.
|
|
84
|
+
- `readWrite` lists attribute codes that the applet can read and write.
|
|
85
|
+
|
|
86
|
+
Set `optional: true` when the applet works for a viewer without access to this object.
|
|
87
|
+
List codes from `readOnly` or `readWrite` in `optionalAttributes` when some viewers cannot see their values.
|
|
88
|
+
Typegen makes these record fields nullable for every viewer.
|
|
89
|
+
These fields never grant or remove access.
|
|
90
|
+
They matter when an org shares the applet with a connected organisation.
|
|
91
|
+
Hitch refuses a share if the connected org lacks access to a required object.
|
|
92
|
+
Hitch also refuses a share if a required object lists a required user attribute outside `optionalAttributes`.
|
|
93
|
+
|
|
94
|
+
- Use the applet code format for each object code.
|
|
95
|
+
- Do not declare the same object code more than once.
|
|
96
|
+
- Use the applet code format for each attribute code.
|
|
97
|
+
- Do not list `id`, `name`, or `expected_version` as an attribute code.
|
|
98
|
+
- Do not repeat an attribute code in one list.
|
|
99
|
+
- Do not put one attribute code in both lists.
|
|
100
|
+
|
|
101
|
+
The object declaration caps record access for the applet identity.
|
|
102
|
+
|
|
103
|
+
- Declare only the objects that the applet uses.
|
|
104
|
+
- Declare only the attributes that the applet uses.
|
|
105
|
+
- The declaration sets both the generated contract and the applet identity's cap.
|
|
106
|
+
- Use `readOnly` for attributes that the applet identity must not change.
|
|
107
|
+
- Use `readWrite` for attributes that the applet identity can change.
|
|
108
|
+
- Set `canCreate` and `canDelete` to the required values.
|
|
109
|
+
- The generated contract omits undeclared objects.
|
|
110
|
+
- The generated contract removes `create` when `canCreate` is false.
|
|
111
|
+
- The generated contract removes `delete` when `canDelete` is false.
|
|
112
|
+
- The generated contract removes `update` and `updateWhere` when `readWrite` is empty.
|
|
113
|
+
- Regenerate after an object grant changes.
|
|
114
|
+
- Do not restore a removed object or method with a cast.
|
|
115
|
+
|
|
116
|
+
Client requests use the current user's identity and access.
|
|
117
|
+
Scheduled requests use the applet identity and its manifest cap.
|
|
118
|
+
|
|
119
|
+
## Declare variables
|
|
120
|
+
|
|
121
|
+
Declare variable names and policies without values.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
variables: {
|
|
125
|
+
API_URL: {},
|
|
126
|
+
API_TOKEN: { secret: true },
|
|
127
|
+
REPORT_CHANNEL: { optional: true },
|
|
128
|
+
},
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- Start each key with a letter or underscore.
|
|
132
|
+
- Use only letters, numbers, and underscores after the first character.
|
|
133
|
+
- Use at most 128 characters.
|
|
134
|
+
- Do not use a key that starts with `HITCH_`, without regard to letter case.
|
|
135
|
+
- Set `secret` to a boolean to mark how the platform stores the value.
|
|
136
|
+
- Set `optional` to a boolean to control whether the value is required.
|
|
137
|
+
- Omit `optional` or set it to false for a required variable.
|
|
138
|
+
- Set variable values on the platform.
|
|
139
|
+
- Do not put variable values in the manifest or source code.
|
|
140
|
+
- Read declared variables from `env` in server code.
|
|
141
|
+
- Expect an optional variable to have type `string | undefined`.
|
|
142
|
+
- Expect a required variable to have type `string`.
|
|
143
|
+
|
|
144
|
+
## Declare schedules
|
|
145
|
+
|
|
146
|
+
Each schedule calls one registered server procedure.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
schedules: [
|
|
150
|
+
{
|
|
151
|
+
code: "refresh_cache",
|
|
152
|
+
name: "Refresh cache",
|
|
153
|
+
cron: "*/5 * * * *",
|
|
154
|
+
path: "/refreshCache",
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- `code` gives the schedule a stable code.
|
|
160
|
+
- `name` gives the schedule a display name.
|
|
161
|
+
- `cron` sets its run times.
|
|
162
|
+
- `path` selects a server router procedure.
|
|
163
|
+
|
|
164
|
+
Apply these validation rules.
|
|
165
|
+
|
|
166
|
+
- Use the applet code format for each schedule code.
|
|
167
|
+
- Do not declare the same schedule code more than once.
|
|
168
|
+
- Use a name with 1 through 120 characters after trimming.
|
|
169
|
+
- Use a valid five-field cron expression.
|
|
170
|
+
- Put fields in minute, hour, day-of-month, month, and day-of-week order.
|
|
171
|
+
- Start `path` with one `/`.
|
|
172
|
+
- Do not start `path` with `//`.
|
|
173
|
+
- Do not put whitespace in `path`.
|
|
174
|
+
- Keep `src/server/main.ts` when the manifest has a schedule.
|
|
175
|
+
|
|
176
|
+
Generation registers the server router type.
|
|
177
|
+
After generation, `path` accepts only paths from that registered router.
|
|
178
|
+
Before router registration, the path type falls back to `string`.
|
|
179
|
+
|
|
180
|
+
## Declare packages
|
|
181
|
+
|
|
182
|
+
Add direct package requirements to the manifest.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
dependencies: {
|
|
186
|
+
"date-fns": "^4.1.0",
|
|
187
|
+
},
|
|
188
|
+
devDependencies: {
|
|
189
|
+
"@types/example": "^1.0.0",
|
|
190
|
+
},
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
- Put runtime imports in `dependencies`.
|
|
194
|
+
- Put build and test tools in `devDependencies`.
|
|
195
|
+
- Do not add a package that the applet CLI already supplies.
|
|
196
|
+
- Do not put one package in both dependency groups.
|
|
197
|
+
- Use package ranges that pnpm accepts.
|
|
198
|
+
- Run `applet generate`, `applet build`, or `applet deploy` to update generated package files.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Markdown
|
|
2
|
+
|
|
3
|
+
Use the SDK components to edit and render markdown with Hitch file images.
|
|
4
|
+
|
|
5
|
+
## Edit markdown
|
|
6
|
+
|
|
7
|
+
`MarkdownEditor` is a controlled Tiptap WYSIWYG editor. Store its markdown value in your form state. Pass `hitch.files` for image paste and drop.
|
|
8
|
+
|
|
9
|
+
The editor supports GFM tables and task lists. A plain table serializes as GFM. A table with block content in a cell (for example a list) serializes as inline HTML, which the `Markdown` renderer also renders.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { hitch } from "@hitch42/applet/client";
|
|
13
|
+
import { MarkdownEditor } from "@hitch42/applet/react";
|
|
14
|
+
|
|
15
|
+
<MarkdownEditor value={value} onChange={setValue} files={hitch.files} />;
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The editor uploads pasted or dropped images. It stores each image as ``.
|
|
19
|
+
|
|
20
|
+
The `files` value must supply both `upload(file)` and `download(id)`. The editor uses `download(id)` to show stored Hitch images.
|
|
21
|
+
|
|
22
|
+
While an upload runs, the editor shows the image from a temporary local URL and blocks native form submission, so a form cannot save a half-done upload. A failed upload removes the image and calls `onUploadError` if you pass one; without it, the editor logs the error and continues with the next file.
|
|
23
|
+
|
|
24
|
+
## Render markdown
|
|
25
|
+
|
|
26
|
+
Pass the same files surface to `Markdown`:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { hitch } from "@hitch42/applet/client";
|
|
30
|
+
import { Markdown } from "@hitch42/applet/react";
|
|
31
|
+
|
|
32
|
+
<Markdown files={hitch.files}>{value}</Markdown>;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`Markdown` calls `hitch.files.download(id)` to replace each Hitch file id with a fresh download link.
|
|
36
|
+
|
|
37
|
+
Store markdown with `hitch-file://` file ids. Never store a presigned download URL because it expires.
|
|
@@ -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.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Records
|
|
2
|
+
|
|
3
|
+
Use the generated Hitch contract as the typed data plane.
|
|
4
|
+
|
|
5
|
+
- Import `hitch` from `@hitch42/applet/client` in client code.
|
|
6
|
+
- Import `hitch` from `@hitch42/applet/server` in server code.
|
|
7
|
+
- Read object codes, attribute codes, inputs, and outputs from the generated types.
|
|
8
|
+
- Use only the target applet's `.hitch` types as schema evidence.
|
|
9
|
+
- Never copy or infer types from a sibling applet.
|
|
10
|
+
- If the target applet has no generated types, resolve generation before you write record code.
|
|
11
|
+
- Do not add local copies of generated types.
|
|
12
|
+
- Do not use a cast to restore a missing object, field, or operation.
|
|
13
|
+
|
|
14
|
+
## Use record verbs
|
|
15
|
+
|
|
16
|
+
- `list` sends `GET /v1/<objectCode>` and lists records with query parameters.
|
|
17
|
+
- `create` sends `POST /v1/<objectCode>` and creates one record.
|
|
18
|
+
- `get` sends `GET /v1/<objectCode>/{id}` and gets one record.
|
|
19
|
+
- `history` sends `GET /v1/<objectCode>/{id}/history` and gets record changes.
|
|
20
|
+
- `update` sends `PATCH /v1/<objectCode>/{id}` and updates one record.
|
|
21
|
+
- `delete` sends `DELETE /v1/<objectCode>/{id}` and deletes one record.
|
|
22
|
+
- `query` sends `POST /v1/<objectCode>/query` and lists records with a structured body.
|
|
23
|
+
- `updateWhere` sends `POST /v1/<objectCode>/update` and updates matching records.
|
|
24
|
+
- `aggregate` sends `POST /v1/<objectCode>/aggregate` and calculates aggregate values.
|
|
25
|
+
|
|
26
|
+
- Pass a record ID in `params.id` for `get`, `history`, `update`, and `delete`.
|
|
27
|
+
- Pass record fields in `body` for `create` and `update`.
|
|
28
|
+
- Pass structured query data in `body` for `query`.
|
|
29
|
+
|
|
30
|
+
## Call records from the client
|
|
31
|
+
|
|
32
|
+
Use TanStack Query options from each generated procedure.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { useQuery } from "@tanstack/react-query";
|
|
36
|
+
import { hitch } from "@hitch42/applet/client";
|
|
37
|
+
|
|
38
|
+
const deals = useQuery(
|
|
39
|
+
hitch.records.deals.query.queryOptions({
|
|
40
|
+
input: {
|
|
41
|
+
body: { filter: { stage: { $eq: "open" } }, limit: 25 },
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { useMutation } from "@tanstack/react-query";
|
|
49
|
+
import { hitch } from "@hitch42/applet/client";
|
|
50
|
+
|
|
51
|
+
const createDeal = useMutation(hitch.records.deals.create.mutationOptions());
|
|
52
|
+
|
|
53
|
+
createDeal.mutate({ body: { name: "New deal" } });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- Use `.queryOptions(...)` for reads and `.mutationOptions(...)` for writes.
|
|
57
|
+
- The client uses the current user's identity and access.
|
|
58
|
+
- Do not add a server proxy for normal access control or filtering.
|
|
59
|
+
|
|
60
|
+
## Call records from the server
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { hitch } from "@hitch42/applet/server";
|
|
64
|
+
|
|
65
|
+
const deals = await hitch.records.deals.query({
|
|
66
|
+
body: { filter: { stage: { $eq: "open" } }, limit: 25 },
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const deal = await hitch.records.deals.get({
|
|
70
|
+
params: { id: dealId },
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- Do not use `.queryOptions(...)` or `.mutationOptions(...)` on the server.
|
|
75
|
+
- Use the server SDK for imperative data access, secrets, or external services.
|
|
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
|
+
|
|
96
|
+
## Build a query
|
|
97
|
+
|
|
98
|
+
The `query` body accepts `filter`, `sort`, `limit`, `cursor`, and `include`.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const page = await hitch.records.deals.query({
|
|
102
|
+
body: {
|
|
103
|
+
filter: {
|
|
104
|
+
$and: [{ stage: { $in: ["open", "won"] } }, { amount: { $gte: 1000 } }],
|
|
105
|
+
},
|
|
106
|
+
sort: { attribute: "amount", direction: "desc" },
|
|
107
|
+
limit: 50,
|
|
108
|
+
cursor,
|
|
109
|
+
include: "company",
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- Use `$and` or `$or` with a non-empty array of filter nodes.
|
|
115
|
+
- Use `$not` with one filter node.
|
|
116
|
+
- Put more than one field in a node to combine those fields with AND.
|
|
117
|
+
- Use a direct field value as shorthand for `$eq`.
|
|
118
|
+
- Use `$eq`, `$in`, or `$not_empty` for identity fields.
|
|
119
|
+
- Use `$eq` or `$not_empty` for checkbox fields.
|
|
120
|
+
- Use `$eq`, `$in`, or `$not_empty` for attachment fields. Pass file IDs.
|
|
121
|
+
- Use `$eq`, `$in`, `$not_empty`, `$contains`, `$starts_with`, or `$ends_with` for text fields.
|
|
122
|
+
- Use `$eq`, `$in`, `$not_empty`, `$lt`, `$lte`, `$gt`, or `$gte` for ordered fields.
|
|
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.
|
|
125
|
+
- Pass an array to `$in`.
|
|
126
|
+
- Pass a string to `$contains`, `$starts_with`, and `$ends_with`.
|
|
127
|
+
- Sort with `{ attribute, direction }`.
|
|
128
|
+
- Use `asc` or `desc` as the sort direction.
|
|
129
|
+
- Do not sort by multi-value, reference, user, attachment, or rich text attributes.
|
|
130
|
+
- Omit `sort` to sort by `createdAt` in descending order.
|
|
131
|
+
- Use a positive integer for `limit`.
|
|
132
|
+
- The service caps `limit` at 200 and uses 50 by default.
|
|
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.
|
|
135
|
+
|
|
136
|
+
## Aggregate records
|
|
137
|
+
|
|
138
|
+
Pass `filter`, `aggregates`, and `groupBy` in the aggregate body.
|
|
139
|
+
An aggregate or group attribute can name one single-value owner reference and a target attribute, for example `company_id.industry`.
|
|
140
|
+
Each aggregate can have its own `filter` with the same syntax as the top-level `filter`, including nested reference filters.
|
|
141
|
+
`groupBy` accepts an array of up to two dimensions.
|
|
142
|
+
The array form returns `keys` and `labels` arrays per group.
|
|
143
|
+
The object form orders category groups by an aggregate value (top-N by value).
|
|
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.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const totals = await hitch.records.deals.aggregate({
|
|
150
|
+
body: { filter, aggregates, groupBy },
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Update matching records
|
|
155
|
+
|
|
156
|
+
Pass `filter`, `set`, optional `expect`, and optional `limit` to `updateWhere`.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const result = await hitch.records.deals.updateWhere({
|
|
160
|
+
body: { filter, set: { stage: "closed" }, expect: 3 },
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
- Use a nonnegative integer for `expect`.
|
|
165
|
+
- Use generated types for query, aggregate, group, set, and limit values.
|
|
166
|
+
|
|
167
|
+
## Fix a missing method
|
|
168
|
+
|
|
169
|
+
The generated contract contains only objects declared in the manifest.
|
|
170
|
+
|
|
171
|
+
- The contract removes `create` when `canCreate` is false.
|
|
172
|
+
- The contract removes `delete` when `canDelete` is false.
|
|
173
|
+
- The contract removes `update` and `updateWhere` when `readWrite` is empty.
|
|
174
|
+
- Treat a missing method as a missing manifest grant, and edit the manifest when required.
|
|
175
|
+
- Run generation after a manifest or Hitch schema change.
|
|
176
|
+
- Do not bypass the contract with a cast.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Scheduled runs
|
|
2
|
+
|
|
3
|
+
Use a schedule to call an applet server procedure from a cron expression.
|
|
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
|
+
|
|
11
|
+
## Define the target procedure
|
|
12
|
+
|
|
13
|
+
- Add the target to the applet's server router.
|
|
14
|
+
- Use `os.handler` to define the procedure.
|
|
15
|
+
- Default-export `{ router }`.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { os } from "@orpc/server";
|
|
19
|
+
|
|
20
|
+
const router = {
|
|
21
|
+
refresh: os.handler(async () => {
|
|
22
|
+
// Run the scheduled work.
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default { router };
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Declare the schedule
|
|
30
|
+
|
|
31
|
+
- Add a `schedules` entry to the applet manifest.
|
|
32
|
+
- Give each schedule a unique `code`.
|
|
33
|
+
- Give each schedule a non-empty `name`.
|
|
34
|
+
- Use a valid five-field cron expression.
|
|
35
|
+
- Set `path` to the server procedure path.
|
|
36
|
+
- Start the path with `/`.
|
|
37
|
+
- Do not add whitespace to the path.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
schedules: [
|
|
41
|
+
{
|
|
42
|
+
code: "refresh",
|
|
43
|
+
name: "Refresh data",
|
|
44
|
+
cron: "*/5 * * * *",
|
|
45
|
+
path: "/refresh",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- The five fields are minute, hour, day of month, month, and day of week.
|
|
51
|
+
- The registered router type checks the path after type generation.
|
|
52
|
+
- A nested procedure uses its nested router path.
|
|
53
|
+
- The scheduler invokes the named router procedure with the input `{}`. Accept an empty object or declare no input.
|
|
54
|
+
|
|
55
|
+
## Understand the run identity
|
|
56
|
+
|
|
57
|
+
- A scheduled run acts as the applet.
|
|
58
|
+
- It has no current user.
|
|
59
|
+
- Hitch data calls use the applet's manifest grants.
|
|
60
|
+
- The manifest grants set the maximum data access.
|
|
61
|
+
- User-only endpoints return HTTP 403.
|
|
62
|
+
- Admin-only endpoints return HTTP 403.
|
|
63
|
+
|
|
64
|
+
## Keep work within the run limits
|
|
65
|
+
|
|
66
|
+
- Each run has a 30,000 millisecond CPU limit.
|
|
67
|
+
- Each run has a 1,000 subrequest limit.
|
|
68
|
+
- Keep the procedure within both limits.
|
|
69
|
+
- The scheduler prevents overlap for 16 minutes after an active run starts.
|
|
70
|
+
- A later due run can start after that overlap window.
|
|
71
|
+
|
|
72
|
+
## Upload files from a run
|
|
73
|
+
|
|
74
|
+
- Import `hitch` from `@hitch42/applet/server`.
|
|
75
|
+
- Call `hitch.files.upload(file)` with a web `File`.
|
|
76
|
+
- The upload uses the scheduled run's applet identity.
|
|
77
|
+
- The file bytes go directly to storage through a presigned URL.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { hitch } from "@hitch42/applet/server";
|
|
81
|
+
|
|
82
|
+
const uploaded = await hitch.files.upload(file);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Diagnose a failed run
|
|
86
|
+
|
|
87
|
+
- Check the applet invocation history first.
|
|
88
|
+
- Read the stored status, path, HTTP status, and error details.
|
|
89
|
+
- Check the applet logs for procedure details.
|
|
90
|
+
- Follow the log commands in the main Hitch applet skill.
|
|
91
|
+
- Do not log secrets, tokens, or personal data.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Applet server
|
|
2
|
+
|
|
3
|
+
Use the server half for work that must not run in the browser.
|
|
4
|
+
|
|
5
|
+
## Choose the correct half
|
|
6
|
+
|
|
7
|
+
- Stay client-only for normal Hitch data queries and mutations.
|
|
8
|
+
- The client SDK uses the current user's identity and access.
|
|
9
|
+
- Use the server half for secrets.
|
|
10
|
+
- Use the server half for external services.
|
|
11
|
+
- Use the server half for imperative Hitch data access.
|
|
12
|
+
- Do not add a server proxy only for filtering or access control.
|
|
13
|
+
|
|
14
|
+
## Define the router
|
|
15
|
+
|
|
16
|
+
- Import `os` from `@orpc/server`.
|
|
17
|
+
- Build an object that contains oRPC procedures.
|
|
18
|
+
- Define each procedure with `os.handler`.
|
|
19
|
+
- Default-export `{ router }`.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { os } from "@orpc/server";
|
|
23
|
+
|
|
24
|
+
const router = {
|
|
25
|
+
hello: os.handler(() => ({ hello: "World!" })),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default { router };
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- Nest procedure objects when the applet needs nested paths.
|
|
32
|
+
|
|
33
|
+
## Call the router from the client
|
|
34
|
+
|
|
35
|
+
- Import `api` from `@hitch42/applet/client`.
|
|
36
|
+
- Use its generated procedure types.
|
|
37
|
+
- Use TanStack query options for a query.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { useQuery } from "@tanstack/react-query";
|
|
41
|
+
import { api } from "@hitch42/applet/client";
|
|
42
|
+
|
|
43
|
+
const greeting = useQuery(api.hello.queryOptions());
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Use the generated mutation options for a mutation.
|
|
47
|
+
- Keep procedure inputs and outputs inferred from the router.
|
|
48
|
+
|
|
49
|
+
## Access Hitch from the server
|
|
50
|
+
|
|
51
|
+
Import the server exports:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { env, hitch } from "@hitch42/applet/server";
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- Use `hitch` for imperative calls inside a procedure.
|
|
58
|
+
- A browser request makes `hitch` use that user's identity.
|
|
59
|
+
- A scheduled request makes `hitch` use the applet identity.
|
|
60
|
+
- A request without a complete user or applet identity receives HTTP 401.
|
|
61
|
+
|
|
62
|
+
## Define variables
|
|
63
|
+
|
|
64
|
+
- Add each server variable to the applet manifest.
|
|
65
|
+
- Set `secret: true` for a secret value.
|
|
66
|
+
- Set `optional: true` when the value can be absent.
|
|
67
|
+
- Use a valid environment key.
|
|
68
|
+
- Do not use a key with the reserved `HITCH_` prefix.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
variables: {
|
|
72
|
+
API_URL: {},
|
|
73
|
+
API_TOKEN: { secret: true },
|
|
74
|
+
OPTIONAL_VALUE: { optional: true },
|
|
75
|
+
},
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- Read a required value as a `string` from `env`.
|
|
79
|
+
- Read an optional value as `string | undefined`.
|
|
80
|
+
- Use only variables declared in the manifest.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const response = await fetch(env.API_URL, {
|
|
84
|
+
headers: { authorization: `Bearer ${env.API_TOKEN}` },
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Understand identity
|
|
89
|
+
|
|
90
|
+
- A user request carries a user ID and session token.
|
|
91
|
+
- Server `hitch` calls then use the user's access.
|
|
92
|
+
- A scheduled run carries an applet ID and applet token.
|
|
93
|
+
- Server `hitch` calls then use the applet's manifest grants.
|
|
94
|
+
- A scheduled run has no user identity.
|
|
95
|
+
- User-only endpoints reject the applet identity with HTTP 403.
|