@notionhq/custom-blocks 0.0.76 → 0.0.78
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/HOST.md +68 -37
- package/bin/src/bridge/manifest.js +3 -3
- package/dist/bridge/SandboxBridge.d.ts +6 -12
- package/dist/bridge/SandboxBridge.d.ts.map +1 -1
- package/dist/bridge/SandboxBridge.js +123 -71
- package/dist/bridge/dataSources/resolve.d.ts +3 -4
- package/dist/bridge/dataSources/resolve.d.ts.map +1 -1
- package/dist/bridge/dataSources/resolve.js +2 -12
- package/dist/bridge/hostState.d.ts +3 -2
- package/dist/bridge/hostState.d.ts.map +1 -1
- package/dist/bridge/loadManifest.d.ts +4 -4
- package/dist/bridge/loadManifest.d.ts.map +1 -1
- package/dist/bridge/loadManifest.js +2 -2
- package/dist/bridge/manifest.d.ts +3 -3
- package/dist/bridge/manifest.js +3 -3
- package/dist/bridge/messages/{ready.d.ts → connect.d.ts} +13 -11
- package/dist/bridge/messages/connect.d.ts.map +1 -0
- package/dist/bridge/messages/{ready.js → connect.js} +14 -13
- package/dist/bridge/messages/hostToSandbox.d.ts +24 -79
- package/dist/bridge/messages/hostToSandbox.d.ts.map +1 -1
- package/dist/bridge/messages/hostToSandbox.js +2 -3
- package/dist/bridge/messages/init.d.ts +35 -6
- package/dist/bridge/messages/init.d.ts.map +1 -1
- package/dist/bridge/messages/init.js +8 -4
- package/dist/bridge/messages/initResult.d.ts +29 -0
- package/dist/bridge/messages/initResult.d.ts.map +1 -0
- package/dist/bridge/messages/initResult.js +25 -0
- package/dist/bridge/messages/queryDataSource.d.ts +3 -7
- package/dist/bridge/messages/queryDataSource.d.ts.map +1 -1
- package/dist/bridge/messages/queryDataSource.js +3 -7
- package/dist/bridge/messages/queryDataSourceResult.d.ts +4 -240
- package/dist/bridge/messages/queryDataSourceResult.d.ts.map +1 -1
- package/dist/bridge/messages/queryDataSourceResult.js +4 -22
- package/dist/bridge/messages/sandboxToHost.d.ts +21 -7
- package/dist/bridge/messages/sandboxToHost.d.ts.map +1 -1
- package/dist/bridge/messages/sandboxToHost.js +4 -2
- package/dist/bridge/sandboxClient.d.ts +2 -4
- package/dist/bridge/sandboxClient.d.ts.map +1 -1
- package/dist/bridge/sandboxClient.js +2 -5
- package/dist/customBlock.d.ts +1 -1
- package/dist/customBlock.d.ts.map +1 -1
- package/dist/customBlock.js +1 -1
- package/dist/host/createCustomBlockHost.d.ts +6 -3
- package/dist/host/createCustomBlockHost.d.ts.map +1 -1
- package/dist/host/createCustomBlockHost.js +227 -58
- package/dist/host/lifecycle/initErrors.d.ts +1 -1
- package/dist/host/lifecycle/initErrors.d.ts.map +1 -1
- package/dist/host/lifecycle/initErrors.js +25 -7
- package/dist/host/lifecycle/types.d.ts +6 -4
- package/dist/host/lifecycle/types.d.ts.map +1 -1
- package/dist/host/messages/invalidSandboxMessage.js +0 -3
- package/dist/host/queries/querySubscriptions.d.ts +10 -10
- package/dist/host/queries/querySubscriptions.d.ts.map +1 -1
- package/dist/host/queries/querySubscriptions.js +35 -19
- package/dist/host/queries/types.d.ts +5 -4
- package/dist/host/queries/types.d.ts.map +1 -1
- package/dist/host.d.ts +5 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/init.d.ts +7 -7
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +70 -50
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/standalonePreview.d.ts.map +1 -1
- package/dist/react/standalonePreview.js +2 -0
- package/dist/react/useCustomBlockInit.d.ts +5 -6
- package/dist/react/useCustomBlockInit.d.ts.map +1 -1
- package/dist/react/useCustomBlockInit.js +3 -19
- package/dist/react/useRuntimeState.d.ts +4 -3
- package/dist/react/useRuntimeState.d.ts.map +1 -1
- package/dist/react/useRuntimeState.js +3 -2
- package/dist/version.js +1 -1
- package/docs/data-sources.md +7 -5
- package/docs/errors.md +5 -1
- package/docs/lifecycle.md +19 -31
- package/docs/manifest.md +1 -1
- package/package.json +1 -1
- package/src/bridge/SandboxBridge.ts +151 -77
- package/src/bridge/dataSources/resolve.ts +3 -14
- package/src/bridge/hostState.ts +3 -2
- package/src/bridge/loadManifest.ts +6 -6
- package/src/bridge/manifest.ts +3 -3
- package/src/bridge/messages/{ready.ts → connect.ts} +15 -14
- package/src/bridge/messages/hostToSandbox.ts +2 -6
- package/src/bridge/messages/init.ts +24 -10
- package/src/bridge/messages/initResult.ts +37 -0
- package/src/bridge/messages/queryDataSource.ts +3 -7
- package/src/bridge/messages/queryDataSourceResult.ts +4 -24
- package/src/bridge/messages/sandboxToHost.ts +4 -2
- package/src/bridge/sandboxClient.ts +3 -8
- package/src/customBlock.ts +2 -2
- package/src/host/createCustomBlockHost.ts +269 -64
- package/src/host/lifecycle/initErrors.ts +25 -7
- package/src/host/lifecycle/types.ts +13 -4
- package/src/host/messages/invalidSandboxMessage.ts +0 -3
- package/src/host/queries/querySubscriptions.ts +58 -39
- package/src/host/queries/types.ts +10 -11
- package/src/host.ts +13 -2
- package/src/index.ts +3 -1
- package/src/init.ts +73 -60
- package/src/react/index.ts +0 -1
- package/src/react/standalonePreview.ts +2 -0
- package/src/react/useCustomBlockInit.ts +6 -27
- package/src/react/useRuntimeState.ts +4 -3
- package/dist/bridge/messages/ready.d.ts.map +0 -1
|
@@ -5,17 +5,27 @@ import { initErrorForFailureReason } from "./lifecycle/initErrors.js";
|
|
|
5
5
|
import { isValidBridgeProtocolVersion } from "./lifecycle/ready.js";
|
|
6
6
|
import { getInvalidSandboxMessageResponse } from "./messages/invalidSandboxMessage.js";
|
|
7
7
|
import { QuerySubscriptions } from "./queries/querySubscriptions.js";
|
|
8
|
-
const
|
|
8
|
+
const DEFAULT_NO_CONNECT_TIMEOUT_MS = 5000;
|
|
9
|
+
const DEFAULT_NO_INIT_RESULT_TIMEOUT_MS = 5000;
|
|
9
10
|
const DEFAULT_MIN_BRIDGE_PROTOCOL_VERSION = 2;
|
|
10
11
|
export function createCustomBlockHost(options) {
|
|
11
|
-
const { iframe, initialState, handlers,
|
|
12
|
+
const { iframe, initialState, handlers, onConnect, onInitResult, onLog, targetOrigin = "*", minBridgeProtocolVersion = DEFAULT_MIN_BRIDGE_PROTOCOL_VERSION, noConnectTimeoutMs = DEFAULT_NO_CONNECT_TIMEOUT_MS, noInitResultTimeoutMs = DEFAULT_NO_INIT_RESULT_TIMEOUT_MS, } = options;
|
|
12
13
|
let theme = initialState.theme;
|
|
13
14
|
let parent = initialState.parent;
|
|
14
15
|
let page = initialState.page;
|
|
15
16
|
let dataSources = initialState.dataSources;
|
|
17
|
+
let manifest = initialState.manifest;
|
|
16
18
|
let currentUser = initialState.currentUser;
|
|
17
|
-
let initStatus = "
|
|
18
|
-
let
|
|
19
|
+
let initStatus = "waitingForConnect";
|
|
20
|
+
let noConnectTimeoutId;
|
|
21
|
+
let noInitResultTimeoutId;
|
|
22
|
+
let activeInitializationId;
|
|
23
|
+
let connectedSandbox;
|
|
24
|
+
let pendingThemeChange = false;
|
|
25
|
+
let pendingParentChange = false;
|
|
26
|
+
let pendingPageChange = false;
|
|
27
|
+
let pendingDataSourcesChange;
|
|
28
|
+
let pendingCurrentUserChange = false;
|
|
19
29
|
const querySubscriptions = new QuerySubscriptions();
|
|
20
30
|
function emit(direction, payload) {
|
|
21
31
|
onLog?.(direction, payload);
|
|
@@ -25,71 +35,116 @@ export function createCustomBlockHost(options) {
|
|
|
25
35
|
iframe.contentWindow?.postMessage(message, targetOrigin);
|
|
26
36
|
}
|
|
27
37
|
function postQueryDataSourceResult(args) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
const message = args.response.status === "error"
|
|
39
|
+
? {
|
|
40
|
+
type: "queryDataSourceResult",
|
|
41
|
+
subscriptionId: args.subscriptionId,
|
|
42
|
+
status: "error",
|
|
43
|
+
error: args.response.error,
|
|
44
|
+
}
|
|
45
|
+
: {
|
|
46
|
+
type: "queryDataSourceResult",
|
|
47
|
+
subscriptionId: args.subscriptionId,
|
|
48
|
+
status: "success",
|
|
49
|
+
items: args.response.items,
|
|
50
|
+
hasMore: args.response.hasMore,
|
|
51
|
+
};
|
|
38
52
|
post(message);
|
|
39
53
|
}
|
|
40
|
-
function
|
|
41
|
-
if (
|
|
42
|
-
window.clearTimeout(
|
|
43
|
-
|
|
54
|
+
function clearNoConnectTimeout() {
|
|
55
|
+
if (noConnectTimeoutId !== undefined) {
|
|
56
|
+
window.clearTimeout(noConnectTimeoutId);
|
|
57
|
+
noConnectTimeoutId = undefined;
|
|
44
58
|
}
|
|
45
59
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
60
|
+
function clearNoInitResultTimeout() {
|
|
61
|
+
if (noInitResultTimeoutId !== undefined) {
|
|
62
|
+
window.clearTimeout(noInitResultTimeoutId);
|
|
63
|
+
noInitResultTimeoutId = undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function resolveDataSourcesPayload(connect) {
|
|
67
|
+
return typeof dataSources === "function"
|
|
68
|
+
? dataSources(connect)
|
|
69
|
+
: dataSources;
|
|
48
70
|
}
|
|
49
|
-
function sendInit(
|
|
50
|
-
if (initStatus !== "
|
|
71
|
+
function sendInit(connect) {
|
|
72
|
+
if (initStatus !== "waitingForConnect") {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
onConnect?.(connect);
|
|
76
|
+
manifest = connect.manifest ?? initialState.manifest;
|
|
77
|
+
const resolvedDataSources = resolveDataSourcesPayload(connect);
|
|
78
|
+
const bindingErrorCode = getBindingErrorCode(manifest, resolvedDataSources);
|
|
79
|
+
if (bindingErrorCode !== undefined) {
|
|
80
|
+
sendInitError(connect.initializationId, initErrorForFailureReason(bindingErrorCode));
|
|
51
81
|
return false;
|
|
52
82
|
}
|
|
53
|
-
initStatus = "
|
|
54
|
-
|
|
83
|
+
initStatus = "waitingForInitResult";
|
|
84
|
+
activeInitializationId = connect.initializationId;
|
|
85
|
+
clearNoConnectTimeout();
|
|
55
86
|
const message = {
|
|
56
87
|
type: "init",
|
|
88
|
+
initializationId: connect.initializationId,
|
|
57
89
|
status: "success",
|
|
58
90
|
theme,
|
|
59
91
|
blockId: initialState.blockId,
|
|
60
92
|
parent,
|
|
61
93
|
page,
|
|
62
|
-
|
|
94
|
+
manifest,
|
|
95
|
+
dataSources: resolvedDataSources,
|
|
63
96
|
currentUser,
|
|
64
97
|
};
|
|
65
98
|
post(message);
|
|
99
|
+
noInitResultTimeoutId = window.setTimeout(() => {
|
|
100
|
+
if (initStatus !== "waitingForInitResult") {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
initStatus = "error";
|
|
104
|
+
emit("warn", {
|
|
105
|
+
message: "Sandbox did not send initResult",
|
|
106
|
+
initializationId: connect.initializationId,
|
|
107
|
+
});
|
|
108
|
+
}, noInitResultTimeoutMs);
|
|
66
109
|
return true;
|
|
67
110
|
}
|
|
68
|
-
function sendInitError(error) {
|
|
69
|
-
if (initStatus !== "
|
|
111
|
+
function sendInitError(initializationId, error) {
|
|
112
|
+
if (initStatus !== "waitingForConnect") {
|
|
70
113
|
return;
|
|
71
114
|
}
|
|
72
115
|
initStatus = "error";
|
|
73
|
-
|
|
116
|
+
activeInitializationId = initializationId;
|
|
117
|
+
clearNoConnectTimeout();
|
|
74
118
|
post({
|
|
75
119
|
type: "init",
|
|
120
|
+
initializationId,
|
|
76
121
|
status: "error",
|
|
77
122
|
error,
|
|
78
123
|
});
|
|
79
124
|
}
|
|
80
125
|
function onIframeLoad() {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
126
|
+
clearNoConnectTimeout();
|
|
127
|
+
if (initStatus !== "waitingForConnect") {
|
|
128
|
+
// Only set the timeout if the iframe loaded before the `connect` message was sent.
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
noConnectTimeoutId = window.setTimeout(() => {
|
|
132
|
+
if (initStatus !== "waitingForConnect") {
|
|
133
|
+
// Only do anything if the iframe loaded before the `connect` message was sent.
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
initStatus = "error";
|
|
137
|
+
emit("warn", initErrorForFailureReason("no_connect"));
|
|
138
|
+
}, noConnectTimeoutMs);
|
|
86
139
|
}
|
|
87
140
|
async function handleQuery(message) {
|
|
88
|
-
querySubscriptions.track(message);
|
|
141
|
+
const token = querySubscriptions.track(message);
|
|
89
142
|
const response = await handlers.queryDataSource(message);
|
|
143
|
+
if (!querySubscriptions.isCurrent(message.subscriptionId, token)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
90
146
|
postQueryDataSourceResult({
|
|
91
|
-
|
|
92
|
-
snapshotId: message.snapshotId,
|
|
147
|
+
subscriptionId: message.subscriptionId,
|
|
93
148
|
response,
|
|
94
149
|
});
|
|
95
150
|
}
|
|
@@ -153,13 +208,13 @@ export function createCustomBlockHost(options) {
|
|
|
153
208
|
};
|
|
154
209
|
post(result);
|
|
155
210
|
}
|
|
156
|
-
function
|
|
211
|
+
function handleConnect(message) {
|
|
157
212
|
// Verify the bridge protocol version is valid.
|
|
158
213
|
if (!isValidBridgeProtocolVersion(message.bridgeProtocolVersion)) {
|
|
159
214
|
const initError = initErrorForFailureReason("invalid_protocol_version", {
|
|
160
215
|
currentBridgeProtocolVersion: message.bridgeProtocolVersion,
|
|
161
216
|
});
|
|
162
|
-
sendInitError(initError);
|
|
217
|
+
sendInitError(message.initializationId, initError);
|
|
163
218
|
return;
|
|
164
219
|
}
|
|
165
220
|
if (message.bridgeProtocolVersion < minBridgeProtocolVersion) {
|
|
@@ -167,17 +222,62 @@ export function createCustomBlockHost(options) {
|
|
|
167
222
|
currentBridgeProtocolVersion: message.bridgeProtocolVersion,
|
|
168
223
|
minBridgeProtocolVersion,
|
|
169
224
|
});
|
|
170
|
-
sendInitError(initError);
|
|
225
|
+
sendInitError(message.initializationId, initError);
|
|
171
226
|
return;
|
|
172
227
|
}
|
|
173
|
-
// If the sandbox reported an error during `
|
|
228
|
+
// If the sandbox reported an error during `connect`, return it as an `init` error.
|
|
174
229
|
if (message.status === "error") {
|
|
175
|
-
sendInitError(message.error);
|
|
230
|
+
sendInitError(message.initializationId, message.error);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (!sendInit(message)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
connectedSandbox = message;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Sends any pending host state changes to the sandbox. It is possible for these to change
|
|
240
|
+
* between when the host sends `init` and when the sandbox responds with `initResult`. The host
|
|
241
|
+
* does send any pending state changes until hearing back from the sandbox.
|
|
242
|
+
*/
|
|
243
|
+
function flushPendingHostStateChanges() {
|
|
244
|
+
if (pendingThemeChange) {
|
|
245
|
+
pendingThemeChange = false;
|
|
246
|
+
post({ type: "themeChanged", theme });
|
|
247
|
+
}
|
|
248
|
+
if (pendingParentChange) {
|
|
249
|
+
pendingParentChange = false;
|
|
250
|
+
post({ type: "parentChanged", parent });
|
|
251
|
+
}
|
|
252
|
+
if (pendingPageChange) {
|
|
253
|
+
pendingPageChange = false;
|
|
254
|
+
post({ type: "pageChanged", page });
|
|
255
|
+
}
|
|
256
|
+
if (pendingDataSourcesChange !== undefined) {
|
|
257
|
+
const nextDataSources = pendingDataSourcesChange;
|
|
258
|
+
pendingDataSourcesChange = undefined;
|
|
259
|
+
post({ type: "dataSourcesChanged", dataSources: nextDataSources });
|
|
260
|
+
}
|
|
261
|
+
if (pendingCurrentUserChange) {
|
|
262
|
+
pendingCurrentUserChange = false;
|
|
263
|
+
post({ type: "currentUserChanged", currentUser });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function handleInitResult(message) {
|
|
267
|
+
if (initStatus !== "waitingForInitResult" ||
|
|
268
|
+
message.initializationId !== activeInitializationId) {
|
|
269
|
+
emit("warn", {
|
|
270
|
+
message: "Ignored stale or unexpected initResult",
|
|
271
|
+
payload: message,
|
|
272
|
+
});
|
|
176
273
|
return;
|
|
177
274
|
}
|
|
178
|
-
|
|
179
|
-
|
|
275
|
+
clearNoInitResultTimeout();
|
|
276
|
+
initStatus = message.status === "success" ? "success" : "error";
|
|
277
|
+
if (initStatus === "success") {
|
|
278
|
+
flushPendingHostStateChanges();
|
|
180
279
|
}
|
|
280
|
+
onInitResult?.(message);
|
|
181
281
|
}
|
|
182
282
|
function onInvalidSandboxMessage(data, issues) {
|
|
183
283
|
emit("warn", {
|
|
@@ -186,11 +286,7 @@ export function createCustomBlockHost(options) {
|
|
|
186
286
|
issues,
|
|
187
287
|
});
|
|
188
288
|
const response = getInvalidSandboxMessageResponse(data);
|
|
189
|
-
if (
|
|
190
|
-
const initError = initErrorForFailureReason("invalid_ready");
|
|
191
|
-
sendInitError(initError);
|
|
192
|
-
}
|
|
193
|
-
else if (response.nack !== undefined) {
|
|
289
|
+
if (response.nack !== undefined) {
|
|
194
290
|
post(response.nack);
|
|
195
291
|
}
|
|
196
292
|
}
|
|
@@ -205,9 +301,21 @@ export function createCustomBlockHost(options) {
|
|
|
205
301
|
}
|
|
206
302
|
const message = parsed.output;
|
|
207
303
|
emit("in", message);
|
|
304
|
+
if (message.type !== "connect" &&
|
|
305
|
+
message.type !== "initResult" &&
|
|
306
|
+
initStatus !== "success") {
|
|
307
|
+
emit("warn", {
|
|
308
|
+
message: `Ignored ${message.type} before initialization completed`,
|
|
309
|
+
payload: message,
|
|
310
|
+
});
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
208
313
|
switch (message.type) {
|
|
209
|
-
case "
|
|
210
|
-
|
|
314
|
+
case "connect":
|
|
315
|
+
handleConnect(message);
|
|
316
|
+
return;
|
|
317
|
+
case "initResult":
|
|
318
|
+
handleInitResult(message);
|
|
211
319
|
return;
|
|
212
320
|
case "queryDataSource":
|
|
213
321
|
void handleQuery(message);
|
|
@@ -242,26 +350,60 @@ export function createCustomBlockHost(options) {
|
|
|
242
350
|
stop() {
|
|
243
351
|
window.removeEventListener("message", onMessage);
|
|
244
352
|
iframe.removeEventListener("load", onIframeLoad);
|
|
245
|
-
|
|
353
|
+
clearNoConnectTimeout();
|
|
354
|
+
clearNoInitResultTimeout();
|
|
355
|
+
querySubscriptions.clear();
|
|
246
356
|
},
|
|
247
357
|
post,
|
|
248
358
|
setTheme(nextTheme) {
|
|
249
359
|
theme = nextTheme;
|
|
360
|
+
if (initStatus !== "success") {
|
|
361
|
+
if (initStatus === "waitingForInitResult") {
|
|
362
|
+
pendingThemeChange = true;
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
250
366
|
const message = { type: "themeChanged", theme };
|
|
251
367
|
post(message);
|
|
252
368
|
},
|
|
253
369
|
setParent(nextParent) {
|
|
254
370
|
parent = nextParent;
|
|
371
|
+
if (initStatus !== "success") {
|
|
372
|
+
if (initStatus === "waitingForInitResult") {
|
|
373
|
+
pendingParentChange = true;
|
|
374
|
+
}
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
255
377
|
const message = { type: "parentChanged", parent };
|
|
256
378
|
post(message);
|
|
257
379
|
},
|
|
258
380
|
setPage(nextPage) {
|
|
259
381
|
page = nextPage;
|
|
382
|
+
if (initStatus !== "success") {
|
|
383
|
+
if (initStatus === "waitingForInitResult") {
|
|
384
|
+
pendingPageChange = true;
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
260
388
|
const message = { type: "pageChanged", page };
|
|
261
389
|
post(message);
|
|
262
390
|
},
|
|
263
391
|
setDataSources(nextDataSources) {
|
|
392
|
+
if (connectedSandbox !== undefined &&
|
|
393
|
+
getBindingErrorCode(manifest, nextDataSources) !== undefined) {
|
|
394
|
+
emit("warn", {
|
|
395
|
+
message: "Ignored invalid data source bindings update",
|
|
396
|
+
payload: nextDataSources,
|
|
397
|
+
});
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
264
400
|
dataSources = nextDataSources;
|
|
401
|
+
if (initStatus !== "success") {
|
|
402
|
+
if (initStatus === "waitingForInitResult") {
|
|
403
|
+
pendingDataSourcesChange = nextDataSources;
|
|
404
|
+
}
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
265
407
|
const message = {
|
|
266
408
|
type: "dataSourcesChanged",
|
|
267
409
|
dataSources: nextDataSources,
|
|
@@ -270,22 +412,49 @@ export function createCustomBlockHost(options) {
|
|
|
270
412
|
},
|
|
271
413
|
setCurrentUser(nextCurrentUser) {
|
|
272
414
|
currentUser = nextCurrentUser;
|
|
415
|
+
if (initStatus !== "success") {
|
|
416
|
+
if (initStatus === "waitingForInitResult") {
|
|
417
|
+
pendingCurrentUserChange = true;
|
|
418
|
+
}
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
273
421
|
post({ type: "currentUserChanged", currentUser });
|
|
274
422
|
},
|
|
275
423
|
refreshQuery({ dataSourceId, response }) {
|
|
276
|
-
|
|
424
|
+
if (initStatus !== "success") {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const refreshes = querySubscriptions.resolveRefreshes({
|
|
277
428
|
dataSourceId,
|
|
278
429
|
response,
|
|
279
430
|
});
|
|
280
|
-
if (
|
|
431
|
+
if (refreshes.length === 0) {
|
|
281
432
|
emit("warn", `No active query exists for data source "${dataSourceId}"`);
|
|
282
433
|
return;
|
|
283
434
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
response: refresh.response,
|
|
288
|
-
});
|
|
435
|
+
for (const refresh of refreshes) {
|
|
436
|
+
postQueryDataSourceResult(refresh);
|
|
437
|
+
}
|
|
289
438
|
},
|
|
290
439
|
};
|
|
291
440
|
}
|
|
441
|
+
function getBindingErrorCode(manifest, dataSources) {
|
|
442
|
+
for (const [dataSourceKey, manifestDataSource] of Object.entries(manifest.dataSources)) {
|
|
443
|
+
const binding = dataSources.bindings[dataSourceKey];
|
|
444
|
+
if (binding?.collectionPointer === undefined ||
|
|
445
|
+
binding.collectionSchema === undefined) {
|
|
446
|
+
return "missing_data_source_binding";
|
|
447
|
+
}
|
|
448
|
+
for (const [propertyKey, manifestProperty] of Object.entries(manifestDataSource.properties ?? {})) {
|
|
449
|
+
const propertyId = binding.propertyIdsByKey?.[propertyKey];
|
|
450
|
+
if (propertyId === undefined) {
|
|
451
|
+
return "missing_property_binding";
|
|
452
|
+
}
|
|
453
|
+
if (binding.collectionSchema.propertiesById[propertyId]?.type !==
|
|
454
|
+
manifestProperty.type) {
|
|
455
|
+
return "invalid_property_binding";
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return undefined;
|
|
460
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { CustomBlockInitErrorCode, CustomBlockInitErrorInfo } from "../../b
|
|
|
3
3
|
* Returns the error info for a given `init` failure code.
|
|
4
4
|
*
|
|
5
5
|
* `init` handshakes are one-shot: after an `init` error, this host instance will not answer another
|
|
6
|
-
* `
|
|
6
|
+
* `connect` message. `isRetryable` means a fresh iframe / `init` attempt could plausibly succeed
|
|
7
7
|
* without changing block code or host configuration.
|
|
8
8
|
*/
|
|
9
9
|
export declare function initErrorForFailureReason(reason: CustomBlockInitErrorCode, data?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initErrors.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/lifecycle/initErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,wBAAwB,EACxB,MAAM,+BAA+B,CAAA;AAKtC;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,wBAAwB,EAChC,IAAI,GAAE;IACL,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,4BAA4B,CAAC,EAAE,MAAM,CAAA;CAChC,GACJ,wBAAwB,
|
|
1
|
+
{"version":3,"file":"initErrors.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/lifecycle/initErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,wBAAwB,EACxB,wBAAwB,EACxB,MAAM,+BAA+B,CAAA;AAKtC;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,wBAAwB,EAChC,IAAI,GAAE;IACL,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,4BAA4B,CAAC,EAAE,MAAM,CAAA;CAChC,GACJ,wBAAwB,CA+I1B"}
|
|
@@ -3,28 +3,28 @@ const UNKNOWN_ERROR_MESSAGE = "Unexpected error while initializing custom block.
|
|
|
3
3
|
* Returns the error info for a given `init` failure code.
|
|
4
4
|
*
|
|
5
5
|
* `init` handshakes are one-shot: after an `init` error, this host instance will not answer another
|
|
6
|
-
* `
|
|
6
|
+
* `connect` message. `isRetryable` means a fresh iframe / `init` attempt could plausibly succeed
|
|
7
7
|
* without changing block code or host configuration.
|
|
8
8
|
*/
|
|
9
9
|
export function initErrorForFailureReason(reason, data = {}) {
|
|
10
10
|
switch (reason) {
|
|
11
|
-
case "
|
|
11
|
+
case "no_connect":
|
|
12
12
|
return {
|
|
13
13
|
code: reason,
|
|
14
|
-
message: "Sandbox did not send a
|
|
14
|
+
message: "Sandbox did not send a connect message.",
|
|
15
15
|
isRetryable: true,
|
|
16
16
|
};
|
|
17
|
-
case "
|
|
17
|
+
case "invalid_connect":
|
|
18
18
|
return {
|
|
19
19
|
code: reason,
|
|
20
|
-
message: "Sandbox sent an invalid
|
|
21
|
-
// The sandbox must be updated to send a valid
|
|
20
|
+
message: "Sandbox sent an invalid connect message.",
|
|
21
|
+
// The sandbox must be updated to send a valid connect message.
|
|
22
22
|
isRetryable: false,
|
|
23
23
|
};
|
|
24
24
|
case "manifest_unavailable":
|
|
25
25
|
return {
|
|
26
26
|
code: reason,
|
|
27
|
-
message: "
|
|
27
|
+
message: "The custom block manifest is unavailable.",
|
|
28
28
|
isRetryable: true,
|
|
29
29
|
};
|
|
30
30
|
case "manifest_invalid":
|
|
@@ -102,6 +102,24 @@ export function initErrorForFailureReason(reason, data = {}) {
|
|
|
102
102
|
// a property with a compatible schema.
|
|
103
103
|
isRetryable: false,
|
|
104
104
|
};
|
|
105
|
+
case "no_init_result":
|
|
106
|
+
return {
|
|
107
|
+
code: reason,
|
|
108
|
+
message: "Sandbox did not acknowledge initialization.",
|
|
109
|
+
isRetryable: true,
|
|
110
|
+
};
|
|
111
|
+
case "invalid_init_result":
|
|
112
|
+
return {
|
|
113
|
+
code: reason,
|
|
114
|
+
message: "Sandbox sent an invalid initialization result.",
|
|
115
|
+
isRetryable: false,
|
|
116
|
+
};
|
|
117
|
+
case "invalid_init_bindings":
|
|
118
|
+
return {
|
|
119
|
+
code: reason,
|
|
120
|
+
message: "Sandbox rejected the host-provided data source bindings.",
|
|
121
|
+
isRetryable: false,
|
|
122
|
+
};
|
|
105
123
|
case "not_in_iframe":
|
|
106
124
|
return {
|
|
107
125
|
code: reason,
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import type { NotionDataSourceBindings } from "../../bridge/dataSources/dataSource.js";
|
|
2
2
|
import type { NotionBlockId } from "../../bridge/ids.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { CustomBlockManifest } from "../../bridge/manifest.js";
|
|
4
|
+
import type { ConnectMessage } from "../../bridge/messages/connect.js";
|
|
4
5
|
import type { CustomBlockPage } from "../../bridge/pages/page.js";
|
|
5
6
|
import type { NotionParent } from "../../bridge/parent.js";
|
|
6
7
|
import type { NotionTheme } from "../../bridge/theme.js";
|
|
7
8
|
import type { NotionUser } from "../../bridge/users/user.js";
|
|
8
|
-
export type
|
|
9
|
+
export type ConnectSuccessMessage = Extract<ConnectMessage, {
|
|
9
10
|
status: "success";
|
|
10
11
|
}>;
|
|
11
|
-
export type InitStatus = "
|
|
12
|
+
export type InitStatus = "waitingForConnect" | "waitingForInitResult" | "success" | "error";
|
|
12
13
|
export type CustomBlockHostDataSourcesPayload = {
|
|
13
14
|
bindings: NotionDataSourceBindings;
|
|
14
15
|
};
|
|
15
|
-
export type CustomBlockHostInitialDataSources = CustomBlockHostDataSourcesPayload | ((
|
|
16
|
+
export type CustomBlockHostInitialDataSources = CustomBlockHostDataSourcesPayload | ((connect: ConnectSuccessMessage) => CustomBlockHostDataSourcesPayload);
|
|
16
17
|
export type CustomBlockHostInitialState = {
|
|
17
18
|
theme: NotionTheme;
|
|
18
19
|
blockId: NotionBlockId;
|
|
19
20
|
parent: NotionParent;
|
|
20
21
|
page: CustomBlockPage;
|
|
22
|
+
manifest: CustomBlockManifest;
|
|
21
23
|
dataSources: CustomBlockHostInitialDataSources;
|
|
22
24
|
currentUser: NotionUser;
|
|
23
25
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/lifecycle/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/lifecycle/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAE5D,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAC1C,cAAc,EACd;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CACrB,CAAA;AAED,MAAM,MAAM,UAAU,GACnB,mBAAmB,GACnB,sBAAsB,GACtB,SAAS,GACT,OAAO,CAAA;AAEV,MAAM,MAAM,iCAAiC,GAAG;IAC/C,QAAQ,EAAE,wBAAwB,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,iCAAiC,GAC1C,iCAAiC,GACjC,CAAC,CAAC,OAAO,EAAE,qBAAqB,KAAK,iCAAiC,CAAC,CAAA;AAE1E,MAAM,MAAM,2BAA2B,GAAG;IACzC,KAAK,EAAE,WAAW,CAAA;IAClB,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,WAAW,EAAE,iCAAiC,CAAA;IAC9C,WAAW,EAAE,UAAU,CAAA;CACvB,CAAA"}
|
|
@@ -14,9 +14,6 @@ export function getInvalidSandboxMessageResponse(data) {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
function nackReasonForInvalidSandboxMessage(incomingType) {
|
|
17
|
-
if (incomingType === "ready") {
|
|
18
|
-
return "Invalid manifest";
|
|
19
|
-
}
|
|
20
17
|
if (incomingType !== undefined) {
|
|
21
18
|
return `Unsupported sandbox message of type "${incomingType}"`;
|
|
22
19
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { NotionDataSourceId } from "../../bridge/ids.js";
|
|
2
2
|
import type { QueryDataSourceMessage } from "../../bridge/messages/queryDataSource.js";
|
|
3
3
|
import type { CustomBlockHostQueryDataSourceResult } from "./types.js";
|
|
4
|
-
export type
|
|
5
|
-
|
|
6
|
-
requestId: string;
|
|
7
|
-
snapshotId: string;
|
|
4
|
+
export type QueryRefresh = {
|
|
5
|
+
subscriptionId: string;
|
|
8
6
|
response: CustomBlockHostQueryDataSourceResult;
|
|
9
|
-
} | {
|
|
10
|
-
status: "missing";
|
|
11
7
|
};
|
|
8
|
+
type QuerySubscriptionToken = object;
|
|
12
9
|
export declare class QuerySubscriptions {
|
|
13
|
-
private readonly
|
|
14
|
-
track(message: QueryDataSourceMessage):
|
|
15
|
-
|
|
10
|
+
private readonly bySubscriptionId;
|
|
11
|
+
track(message: QueryDataSourceMessage): QuerySubscriptionToken;
|
|
12
|
+
isCurrent(subscriptionId: string, token: QuerySubscriptionToken): boolean;
|
|
13
|
+
resolveRefreshes(args: {
|
|
16
14
|
dataSourceId: NotionDataSourceId;
|
|
17
15
|
response: CustomBlockHostQueryDataSourceResult;
|
|
18
|
-
}):
|
|
16
|
+
}): QueryRefresh[];
|
|
17
|
+
clear(): void;
|
|
19
18
|
}
|
|
19
|
+
export {};
|
|
20
20
|
//# sourceMappingURL=querySubscriptions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"querySubscriptions.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/queries/querySubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAA;AACtF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"querySubscriptions.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/host/queries/querySubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAA;AACtF,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,YAAY,CAAA;AAEtE,MAAM,MAAM,YAAY,GAAG;IAC1B,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,oCAAoC,CAAA;CAC9C,CAAA;AAED,KAAK,sBAAsB,GAAG,MAAM,CAAA;AAQpC,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4C;IAE7E,KAAK,CAAC,OAAO,EAAE,sBAAsB,GAAG,sBAAsB;IAU9D,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO;IAIzE,gBAAgB,CAAC,IAAI,EAAE;QACtB,YAAY,EAAE,kBAAkB,CAAA;QAChC,QAAQ,EAAE,oCAAoC,CAAA;KAC9C,GAAG,YAAY,EAAE;IAelB,KAAK;CAGL"}
|
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
export class QuerySubscriptions {
|
|
2
2
|
constructor() {
|
|
3
|
-
this.
|
|
3
|
+
this.bySubscriptionId = new Map();
|
|
4
4
|
}
|
|
5
5
|
track(message) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const token = {};
|
|
7
|
+
this.bySubscriptionId.set(message.subscriptionId, {
|
|
8
|
+
dataSourceId: message.dataSourceId,
|
|
9
9
|
limit: message.limit,
|
|
10
|
+
token,
|
|
10
11
|
});
|
|
12
|
+
return token;
|
|
11
13
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
isCurrent(subscriptionId, token) {
|
|
15
|
+
return this.bySubscriptionId.get(subscriptionId)?.token === token;
|
|
16
|
+
}
|
|
17
|
+
resolveRefreshes(args) {
|
|
18
|
+
const refreshes = [];
|
|
19
|
+
for (const [subscriptionId, queryState] of this.bySubscriptionId) {
|
|
20
|
+
if (queryState.dataSourceId !== args.dataSourceId) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
queryState.token = {};
|
|
24
|
+
refreshes.push({
|
|
25
|
+
subscriptionId,
|
|
26
|
+
response: limitResponse(args.response, queryState.limit),
|
|
27
|
+
});
|
|
16
28
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
};
|
|
29
|
+
return refreshes;
|
|
30
|
+
}
|
|
31
|
+
clear() {
|
|
32
|
+
this.bySubscriptionId.clear();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function limitResponse(response, limit) {
|
|
36
|
+
if (response.status === "error") {
|
|
37
|
+
return response;
|
|
28
38
|
}
|
|
39
|
+
const items = response.items.slice(0, limit);
|
|
40
|
+
return {
|
|
41
|
+
status: "success",
|
|
42
|
+
items,
|
|
43
|
+
hasMore: response.hasMore || response.items.length > items.length,
|
|
44
|
+
};
|
|
29
45
|
}
|