@notionhq/custom-blocks-host 0.1.7 → 0.1.8

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
@@ -179,6 +179,11 @@ held until initialization completes. `refreshQuery()` re-runs
179
179
  `queryDataSource` for every active subscription with the supplied data source
180
180
  ID, preserving each subscription's filter, sorts, and limit.
181
181
 
182
+ The runtime removes a subscription when it receives
183
+ `unsubscribeDataSourceQuery`. Repeated unsubscribe messages are safe. The
184
+ runtime also ignores results from a query that was unsubscribed while its
185
+ handler was running.
186
+
182
187
  Call `stop()` when the iframe is removed. It detaches listeners, clears
183
188
  timeouts, and discards active query subscriptions.
184
189
 
@@ -1 +1 @@
1
- {"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AAEjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAa1F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AAEtG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAahG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF,OAAO,KAAK,EACX,qBAAqB,EACrB,iCAAiC,EACjC,2BAA2B,EAC3B,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,uBAAuB,EACvB,oCAAoC,EACpC,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAU5B,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,oCAAoC,KACvC,IAAI,CAAA;IACT;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IACpD,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAC1E,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACtC,eAAe,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC,cAAc,EAAE,CAAC,WAAW,EAAE,iCAAiC,KAAK,IAAI,CAAA;IACxE,cAAc,EAAE,CAAC,WAAW,EAAE,UAAU,KAAK,IAAI,CAAA;IACjD,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,YAAY,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAA;CAClE,CAAA;AAMD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CAidvB"}
1
+ {"version":3,"file":"createCustomBlockHost.d.ts","sourceRoot":"","sources":["../src/createCustomBlockHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,8CAA8C,CAAA;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AAEjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAA;AAa1F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4DAA4D,CAAA;AAEtG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yDAAyD,CAAA;AAahG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF,OAAO,KAAK,EACX,qBAAqB,EACrB,iCAAiC,EACjC,2BAA2B,EAC3B,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EACX,uBAAuB,EACvB,oCAAoC,EACpC,2BAA2B,EAC3B,MAAM,qBAAqB,CAAA;AAU5B,MAAM,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,wBAAwB,EAAE,MAAM,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,YAAY,EAAE,2BAA2B,CAAA;IACzC,QAAQ,EAAE,uBAAuB,CAAA;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,oCAAoC,KACvC,IAAI,CAAA;IACT;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IACpD,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAC1E,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACtC,eAAe,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC,cAAc,EAAE,CAAC,WAAW,EAAE,iCAAiC,KAAK,IAAI,CAAA;IACxE,cAAc,EAAE,CAAC,WAAW,EAAE,UAAU,KAAK,IAAI,CAAA;IACjD,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,YAAY,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAA;CAClE,CAAA;AAMD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,sBAAsB,GAC7B,qBAAqB,CAodvB"}
@@ -54,7 +54,7 @@ export function createCustomBlockHost(options) {
54
54
  };
55
55
  post(message);
56
56
  }
