@notionhq/workers 0.2.0 → 0.3.0

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.
Files changed (50) hide show
  1. package/dist/capabilities/ai_connector.d.ts +97 -0
  2. package/dist/capabilities/ai_connector.d.ts.map +1 -0
  3. package/dist/capabilities/ai_connector.js +54 -0
  4. package/dist/capabilities/ai_connector.test.d.ts +2 -0
  5. package/dist/capabilities/ai_connector.test.d.ts.map +1 -0
  6. package/dist/capabilities/automation.d.ts.map +1 -1
  7. package/dist/capabilities/automation.js +10 -10
  8. package/dist/capabilities/output.d.ts +5 -0
  9. package/dist/capabilities/output.d.ts.map +1 -0
  10. package/dist/capabilities/output.js +10 -0
  11. package/dist/capabilities/stateful-capability.d.ts +30 -0
  12. package/dist/capabilities/stateful-capability.d.ts.map +1 -0
  13. package/dist/capabilities/stateful-capability.js +50 -0
  14. package/dist/capabilities/stateful-capability.test.d.ts +2 -0
  15. package/dist/capabilities/stateful-capability.test.d.ts.map +1 -0
  16. package/dist/capabilities/sync.d.ts +9 -19
  17. package/dist/capabilities/sync.d.ts.map +1 -1
  18. package/dist/capabilities/sync.js +13 -60
  19. package/dist/capabilities/tool.d.ts.map +1 -1
  20. package/dist/capabilities/tool.js +5 -20
  21. package/dist/capabilities/webhook.d.ts +101 -0
  22. package/dist/capabilities/webhook.d.ts.map +1 -0
  23. package/dist/capabilities/webhook.js +47 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -0
  27. package/dist/pacer_internal.d.ts +7 -0
  28. package/dist/pacer_internal.d.ts.map +1 -1
  29. package/dist/pacer_internal.js +17 -0
  30. package/dist/schema.d.ts +28 -11
  31. package/dist/schema.d.ts.map +1 -1
  32. package/dist/schema.js +2 -2
  33. package/dist/worker.d.ts +91 -9
  34. package/dist/worker.d.ts.map +1 -1
  35. package/dist/worker.js +111 -1
  36. package/package.json +1 -1
  37. package/src/capabilities/ai_connector.test.ts +341 -0
  38. package/src/capabilities/ai_connector.ts +194 -0
  39. package/src/capabilities/automation.ts +10 -6
  40. package/src/capabilities/output.ts +8 -0
  41. package/src/capabilities/stateful-capability.test.ts +25 -0
  42. package/src/capabilities/stateful-capability.ts +87 -0
  43. package/src/capabilities/sync.test.ts +89 -3
  44. package/src/capabilities/sync.ts +22 -86
  45. package/src/capabilities/tool.ts +5 -12
  46. package/src/capabilities/webhook.ts +148 -0
  47. package/src/index.ts +22 -0
  48. package/src/pacer_internal.ts +34 -0
  49. package/src/schema.ts +29 -12
  50. package/src/worker.ts +137 -10
@@ -9,6 +9,7 @@ import {
9
9
  } from "vitest";
10
10
  import * as Builder from "../builder.js";
11
11
  import { ExecutionError } from "../error.js";
12
+ import { getPacerState } from "../pacer_internal.js";
12
13
  import * as Schema from "../schema.js";
13
14
  import { Worker } from "../worker.js";
14
15
  import { createSyncCapability } from "./sync.js";
