@notionhq/custom-blocks 0.1.32 → 0.1.33
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/bridge/SandboxBridge.d.ts +1 -0
- package/dist/bridge/SandboxBridge.d.ts.map +1 -1
- package/dist/bridge/SandboxBridge.js +43 -12
- package/dist/protocol/messages/init.d.ts +1 -1
- package/dist/protocol/messages/init.d.ts.map +1 -1
- package/dist/protocol/messages/initResult.d.ts +8 -1
- package/dist/protocol/messages/initResult.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/docs/errors.md +16 -0
- package/docs/lifecycle.md +74 -20
- package/package.json +1 -1
- package/src/bridge/SandboxBridge.ts +52 -12
|
@@ -58,6 +58,7 @@ export declare class SandboxBridge {
|
|
|
58
58
|
*/
|
|
59
59
|
setMockState(message: InitMessage): void;
|
|
60
60
|
completeInitialization(): void;
|
|
61
|
+
private sendInitResultError;
|
|
61
62
|
private applyInit;
|
|
62
63
|
/**
|
|
63
64
|
* Lets the initial application render commit before we acknowledge init. The host keeps the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SandboxBridge.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/SandboxBridge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,gBAAgB,EAEhB,MAAM,4DAA4D,CAAA;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAA;AAW3E,OAAO,EAEN,KAAK,WAAW,EAEhB,MAAM,mDAAmD,CAAA;AAc1D,OAAO,KAAK,EACX,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,aAAa,EACb,eAAe,EACf,8BAA8B,EAC9B,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,MAAM,aAAa,CAAA;AASpB,OAAO,EACN,KAAK,oBAAoB,EAGzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAG3D;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,UAAU,CAAA;IAC9B,IAAI,EAAE,OAAO,CAAA;CACb,CAAA;AAID,qBAAa,aAAa;IACzB,OAAO,CAAC,SAAS,CAIhB;IACD,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,UAAU,CAAwB;IAC1C,OAAO,CAAC,mBAAmB,CAAwB;IACnD,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAEjC;IACD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAE9B;IACD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAE9B;IACD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAEhC;IACD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAEjC;IACD,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,uBAAuB,CAAgB;IAC/C,OAAO,CAAC,wBAAwB,CAA+B;IAC/D,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,UAAU,CAAuC;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAK3B;IAED,cAAc;IAQd,OAAO,CAAC,MAAM,CAAC,eAAe,CAAM;IAEpC,OAAO,CAAC,UAAU;IAclB,aAAa,IAAI,SAAS,eAAe,EAAE;IAI3C,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI;IAK1C,SAAS,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB9C,WAAW,CAAC,cAAc,EAAE,kBAAkB;IAkC9C,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,MAAM,CAIb;IAED,OAAO,CAAC,aAAa,
|
|
1
|
+
{"version":3,"file":"SandboxBridge.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/SandboxBridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -53,6 +53,14 @@ export class SandboxBridge {
|
|
|
53
53
|
if (!parsed.success) {
|
|
54
54
|
console.warn("[custom-blocks-sdk] ignoring malformed host message", parsed.issues);
|
|
55
55
|
const incomingType = readIncomingType(event.data);
|
|
56
|
+
if (!this.hasReceivedInit &&
|
|
57
|
+
incomingType === "init" &&
|
|
58
|
+
this.initializationId !== undefined &&
|
|
59
|
+
readInitializationId(event.data) === this.initializationId) {
|
|
60
|
+
this.hasReceivedInit = true;
|
|
61
|
+
this.sendInitResultError(invalidInitPayloadError(parsed.issues));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
56
64
|
// Loop guard: never NACK a NACK. Excludes both directions —
|
|
57
65
|
// `invalidSandboxMessage` is what the host normally sends, but
|
|
58
66
|
// a buggy host that echoes our own `invalidHostMessage` back
|
|
@@ -400,6 +408,24 @@ export class SandboxBridge {
|
|
|
400
408
|
this.postToHost(result);
|
|
401
409
|
this.flushPendingOutboundMessages();
|
|
402
410
|
}
|
|
411
|
+
sendInitResultError(error) {
|
|
412
|
+
if (this.hasSentInitResult || this.initializationId === undefined) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const result = {
|
|
416
|
+
type: "initResult",
|
|
417
|
+
initializationId: this.initializationId,
|
|
418
|
+
status: "error",
|
|
419
|
+
error,
|
|
420
|
+
};
|
|
421
|
+
this.hasSentInitResult = true;
|
|
422
|
+
this.postToHost(result);
|
|
423
|
+
if (this.rejectInit) {
|
|
424
|
+
this.rejectInit(new CustomBlockInitializationError(error));
|
|
425
|
+
this.resolveInit = undefined;
|
|
426
|
+
this.rejectInit = undefined;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
403
429
|
applyInit(message, postResult) {
|
|
404
430
|
if (postResult) {
|
|
405
431
|
this.isMockState = false;
|
|
@@ -429,18 +455,7 @@ export class SandboxBridge {
|
|
|
429
455
|
: resolveMockDataSources(this.latestDataSourceBindings);
|
|
430
456
|
const bindingError = getInitBindingError(message.manifest, dataSources);
|
|
431
457
|
if (postResult && bindingError !== undefined) {
|
|
432
|
-
|
|
433
|
-
type: "initResult",
|
|
434
|
-
initializationId: message.initializationId,
|
|
435
|
-
status: "error",
|
|
436
|
-
error: bindingError,
|
|
437
|
-
};
|
|
438
|
-
this.postToHost(result);
|
|
439
|
-
if (this.rejectInit) {
|
|
440
|
-
this.rejectInit(new CustomBlockInitializationError(bindingError));
|
|
441
|
-
this.resolveInit = undefined;
|
|
442
|
-
this.rejectInit = undefined;
|
|
443
|
-
}
|
|
458
|
+
this.sendInitResultError(bindingError);
|
|
444
459
|
return;
|
|
445
460
|
}
|
|
446
461
|
this.hostState = {
|
|
@@ -840,6 +855,22 @@ function makeDataSourceSubscriptionId(args) {
|
|
|
840
855
|
const { key } = args;
|
|
841
856
|
return `data-source:${encodeURIComponent(key)}`;
|
|
842
857
|
}
|
|
858
|
+
function invalidInitPayloadError(issues) {
|
|
859
|
+
return {
|
|
860
|
+
code: "invalid_init_payload",
|
|
861
|
+
message: formatInvalidHostReason("init", issues),
|
|
862
|
+
isRetryable: false,
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
function readInitializationId(data) {
|
|
866
|
+
if (typeof data === "object" &&
|
|
867
|
+
data !== null &&
|
|
868
|
+
"initializationId" in data &&
|
|
869
|
+
typeof data.initializationId === "string") {
|
|
870
|
+
return data.initializationId;
|
|
871
|
+
}
|
|
872
|
+
return undefined;
|
|
873
|
+
}
|
|
843
874
|
function getInitBindingError(manifest, dataSources) {
|
|
844
875
|
for (const [dataSourceKey, manifestDataSource] of Object.entries(manifest.dataSources)) {
|
|
845
876
|
const dataSource = dataSources.find(entry => entry.key === dataSourceKey);
|
|
@@ -2,7 +2,7 @@ import * as v from "valibot";
|
|
|
2
2
|
import type { CustomBlockErrorCode, CustomBlockErrorInfo } from "../errors.js";
|
|
3
3
|
import type { CustomBlockInitResultErrorCode } from "./initResult.js";
|
|
4
4
|
export declare const customBlockInitErrorCodeSchema: v.StringSchema<undefined>;
|
|
5
|
-
export type CustomBlockInitErrorCode = CustomBlockErrorCode<"manifest_unavailable" | "manifest_invalid" | "invalid_protocol_version" | "unsupported_protocol_version" | "context_unavailable" | "current_user_unavailable" | "missing_data_source_binding" | "data_source_unavailable" | "missing_property_binding" | "invalid_property_binding">;
|
|
5
|
+
export type CustomBlockInitErrorCode = CustomBlockErrorCode<"manifest_unavailable" | "manifest_invalid" | "invalid_connect_payload" | "invalid_protocol_version" | "unsupported_protocol_version" | "context_unavailable" | "current_user_unavailable" | "missing_data_source_binding" | "data_source_unavailable" | "missing_property_binding" | "invalid_property_binding">;
|
|
6
6
|
export type CustomBlockInitErrorInfo = CustomBlockErrorInfo<CustomBlockInitErrorCode>;
|
|
7
7
|
/**
|
|
8
8
|
* Every initialization failure visible to block code. This combines errors
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/protocol/src/messages/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAG5B,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAO9E,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AAIrE,eAAO,MAAM,8BAA8B,2BAAa,CAAA;AAExD,MAAM,MAAM,wBAAwB,GAAG,oBAAoB,CACxD,sBAAsB,GACtB,kBAAkB,GAClB,0BAA0B,GAC1B,8BAA8B,GAC9B,qBAAqB,GACrB,0BAA0B,GAC1B,6BAA6B,GAC7B,yBAAyB,GACzB,0BAA0B,GAC1B,0BAA0B,CAC5B,CAAA;AAED,MAAM,MAAM,wBAAwB,GACnC,oBAAoB,CAAC,wBAAwB,CAAC,CAAA;AAE/C;;;;GAIG;AACH,MAAM,MAAM,kCAAkC,GAC3C,wBAAwB,GACxB,8BAA8B,GAC9B,oBAAoB,CAAC,eAAe,GAAG,cAAc,CAAC,CAAA;AAEzD,MAAM,MAAM,kCAAkC,GAC7C,oBAAoB,CAAC,kCAAkC,CAAC,CAAA;AAEzD,eAAO,MAAM,8BAA8B;;;;aAIzC,CAAA;AAEF,qBAAa,8BACZ,SAAQ,KACR,YAAW,oBAAoB;gBAEnB,KAAK,EAAE,kCAAkC;IAOrD,IAAI,EAAE,kCAAkC,CAAA;IACxC,WAAW,EAAE,OAAO,CAAA;CACpB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAuB5B,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,iBAAiB,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/protocol/src/messages/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAG5B,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAO9E,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AAIrE,eAAO,MAAM,8BAA8B,2BAAa,CAAA;AAExD,MAAM,MAAM,wBAAwB,GAAG,oBAAoB,CACxD,sBAAsB,GACtB,kBAAkB,GAClB,yBAAyB,GACzB,0BAA0B,GAC1B,8BAA8B,GAC9B,qBAAqB,GACrB,0BAA0B,GAC1B,6BAA6B,GAC7B,yBAAyB,GACzB,0BAA0B,GAC1B,0BAA0B,CAC5B,CAAA;AAED,MAAM,MAAM,wBAAwB,GACnC,oBAAoB,CAAC,wBAAwB,CAAC,CAAA;AAE/C;;;;GAIG;AACH,MAAM,MAAM,kCAAkC,GAC3C,wBAAwB,GACxB,8BAA8B,GAC9B,oBAAoB,CAAC,eAAe,GAAG,cAAc,CAAC,CAAA;AAEzD,MAAM,MAAM,kCAAkC,GAC7C,oBAAoB,CAAC,kCAAkC,CAAC,CAAA;AAEzD,eAAO,MAAM,8BAA8B;;;;aAIzC,CAAA;AAEF,qBAAa,8BACZ,SAAQ,KACR,YAAW,oBAAoB;gBAEnB,KAAK,EAAE,kCAAkC;IAOrD,IAAI,EAAE,kCAAkC,CAAA;IACxC,WAAW,EAAE,OAAO,CAAA;CACpB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAuB5B,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,iBAAiB,CAAC,CAAA"}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
2
|
import type { CustomBlockErrorCode, CustomBlockErrorInfo } from "../errors.js";
|
|
3
3
|
export declare const customBlockInitResultErrorCodeSchema: v.StringSchema<undefined>;
|
|
4
|
-
export type CustomBlockInitResultErrorCode = CustomBlockErrorCode<
|
|
4
|
+
export type CustomBlockInitResultErrorCode = CustomBlockErrorCode<
|
|
5
|
+
/**
|
|
6
|
+
* The host-provided bindings defined in `init.dataSources` were rejected by the sandbox because
|
|
7
|
+
* they did not completely match the expected manifest keys.
|
|
8
|
+
*/
|
|
9
|
+
"invalid_init_bindings"
|
|
10
|
+
/** The host-provided `init` payload failed to parse. */
|
|
11
|
+
| "invalid_init_payload">;
|
|
5
12
|
export type CustomBlockInitResultErrorInfo = CustomBlockErrorInfo<CustomBlockInitResultErrorCode>;
|
|
6
13
|
export declare const customBlockInitResultErrorInfoSchema: v.ObjectSchema<{
|
|
7
14
|
readonly code: v.StringSchema<undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initResult.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/protocol/src/messages/initResult.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAI9E,eAAO,MAAM,oCAAoC,2BAAa,CAAA;AAE9D,MAAM,MAAM,8BAA8B,
|
|
1
|
+
{"version":3,"file":"initResult.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/protocol/src/messages/initResult.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAI9E,eAAO,MAAM,oCAAoC,2BAAa,CAAA;AAE9D,MAAM,MAAM,8BAA8B,GAAG,oBAAoB;AAChE;;;GAGG;AACD,uBAAuB;AACzB,wDAAwD;GACtD,sBAAsB,CACxB,CAAA;AAED,MAAM,MAAM,8BAA8B,GACzC,oBAAoB,CAAC,8BAA8B,CAAC,CAAA;AAErD,eAAO,MAAM,oCAAoC;;;;aAI/C,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;IAKlC,0EAA0E;;;;;;;;;;;0BAU1E,CAAA;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAA"}
|
package/dist/version.js
CHANGED
package/docs/errors.md
CHANGED
|
@@ -39,6 +39,22 @@ Unlike request helpers, `initCustomBlock` rejects with `CustomBlockInitializatio
|
|
|
39
39
|
|
|
40
40
|
`CustomBlockInitializationErrorInfo` is the matching `{ code, message, isRetryable }` object type.
|
|
41
41
|
|
|
42
|
+
Initialization errors have direction-specific meanings:
|
|
43
|
+
|
|
44
|
+
- `invalid_connect_payload` means that the host could not parse the SDK's
|
|
45
|
+
`connect` message. The host sends this code in `init.error` when it can read
|
|
46
|
+
the `initializationId`.
|
|
47
|
+
- `invalid_init_payload` means that the SDK could not parse the host's `init`
|
|
48
|
+
message. The SDK sends this code in `initResult.error` when it can read the
|
|
49
|
+
expected `initializationId`.
|
|
50
|
+
- `invalid_init_bindings` means that the SDK could not apply the data source
|
|
51
|
+
bindings in the host's valid `init` message. The SDK sends this code in
|
|
52
|
+
`initResult.error`.
|
|
53
|
+
- `invalid_init_result_payload` means that the host could not parse the SDK's
|
|
54
|
+
`initResult`. This is a host-local error. The SDK does not send this code.
|
|
55
|
+
- `no_init_result` means that the host did not receive a valid `initResult`
|
|
56
|
+
before its timeout.
|
|
57
|
+
|
|
42
58
|
See [Lifecycle and initialization](./lifecycle.md) for initialization-specific error codes.
|
|
43
59
|
|
|
44
60
|
## Handling errors
|
package/docs/lifecycle.md
CHANGED
|
@@ -1,27 +1,65 @@
|
|
|
1
1
|
# Lifecycle
|
|
2
2
|
|
|
3
|
-
The SDK
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
The SDK and host use a three-message `postMessage` handshake to initialize
|
|
4
|
+
a custom block.
|
|
5
|
+
|
|
6
|
+
This handshake can be completed with one of these APIs:
|
|
7
|
+
|
|
8
|
+
- `<NotionCustomBlock>` for most React blocks. It starts initialization and
|
|
9
|
+
renders `children` after the host state is ready.
|
|
10
|
+
- `useCustomBlockInit()` for React blocks that need to control loading and
|
|
11
|
+
error states.
|
|
12
|
+
- `initCustomBlock()` for framework-neutral code or code that must await
|
|
13
|
+
initialization before it renders.
|
|
14
|
+
|
|
15
|
+
Custom bridge integrations can also handle the `postMessage` wire messages
|
|
16
|
+
directly. This is advanced usage for host or integration authors.
|
|
17
|
+
|
|
18
|
+
## Initialization protocol message
|
|
19
|
+
|
|
20
|
+
The full `postMessage` handshake is:
|
|
21
|
+
|
|
22
|
+
1. The SDK sends `connect` with a new `initializationId` and version
|
|
23
|
+
information.
|
|
24
|
+
2. The host sends `init` with the same `initializationId`, the manifest, data source bindings,
|
|
25
|
+
and additional block and app context.
|
|
26
|
+
3. The SDK validates and applies `init`, renders the initial content, and sends
|
|
27
|
+
`initResult` with the same `initializationId` and the initial block height.
|
|
28
|
+
|
|
29
|
+
Both sides must check that the `initializationId` matches. The SDK ignores an
|
|
30
|
+
`init` with a different ID. It sends `initResult` only after it applies a
|
|
31
|
+
matching `init`.
|
|
32
|
+
|
|
33
|
+
The initialization promise resolves after the SDK applies the host state.
|
|
34
|
+
Await it before rendering code that reads initialized state. The SDK sends
|
|
35
|
+
`initResult` after the initial render.
|
|
36
|
+
|
|
37
|
+
- The promise rejects with `CustomBlockInitializationError` code
|
|
38
|
+
`init_timeout` if the host does not send a usable `init`.
|
|
39
|
+
- If a matching `init` is malformed, the SDK sends `initResult.error` with code
|
|
40
|
+
`invalid_init_payload` and rejects with the same structured error.
|
|
41
|
+
- If the host sends incomplete or invalid data source bindings, the SDK sends
|
|
42
|
+
`initResult.error` with code `invalid_init_bindings` and rejects with the same
|
|
43
|
+
structured error.
|
|
44
|
+
- If the SDK cannot correlate a malformed `init` to the handshake, it sends
|
|
45
|
+
`invalidHostMessage` when safe.
|
|
46
|
+
- After initialization, compatible hosts use `*Changed` messages to push live
|
|
47
|
+
updates.
|
|
48
|
+
- `initCustomBlock()` is idempotent. Later calls return the same promise.
|
|
49
|
+
|
|
50
|
+
## Rendering inside a host
|
|
51
|
+
|
|
52
|
+
The SDK expects the custom block to run inside an iframe. In a top-level
|
|
53
|
+
browser tab, initialization rejects with `NotInIframeError` code
|
|
54
|
+
`not_in_iframe`.
|
|
55
|
+
|
|
56
|
+
`<NotionCustomBlock>` catches this error and renders a standalone preview with
|
|
57
|
+
a warning. This supports local development when no host is available.
|
|
19
58
|
|
|
20
59
|
## Sizing
|
|
21
60
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
To set a shorter height, set `max-height` and `overflow-y` on `#root`:
|
|
61
|
+
Custom blocks resize to fit their content by default. To set a shorter height, set
|
|
62
|
+
`max-height` and `overflow-y` on `#root`:
|
|
25
63
|
|
|
26
64
|
```css
|
|
27
65
|
#root {
|
|
@@ -30,9 +68,25 @@ To set a shorter height, set `max-height` and `overflow-y` on `#root`:
|
|
|
30
68
|
}
|
|
31
69
|
```
|
|
32
70
|
|
|
71
|
+
The Notion app enforces a maximum custom block height of 10,000 pixels.
|
|
72
|
+
|
|
73
|
+
The SDK measures the `#root` element and sends its initial height in
|
|
74
|
+
`initResult.success`. It sends later height changes in `resize` messages. The
|
|
75
|
+
host uses these messages to keep the iframe height in sync.
|
|
76
|
+
|
|
77
|
+
Using `<NotionCustomBlock>` enables this behavior automatically. Use
|
|
78
|
+
`customBlock.autoResize()` for a custom, framework-neutral initialization wrapper.
|
|
79
|
+
|
|
33
80
|
## API
|
|
34
81
|
|
|
35
|
-
Import framework-neutral APIs from `@notionhq/custom-blocks
|
|
82
|
+
Import framework-neutral APIs from `@notionhq/custom-blocks`. Import React
|
|
83
|
+
hooks and components from `@notionhq/custom-blocks/react`.
|
|
84
|
+
|
|
85
|
+
The APIs below require successful initialization unless stated otherwise.
|
|
86
|
+
Initialized-only hooks and `customBlock` getters throw before
|
|
87
|
+
`initCustomBlock()` resolves. Inside `<NotionCustomBlock>`, or after the
|
|
88
|
+
`isLoaded` gate of `useCustomBlockInit`, single-value hooks return non-nullable
|
|
89
|
+
values.
|
|
36
90
|
|
|
37
91
|
### `<NotionCustomBlock>`
|
|
38
92
|
|
package/package.json
CHANGED
|
@@ -258,6 +258,16 @@ export class SandboxBridge {
|
|
|
258
258
|
parsed.issues,
|
|
259
259
|
)
|
|
260
260
|
const incomingType = readIncomingType(event.data)
|
|
261
|
+
if (
|
|
262
|
+
!this.hasReceivedInit &&
|
|
263
|
+
incomingType === "init" &&
|
|
264
|
+
this.initializationId !== undefined &&
|
|
265
|
+
readInitializationId(event.data) === this.initializationId
|
|
266
|
+
) {
|
|
267
|
+
this.hasReceivedInit = true
|
|
268
|
+
this.sendInitResultError(invalidInitPayloadError(parsed.issues))
|
|
269
|
+
return
|
|
270
|
+
}
|
|
261
271
|
// Loop guard: never NACK a NACK. Excludes both directions —
|
|
262
272
|
// `invalidSandboxMessage` is what the host normally sends, but
|
|
263
273
|
// a buggy host that echoes our own `invalidHostMessage` back
|
|
@@ -548,6 +558,25 @@ export class SandboxBridge {
|
|
|
548
558
|
this.flushPendingOutboundMessages()
|
|
549
559
|
}
|
|
550
560
|
|
|
561
|
+
private sendInitResultError(error: CustomBlockInitResultErrorInfo) {
|
|
562
|
+
if (this.hasSentInitResult || this.initializationId === undefined) {
|
|
563
|
+
return
|
|
564
|
+
}
|
|
565
|
+
const result: InitResultMessage = {
|
|
566
|
+
type: "initResult",
|
|
567
|
+
initializationId: this.initializationId,
|
|
568
|
+
status: "error",
|
|
569
|
+
error,
|
|
570
|
+
}
|
|
571
|
+
this.hasSentInitResult = true
|
|
572
|
+
this.postToHost(result)
|
|
573
|
+
if (this.rejectInit) {
|
|
574
|
+
this.rejectInit(new CustomBlockInitializationError(error))
|
|
575
|
+
this.resolveInit = undefined
|
|
576
|
+
this.rejectInit = undefined
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
551
580
|
private applyInit(message: InitMessage, postResult: boolean) {
|
|
552
581
|
if (postResult) {
|
|
553
582
|
this.isMockState = false
|
|
@@ -581,18 +610,7 @@ export class SandboxBridge {
|
|
|
581
610
|
: resolveMockDataSources(this.latestDataSourceBindings)
|
|
582
611
|
const bindingError = getInitBindingError(message.manifest, dataSources)
|
|
583
612
|
if (postResult && bindingError !== undefined) {
|
|
584
|
-
|
|
585
|
-
type: "initResult",
|
|
586
|
-
initializationId: message.initializationId,
|
|
587
|
-
status: "error",
|
|
588
|
-
error: bindingError,
|
|
589
|
-
}
|
|
590
|
-
this.postToHost(result)
|
|
591
|
-
if (this.rejectInit) {
|
|
592
|
-
this.rejectInit(new CustomBlockInitializationError(bindingError))
|
|
593
|
-
this.resolveInit = undefined
|
|
594
|
-
this.rejectInit = undefined
|
|
595
|
-
}
|
|
613
|
+
this.sendInitResultError(bindingError)
|
|
596
614
|
return
|
|
597
615
|
}
|
|
598
616
|
this.hostState = {
|
|
@@ -1066,6 +1084,28 @@ function makeDataSourceSubscriptionId(args: { key: string }): string {
|
|
|
1066
1084
|
return `data-source:${encodeURIComponent(key)}`
|
|
1067
1085
|
}
|
|
1068
1086
|
|
|
1087
|
+
function invalidInitPayloadError(
|
|
1088
|
+
issues: readonly v.BaseIssue<unknown>[],
|
|
1089
|
+
): CustomBlockInitResultErrorInfo {
|
|
1090
|
+
return {
|
|
1091
|
+
code: "invalid_init_payload",
|
|
1092
|
+
message: formatInvalidHostReason("init", issues),
|
|
1093
|
+
isRetryable: false,
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
function readInitializationId(data: unknown): string | undefined {
|
|
1098
|
+
if (
|
|
1099
|
+
typeof data === "object" &&
|
|
1100
|
+
data !== null &&
|
|
1101
|
+
"initializationId" in data &&
|
|
1102
|
+
typeof data.initializationId === "string"
|
|
1103
|
+
) {
|
|
1104
|
+
return data.initializationId
|
|
1105
|
+
}
|
|
1106
|
+
return undefined
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1069
1109
|
function getInitBindingError(
|
|
1070
1110
|
manifest: CustomBlockManifest,
|
|
1071
1111
|
dataSources: NotionDataSource[],
|