@checkcourt/sdk 0.3.1 → 0.4.0
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/README.md +24 -10
- package/dist/ui.d.ts +20 -7
- package/dist/ui.js +12 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,18 +21,11 @@ Cloudflare Workers, Vercel Edge or Deno. Its only runtime dependency is `openapi
|
|
|
21
21
|
|
|
22
22
|
## Install
|
|
23
23
|
|
|
24
|
-
The package is not on npm yet. Until it is, install a tagged release straight from GitHub:
|
|
25
|
-
|
|
26
24
|
```bash
|
|
27
|
-
npm install
|
|
25
|
+
npm install @checkcourt/sdk
|
|
28
26
|
```
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
`@checkcourt/sdk`:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npm install @checkcourt/sdk # coming soon
|
|
35
|
-
```
|
|
28
|
+
Every release is also tagged on [GitHub](https://github.com/CheckCourt/sdk/releases).
|
|
36
29
|
|
|
37
30
|
## Quick start
|
|
38
31
|
|
|
@@ -122,6 +115,17 @@ export async function POST(request: Request) {
|
|
|
122
115
|
}
|
|
123
116
|
```
|
|
124
117
|
|
|
118
|
+
CheckCourt keeps a successful render for 30 seconds. Pass `maxAge` (seconds, at most 300,
|
|
119
|
+
sent as `cache.max_age`) to change that for one answer, or `0` when the panel must always
|
|
120
|
+
be fresh:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
return Response.json(ui.doc([ui.stat("Battery", `${level} %`)], { maxAge: 0 }));
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
A `Cache-Control` response header (`no-store`, `max-age=N`) works too; `maxAge` wins when
|
|
127
|
+
both are set. In your sandbox club nothing is cached.
|
|
128
|
+
|
|
125
129
|
Compare `context.installation_id` and `context.tenant_id` with what you stored from
|
|
126
130
|
`app.installed` before you act on a request. For the context token alone (for example in
|
|
127
131
|
the backend of an iframe extension), use `verifyExtensionContext(token, secret)`.
|
|
@@ -192,7 +196,7 @@ Full guides and the API reference: <https://docs.checkcourt.de/docs/developer/sd
|
|
|
192
196
|
## Versioning
|
|
193
197
|
|
|
194
198
|
The SDK follows semantic versioning but is still in `0.x`: minor releases may contain
|
|
195
|
-
breaking changes until 1.0. Pin a
|
|
199
|
+
breaking changes until 1.0. Pin a version and read the [changelog](CHANGELOG.md) before you
|
|
196
200
|
upgrade.
|
|
197
201
|
|
|
198
202
|
The exported constant `OPENAPI_SPEC_SHA256` is the SHA-256 of the spec the bundled types
|
|
@@ -226,6 +230,16 @@ CHECKCOURT_OPENAPI=./openapi.json npm run generate # a local file or another UR
|
|
|
226
230
|
The generated types follow the platform. A release may ship types for endpoints that are
|
|
227
231
|
about to be deployed, so do not regenerate them in an unrelated pull request.
|
|
228
232
|
|
|
233
|
+
### Releasing
|
|
234
|
+
|
|
235
|
+
1. Bump `version` in `package.json` (`npm version <x.y.z> --no-git-tag-version`).
|
|
236
|
+
2. Add the release to `CHANGELOG.md`.
|
|
237
|
+
3. Run `npm run build` and commit, including the rebuilt `dist/`.
|
|
238
|
+
4. Tag the commit `vX.Y.Z` and push the tag (`git push origin vX.Y.Z`).
|
|
239
|
+
|
|
240
|
+
The release workflow checks that the tag matches the package version and that `dist/` is
|
|
241
|
+
up to date, then publishes to npm with provenance via trusted publishing.
|
|
242
|
+
|
|
229
243
|
## License
|
|
230
244
|
|
|
231
245
|
[MIT](LICENSE)
|
package/dist/ui.d.ts
CHANGED
|
@@ -112,17 +112,28 @@ export type UiToast = {
|
|
|
112
112
|
kind: "success" | "error";
|
|
113
113
|
message: string;
|
|
114
114
|
};
|
|
115
|
-
/**
|
|
115
|
+
/** CheckCourt caps every render lifetime at this many seconds. */
|
|
116
|
+
export declare const UI_CACHE_MAX_AGE_LIMIT = 300;
|
|
117
|
+
/**
|
|
118
|
+
* How long CheckCourt may reuse a render, in whole seconds. `0` means never. Without it
|
|
119
|
+
* CheckCourt follows the response's `Cache-Control` header, else keeps a render for 30 seconds.
|
|
120
|
+
*/
|
|
121
|
+
export type UiCache = {
|
|
122
|
+
max_age: number;
|
|
123
|
+
};
|
|
124
|
+
/** The response of a declarative extension: `{ ui: "v1", blocks, toast?, cache? }`. */
|
|
116
125
|
export interface UiDocument {
|
|
117
126
|
ui: typeof UI_VERSION;
|
|
118
127
|
blocks: UiBlock[];
|
|
119
128
|
toast?: UiToast;
|
|
129
|
+
cache?: UiCache;
|
|
120
130
|
}
|
|
121
131
|
/** Tells CheckCourt to show no card for this subject and viewer; on an action, removes the panel. */
|
|
122
132
|
export interface UiHiddenDocument {
|
|
123
133
|
ui: typeof UI_VERSION;
|
|
124
134
|
hidden: true;
|
|
125
135
|
toast?: UiToast;
|
|
136
|
+
cache?: UiCache;
|
|
126
137
|
}
|
|
127
138
|
/** Anything a declarative extension may answer with. */
|
|
128
139
|
export type UiResponse = UiDocument | UiHiddenDocument;
|
|
@@ -131,18 +142,20 @@ export type UiFormValues = Record<string, string | number | boolean>;
|
|
|
131
142
|
type Opt<T> = {
|
|
132
143
|
[K in keyof T]?: T[K];
|
|
133
144
|
};
|
|
145
|
+
/** Options shared by `ui.doc` and `ui.hidden`. */
|
|
146
|
+
export interface UiDocumentOptions {
|
|
147
|
+
toast?: UiToast;
|
|
148
|
+
/** Seconds CheckCourt may reuse this render, sent as `cache.max_age`; `0` disables caching. Capped at 300. */
|
|
149
|
+
maxAge?: number;
|
|
150
|
+
}
|
|
134
151
|
/**
|
|
135
152
|
* Builds `ui: "v1"` documents. Strings are plain text (no HTML, no Markdown); CheckCourt
|
|
136
153
|
* renders them with its own design system.
|
|
137
154
|
*/
|
|
138
155
|
export declare const ui: {
|
|
139
|
-
readonly doc: (blocks: UiBlock[], options?:
|
|
140
|
-
toast?: UiToast;
|
|
141
|
-
}) => UiDocument;
|
|
156
|
+
readonly doc: (blocks: UiBlock[], options?: UiDocumentOptions) => UiDocument;
|
|
142
157
|
/** Nothing relevant here: no card at all. Answering `204 No Content` does the same. */
|
|
143
|
-
readonly hidden: (options?:
|
|
144
|
-
toast?: UiToast;
|
|
145
|
-
}) => UiHiddenDocument;
|
|
158
|
+
readonly hidden: (options?: UiDocumentOptions) => UiHiddenDocument;
|
|
146
159
|
readonly text: (text: string, options?: {
|
|
147
160
|
tone?: "muted";
|
|
148
161
|
}) => UiTextBlock;
|
package/dist/ui.js
CHANGED
|
@@ -4,6 +4,8 @@ export const MAX_UI_BLOCKS = 50;
|
|
|
4
4
|
export const MAX_UI_DEPTH = 3;
|
|
5
5
|
export const BADGE_VARIANTS = ["default", "secondary", "outline", "destructive"];
|
|
6
6
|
export const BUTTON_VARIANTS = ["default", "secondary", "outline", "destructive"];
|
|
7
|
+
/** CheckCourt caps every render lifetime at this many seconds. */
|
|
8
|
+
export const UI_CACHE_MAX_AGE_LIMIT = 300;
|
|
7
9
|
const field = {
|
|
8
10
|
text(name, label, options = {}) {
|
|
9
11
|
return { type: "text", name, label, ...options };
|
|
@@ -21,17 +23,25 @@ const field = {
|
|
|
21
23
|
function compact(value) {
|
|
22
24
|
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined));
|
|
23
25
|
}
|
|
26
|
+
function cacheOf(maxAge) {
|
|
27
|
+
if (maxAge === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
if (!Number.isInteger(maxAge) || maxAge < 0) {
|
|
30
|
+
throw new RangeError(`maxAge must be a whole number of seconds >= 0, got ${maxAge}`);
|
|
31
|
+
}
|
|
32
|
+
return { max_age: maxAge };
|
|
33
|
+
}
|
|
24
34
|
/**
|
|
25
35
|
* Builds `ui: "v1"` documents. Strings are plain text (no HTML, no Markdown); CheckCourt
|
|
26
36
|
* renders them with its own design system.
|
|
27
37
|
*/
|
|
28
38
|
export const ui = {
|
|
29
39
|
doc(blocks, options = {}) {
|
|
30
|
-
return compact({ ui: UI_VERSION, blocks, toast: options.toast });
|
|
40
|
+
return compact({ ui: UI_VERSION, blocks, toast: options.toast, cache: cacheOf(options.maxAge) });
|
|
31
41
|
},
|
|
32
42
|
/** Nothing relevant here: no card at all. Answering `204 No Content` does the same. */
|
|
33
43
|
hidden(options = {}) {
|
|
34
|
-
return compact({ ui: UI_VERSION, hidden: true, toast: options.toast });
|
|
44
|
+
return compact({ ui: UI_VERSION, hidden: true, toast: options.toast, cache: cacheOf(options.maxAge) });
|
|
35
45
|
},
|
|
36
46
|
text(text, options = {}) {
|
|
37
47
|
return compact({ type: "text", text, tone: options.tone });
|