@@ -28,11 +29,11 @@ describe("Worker.sync", () => {
28
29
 
29
30
  describe("Schema.relation", () => {
30
31
  it("creates a relation property configuration", () => {
31
- const relationProp = Schema.relation("projectsSync");
32
+ const relationProp = Schema.relation("projects");
32
33
 
33
34
  expect(relationProp).toEqual({
34
35
  type: "relation",
35
- relatedSyncKey: "projectsSync",
36
+ relatedDatabaseKey: "projects",
36
37
  config: { twoWay: false },
37
38
  });
38
39
  });
@@ -68,7 +69,7 @@ describe("Worker.sync", () => {
68
69
  schema: {
69
70
  properties: {
70
71
  "Task ID": Schema.title(),
71
- Project: Schema.relation("projectsSync"),
72
+ Project: Schema.relation("projects"),
72
73
  },
73
74
  },
74
75
  });
@@ -106,6 +107,91 @@ describe("Worker.sync", () => {
106
107
  });
107
108
  });
108
109
 
110
+ describe("pacer local fallback", () => {
111
+ it("seeds pacer state from declarations when runtime context has no pacers", async () => {
112
+ const worker = new Worker();
113
+ const db = worker.database("test", {
114
+ type: "managed",
115
+ initialTitle: "Test",
116
+ primaryKeyProperty: "ID",
117
+ schema: { properties: { ID: Schema.title() } },
118
+ });
119
+ const pacer = worker.pacer("myApi", {
120
+ allowedRequests: 10,
121
+ intervalMs: 1000,
122
+ });
123
+
124
+ let pacerWaitCalled = false;
125
+ const capability = createSyncCapability(
126
+ "testSync",
127
+ {
128
+ database: db,
129
+ execute: async () => {
130
+ await pacer.wait();
131
+ pacerWaitCalled = true;
132
+ return { changes: [], hasMore: false };
133
+ },
134
+ },
135
+ [{ key: "myApi", config: { allowedRequests: 10, intervalMs: 1000 } }],
136
+ );
137
+
138
+ // No runtimeContext → no pacers from server → should fall back to declarations
139
+ await capability.handler(undefined, { concreteOutput: true });
140
+
141
+ expect(pacerWaitCalled).toBe(true);
142
+ const state = getPacerState();
143
+ expect(state.pacers.myApi).toBeDefined();
144
+ expect(state.pacers.myApi?.allowedRequests).toBe(10);
145
+ expect(state.pacers.myApi?.intervalMs).toBe(1000);
146
+ });
147
+
148
+ it("uses server-provided pacers when available", async () => {
149
+ const worker = new Worker();
150
+ const db = worker.database("test", {
151
+ type: "managed",
152
+ initialTitle: "Test",
153
+ primaryKeyProperty: "ID",
154
+ schema: { properties: { ID: Schema.title() } },
155
+ });
156
+ const pacer = worker.pacer("myApi", {
157
+ allowedRequests: 10,
158
+ intervalMs: 1000,
159
+ });
160
+
161
+ let pacerWaitCalled = false;
162
+ const capability = createSyncCapability(
163
+ "testSync",
164
+ {
165
+ database: db,
166
+ execute: async () => {
167
+ await pacer.wait();
168
+ pacerWaitCalled = true;
169
+ return { changes: [], hasMore: false };
170
+ },
171
+ },
172
+ [{ key: "myApi", config: { allowedRequests: 10, intervalMs: 1000 } }],
173
+ );
174
+
175
+ // Server provides apportioned budget (e.g., 5 instead of 10)
176
+ await capability.handler(
177
+ {
178
+ pacers: {
179
+ myApi: {
180
+ lastScheduledAtMs: 0,
181
+ allowedRequests: 5,
182
+ intervalMs: 1000,
183
+ },
184
+ },
185
+ },
186
+ { concreteOutput: true },
187
+ );
188
+
189
+ expect(pacerWaitCalled).toBe(true);
190
+ // Should use server-provided value, not the declaration
191
+ expect(getPacerState().pacers.myApi?.allowedRequests).toBe(5);
192
+ });
193
+ });
194
+
109
195
  describe("handler output envelope", () => {
110
196
  it("wraps successful result in success envelope", async () => {
111
197
  const worker = new Worker();
@@ -1,8 +1,8 @@
1
- import { ExecutionError, unreachable } from "../error.js";
1
+ import { ExecutionError } from "../error.js";
2
2
  import {
3
3
  getPacerState,
4
- type PacerEntry,
5
- setPacerState,
4
+ initPacerState,
5
+ type PacerDeclaration,
6
6
  } from "../pacer_internal.js";
7
7
  import type {
8
8
  PropertyConfiguration,
@@ -16,13 +16,17 @@ import type {
16
16
  PlaceValue,
17
17
  RelationValue,
18
18
  Schedule,
19
- SyncSchedule,
20
19
  TextValue,
21
- TimeUnit,
22
20
  } from "../types.js";
23
21
  import type { DatabaseHandle } from "../worker.js";
24
22
  import type { CapabilityContext } from "./context.js";
25
23
  import { createCapabilityContext } from "./context.js";
24
+ import { writeOutput } from "./output.js";
25
+ import {
26
+ parseSchedule,
27
+ type StatefulCapabilityMode,
28
+ type RuntimeContext as StatefulRuntimeContext,
29
+ } from "./stateful-capability.js";
26
30
 
27
31
  /**
28
32
  * Maps a property configuration to its corresponding value type.
@@ -40,7 +44,7 @@ type PropertyValueType<T extends PropertyConfiguration> = T extends {
40
44
  /**
41
45
  * Sync mode determines how the sync handles data lifecycle.
42
46
  */
43
- export type SyncMode = "replace" | "incremental";
47
+ export type SyncMode = StatefulCapabilityMode;
44
48
 
45
49
  /**
46
50
  * A change representing a record to be created or updated.
@@ -204,18 +208,6 @@ export type SyncConfiguration<
204
208
 
205
209
  export type SyncCapability = ReturnType<typeof createSyncCapability>;
206
210
 
207
- /**
208
- * Runtime context object passed from the runtime to sync capability handlers.
209
- */
210
- type RuntimeContext<UserContext = unknown> = {
211
- /** The user-defined/-controlled state (cursor, pagination state, etc.) */
212
- state?: UserContext;
213
- /** Legacy field for user-defined/-controlled state. */
214
- userContext?: UserContext;
215
- /** Pacer state from the server for rate limiting. */
216
- pacers?: Record<string, PacerEntry>;
217
- };
218
-
219
211
  /**
220
212
  * Creates a special handler for syncing third-party data to a database.
221
213
  *
@@ -227,7 +219,11 @@ export function createSyncCapability<
227
219
  PK extends string,
228
220
  S extends Schema<PK>,
229
221
  Context = unknown,
230
- >(key: string, syncConfiguration: SyncConfiguration<PK, S, Context>) {
222
+ >(
223
+ key: string,
224
+ syncConfiguration: SyncConfiguration<PK, S, Context>,
225
+ pacerDeclarations?: readonly PacerDeclaration[],
226
+ ) {
231
227
  return {
232
228
  _tag: "sync" as const,
233
229
  key,
@@ -240,14 +236,13 @@ export function createSyncCapability<
240
236
  : undefined,
241
237
  },
242
238
  async handler(
243
- runtimeContext?: RuntimeContext<Context>,
239
+ runtimeContext?: StatefulRuntimeContext<Context>,
244
240
  options?: HandlerOptions,
245
241
  ) {
246
242
  const capabilityContext = createCapabilityContext();
247
243
  const state = runtimeContext?.state ?? runtimeContext?.userContext;
248
244
 
249
- // Initialize pacer state from runtime context
250
- setPacerState({ pacers: runtimeContext?.pacers ?? {} });
245
+ initPacerState(runtimeContext?.pacers, pacerDeclarations);
251
246
 
252
247
  let executionResult: SyncExecutionResult<PK, Context>;
253
248
  try {
@@ -258,9 +253,10 @@ export function createSyncCapability<
258
253
  } catch (err) {
259
254
  const error = new ExecutionError(err);
260
255
  if (!options?.concreteOutput) {
261
- process.stdout.write(
262
- `\n<__notion_output__>${JSON.stringify({ _tag: "error", error: { name: error.name, message: error.message } })}</__notion_output__>\n`,
263
- );
256
+ writeOutput({
257
+ _tag: "error",
258
+ error: { name: error.name, message: error.message },
259
+ });
264
260
  }
265
261
  throw error;
266
262
  }
@@ -281,70 +277,10 @@ export function createSyncCapability<
281
277
  if (options?.concreteOutput) {
282
278
  return result;
283
279
  } else {
284
- process.stdout.write(
285
- `\n<__notion_output__>${JSON.stringify({ _tag: "success", value: result })}</__notion_output__>\n`,
286
- );
280
+ writeOutput({ _tag: "success", value: result });
287
281
  }
288
282
 
289
283
  return result;
290
284
  },
291
285
  };
292
286
  }
293
-
294
- const MS_PER_MINUTE = 60 * 1000;
295
- const MS_PER_HOUR = 60 * MS_PER_MINUTE;
296
- const MS_PER_DAY = 24 * MS_PER_HOUR;
297
-
298
- const MIN_INTERVAL_MS = MS_PER_MINUTE; // 1m
299
- const MAX_INTERVAL_MS = 7 * MS_PER_DAY; // 7d
300
-
301
- /**
302
- * Parses a user-friendly schedule string into the normalized backend format.
303
- */
304
- function parseSchedule(schedule: Schedule): SyncSchedule {
305
- if (schedule === "continuous") {
306
- return { type: "continuous" };
307
- }
308
-
309
- if (schedule === "manual") {
310
- return { type: "manual" };
311
- }
312
-
313
- const match = schedule.match(/^(\d+)(m|h|d)$/);
314
- if (!match || !match[1] || !match[2]) {
315
- throw new Error(
316
- `Invalid schedule format: "${schedule}". Use "continuous" or an interval like "30m", "1h", "1d".`,
317
- );
318
- }
319
-
320
- const value = parseInt(match[1], 10);
321
- const unit = match[2] as TimeUnit;
322
-
323
- let intervalMs: number;
324
- switch (unit) {
325
- case "m":
326
- intervalMs = value * MS_PER_MINUTE;
327
- break;
328
- case "h":
329
- intervalMs = value * MS_PER_HOUR;
330
- break;
331
- case "d":
332
- intervalMs = value * MS_PER_DAY;
333
- break;
334
- default:
335
- unreachable(unit);
336
- }
337
-
338
- if (intervalMs < MIN_INTERVAL_MS) {
339
- throw new Error(
340
- `Schedule interval must be at least 1 minute. Got: "${schedule}"`,
341
- );
342
- }
343
- if (intervalMs > MAX_INTERVAL_MS) {
344
- throw new Error(
345
- `Schedule interval must be at most 7 days. Got: "${schedule}"`,
346
- );
347
- }
348
-
349
- return { type: "interval", intervalMs };
350
- }
@@ -6,6 +6,7 @@ import { getSchema } from "../schema-builder.js";
6
6
  import type { HandlerOptions, JSONValue } from "../types.js";
7
7
  import type { CapabilityContext } from "./context.js";
8
8
  import { createCapabilityContext } from "./context.js";
9
+ import { writeOutput } from "./output.js";
9
10
 
10
11
  /**
11
12
  * Validates that every object schema in the tree has all its `properties`
@@ -211,9 +212,7 @@ export function createToolCapability<
211
212
  error: { name: error.name, message: error.message, trace: error.stack },
212
213
  };
213
214
 
214
- process.stdout.write(
215
- `\n<__notion_output__>${JSON.stringify(result)}</__notion_output__>\n`,
216
- );
215
+ writeOutput(result);
217
216
 
218
217
  return result;
219
218
  }
@@ -239,9 +238,7 @@ export function createToolCapability<
239
238
  },
240
239
  };
241
240
 
242
- process.stdout.write(
243
- `\n<__notion_output__>${JSON.stringify(result)}</__notion_output__>\n`,
244
- );
241
+ writeOutput(result);
245
242
 
246
243
  return result;
247
244
  }
@@ -250,9 +247,7 @@ export function createToolCapability<
250
247
  return result;
251
248
  }
252
249
 
253
- process.stdout.write(
254
- `\n<__notion_output__>${JSON.stringify({ _tag: "success", value: result })}</__notion_output__>\n`,
255
- );
250
+ writeOutput({ _tag: "success", value: result });
256
251
 
257
252
  return {
258
253
  _tag: "success" as const,
@@ -272,9 +267,7 @@ export function createToolCapability<
272
267
  error: { name: error.name, message: error.message, trace: error.stack },
273
268
  };
274
269
 
275
- process.stdout.write(
276
- `\n<__notion_output__>${JSON.stringify(result)}</__notion_output__>\n`,
277
- );
270
+ writeOutput(result);
278
271
 
279
272
  return result;
280
273
  }
@@ -0,0 +1,148 @@
1
+ import { ExecutionError } from "../error.js";
2
+ import type { HandlerOptions } from "../types.js";
3
+ import type { CapabilityContext } from "./context.js";
4
+ import { createCapabilityContext } from "./context.js";
5
+ import { writeOutput } from "./output.js";
6
+
7
+ /**
8
+ * A single incoming webhook request.
9
+ */
10
+ export interface WebhookEvent {
11
+ /**
12
+ * Unique ID for this webhook delivery, stable across retries.
13
+ * Best-effort idempotency key — prefer using the webhook provider's
14
+ * own delivery/event ID when available, since providers may redeliver
15
+ * the same event with a new deliveryId.
16
+ */
17
+ deliveryId: string;
18
+ /**
19
+ * The parsed JSON body of the incoming request, or an empty object if
20
+ * the body is not valid JSON.
21
+ */
22
+ body: Record<string, unknown>;
23
+ /**
24
+ * The raw request body as a string, useful for signature verification.
25
+ */
26
+ rawBody: string;
27
+ /**
28
+ * The HTTP headers from the incoming request
29
+ */
30
+ headers: Record<string, string>;
31
+ /**
32
+ * The HTTP method of the incoming request (e.g. "POST")
33
+ */
34
+ method: string;
35
+ }
36
+
37
+ /**
38
+ * Throw this error from your webhook execute handler to signal that
39
+ * signature verification failed. After 5 consecutive verification
40
+ * failures, the platform will short-circuit and reject all incoming
41
+ * requests for this webhook without executing the handler.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * worker.webhook("onGithubPush", {
46
+ * title: "GitHub Push",
47
+ * description: "Handles GitHub push events",
48
+ * execute: async (events) => {
49
+ * const secret = process.env.GITHUB_WEBHOOK_SECRET;
50
+ * if (!secret) {
51
+ * throw new WebhookVerificationError("GITHUB_WEBHOOK_SECRET not set");
52
+ * }
53
+ * for (const event of events) {
54
+ * if (!verifyGitHubSignature(event.rawBody, event.headers, secret)) {
55
+ * throw new WebhookVerificationError("Invalid GitHub signature");
56
+ * }
57
+ * // ... handle verified event
58
+ * }
59
+ * },
60
+ * });
61
+ * ```
62
+ */
63
+ export class WebhookVerificationError extends ExecutionError {
64
+ constructor(cause?: unknown) {
65
+ super(cause ?? "Webhook signature verification failed");
66
+ this.name = "WebhookVerificationError";
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Configuration for a webhook capability
72
+ */
73
+ export interface WebhookConfiguration {
74
+ /**
75
+ * Title of the webhook - shown in the UI when viewing webhooks
76
+ */
77
+ title: string;
78
+
79
+ /**
80
+ * Description of what this webhook does - shown in the UI
81
+ */
82
+ description: string;
83
+
84
+ /**
85
+ * The function that executes when the webhook is triggered.
86
+ * Receives an array of events (currently always a single event,
87
+ * but may be batched in the future).
88
+ *
89
+ * To signal a verification failure, throw a `WebhookVerificationError`.
90
+ * After 5 consecutive verification failures, the platform will
91
+ * short-circuit and stop executing the handler.
92
+ *
93
+ * @param events - The incoming webhook events
94
+ * @param context - The capability execution context (Notion client, etc.)
95
+ * @returns A promise that resolves when the webhook processing completes
96
+ */
97
+ execute: (
98
+ events: WebhookEvent[],
99
+ context: CapabilityContext,
100
+ ) => Promise<void> | void;
101
+ }
102
+
103
+ export type WebhookCapability = ReturnType<typeof createWebhookCapability>;
104
+
105
+ export function createWebhookCapability(
106
+ key: string,
107
+ config: WebhookConfiguration,
108
+ ) {
109
+ return {
110
+ _tag: "webhook" as const,
111
+ key,
112
+ config: {
113
+ title: config.title,
114
+ description: config.description,
115
+ },
116
+ async handler(
117
+ events: WebhookEvent[],
118
+ options?: HandlerOptions,
119
+ ): Promise<{ status: "success" } | undefined> {
120
+ try {
121
+ const capabilityContext = createCapabilityContext();
122
+ await config.execute(events, capabilityContext);
123
+
124
+ if (options?.concreteOutput) {
125
+ return { status: "success" };
126
+ } else {
127
+ writeOutput({ _tag: "success", value: { status: "success" } });
128
+ }
129
+ } catch (err) {
130
+ const error =
131
+ err instanceof ExecutionError ? err : new ExecutionError(err);
132
+
133
+ if (!options?.concreteOutput) {
134
+ writeOutput({
135
+ _tag: "error",
136
+ error: {
137
+ name: error.name,
138
+ message: error.message,
139
+ trace: error.stack,
140
+ },
141
+ });
142
+ }
143
+
144
+ throw error;
145
+ }
146
+ },
147
+ };
148
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,20 @@
1
1
  export { emojiIcon, imageIcon, notionIcon, place } from "./builder.js";
2
+ export type {
3
+ AiConnectorArchetype,
4
+ AiConnectorCapability,
5
+ AiConnectorChange,
6
+ AiConnectorChangeUpsert,
7
+ AiConnectorChatAuthor,
8
+ AiConnectorChatChannel,
9
+ AiConnectorChatMessage,
10
+ AiConnectorChatRecord,
11
+ AiConnectorConfiguration,
12
+ AiConnectorExecutionResult,
13
+ AiConnectorMode,
14
+ AiConnectorProjectBlock,
15
+ AiConnectorProjectRecord,
16
+ AiConnectorRecordByArchetype,
17
+ } from "./capabilities/ai_connector.js";
2
18
  export type {
3
19
  AutomationCapability,
4
20
  AutomationConfiguration,
@@ -22,6 +38,12 @@ export type {
22
38
  SyncMode,
23
39
  } from "./capabilities/sync.js";
24
40
  export type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
41
+ export type {
42
+ WebhookCapability,
43
+ WebhookConfiguration,
44
+ WebhookEvent,
45
+ } from "./capabilities/webhook.js";
46
+ export { WebhookVerificationError } from "./capabilities/webhook.js";
25
47
  export type { AnyJSONSchema, JSONSchema } from "./json-schema.js";
26
48
  export type { Infer, SchemaBuilder } from "./schema-builder.js";
27
49
  export { getSchema, j } from "./schema-builder.js";
@@ -14,6 +14,11 @@ export type PacerState = {
14
14
  pacers: Record<string, PacerEntry>;
15
15
  };
16
16
 
17
+ export type PacerDeclaration = {
18
+ readonly key: string;
19
+ readonly config: { allowedRequests: number; intervalMs: number };
20
+ };
21
+
17
22
  let pacerState: PacerState = { pacers: {} };
18
23
 
19
24
  export function setPacerState(state: PacerState): void {
@@ -24,6 +29,35 @@ export function getPacerState(): PacerState {
24
29
  return pacerState;
25
30
  }
26
31
 
32
+ /**
33
+ * Initialize pacer state from runtime context, falling back to the worker's
34
+ * own declarations for local execution.
35
+ * @internal
36
+ */
37
+ export function initPacerState(
38
+ runtimePacers: Record<string, PacerEntry> | undefined,
39
+ declarations?: readonly PacerDeclaration[],
40
+ ): void {
41
+ const pacers = runtimePacers ?? {};
42
+ if (
43
+ Object.keys(pacers).length === 0 &&
44
+ declarations &&
45
+ declarations.length > 0
46
+ ) {
47
+ const localPacers: Record<string, PacerEntry> = {};
48
+ for (const decl of declarations) {
49
+ localPacers[decl.key] = {
50
+ lastScheduledAtMs: 0,
51
+ allowedRequests: decl.config.allowedRequests,
52
+ intervalMs: decl.config.intervalMs,
53
+ };
54
+ }
55
+ setPacerState({ pacers: localPacers });
56
+ } else {
57
+ setPacerState({ pacers });
58
+ }
59
+ }
60
+
27
61
  /**
28
62
  * Core wait logic for pacing API requests.
29
63
  * Sleeps if needed to stay under the rate limit.
package/src/schema.ts CHANGED
@@ -68,10 +68,10 @@ export type PropertyConfiguration =
68
68
  | {
69
69
  type: "relation";
70
70
  /**
71
- * The export name of the sync capability that defines the related database.
72
- * This must match the export name used when defining the related sync capability.
71
+ * The key of the related database declaration.
72
+ * This must match the key passed to `worker.database()`.
73
73
  */
74
- relatedSyncKey: string;
74
+ relatedDatabaseKey: string;
75
75
  config: { twoWay: false } | { twoWay: true; relatedPropertyName: string };
76
76
  };
77
77
 
@@ -203,32 +203,49 @@ export function place(): PropertyConfiguration {
203
203
  }
204
204
 
205
205
  /**
206
- * Creates a relation property definition that references another sync capability.
207
- * The related database must be defined by a sync capability in the same worker.
206
+ * Creates a relation property definition that references another database.
207
+ * The related database must be declared in the same worker.
208
208
  *
209
- * @param relatedSyncKey - The export name of the sync capability that defines the related database.
209
+ * @param relatedDatabaseKey - The key of the related database declaration.
210
210
  * @example
211
211
  * ```typescript
212
- * export const projectsSync = sync({...});
212
+ * const projects = worker.database("projects", {
213
+ * type: "managed",
214
+ * initialTitle: "Projects",
215
+ * primaryKeyProperty: "Project ID",
216
+ * schema: {
217
+ * properties: {
218
+ * "Project Name": Schema.title(),
219
+ * "Project ID": Schema.richText(),
220
+ * },
221
+ * },
222
+ * });
213
223
  *
214
- * export const tasksSync = sync({
224
+ * const tasks = worker.database("tasks", {
225
+ * type: "managed",
226
+ * initialTitle: "Tasks",
227
+ * primaryKeyProperty: "Task ID",
215
228
  * schema: {
216
229
  * properties: {
217
230
  * "Task Title": Schema.title(),
218
- * "Project": Schema.relation("projectsSync"),
231
+ * "Project": Schema.relation("projects"),
219
232
  * },
220
233
  * },
221
- * ...
234
+ * });
235
+ *
236
+ * export const tasksSync = worker.sync("tasksSync", {
237
+ * database: tasks,
238
+ * execute: async () => ({ changes: [], hasMore: false }),
222
239
  * });
223
240
  * ```
224
241
  */
225
242
  export function relation(
226
- relatedSyncKey: string,
243
+ relatedDatabaseKey: string,
227
244
  config?: { twoWay: false } | { twoWay: true; relatedPropertyName: string },
228
245
  ): PropertyConfiguration {
229
246
  return {
230
247
  type: "relation",
231
- relatedSyncKey,
248
+ relatedDatabaseKey,
232
249
  config: config ?? { twoWay: false },
233
250
  };
234
251
  }