@notionhq/custom-blocks 0.1.34 → 0.1.35

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.
@@ -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":"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"}
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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"}
@@ -580,6 +580,7 @@ export class SandboxBridge {
580
580
  dataSourceId: query.dataSourceId,
581
581
  limit: query.limit,
582
582
  ...(query.filter !== undefined ? { filter: query.filter } : {}),
583
+ ...(query.sorts !== undefined ? { sorts: query.sorts } : {}),
583
584
  };
584
585
  this.postToHost(outbound);
585
586
  }
@@ -1,11 +1,12 @@
1
1
  import type { NotionDataSource } from "../../protocol/dataSources/dataSource.js";
2
2
  import type { NotionDataSourceId } from "../../protocol/ids.js";
3
- import { type ResolvedCustomBlockDataSourceFilter } from "../../protocol/messages/queryDataSource.js";
3
+ import { type ResolvedCustomBlockDataSourceFilter, type ResolvedCustomBlockDataSourceSort } from "../../protocol/messages/queryDataSource.js";
4
4
  import type { UseDataSourceOptions } from "../../types.js";
5
5
  export type ResolvedDataSourceQuery = {
6
6
  dataSourceId: NotionDataSourceId;
7
7
  limit: number;
8
8
  filter?: ResolvedCustomBlockDataSourceFilter;
9
+ sorts?: ResolvedCustomBlockDataSourceSort[];
9
10
  identity: string;
10
11
  };
11
12
  export type ResolveDataSourceQueryResult = {
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/dataSources/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4DAA4D,CAAA;AAClG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,EAON,KAAK,mCAAmC,EAExC,MAAM,8DAA8D,CAAA;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAM1D,MAAM,MAAM,uBAAuB,GAAG;IACrC,YAAY,EAAE,kBAAkB,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,mCAAmC,CAAA;IAC5C,QAAQ,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,4BAA4B,GACrC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IACA,MAAM,EAAE,IAAI,CAAA;IACZ,UAAU,EAAE,gBAAgB,CAAA;IAC5B,KAAK,EAAE,uBAAuB,CAAA;CAC7B,CAAA;AAEJ,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC5C,WAAW,EAAE,gBAAgB,EAAE,CAAA;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC,GAAG,4BAA4B,CAuD/B;AA6BD,wBAAgB,iCAAiC,CAChD,OAAO,EAAE,oBAAoB,GAAG,SAAS,GACvC,MAAM,CAMR"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/bridge/dataSources/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4DAA4D,CAAA;AAElG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,EAQN,KAAK,mCAAmC,EAExC,KAAK,iCAAiC,EACtC,MAAM,8DAA8D,CAAA;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAU1D,MAAM,MAAM,uBAAuB,GAAG;IACrC,YAAY,EAAE,kBAAkB,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,mCAAmC,CAAA;IAC5C,KAAK,CAAC,EAAE,iCAAiC,EAAE,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,4BAA4B,GACrC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IACA,MAAM,EAAE,IAAI,CAAA;IACZ,UAAU,EAAE,gBAAgB,CAAA;IAC5B,KAAK,EAAE,uBAAuB,CAAA;CAC7B,CAAA;AAYJ,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC5C,WAAW,EAAE,gBAAgB,EAAE,CAAA;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC,GAAG,4BAA4B,CAmD/B;AAiFD,wBAAgB,iCAAiC,CAChD,OAAO,EAAE,oBAAoB,GAAG,SAAS,GACvC,MAAM,CAMR"}
@@ -1,23 +1,18 @@
1
- import { customBlockCheckboxFilterOperatorSchema, customBlockContainsFilterOperatorSchema, customBlockDateFilterOperatorSchema, customBlockNumberFilterOperatorSchema, customBlockOptionFilterOperatorSchema, customBlockTextFilterOperatorSchema, } from "../../protocol/messages/queryDataSource.js";
1
+ import { CUSTOM_BLOCK_DATA_SOURCE_SORTABLE_PROPERTY_TYPES } from "../../protocol/dataSources/propertySchema.js";
2
+ import { CUSTOM_BLOCK_DATA_SOURCE_QUERY_MAX_SORTS, customBlockCheckboxFilterOperatorSchema, customBlockContainsFilterOperatorSchema, customBlockDateFilterOperatorSchema, customBlockNumberFilterOperatorSchema, customBlockOptionFilterOperatorSchema, customBlockTextFilterOperatorSchema, } from "../../protocol/messages/queryDataSource.js";
2
3
  import * as v from "valibot";
