@notionhq/custom-blocks 0.1.39 → 0.1.40

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.
@@ -23,44 +23,34 @@ const RESPONSE_TYPE_BY_REQUEST = new Map([
23
23
  ]);
24
24
  const INIT_RESULT_TIMER_FALLBACK_MS = 100;
25
25
  export class SandboxBridge {
26
- constructor() {
27
- this.hostState = {
28
- status: "uninitialized",
29
- theme: "light",
30
- contrastMode: DEFAULT_CONTRAST_MODE,
31
- };
32
- this.listeners = new Set();
33
- this.messageLog = [];
34
- this.messageLogListeners = new Set();
35
- this.nextRequestId = 1;
36
- this.pendingCreatePage = new PendingRequests("custom-block-create-page");
37
- this.pendingGetPage = new PendingRequests("custom-block-get-page");
38
- this.pendingGetUser = new PendingRequests("custom-block-get-user");
39
- this.pendingListUsers = new PendingRequests("custom-block-list-users");
40
- this.pendingUpdatePage = new PendingRequests("custom-block-update-page");
41
- this.hasSentConnect = false;
42
- this.hasReceivedInit = false;
43
- this.hasSentInitResult = false;
44
- this.pendingOutboundMessages = [];
45
- this.latestDataSourceBindings = {};
46
- this.isMockState = false;
47
- this.isListening = false;
48
- this.initMessage = new Promise((resolve, reject) => {
49
- this.resolveInit = resolve;
50
- this.rejectInit = reject;
51
- });
52
- this.notify = () => {
53
- for (const listener of this.listeners) {
54
- listener();
55
- }
56
- };
57
- this.handleMessage = (event) => {
58
- if (event.source !== window.parent) {
59
- return;
60
- }
61
- this.receiveMessage(event.data);
62
- };
63
- }
26
+ hostState = {
27
+ status: "uninitialized",
28
+ theme: "light",
29
+ contrastMode: DEFAULT_CONTRAST_MODE,
30
+ };
31
+ listeners = new Set();
32
+ messageLog = [];
33
+ messageLogListeners = new Set();
34
+ nextRequestId = 1;
35
+ pendingCreatePage = new PendingRequests("custom-block-create-page");
36
+ pendingGetPage = new PendingRequests("custom-block-get-page");
37
+ pendingGetUser = new PendingRequests("custom-block-get-user");
38
+ pendingListUsers = new PendingRequests("custom-block-list-users");
39
+ pendingUpdatePage = new PendingRequests("custom-block-update-page");
40
+ hasSentConnect = false;
41
+ hasReceivedInit = false;
42
+ initializationId;
43
+ hasSentInitResult = false;
44
+ pendingOutboundMessages = [];
45
+ latestDataSourceBindings = {};
46
+ isMockState = false;
47
+ isListening = false;
48
+ resolveInit;
49
+ rejectInit;
50
+ initMessage = new Promise((resolve, reject) => {
51
+ this.resolveInit = resolve;
52
+ this.rejectInit = reject;
53
+ });
64
54
  startListening() {
65
55
  if (this.isListening || typeof window === "undefined") {
66
56
  return;
@@ -68,6 +58,7 @@ export class SandboxBridge {
68
58
  this.isListening = true;
69
59
  window.addEventListener("message", this.handleMessage);
70
60
  }
61
+ static MAX_LOG_ENTRIES = 100;
71
62
  logMessage(direction, data) {
72
63
  if (this.messageLog.length >= SandboxBridge.MAX_LOG_ENTRIES) {
73
64
  this.messageLog.shift();
@@ -188,6 +179,17 @@ export class SandboxBridge {
188
179
  ? message.type
189
180
  : undefined;
190
181
  }
182
+ notify = () => {
183
+ for (const listener of this.listeners) {
184
+ listener();
185
+ }
186
+ };
187
+ handleMessage = (event) => {
188
+ if (event.source !== window.parent) {
189
+ return;
190
+ }
191
+ this.receiveMessage(event.data);
192
+ };
191
193
  receiveMessage(data) {
192
194
  this.logMessage("received", data);
193
195
  const parsed = v.safeParse(hostToSandboxMessageSchema, data);
@@ -846,7 +848,6 @@ export class SandboxBridge {
846
848
  }
847
849
  }
848
850
  }
849
- SandboxBridge.MAX_LOG_ENTRIES = 100;
850
851
  function getInitialContentHeight() {
851
852
  if (typeof document === "undefined") {
852
853
  return 0;
@@ -85,13 +85,13 @@ function resolveDataSourceQueryOptions(options) {
85
85
  return {
86
86
  status: "ok",
87
87
  options: {
88
- limit: Object.prototype.hasOwnProperty.call(optionsObject, "limit")
88
+ limit: Object.hasOwn(optionsObject, "limit")
89
89
  ? optionsObject.limit
90
90
  : undefined,
91
- filter: Object.prototype.hasOwnProperty.call(optionsObject, "filter")
91
+ filter: Object.hasOwn(optionsObject, "filter")
92
92
  ? optionsObject.filter
93
93
  : undefined,
94
- sorts: Object.prototype.hasOwnProperty.call(optionsObject, "sorts")
94
+ sorts: Object.hasOwn(optionsObject, "sorts")
95
95
  ? optionsObject.sorts
96
96
  : undefined,
97
97
  },
@@ -138,7 +138,7 @@ function resolveFilter(filter, dataSource) {
138
138
  };
139
139
  }
140
140
  const filterObject = filter;
141
- if (Object.prototype.hasOwnProperty.call(filterObject, "and")) {
141
+ if (Object.hasOwn(filterObject, "and")) {
142
142
  if (Object.keys(filterObject).length !== 1 ||
143
143
  !Array.isArray(filterObject.and)) {
144
144
  return {
@@ -226,7 +226,7 @@ function resolveSort(sort, dataSource) {
226
226
  };
227
227
  }
228
228
  const direction = sortObject.direction;
229
- if (!Object.prototype.hasOwnProperty.call(sortObject, "direction") ||
229
+ if (!Object.hasOwn(sortObject, "direction") ||
230
230
  (direction !== "ascending" && direction !== "descending")) {
231
231
  return {
232
232
  status: "error",
@@ -401,8 +401,8 @@ function resolveDatePropertyFilter(propertyId, value) {
401
401
  };
402
402
  }
403
403
  function resolvePropertyAddress(value, dataSource) {
404
- const hasKey = Object.prototype.hasOwnProperty.call(value, "key");
405
- const hasPropertyId = Object.prototype.hasOwnProperty.call(value, "propertyId");
404
+ const hasKey = Object.hasOwn(value, "key");
405
+ const hasPropertyId = Object.hasOwn(value, "propertyId");
406
406
  if (hasKey === hasPropertyId) {
407
407
  return {
408
408
  status: "error",
@@ -417,7 +417,7 @@ function resolvePropertyAddress(value, dataSource) {
417
417
  error: "Data source query property key must be a string.",
418
418
  };
419
419
  }
420
- if (!Object.prototype.hasOwnProperty.call(dataSource.propertyIdsByKey, value.key)) {
420
+ if (!Object.hasOwn(dataSource.propertyIdsByKey, value.key)) {
421
421
  return {
422
422
  status: "error",
423
423
  error: `Unknown property key "${value.key}" for data source "${dataSource.key}".`,
@@ -441,7 +441,7 @@ function resolvePropertyAddress(value, dataSource) {
441
441
  }
442
442
  propertyId = value.propertyId;
443
443
  }
444
- if (!Object.prototype.hasOwnProperty.call(dataSource.propertySchemasById, propertyId)) {
444
+ if (!Object.hasOwn(dataSource.propertySchemasById, propertyId)) {
445
445
  return {
446
446
  status: "error",
447
447
  error: `Unknown property ID "${propertyId}" for data source "${dataSource.key}".`,
@@ -4,10 +4,11 @@
4
4
  * and calls it, then removes the entry so the same `requestId` cannot be resolved twice.
5
5
  */
6
6
  export class PendingRequests {
7
+ prefix;
8
+ nextId = 1;
9
+ entries = new Map();
7
10
  constructor(prefix) {
8
11
  this.prefix = prefix;
9
- this.nextId = 1;
10
- this.entries = new Map();
11
12
  }
12
13
  allocate(resolver) {
13
14
  const requestId = `${this.prefix}-${this.nextId}`;
package/dist/init.js CHANGED
@@ -18,7 +18,7 @@ export class NotInIframeError extends CustomBlockInitializationError {
18
18
  this.name = "NotInIframeError";
19
19
  }
20
20
  }
21
- const DEFAULT_INIT_TIMEOUT_MS = 15000;
21
+ const DEFAULT_INIT_TIMEOUT_MS = 15_000;
22
22
  const NOT_IN_IFRAME_MESSAGE = "<NotionCustomBlock> only works inside an iframe — use the dev shell or deploy to Notion.";
23
23
  let initPromise;
24
24
  /**
@@ -22,6 +22,8 @@ export class CustomBlockInitializationError extends Error {
22
22
  this.code = error.code;
23
23
  this.isRetryable = error.isRetryable;
24
24
  }
25
+ code;
26
+ isRetryable;
25
27
  }
26
28
  /**
27
29
  * Initialization message sent by the host to the sandbox exactly once, in response to the
@@ -1 +1 @@
1
- {"version":3,"file":"useCustomBlockInit.d.ts","sourceRoot":"","sources":["../../src/react/useCustomBlockInit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,mDAAmD,CAAA;AAEvG,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAE3B,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;GASG;AACH,MAAM,MAAM,wBAAwB,GACjC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACrC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,8BAA8B,CAAA;CAAE,GAC1D;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,sBAAsB,CAAA;CAAE,CAAA;AAExE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,CAAC,EAAE,sBAAsB,GAC3B,wBAAwB,CA2B1B"}
1
+ {"version":3,"file":"useCustomBlockInit.d.ts","sourceRoot":"","sources":["../../src/react/useCustomBlockInit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,mDAAmD,CAAA;AAEvG,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAE3B,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;GASG;AACH,MAAM,MAAM,wBAAwB,GACjC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACrC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,8BAA8B,CAAA;CAAE,GAC1D;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,sBAAsB,CAAA;CAAE,CAAA;AAExE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,CAAC,EAAE,sBAAsB,GAC3B,wBAAwB,CAyB1B"}
@@ -22,6 +22,7 @@ export function useCustomBlockInit(opts) {
22
22
  isLoaded: false,
23
23
  error: undefined,
24
24
  });
25
+ // biome-ignore lint/correctness/useExhaustiveDependencies(opts): Initialization caches its first options and promise. Later options are ignored, so changes should not resubscribe to the same promise.
25
26
  useEffect(() => {
26
27
  let cancelled = false;
27
28
  initCustomBlock(opts).then(initial => {
@@ -36,9 +37,6 @@ export function useCustomBlockInit(opts) {
36
37
  return () => {
37
38
  cancelled = true;
38
39
  };
39
- // `initCustomBlock` caches its result, so options after the first call
40
- // are ignored — re-running on opts changes would be misleading.
41
- // eslint-disable-next-line react-hooks/exhaustive-deps
42
40
  }, []);
43
41
  return state;
44
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useDataSource.d.ts","sourceRoot":"","sources":["../../src/react/useDataSource.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAG5E;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC5B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,oBAAoB,GAC5B,mBAAmB,CAuCrB"}
1
+ {"version":3,"file":"useDataSource.d.ts","sourceRoot":"","sources":["../../src/react/useDataSource.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAG5E;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC5B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,oBAAoB,GAC5B,mBAAmB,CA0CrB"}
@@ -20,6 +20,9 @@ export function useDataSource(key, options) {
20
20
  ? host.dataSources.find(dataSource => dataSource.key === key)
21
21
  : undefined;
22
22
  const isInitialized = host.status === "initialized";
23
+ // biome-ignore lint/correctness/useExhaustiveDependencies(options): Compare options by value through optionsIdentity. Object identity alone must not trigger queries.
24
+ // biome-ignore lint/correctness/useExhaustiveDependencies(optionsIdentity): Changes to the serialized options must trigger a query even though the callback reads options.
25
+ // biome-ignore lint/correctness/useExhaustiveDependencies(matchingDataSource): Requery when the matching data source changes, even when the key and options are unchanged.
23
26
  useEffect(() => {
24
27
  if (!isInitialized) {
25
28
  return;
package/dist/version.js CHANGED
@@ -4,4 +4,4 @@
4
4
  *
5
5
  * WARNING: Generated during SDK publish. Do not edit in the published package.
6
6
  */
7
- export const CUSTOM_BLOCKS_SDK_VERSION = "0.1.39"
7
+ export const CUSTOM_BLOCKS_SDK_VERSION = "0.1.40"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -16,7 +16,7 @@ import type { CustomBlockCreatePageErrorInfo } from "@notionhq/custom-blocks-pro
16
16
  import type { GetUserMessage } from "@notionhq/custom-blocks-protocol/messages/getUser.js"
17
17
  import {
18
18
  hostToSandboxMessageSchema,
19
- PrimeableHostToSandboxMessage,
19
+ type PrimeableHostToSandboxMessage,
20
20
  } from "@notionhq/custom-blocks-protocol/messages/hostToSandbox.js"
21
21
  import {
22
22
  CustomBlockInitializationError,
@@ -147,13 +147,13 @@ function resolveDataSourceQueryOptions(
147
147
  return {
148
148
  status: "ok",
149
149
  options: {
150
- limit: Object.prototype.hasOwnProperty.call(optionsObject, "limit")
150
+ limit: Object.hasOwn(optionsObject, "limit")
151
151
  ? optionsObject.limit
152
152
  : undefined,
153
- filter: Object.prototype.hasOwnProperty.call(optionsObject, "filter")
153
+ filter: Object.hasOwn(optionsObject, "filter")
154
154
  ? optionsObject.filter
155
155
  : undefined,
156
- sorts: Object.prototype.hasOwnProperty.call(optionsObject, "sorts")
156
+ sorts: Object.hasOwn(optionsObject, "sorts")
157
157
  ? optionsObject.sorts
158
158
  : undefined,
159
159
  },
@@ -217,7 +217,7 @@ function resolveFilter(
217
217
  }
218
218
  }
219
219
  const filterObject = filter as Record<string, unknown>
220
- if (Object.prototype.hasOwnProperty.call(filterObject, "and")) {
220
+ if (Object.hasOwn(filterObject, "and")) {
221
221
  if (
222
222
  Object.keys(filterObject).length !== 1 ||
223
223
  !Array.isArray(filterObject.and)
@@ -325,7 +325,7 @@ function resolveSort(
325
325
  }
326
326
  const direction = sortObject.direction
327
327
  if (
328
- !Object.prototype.hasOwnProperty.call(sortObject, "direction") ||
328
+ !Object.hasOwn(sortObject, "direction") ||
329
329
  (direction !== "ascending" && direction !== "descending")
330
330
  ) {
331
331
  return {
@@ -557,11 +557,8 @@ function resolvePropertyAddress(
557
557
  ):
558
558
  | { status: "ok"; propertyId: string; propertyType: string }
559
559
  | { status: "error"; error: string } {
560
- const hasKey = Object.prototype.hasOwnProperty.call(value, "key")
561
- const hasPropertyId = Object.prototype.hasOwnProperty.call(
562
- value,
563
- "propertyId",
564
- )
560
+ const hasKey = Object.hasOwn(value, "key")
561
+ const hasPropertyId = Object.hasOwn(value, "propertyId")
565
562
  if (hasKey === hasPropertyId) {
566
563
  return {
567
564
  status: "error",
@@ -577,12 +574,7 @@ function resolvePropertyAddress(
577
574
  error: "Data source query property key must be a string.",
578
575
  }
579
576
  }
580
- if (
581
- !Object.prototype.hasOwnProperty.call(
582
- dataSource.propertyIdsByKey,
583
- value.key,
584
- )
585
- ) {
577
+ if (!Object.hasOwn(dataSource.propertyIdsByKey, value.key)) {
586
578
  return {
587
579
  status: "error",
588
580
  error: `Unknown property key "${value.key}" for data source "${dataSource.key}".`,
@@ -605,12 +597,7 @@ function resolvePropertyAddress(
605
597
  }
606
598
  propertyId = value.propertyId
607
599
  }
608
- if (
609
- !Object.prototype.hasOwnProperty.call(
610
- dataSource.propertySchemasById,
611
- propertyId,
612
- )
613
- ) {
600
+ if (!Object.hasOwn(dataSource.propertySchemasById, propertyId)) {
614
601
  return {
615
602
  status: "error",
616
603
  error: `Unknown property ID "${propertyId}" for data source "${dataSource.key}".`,
@@ -45,6 +45,7 @@ export function useCustomBlockInit(
45
45
  isLoaded: false,
46
46
  error: undefined,
47
47
  })
48
+ // biome-ignore lint/correctness/useExhaustiveDependencies(opts): Initialization caches its first options and promise. Later options are ignored, so changes should not resubscribe to the same promise.
48
49
  useEffect(() => {
49
50
  let cancelled = false
50
51
  initCustomBlock(opts).then(
@@ -62,9 +63,6 @@ export function useCustomBlockInit(
62
63
  return () => {
63
64
  cancelled = true
64
65
  }
65
- // `initCustomBlock` caches its result, so options after the first call
66
- // are ignored — re-running on opts changes would be misleading.
67
- // eslint-disable-next-line react-hooks/exhaustive-deps
68
66
  }, [])
69
67
  return state
70
68
  }
@@ -29,6 +29,9 @@ export function useDataSource(
29
29
  : undefined
30
30
 
31
31
  const isInitialized = host.status === "initialized"
32
+ // biome-ignore lint/correctness/useExhaustiveDependencies(options): Compare options by value through optionsIdentity. Object identity alone must not trigger queries.
33
+ // biome-ignore lint/correctness/useExhaustiveDependencies(optionsIdentity): Changes to the serialized options must trigger a query even though the callback reads options.
34
+ // biome-ignore lint/correctness/useExhaustiveDependencies(matchingDataSource): Requery when the matching data source changes, even when the key and options are unchanged.
32
35
  useEffect(() => {
33
36
  if (!isInitialized) {
34
37
  return