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 +2 -0
- package/dist/sdk/tands-sdk-server.js +25 -1
- package/dist/sdk/tands-sdk.js +57 -1
- package/dist/tands.js +1539 -358
- package/package.json +3 -3
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
|
};
|
package/dist/sdk/tands-sdk.js
CHANGED
|
@@ -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
|
};
|