@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 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 github:CheckCourt/sdk#v0.3.1
25
+ npm install @checkcourt/sdk
28
26
  ```
29
27
 
30
- npm builds the package on install. Once it is published, the package name will be
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 tag and read the [changelog](CHANGELOG.md) before you
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
- /** The response of a declarative extension: `{ ui: "v1", blocks, toast? }`. */
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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkcourt/sdk",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Official TypeScript SDK for the CheckCourt app platform",
5
5
  "private": false,
6
6
  "license": "MIT",