tands 0.1.0 → 0.3.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
@@ -13,6 +13,8 @@ npx tands dev # a local workspace for it, on http://localho
13
13
  - **`tands check [manifest]`** checks a manifest (a file, or its address) as a workspace would.
14
14
  - **`tands dev [manifest address]`** stands in for a workspace: your tab as someone you choose,
15
15
  real tokens signed with its own keys, your data in memory, your events and your actions.
16
+ - **`tands publish [--changes "…"]`** sends a new version of your listed app to the marketplace
17
+ for review, with a publishing key from its workspace (`TANDS_URL`, `TANDS_PUBLISH_KEY`).
16
18
 
17
19
  For your own build, the SDK is [`@tabs-and-spaces/sdk`](https://www.npmjs.com/package/@tabs-and-spaces/sdk).
18
20
 
@@ -1,3 +1,11 @@
1
+ // ../sdk/src/protocol.ts
2
+ var isHome = (context) => Boolean(context.home);
3
+ function withReadOnly(context) {
4
+ const workspace = context.workspace;
5
+ if (!workspace || typeof workspace.readOnly === "boolean") return context;
6
+ return { ...context, workspace: { ...workspace, readOnly: false } };
7
+ }
8
+
1
9
  // ../sdk/src/server.ts
2
10
  var TokenError = class extends Error {
3
11
  };
@@ -55,7 +63,7 @@ async function verifyToken(token, {
55
63
  throw new TokenError("It has expired.");
56
64
  }
57
65
  return {
58
- context: payload.ctx,
66
+ context: withReadOnly(payload.ctx),
59
67
  issuer: String(payload.iss),
60
68
  expiresAt: new Date(payload.exp * 1e3),
61
69
  action: typeof payload.act === "string" ? payload.act : null
@@ -121,6 +129,20 @@ function dataClient({ dataKey, dataUrl }, fetcher = fetch) {
121
129
  }
122
130
  };
123
131
  }
132
+ async function reportEvent({ key, eventsUrl }, name, fields = {}, fetcher = fetch) {
133
+ const response = await fetcher(eventsUrl, {
134
+ method: "POST",
135
+ headers: { "content-type": "application/json", authorization: `Bearer ${key}` },
136
+ body: JSON.stringify({ name, data: fields })
137
+ });
138
+ if (!response.ok) {
139
+ const body = await response.json().catch(() => null);
140
+ throw new DataError(
141
+ body?.error?.message ?? `It answered ${response.status}.`,
142
+ body?.error?.code ?? null
143
+ );
144
+ }
145
+ }
124
146
  var DataError = class extends Error {
125
147
  constructor(message, code) {
126
148
  super(message);
@@ -133,5 +155,7 @@ export {
133
155
  TokenError,
134
156
  dataClient,
135
157
  fetchKeys,
158
+ isHome,
159
+ reportEvent,
136
160
  verifyToken
137
161
  };
@@ -1,5 +1,6 @@
1
1
  // ../sdk/src/protocol.ts
2
2
  var PROTOCOL_VERSION = 1;
3
+ var isHome = (context) => Boolean(context.home);
3
4
  var isRecord = (value) => typeof value === "object" && value !== null;
4
5
  function readAppMessage(data) {
5
6
  if (!isRecord(data) || data.tands !== PROTOCOL_VERSION) return null;
@@ -10,6 +11,27 @@ function readAppMessage(data) {
10
11
  if (data.type === "data" && typeof data.id === "string" && data.id.length <= 64 && isRecord(data.request) && typeof data.request.op === "string") {
11
12
  return { tands: 1, type: "data", id: data.id, request: data.request };
12
13
  }
14
+ if (data.type === "eventKey" && typeof data.id === "string" && data.id.length <= 64) {
15
+ return { tands: 1, type: "eventKey", id: data.id };
16
+ }
17
+ if (data.type === "openTrigger" && typeof data.id === "string" && data.id.length <= 64 && (data.event === void 0 || data.event === null || typeof data.event === "string" && data.event.length <= 40)) {
18
+ return {
19
+ tands: 1,
20
+ type: "openTrigger",
21
+ id: data.id,
22
+ event: typeof data.event === "string" ? data.event : null
23
+ };
24
+ }
25
+ const id = (value) => typeof value === "string" && value.length > 0 && value.length <= 64;
26
+ if (data.type === "open" && id(data.id) && id(data.space) && (data.tab === void 0 || data.tab === null || id(data.tab))) {
27
+ return {
28
+ tands: 1,
29
+ type: "open",
30
+ id: data.id,
31
+ space: data.space,
32
+ tab: typeof data.tab === "string" ? data.tab : null
33
+ };
34
+ }
13
35
  if (data.type === "event" && typeof data.id === "string" && data.id.length <= 64 && typeof data.name === "string" && data.name.length <= 40 && (data.data === void 0 || isRecord(data.data))) {
14
36
  return {
15
37
  tands: 1,
@@ -21,6 +43,11 @@ function readAppMessage(data) {
21
43
  }
22
44
  return null;
23
45
  }
46
+ function withReadOnly(context) {
47
+ const workspace = context.workspace;
48
+ if (!workspace || typeof workspace.readOnly === "boolean") return context;
49
+ return { ...context, workspace: { ...workspace, readOnly: false } };
50
+ }
24
51
  function readHostMessage(data) {
25
52
  if (!isRecord(data) || data.tands !== PROTOCOL_VERSION) return null;
26
53
  const theme = data.theme === "dark" ? "dark" : "light";
@@ -29,7 +56,7 @@ function readHostMessage(data) {
29
56
  return isRecord(data.context) && typeof data.token === "string" && typeof data.expiresAt === "string" ? {
30
57
  tands: 1,
31
58
  type: "context",
32
- context: data.context,
59
+ context: withReadOnly(data.context),
33
60
  token: data.token,
34
61
  expiresAt: data.expiresAt,
35
62
  theme
@@ -48,6 +75,8 @@ function readHostMessage(data) {
48
75
  } : null;
49
76
  case "theme":
50
77
  return { tands: 1, type: "theme", theme };
78
+ case "readOnly":
79
+ return typeof data.readOnly === "boolean" ? { tands: 1, type: "readOnly", readOnly: data.readOnly } : null;
51
80
  default:
52
81
  return null;
53
82
  }
@@ -75,6 +104,7 @@ function connect({ timeoutMs = 1e4, window: win = window } = {}) {
75
104
  let theme = "light";
76
105
  let counter = 0;
77
106
  const themeListeners = /* @__PURE__ */ new Set();
107
+ const readOnlyListeners = /* @__PURE__ */ new Set();
78
108
  const waiting = /* @__PURE__ */ new Map();
79
109
  let pending = null;
80
110
  const send = (message) => parent.postMessage(message, hostOrigin ?? "*");
@@ -119,6 +149,11 @@ function connect({ timeoutMs = 1e4, window: win = window } = {}) {
119
149
  waiter.reject(new WorkspaceError(message.message, message.code ?? null));
120
150
  return;
121
151
  }
152
+ case "readOnly":
153
+ if (!context || context.workspace.readOnly === message.readOnly) return;
154
+ context = { ...context, workspace: { ...context.workspace, readOnly: message.readOnly } };
155
+ for (const listener of readOnlyListeners) listener(message.readOnly);
156
+ return;
122
157
  case "theme":
123
158
  theme = message.theme;
124
159
  for (const listener of themeListeners) listener(theme);
@@ -137,6 +172,9 @@ function connect({ timeoutMs = 1e4, window: win = window } = {}) {
137
172
  get context() {
138
173
  return context;
139
174
  },
175
+ get readOnly() {
176
+ return context.workspace.readOnly;
177
+ },
140
178
  get theme() {
141
179
  return theme;
142
180
  },
@@ -155,6 +193,19 @@ function connect({ timeoutMs = 1e4, window: win = window } = {}) {
155
193
  async emit(name, fields = {}) {
156
194
  await ask((id) => ({ tands: 1, type: "event", id, name, data: fields }));
157
195
  },
196
+ requestEventKey: () => ask((id) => ({ tands: 1, type: "eventKey", id })),
197
+ async open(target) {
198
+ await ask((id) => ({
199
+ tands: 1,
200
+ type: "open",
201
+ id,
202
+ space: target.space,
203
+ tab: target.tab ?? null
204
+ }));
205
+ },
206
+ async openTrigger(event) {
207
+ await ask((id) => ({ tands: 1, type: "openTrigger", id, event: event ?? null }));
208
+ },
158
209
  values(scope) {
159
210
  return {
160
211
  get: async (key) => (await data({ op: "values.get", scope, key })).value,
@@ -180,6 +231,10 @@ function connect({ timeoutMs = 1e4, window: win = window } = {}) {
180
231
  }
181
232
  };
182
233
  },
234
+ onReadOnly(listener) {
235
+ readOnlyListeners.add(listener);
236
+ return () => readOnlyListeners.delete(listener);
237
+ },
183
238
  onTheme(listener) {
184
239
  themeListeners.add(listener);
185
240
  return () => themeListeners.delete(listener);
@@ -198,6 +253,7 @@ export {
198
253
  PROTOCOL_VERSION,
199
254
  WorkspaceError,
200
255
  connect,
256
+ isHome,
201
257
  readAppMessage,
202
258
  readHostMessage
203
259
  };