4
+ import { unreachable } from "../../utils.js";
3
5
  const DEFAULT_DATA_SOURCE_QUERY_LIMIT = 20;
4
6
  const MAX_DATA_SOURCE_QUERY_LIMIT = 999;
5
7
  const MAX_DATA_SOURCE_QUERY_FILTER_CHILDREN = 25;
8
+ const supportedDataSourceSortPropertyTypes = new Set(CUSTOM_BLOCK_DATA_SOURCE_SORTABLE_PROPERTY_TYPES);
6
9
  export function resolveDataSourceQuery(args) {
7
10
  const { dataSources, key, options, warn } = args;
8
- if (options !== undefined && !isPlainRecord(options)) {
9
- return {
10
- status: "error",
11
- error: "Data source query options must be an object.",
12
- };
13
- }
14
- if (options !== undefined &&
15
- Object.keys(options).some(key => !["limit", "filter"].includes(key))) {
16
- return {
17
- status: "error",
18
- error: "Data source query options contain unsupported fields.",
19
- };
11
+ const resolvedOptions = resolveDataSourceQueryOptions(options);
12
+ if (resolvedOptions.status === "error") {
13
+ return resolvedOptions;
20
14
  }
15
+ const queryOptions = resolvedOptions.options;
21
16
  const dataSource = dataSources.find(entry => entry.key === key);
22
17
  if (dataSource === undefined) {
23
18
  return {
@@ -31,18 +26,23 @@ export function resolveDataSourceQuery(args) {
31
26
  error: `Data source "${key}" has not been mapped to a database yet.`,
32
27
  };
33
28
  }
34
- const limit = resolveDataSourceQueryLimit(options?.limit, warn);
29
+ const limit = resolveDataSourceQueryLimit(queryOptions?.limit, warn);
35
30
  if (limit.status === "error") {
36
31
  return limit;
37
32
  }
38
- const filter = resolveFilter(options?.filter, dataSource);
33
+ const filter = resolveFilter(queryOptions?.filter, dataSource);
39
34
  if (filter.status === "error") {
40
35
  return filter;
41
36
  }
37
+ const sorts = resolveSorts(queryOptions?.sorts, dataSource);
38
+ if (sorts.status === "error") {
39
+ return sorts;
40
+ }
42
41
  const identity = stableJsonStringify({
43
42
  dataSourceId: dataSource.collectionPointer.id,
44
43
  limit: limit.limit,
45
44
  filter: filter.filter ?? null,
45
+ sorts: sorts.sorts ?? [],
46
46
  });
47
47
  return {
48
48
  status: "ok",
@@ -51,10 +51,52 @@ export function resolveDataSourceQuery(args) {
51
51
  dataSourceId: dataSource.collectionPointer.id,
52
52
  limit: limit.limit,
53
53
  filter: filter.filter,
54
+ sorts: sorts.sorts,
54
55
  identity,
55
56
  },
56
57
  };
57
58
  }
59
+ function resolveDataSourceQueryOptions(options) {
60
+ if (options === undefined) {
61
+ return { status: "ok" };
62
+ }
63
+ if (typeof options !== "object" ||
64
+ options === null ||
65
+ Array.isArray(options)) {
66
+ return {
67
+ status: "error",
68
+ error: "Data source query options must be an object.",
69
+ };
70
+ }
71
+ const prototype = Object.getPrototypeOf(options);
72
+ if (prototype !== Object.prototype && prototype !== null) {
73
+ return {
74
+ status: "error",
75
+ error: "Data source query options must be an object.",
76
+ };
77
+ }
78
+ const optionsObject = options;
79
+ if (Object.keys(optionsObject).some(key => !["limit", "filter", "sorts"].includes(key))) {
80
+ return {
81
+ status: "error",
82
+ error: "Data source query options contain unsupported fields.",
83
+ };
84
+ }
85
+ return {
86
+ status: "ok",
87
+ options: {
88
+ limit: Object.prototype.hasOwnProperty.call(optionsObject, "limit")
89
+ ? optionsObject.limit
90
+ : undefined,
91
+ filter: Object.prototype.hasOwnProperty.call(optionsObject, "filter")
92
+ ? optionsObject.filter
93
+ : undefined,
94
+ sorts: Object.prototype.hasOwnProperty.call(optionsObject, "sorts")
95
+ ? optionsObject.sorts
96
+ : undefined,
97
+ },
98
+ };
99
+ }
58
100
  function resolveDataSourceQueryLimit(limit, warn = console.warn) {
59
101
  if (limit === undefined) {
60
102
  return { status: "ok", limit: DEFAULT_DATA_SOURCE_QUERY_LIMIT };
@@ -89,27 +131,29 @@ function resolveFilter(filter, dataSource) {
89
131
  if (filter === undefined) {
90
132
  return { status: "ok" };
91
133
  }
92
- if (!isPlainRecord(filter)) {
134
+ if (typeof filter !== "object" || filter === null || Array.isArray(filter)) {
93
135
  return {
94
136
  status: "error",
95
137
  error: "Data source query filter must be an object.",
96
138
  };
97
139
  }
98
- if ("and" in filter) {
99
- if (Object.keys(filter).length !== 1 || !Array.isArray(filter.and)) {
140
+ const filterObject = filter;
141
+ if (Object.prototype.hasOwnProperty.call(filterObject, "and")) {
142
+ if (Object.keys(filterObject).length !== 1 ||
143
+ !Array.isArray(filterObject.and)) {
100
144
  return {
101
145
  status: "error",
102
146
  error: 'Data source query filter "and" must be an array.',
103
147
  };
104
148
  }
105
- if (filter.and.length > MAX_DATA_SOURCE_QUERY_FILTER_CHILDREN) {
149
+ if (filterObject.and.length > MAX_DATA_SOURCE_QUERY_FILTER_CHILDREN) {
106
150
  return {
107
151
  status: "error",
108
152
  error: `Data source query filter "and" may contain at most ${MAX_DATA_SOURCE_QUERY_FILTER_CHILDREN} children.`,
109
153
  };
110
154
  }
111
155
  const and = [];
112
- for (const child of filter.and) {
156
+ for (const child of filterObject.and) {
113
157
  const resolved = resolvePropertyFilter(child, dataSource);
114
158
  if (resolved.status === "error") {
115
159
  return resolved;
@@ -118,20 +162,95 @@ function resolveFilter(filter, dataSource) {
118
162
  }
119
163
  return { status: "ok", filter: { and } };
120
164
  }
121
- return resolvePropertyFilter(filter, dataSource);
165
+ return resolvePropertyFilter(filterObject, dataSource);
166
+ }
167
+ function resolveSorts(sorts, dataSource) {
168
+ if (sorts === undefined) {
169
+ return { status: "ok" };
170
+ }
171
+ if (!Array.isArray(sorts)) {
172
+ return {
173
+ status: "error",
174
+ error: "Data source query sorts must be an array.",
175
+ };
176
+ }
177
+ if (sorts.length === 0) {
178
+ return { status: "ok" };
179
+ }
180
+ if (sorts.length > CUSTOM_BLOCK_DATA_SOURCE_QUERY_MAX_SORTS) {
181
+ return {
182
+ status: "error",
183
+ error: `Data source query sorts may contain at most ${CUSTOM_BLOCK_DATA_SOURCE_QUERY_MAX_SORTS} entries.`,
184
+ };
185
+ }
186
+ const resolvedSorts = [];
187
+ const seenPropertyIds = new Set();
188
+ for (const sort of sorts) {
189
+ const resolved = resolveSort(sort, dataSource);
190
+ if (resolved.status === "error") {
191
+ return resolved;
192
+ }
193
+ if (seenPropertyIds.has(resolved.sort.propertyId)) {
194
+ return {
195
+ status: "error",
196
+ error: `Data source query sorts cannot contain duplicate property ID "${resolved.sort.propertyId}".`,
197
+ };
198
+ }
199
+ seenPropertyIds.add(resolved.sort.propertyId);
200
+ resolvedSorts.push(resolved.sort);
201
+ }
202
+ return { status: "ok", sorts: resolvedSorts };
203
+ }
204
+ function resolveSort(sort, dataSource) {
205
+ if (typeof sort !== "object" || sort === null || Array.isArray(sort)) {
206
+ return {
207
+ status: "error",
208
+ error: "Data source query sort must be an object.",
209
+ };
210
+ }
211
+ const sortObject = sort;
212
+ if (Object.keys(sortObject).some(key => !["key", "propertyId", "direction"].includes(key))) {
213
+ return {
214
+ status: "error",
215
+ error: "Data source query sort contains unsupported fields.",
216
+ };
217
+ }
218
+ const property = resolvePropertyAddress(sortObject, dataSource);
219
+ if (property.status === "error") {
220
+ return property;
221
+ }
222
+ if (!supportedDataSourceSortPropertyTypes.has(property.propertyType)) {
223
+ return {
224
+ status: "error",
225
+ error: `Data source query sorts do not yet support property type "${property.propertyType}".`,
226
+ };
227
+ }
228
+ const direction = sortObject.direction;
229
+ if (!Object.prototype.hasOwnProperty.call(sortObject, "direction") ||
230
+ (direction !== "ascending" && direction !== "descending")) {
231
+ return {
232
+ status: "error",
233
+ error: 'Data source query sort direction must be "ascending" or "descending".',
234
+ };
235
+ }
236
+ return {
237
+ status: "ok",
238
+ sort: { propertyId: property.propertyId, direction },
239
+ };
122
240
  }
123
241
  function resolvePropertyFilter(filter, dataSource) {
124
- if (!isPlainRecord(filter)) {
242
+ if (typeof filter !== "object" || filter === null || Array.isArray(filter)) {
125
243
  return {
126
244
  status: "error",
127
245
  error: "Data source query property filter must be an object.",
128
246
  };
129
247
  }
130
- const property = resolvePropertyAddress(filter, dataSource);
248
+ const filterObject = filter;
249
+ const property = resolvePropertyAddress(filterObject, dataSource);
131
250
  if (property.status === "error") {
132
251
  return property;
133
252
  }
134
- const branchKeys = Object.keys(filter).filter(key => key !== "key" && key !== "propertyId");
253
+ const branchKeys = Object.keys(filterObject).filter(key => key !== "key" && key !== "propertyId");
135
254
  if (branchKeys.length !== 1) {
136
255
  return {
137
256
  status: "error",
@@ -139,8 +258,11 @@ function resolvePropertyFilter(filter, dataSource) {
139
258
  };
140
259
  }
141
260
  const branch = branchKeys[0];
142
- const value = filter[branch];
143
- if (!isPlainRecord(value) || Object.keys(value).length !== 1) {
261
+ const value = filterObject[branch];
262
+ if (typeof value !== "object" ||
263
+ value === null ||
264
+ Array.isArray(value) ||
265
+ Object.keys(value).length !== 1) {
144
266
  return invalidOperator(branch);
145
267
  }
146
268
  if (property.propertyType !== branch) {
@@ -149,133 +271,142 @@ function resolvePropertyFilter(filter, dataSource) {
149
271
  error: `Data source query filter branch "${branch}" does not match property type "${property.propertyType}".`,
150
272
  };
151
273
  }
152
- switch (branch) {
153
- case "title": {
154
- const parsed = v.safeParse(customBlockTextFilterOperatorSchema, value);
155
- return parsed.success
156
- ? {
157
- status: "ok",
158
- filter: { propertyId: property.propertyId, title: parsed.output },
159
- }
160
- : invalidOperator(branch);
161
- }
162
- case "rich_text": {
163
- const parsed = v.safeParse(customBlockTextFilterOperatorSchema, value);
164
- return parsed.success
165
- ? {
166
- status: "ok",
167
- filter: {
168
- propertyId: property.propertyId,
169
- rich_text: parsed.output,
170
- },
171
- }
172
- : invalidOperator(branch);
173
- }
274
+ return resolvePropertyFilterBranch({
275
+ propertyId: property.propertyId,
276
+ branch,
277
+ value: value,
278
+ });
279
+ }
280
+ function resolvePropertyFilterBranch(args) {
281
+ switch (args.branch) {
282
+ case "title":
283
+ case "rich_text":
174
284
  case "url":
175
285
  case "email":
176
- case "phone_number": {
177
- const parsed = v.safeParse(customBlockTextFilterOperatorSchema, value);
178
- if (!parsed.success) {
179
- return invalidOperator(branch);
180
- }
181
- if (branch === "url") {
182
- return {
183
- status: "ok",
184
- filter: { propertyId: property.propertyId, url: parsed.output },
185
- };
186
- }
187
- if (branch === "email") {
188
- return {
189
- status: "ok",
190
- filter: { propertyId: property.propertyId, email: parsed.output },
191
- };
192
- }
286
+ case "phone_number":
287
+ return resolveTextPropertyFilter({
288
+ propertyId: args.propertyId,
289
+ branch: args.branch,
290
+ value: args.value,
291
+ });
292
+ case "number":
293
+ return resolveNumberPropertyFilter(args.propertyId, args.value);
294
+ case "checkbox":
295
+ return resolveCheckboxPropertyFilter(args.propertyId, args.value);
296
+ case "select":
297
+ case "status":
298
+ return resolveOptionPropertyFilter({
299
+ propertyId: args.propertyId,
300
+ branch: args.branch,
301
+ value: args.value,
302
+ });
303
+ case "multi_select":
304
+ return resolveMultiSelectPropertyFilter(args.propertyId, args.value);
305
+ case "date":
306
+ return resolveDatePropertyFilter(args.propertyId, args.value);
307
+ default:
308
+ return {
309
+ status: "error",
310
+ error: `Data source query filter branch "${args.branch}" is not supported.`,
311
+ };
312
+ }
313
+ }
314
+ function resolveTextPropertyFilter(args) {
315
+ const parsed = v.safeParse(customBlockTextFilterOperatorSchema, args.value);
316
+ if (!parsed.success) {
317
+ return invalidOperator(args.branch);
318
+ }
319
+ switch (args.branch) {
320
+ case "title":
193
321
  return {
194
322
  status: "ok",
195
- filter: {
196
- propertyId: property.propertyId,
197
- phone_number: parsed.output,
198
- },
323
+ filter: { propertyId: args.propertyId, title: parsed.output },
324
+ };
325
+ case "rich_text":
326
+ return {
327
+ status: "ok",
328
+ filter: { propertyId: args.propertyId, rich_text: parsed.output },
329
+ };
330
+ case "url":
331
+ return {
332
+ status: "ok",
333
+ filter: { propertyId: args.propertyId, url: parsed.output },
334
+ };
335
+ case "email":
336
+ return {
337
+ status: "ok",
338
+ filter: { propertyId: args.propertyId, email: parsed.output },
339
+ };
340
+ case "phone_number":
341
+ return {
342
+ status: "ok",
343
+ filter: { propertyId: args.propertyId, phone_number: parsed.output },
199
344
  };
200
- }
201
- case "number": {
202
- const parsed = v.safeParse(customBlockNumberFilterOperatorSchema, value);
203
- if (parsed.success &&
204
- Object.values(parsed.output).every(entry => typeof entry !== "number" || Number.isFinite(entry))) {
205
- return {
206
- status: "ok",
207
- filter: { propertyId: property.propertyId, number: parsed.output },
208
- };
209
- }
210
- return invalidOperator(branch);
211
- }
212
- case "checkbox": {
213
- const parsed = v.safeParse(customBlockCheckboxFilterOperatorSchema, value);
214
- return parsed.success
215
- ? {
216
- status: "ok",
217
- filter: {
218
- propertyId: property.propertyId,
219
- checkbox: parsed.output,
220
- },
221
- }
222
- : invalidOperator(branch);
223
- }
224
- case "select": {
225
- const parsed = v.safeParse(customBlockOptionFilterOperatorSchema, value);
226
- return parsed.success
227
- ? {
228
- status: "ok",
229
- filter: { propertyId: property.propertyId, select: parsed.output },
230
- }
231
- : invalidOperator(branch);
232
- }
233
- case "multi_select": {
234
- const parsed = v.safeParse(customBlockContainsFilterOperatorSchema, value);
235
- return parsed.success
236
- ? {
237
- status: "ok",
238
- filter: {
239
- propertyId: property.propertyId,
240
- multi_select: parsed.output,
241
- },
242
- }
243
- : invalidOperator(branch);
244
- }
245
- case "status": {
246
- const parsed = v.safeParse(customBlockOptionFilterOperatorSchema, value);
247
- return parsed.success
248
- ? {
249
- status: "ok",
250
- filter: { propertyId: property.propertyId, status: parsed.output },
251
- }
252
- : invalidOperator(branch);
253
- }
254
- case "date": {
255
- const parsed = v.safeParse(customBlockDateFilterOperatorSchema, value);
256
- if (parsed.success &&
257
- Object.values(parsed.output).every(entry => entry === true || isValidIsoDate(entry))) {
258
- return {
259
- status: "ok",
260
- filter: { propertyId: property.propertyId, date: parsed.output },
261
- };
262
- }
263
- return invalidOperator(branch);
264
- }
265
345
  default:
346
+ return unreachable(args.branch);
347
+ }
348
+ }
349
+ function resolveNumberPropertyFilter(propertyId, value) {
350
+ const parsed = v.safeParse(customBlockNumberFilterOperatorSchema, value);
351
+ if (!parsed.success ||
352
+ !Object.values(parsed.output).every(entry => typeof entry !== "number" || Number.isFinite(entry))) {
353
+ return invalidOperator("number");
354
+ }
355
+ return {
356
+ status: "ok",
357
+ filter: { propertyId, number: parsed.output },
358
+ };
359
+ }
360
+ function resolveCheckboxPropertyFilter(propertyId, value) {
361
+ const parsed = v.safeParse(customBlockCheckboxFilterOperatorSchema, value);
362
+ return parsed.success
363
+ ? { status: "ok", filter: { propertyId, checkbox: parsed.output } }
364
+ : invalidOperator("checkbox");
365
+ }
366
+ function resolveOptionPropertyFilter(args) {
367
+ const parsed = v.safeParse(customBlockOptionFilterOperatorSchema, args.value);
368
+ if (!parsed.success) {
369
+ return invalidOperator(args.branch);
370
+ }
371
+ switch (args.branch) {
372
+ case "select":
266
373
  return {
267
- status: "error",
268
- error: `Data source query filter branch "${branch}" is not supported.`,
374
+ status: "ok",
375
+ filter: { propertyId: args.propertyId, select: parsed.output },
269
376
  };
377
+ case "status":
378
+ return {
379
+ status: "ok",
380
+ filter: { propertyId: args.propertyId, status: parsed.output },
381
+ };
382
+ default:
383
+ return unreachable(args.branch);
270
384
  }
271
385
  }
386
+ function resolveMultiSelectPropertyFilter(propertyId, value) {
387
+ const parsed = v.safeParse(customBlockContainsFilterOperatorSchema, value);
388
+ return parsed.success
389
+ ? { status: "ok", filter: { propertyId, multi_select: parsed.output } }
390
+ : invalidOperator("multi_select");
391
+ }
392
+ function resolveDatePropertyFilter(propertyId, value) {
393
+ const parsed = v.safeParse(customBlockDateFilterOperatorSchema, value);
394
+ if (!parsed.success ||
395
+ !Object.values(parsed.output).every(entry => entry === true || isValidIsoDate(entry))) {
396
+ return invalidOperator("date");
397
+ }
398
+ return {
399
+ status: "ok",
400
+ filter: { propertyId, date: parsed.output },
401
+ };
402
+ }
272
403
  function resolvePropertyAddress(value, dataSource) {
273
- const hasKey = "key" in value;
274
- const hasPropertyId = "propertyId" in value;
404
+ const hasKey = Object.prototype.hasOwnProperty.call(value, "key");
405
+ const hasPropertyId = Object.prototype.hasOwnProperty.call(value, "propertyId");
275
406
  if (hasKey === hasPropertyId) {
276
407
  return {
277
408
  status: "error",
278
- error: "Data source query filters must use exactly one of key or propertyId.",
409
+ error: "Data source query filters and sorts must use exactly one of key or propertyId.",
279
410
  };
280
411
  }
281
412
  let propertyId;
@@ -286,7 +417,7 @@ function resolvePropertyAddress(value, dataSource) {
286
417
  error: "Data source query property key must be a string.",
287
418
  };
288
419
  }
289
- if (!(value.key in dataSource.propertyIdsByKey)) {
420
+ if (!Object.prototype.hasOwnProperty.call(dataSource.propertyIdsByKey, value.key)) {
290
421
  return {
291
422
  status: "error",
292
423
  error: `Unknown property key "${value.key}" for data source "${dataSource.key}".`,
@@ -310,13 +441,13 @@ function resolvePropertyAddress(value, dataSource) {
310
441
  }
311
442
  propertyId = value.propertyId;
312
443
  }
313
- const propertySchema = dataSource.propertySchemasById[propertyId];
314
- if (propertySchema === undefined) {
444
+ if (!Object.prototype.hasOwnProperty.call(dataSource.propertySchemasById, propertyId)) {
315
445
  return {
316
446
  status: "error",
317
447
  error: `Unknown property ID "${propertyId}" for data source "${dataSource.key}".`,
318
448
  };
319
449
  }
450
+ const propertySchema = dataSource.propertySchemasById[propertyId];
320
451
  return {
321
452
  status: "ok",
322
453
  propertyId,
@@ -336,32 +467,36 @@ function normalizeJsonValue(value) {
336
467
  return value;
337
468
  }
338
469
  if (typeof value === "number") {
339
- if (!Number.isFinite(value)) {
340
- throw new Error("Data source query values must be finite JSON numbers.");
341
- }
342
- return value;
470
+ return normalizeJsonNumber(value);
343
471
  }
344
472
  if (Array.isArray(value)) {
345
473
  return value.map(entry => normalizeJsonValue(entry));
346
474
  }
347
- if (isPlainRecord(value)) {
348
- const normalized = {};
349
- for (const key of Object.keys(value).sort()) {
350
- const child = value[key];
351
- if (child !== undefined) {
352
- normalized[key] = normalizeJsonValue(child);
353
- }
354
- }
355
- return normalized;
475
+ if (typeof value === "object") {
476
+ return normalizeJsonObject(value);
356
477
  }
357
478
  throw new Error("Data source query values must be JSON-compatible.");
358
479
  }
359
- function isPlainRecord(value) {
360
- return (typeof value === "object" &&
361
- value !== null &&
362
- !Array.isArray(value) &&
363
- (Object.getPrototypeOf(value) === Object.prototype ||
364
- Object.getPrototypeOf(value) === null));
480
+ function normalizeJsonNumber(value) {
481
+ if (!Number.isFinite(value)) {
482
+ throw new Error("Data source query values must be finite JSON numbers.");
483
+ }
484
+ return value;
485
+ }
486
+ function normalizeJsonObject(value) {
487
+ const prototype = Object.getPrototypeOf(value);
488
+ if (prototype !== Object.prototype && prototype !== null) {
489
+ throw new Error("Data source query values must be JSON-compatible.");
490
+ }
491
+ const objectValue = value;
492
+ const normalized = {};
493
+ for (const key of Object.keys(objectValue).sort()) {
494
+ const child = objectValue[key];
495
+ if (child !== undefined) {
496
+ normalized[key] = normalizeJsonValue(child);
497
+ }
498
+ }
499
+ return normalized;
365
500
  }
366
501
  function isValidIsoDate(value) {
367
502
  if (typeof value !== "string") {
@@ -43,6 +43,13 @@ export declare const notionPropertyTypeSchema: v.PicklistSchema<readonly ["title
43
43
  * payload shapes.
44
44
  */
45
45
  export type NotionPropertyType = v.InferOutput<typeof notionPropertyTypeSchema>;
46
+ /**
47
+ * Property types currently supported by the custom-block data source sort API.
48
+ * Keep this list narrower than {@link NOTION_PROPERTY_TYPES} when a property
49
+ * type is exposed but its sort value is not yet represented by every host.
50
+ */
51
+ export declare const CUSTOM_BLOCK_DATA_SOURCE_SORTABLE_PROPERTY_TYPES: readonly ["title", "rich_text", "number", "checkbox", "url", "email", "phone_number", "date", "created_time", "last_edited_time"];
52
+ export type CustomBlockDataSourceSortablePropertyType = (typeof CUSTOM_BLOCK_DATA_SOURCE_SORTABLE_PROPERTY_TYPES)[number];
46
53
  /**
47
54
  * Per-property schema as exposed by the host over the custom-block bridge.
48
55
  * The `type` discriminator must be one of {@link NOTION_PROPERTY_TYPES}.
@@ -1 +1 @@
1
- {"version":3,"file":"propertySchema.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/protocol/src/dataSources/propertySchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,iUAqBpC,CAAA;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,WAAW,CAC9C,OAAO,yBAAyB,CAChC,CAAA;AAED,eAAO,MAAM,0BAA0B;;;;;aAKrC,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAC/C,OAAO,0BAA0B,CACjC,CAAA;AAED,eAAO,MAAM,uBAAuB;;;;;aAKlC,CAAA;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAE7E,eAAO,MAAM,wBAAwB;;;aAGnC,CAAA;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAO/E;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,4UA2BxB,CAAA;AAEV,eAAO,MAAM,wBAAwB,yWAAoC,CAAA;AAEzE;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAE/E;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAiDrC,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAC/C,OAAO,0BAA0B,CACjC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,+EAK9B,CAAA;AAEV,MAAM,MAAM,uBAAuB,GAClC,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"propertySchema.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/protocol/src/dataSources/propertySchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,iUAqBpC,CAAA;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,WAAW,CAC9C,OAAO,yBAAyB,CAChC,CAAA;AAED,eAAO,MAAM,0BAA0B;;;;;aAKrC,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAC/C,OAAO,0BAA0B,CACjC,CAAA;AAED,eAAO,MAAM,uBAAuB;;;;;aAKlC,CAAA;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAE7E,eAAO,MAAM,wBAAwB;;;aAGnC,CAAA;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAO/E;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,4UA2BxB,CAAA;AAEV,eAAO,MAAM,wBAAwB,yWAAoC,CAAA;AAEzE;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAE/E;;;;GAIG;AACH,eAAO,MAAM,gDAAgD,mIAWX,CAAA;AAElD,MAAM,MAAM,yCAAyC,GACpD,CAAC,OAAO,gDAAgD,CAAC,CAAC,MAAM,CAAC,CAAA;AAElE;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAiDrC,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAC/C,OAAO,0BAA0B,CACjC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,+EAK9B,CAAA;AAEV,MAAM,MAAM,uBAAuB,GAClC,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAA"}