@tabs-and-spaces/sdk 0.1.0 → 0.2.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/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type DataRecord, type DataRequest, type DataScope, type TabContext, type Theme } from "./protocol.js";
1
+ import { type DataRecord, type DataRequest, type DataScope, type EventKey, type TabContext, type Theme } from "./protocol.js";
2
2
  /** A refusal from the workspace, with its reason's code (like `INVALID` or `FORBIDDEN`). */
3
3
  export declare class WorkspaceError extends Error {
4
4
  readonly code: string | null;
@@ -54,6 +54,19 @@ export interface Connection {
54
54
  * (triggers in this space may start a task). Resolves once it's taken.
55
55
  */
56
56
  emit(name: string, data?: Record<string, unknown>): Promise<void>;
57
+ /**
58
+ * Asks for a key your server can report events with, in this tab, whenever they
59
+ * happen (a webhook from another service, say). The person sees what's asked and
60
+ * allows it (only those who manage the space's tabs can); a new key replaces this
61
+ * tab's last one. Fails with `DECLINED` if they don't, `FORBIDDEN` if they can't.
62
+ */
63
+ requestEventKey(): Promise<EventKey>;
64
+ /**
65
+ * Opens a new trigger in this space, starting on one of your manifest's events in this
66
+ * tab (or the first, without `event`): the person sets the task and saves it. Fails
67
+ * with `NO_TASKS_TAB` when the space has no Tasks tab to keep triggers in.
68
+ */
69
+ openTrigger(event?: string): Promise<void>;
57
70
  /** Called when the reader switches between light and dark. Returns an unsubscribe. */
58
71
  onTheme(listener: (theme: Theme) => void): () => void;
59
72
  /** Stops listening. */
package/dist/client.js CHANGED
@@ -124,6 +124,10 @@ export function connect({ timeoutMs = 10_000, window: win = window } = {}) {
124
124
  async emit(name, fields = {}) {
125
125
  await ask((id) => ({ tands: 1, type: "event", id, name, data: fields }));
126
126
  },
127
+ requestEventKey: () => ask((id) => ({ tands: 1, type: "eventKey", id })),
128
+ async openTrigger(event) {
129
+ await ask((id) => ({ tands: 1, type: "openTrigger", id, event: event ?? null }));
130
+ },
127
131
  values(scope) {
128
132
  return {
129
133
  get: async (key) => (await data({ op: "values.get", scope, key })).value,
package/dist/index.d.ts CHANGED
@@ -3,4 +3,4 @@
3
3
  * server: `verifyToken()` from "@tabs-and-spaces/sdk/server".
4
4
  */
5
5
  export { connect, WorkspaceError, type Collection, type Connection, type ConnectOptions, type Values, } from "./client.js";
6
- export { PROTOCOL_VERSION, readAppMessage, readHostMessage, type AppMessage, type DataRecord, type DataRequest, type DataScope, type HostMessage, type TabContext, type Theme, } from "./protocol.js";
6
+ export { PROTOCOL_VERSION, readAppMessage, readHostMessage, type AppMessage, type DataRecord, type DataRequest, type DataScope, type EventKey, type HostMessage, type TabContext, type Theme, } from "./protocol.js";
@@ -120,7 +120,31 @@ export type AppMessage = {
120
120
  id: string;
121
121
  name: string;
122
122
  data: Record<string, unknown>;
123
+ }
124
+ /**
125
+ * A key for the app's server to report events in this tab on its own (answered like
126
+ * data, with an `EventKey`), once the person allows it.
127
+ */
128
+ | {
129
+ tands: 1;
130
+ type: "eventKey";
131
+ id: string;
132
+ }
133
+ /**
134
+ * Opens a new trigger in this space, starting on one of the app's events in this tab
135
+ * (decision 174). Answered like data once it's open.
136
+ */
137
+ | {
138
+ tands: 1;
139
+ type: "openTrigger";
140
+ id: string;
141
+ event: string | null;
123
142
  };
143
+ /** What the app's server reports events with, at `eventsUrl` (decision 170). */
144
+ export interface EventKey {
145
+ key: string;
146
+ eventsUrl: string;
147
+ }
124
148
  /** From the workspace to the page. */
125
149
  export type HostMessage = {
126
150
  tands: 1;
package/dist/protocol.js CHANGED
@@ -23,6 +23,22 @@ export function readAppMessage(data) {
23
23
  // Checked in full by the workspace's server.
24
24
  return { tands: 1, type: "data", id: data.id, request: data.request };
25
25
  }
26
+ if (data.type === "eventKey" && typeof data.id === "string" && data.id.length <= 64) {
27
+ return { tands: 1, type: "eventKey", id: data.id };
28
+ }
29
+ if (data.type === "openTrigger" &&
30
+ typeof data.id === "string" &&
31
+ data.id.length <= 64 &&
32
+ (data.event === undefined ||
33
+ data.event === null ||
34
+ (typeof data.event === "string" && data.event.length <= 40))) {
35
+ return {
36
+ tands: 1,
37
+ type: "openTrigger",
38
+ id: data.id,
39
+ event: typeof data.event === "string" ? data.event : null,
40
+ };
41
+ }
26
42
  if (data.type === "event" &&
27
43
  typeof data.id === "string" &&
28
44
  data.id.length <= 64 &&
package/dist/server.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { DataRecord, DataRequest, DataScope, TabContext } from "./protocol.js";
1
+ import type { DataRecord, DataRequest, DataScope, EventKey, TabContext } from "./protocol.js";
2
2
  /**
3
3
  * Checking a Custom tab's token on your server (Node 22+, Deno, Bun, Workers:
4
4
  * anywhere with Web Crypto's Ed25519). The workspace signs it (EdDSA); its
@@ -89,8 +89,19 @@ export declare function dataClient({ dataKey, dataUrl }: Pick<ActionRequest, "da
89
89
  delete: (id: string) => Promise<void>;
90
90
  };
91
91
  };
92
+ /**
93
+ * Reports an event from your server at any time (decision 170), with the event key
94
+ * your tab got from `requestEventKey()`: in that tab's space, for the person who
95
+ * allowed it. Keep the key secret, like a password.
96
+ *
97
+ * ```ts
98
+ * await reportEvent(stored, "pull_request_opened", { title: "Fix login", number: 42 });
99
+ * ```
100
+ */
101
+ export declare function reportEvent({ key, eventsUrl }: EventKey, name: string, fields?: Record<string, unknown>, fetcher?: typeof fetch): Promise<void>;
92
102
  /** A refusal from the workspace's data endpoint, with its code. */
93
103
  export declare class DataError extends Error {
94
104
  readonly code: string | null;
95
105
  constructor(message: string, code: string | null);
96
106
  }
107
+ export type { EventKey } from "./protocol.js";
package/dist/server.js CHANGED
@@ -122,6 +122,26 @@ export function dataClient({ dataKey, dataUrl }, fetcher = fetch) {
122
122
  },
123
123
  };
124
124
  }
125
+ /**
126
+ * Reports an event from your server at any time (decision 170), with the event key
127
+ * your tab got from `requestEventKey()`: in that tab's space, for the person who
128
+ * allowed it. Keep the key secret, like a password.
129
+ *
130
+ * ```ts
131
+ * await reportEvent(stored, "pull_request_opened", { title: "Fix login", number: 42 });
132
+ * ```
133
+ */
134
+ export async function reportEvent({ key, eventsUrl }, name, fields = {}, fetcher = fetch) {
135
+ const response = await fetcher(eventsUrl, {
136
+ method: "POST",
137
+ headers: { "content-type": "application/json", authorization: `Bearer ${key}` },
138
+ body: JSON.stringify({ name, data: fields }),
139
+ });
140
+ if (!response.ok) {
141
+ const body = (await response.json().catch(() => null));
142
+ throw new DataError(body?.error?.message ?? `It answered ${response.status}.`, body?.error?.code ?? null);
143
+ }
144
+ }
125
145
  /** A refusal from the workspace's data endpoint, with its code. */
126
146
  export class DataError extends Error {
127
147
  code;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tabs-and-spaces/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Building apps for Tabs & Spaces: a Custom tab's page talks to the workspace, its server checks who's looking. No dependencies.",
5
5
  "license": "MIT",
6
6
  "author": "Tabs & Spaces",