57
- async function handleQuery(message, token = querySubscriptions.track(message)) {
57
+ async function handleQuery(message, token = querySubscriptions.subscribe(message)) {
58
58
  const collectionSchema = getCollectionSchema(activeDataSources, message.dataSourceId);
59
59
  const filterError = validateQueryDataSourceFilter(message.filter, collectionSchema);
60
60
  if (filterError !== undefined) {
@@ -282,6 +282,9 @@ export function createCustomBlockHost(options) {
282
282
  case "queryDataSource":
283
283
  void handleQuery(message);
284
284
  return;
285
+ case "unsubscribeDataSourceQuery":
286
+ querySubscriptions.unsubscribe(message.subscriptionId);
287
+ return;
285
288
  case "createPage":
286
289
  void handleCreatePage(message);
287
290
  return;
@@ -313,7 +316,7 @@ export function createCustomBlockHost(options) {
313
316
  window.removeEventListener("message", onMessage);
314
317
  iframe.removeEventListener("load", initializationController.onIframeLoad);
315
318
  initializationController.stop();
316
- querySubscriptions.clear();
319
+ querySubscriptions.unsubscribeAll();
317
320
  },
318
321
  post,
319
322
  setTheme(nextTheme) {
@@ -39,6 +39,7 @@ export * from "./messages/queryDataSourceResult.js";
39
39
  export * from "./messages/resize.js";
40
40
  export * from "./messages/sandboxToHost.js";
41
41
  export * from "./messages/themeChanged.js";
42
+ export * from "./messages/unsubscribeDataSourceQuery.js";
42
43
  export * from "./messages/updatePage.js";
43
44
  export * from "./messages/updatePageResult.js";
44
45
  export * from "./pages/page.js";
@@ -39,6 +39,7 @@ export * from "./messages/queryDataSourceResult.js";
39
39
  export * from "./messages/resize.js";
40
40
  export * from "./messages/sandboxToHost.js";
41
41
  export * from "./messages/themeChanged.js";
42
+ export * from "./messages/unsubscribeDataSourceQuery.js";
42
43
  export * from "./messages/updatePage.js";
43
44
  export * from "./messages/updatePageResult.js";
44
45
  export * from "./pages/page.js";
@@ -411,6 +411,9 @@ export declare const sandboxToHostMessageSchema: v.VariantSchema<"type", [v.Vari
411
411
  readonly propertyId: v.StringSchema<undefined>;
412
412
  readonly direction: v.PicklistSchema<["ascending", "descending"], undefined>;
413
413
  }, undefined>, undefined>, undefined>;
414
+ }, undefined>, v.ObjectSchema<{
415
+ readonly type: v.LiteralSchema<"unsubscribeDataSourceQuery", undefined>;
416
+ readonly subscriptionId: v.StringSchema<undefined>;
414
417
  }, undefined>, v.ObjectSchema<{
415
418
  readonly type: v.LiteralSchema<"createPage", undefined>;
416
419
  readonly requestId: v.StringSchema<undefined>;
@@ -8,6 +8,7 @@ import { invalidHostMessageSchema } from "./invalidHostMessage.js";
8
8
  import { listUsersMessageSchema } from "./listUsers.js";
9
9
  import { queryDataSourceMessageSchema } from "./queryDataSource.js";
10
10
  import { resizeMessageSchema } from "./resize.js";
11
+ import { unsubscribeDataSourceQueryMessageSchema } from "./unsubscribeDataSourceQuery.js";
11
12
  import { updatePageMessageSchema } from "./updatePage.js";
12
13
  /**
13
14
  * Discriminated union of every message the sandbox is allowed to send the host. Useful for hosts
@@ -17,6 +18,7 @@ export const sandboxToHostMessageSchema = v.variant("type", [
17
18
  connectMessageSchema,
18
19
  initResultMessageSchema,
19
20
  queryDataSourceMessageSchema,
21
+ unsubscribeDataSourceQueryMessageSchema,
20
22
  createPageMessageSchema,
21
23
  getPageMessageSchema,
22
24
  getUserMessageSchema,
@@ -0,0 +1,10 @@
1
+ import * as v from "valibot";
2
+ /**
3
+ * Tells the host to stop an active data source query subscription.
4
+ */
5
+ export declare const unsubscribeDataSourceQueryMessageSchema: v.ObjectSchema<{
6
+ readonly type: v.LiteralSchema<"unsubscribeDataSourceQuery", undefined>;
7
+ /** The subscription to remove from the host. */
8
+ readonly subscriptionId: v.StringSchema<undefined>;
9
+ }, undefined>;
10
+ export type UnsubscribeDataSourceQueryMessage = v.InferOutput<typeof unsubscribeDataSourceQueryMessageSchema>;
@@ -0,0 +1,9 @@
1
+ import * as v from "valibot";
2
+ /**
3
+ * Tells the host to stop an active data source query subscription.
4
+ */
5
+ export const unsubscribeDataSourceQueryMessageSchema = v.object({
6
+ type: v.literal("unsubscribeDataSourceQuery"),
7
+ /** The subscription to remove from the host. */
8
+ subscriptionId: v.string(),
9
+ });
@@ -9,9 +9,10 @@ export type QuerySubscriptionToken = {
9
9
  };
10
10
  export declare class QuerySubscriptions {
11
11
  private readonly bySubscriptionId;
12
- track(message: QueryDataSourceMessage): QuerySubscriptionToken;
12
+ subscribe(message: QueryDataSourceMessage): QuerySubscriptionToken;
13
13
  isCurrent(subscriptionId: string, token: QuerySubscriptionToken): boolean;
14
+ unsubscribe(subscriptionId: string): void;
14
15
  refresh(dataSourceId: NotionDataSourceId): QueryRefresh[];
15
- clear(): void;
16
+ unsubscribeAll(): void;
16
17
  }
17
18
  //# sourceMappingURL=querySubscriptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"querySubscriptions.d.ts","sourceRoot":"","sources":["../../src/queries/querySubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAE1G,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,sBAAsB,CAAA;IAC/B,KAAK,EAAE,sBAAsB,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACpC,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAA;CACvC,CAAA;AAOD,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4C;IAE7E,KAAK,CAAC,OAAO,EAAE,sBAAsB,GAAG,sBAAsB,CAS7D;IAED,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAExE;IAED,OAAO,CAAC,YAAY,EAAE,kBAAkB,GAAG,YAAY,EAAE,CAgBxD;IAED,KAAK,SAEJ;CACD"}
1
+ {"version":3,"file":"querySubscriptions.d.ts","sourceRoot":"","sources":["../../src/queries/querySubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAA;AAE1G,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,sBAAsB,CAAA;IAC/B,KAAK,EAAE,sBAAsB,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACpC,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAA;CACvC,CAAA;AAOD,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4C;IAE7E,SAAS,CAAC,OAAO,EAAE,sBAAsB,GAAG,sBAAsB,CASjE;IAED,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAExE;IAED,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAExC;IAED,OAAO,CAAC,YAAY,EAAE,kBAAkB,GAAG,YAAY,EAAE,CAgBxD;IAED,cAAc,SAEb;CACD"}
@@ -1,6 +1,6 @@
1
1
  export class QuerySubscriptions {
2
2
  bySubscriptionId = new Map();
3
- track(message) {
3
+ subscribe(message) {
4
4
  const token = {
5
5
  _tag: "QuerySubscriptionToken",
6
6
  };
@@ -13,6 +13,9 @@ export class QuerySubscriptions {
13
13
  isCurrent(subscriptionId, token) {
14
14
  return this.bySubscriptionId.get(subscriptionId)?.token === token;
15
15
  }
16
+ unsubscribe(subscriptionId) {
17
+ this.bySubscriptionId.delete(subscriptionId);
18
+ }
16
19
  refresh(dataSourceId) {
17
20
  const refreshes = [];
18
21
  for (const queryState of this.bySubscriptionId.values()) {
@@ -30,7 +33,7 @@ export class QuerySubscriptions {
30
33
  }
31
34
  return refreshes;
32
35
  }
33
- clear() {
36
+ unsubscribeAll() {
34
37
  this.bySubscriptionId.clear();
35
38
  }
36
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks-host",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Reusable host runtime for Notion custom blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -171,7 +171,7 @@ export function createCustomBlockHost(
171
171
 
172
172
  async function handleQuery(
173
173
  message: QueryDataSourceMessage,
174
- token: QuerySubscriptionToken = querySubscriptions.track(message),
174
+ token: QuerySubscriptionToken = querySubscriptions.subscribe(message),
175
175
  ) {
176
176
  const collectionSchema = getCollectionSchema(
177
177
  activeDataSources,
@@ -435,6 +435,9 @@ export function createCustomBlockHost(
435
435
  case "queryDataSource":
436
436
  void handleQuery(message)
437
437
  return
438
+ case "unsubscribeDataSourceQuery":
439
+ querySubscriptions.unsubscribe(message.subscriptionId)
440
+ return
438
441
  case "createPage":
439
442
  void handleCreatePage(message)
440
443
  return
@@ -468,7 +471,7 @@ export function createCustomBlockHost(
468
471
  window.removeEventListener("message", onMessage)
469
472
  iframe.removeEventListener("load", initializationController.onIframeLoad)
470
473
  initializationController.stop()
471
- querySubscriptions.clear()
474
+ querySubscriptions.unsubscribeAll()
472
475
  },
473
476
  post,
474
477
  setTheme(nextTheme) {
@@ -18,7 +18,7 @@ type QuerySubscriptionState = {
18
18
  export class QuerySubscriptions {
19
19
  private readonly bySubscriptionId = new Map<string, QuerySubscriptionState>()
20
20
 
21
- track(message: QueryDataSourceMessage): QuerySubscriptionToken {
21
+ subscribe(message: QueryDataSourceMessage): QuerySubscriptionToken {
22
22
  const token: QuerySubscriptionToken = {
23
23
  _tag: "QuerySubscriptionToken",
24
24
  }
@@ -33,6 +33,10 @@ export class QuerySubscriptions {
33
33
  return this.bySubscriptionId.get(subscriptionId)?.token === token
34
34
  }
35
35
 
36
+ unsubscribe(subscriptionId: string): void {
37
+ this.bySubscriptionId.delete(subscriptionId)
38
+ }
39
+
36
40
  refresh(dataSourceId: NotionDataSourceId): QueryRefresh[] {
37
41
  const refreshes: QueryRefresh[] = []
38
42
  for (const queryState of this.bySubscriptionId.values()) {
@@ -51,7 +55,7 @@ export class QuerySubscriptions {
51
55
  return refreshes
52
56
  }
53
57
 
54
- clear() {
58
+ unsubscribeAll() {
55
59
  this.bySubscriptionId.clear()
56
60
  }
57
61
  }