@notionhq/workers 0.2.0 → 0.4.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.
- package/dist/capabilities/ai_connector.d.ts +97 -0
- package/dist/capabilities/ai_connector.d.ts.map +1 -0
- package/dist/capabilities/ai_connector.js +54 -0
- package/dist/capabilities/ai_connector.test.d.ts +2 -0
- package/dist/capabilities/ai_connector.test.d.ts.map +1 -0
- package/dist/capabilities/automation.d.ts.map +1 -1
- package/dist/capabilities/automation.js +11 -11
- package/dist/capabilities/context.d.ts +2 -1
- package/dist/capabilities/context.d.ts.map +1 -1
- package/dist/capabilities/context.js +21 -3
- package/dist/capabilities/output.d.ts +5 -0
- package/dist/capabilities/output.d.ts.map +1 -0
- package/dist/capabilities/output.js +10 -0
- package/dist/capabilities/stateful-capability.d.ts +30 -0
- package/dist/capabilities/stateful-capability.d.ts.map +1 -0
- package/dist/capabilities/stateful-capability.js +50 -0
- package/dist/capabilities/stateful-capability.test.d.ts +2 -0
- package/dist/capabilities/stateful-capability.test.d.ts.map +1 -0
- package/dist/capabilities/sync.d.ts +9 -19
- package/dist/capabilities/sync.d.ts.map +1 -1
- package/dist/capabilities/sync.js +34 -61
- package/dist/capabilities/tool.d.ts +21 -0
- package/dist/capabilities/tool.d.ts.map +1 -1
- package/dist/capabilities/tool.js +8 -22
- package/dist/capabilities/webhook.d.ts +101 -0
- package/dist/capabilities/webhook.d.ts.map +1 -0
- package/dist/capabilities/webhook.js +47 -0
- package/dist/error.d.ts +36 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +14 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/pacer_internal.d.ts +7 -0
- package/dist/pacer_internal.d.ts.map +1 -1
- package/dist/pacer_internal.js +17 -0
- package/dist/schema.d.ts +28 -11
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/worker.d.ts +92 -9
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +111 -1
- package/package.json +2 -2
- package/src/capabilities/ai_connector.test.ts +341 -0
- package/src/capabilities/ai_connector.ts +194 -0
- package/src/capabilities/automation.ts +11 -7
- package/src/capabilities/context.ts +45 -3
- package/src/capabilities/output.ts +8 -0
- package/src/capabilities/stateful-capability.test.ts +25 -0
- package/src/capabilities/stateful-capability.ts +87 -0
- package/src/capabilities/sync.test.ts +197 -4
- package/src/capabilities/sync.ts +58 -87
- package/src/capabilities/tool.test.ts +63 -0
- package/src/capabilities/tool.ts +28 -13
- package/src/capabilities/webhook.ts +148 -0
- package/src/error.ts +40 -0
- package/src/index.ts +28 -1
- package/src/pacer_internal.ts +34 -0
- package/src/schema.ts +29 -12
- package/src/worker.ts +139 -10
|
@@ -222,6 +222,69 @@ describe("Worker.tool", () => {
|
|
|
222
222
|
expect(capability.config.schema).toBeDefined();
|
|
223
223
|
});
|
|
224
224
|
|
|
225
|
+
describe("hints", () => {
|
|
226
|
+
it("omits hints from config when not specified (existing tools unchanged)", () => {
|
|
227
|
+
const capability = createToolCapability<{ name: string }, string>(
|
|
228
|
+
"noHints",
|
|
229
|
+
{
|
|
230
|
+
title: "No Hints",
|
|
231
|
+
description: "A tool without hints",
|
|
232
|
+
schema: j.object({ name: j.string() }),
|
|
233
|
+
execute: ({ name }) => `Hello, ${name}`,
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
expect(capability.config.hints).toBeUndefined();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("flows readOnlyHint=true through to config", () => {
|
|
241
|
+
const capability = createToolCapability<{ query: string }, string>(
|
|
242
|
+
"readOnly",
|
|
243
|
+
{
|
|
244
|
+
title: "Read Only",
|
|
245
|
+
description: "A read-only tool",
|
|
246
|
+
schema: j.object({ query: j.string() }),
|
|
247
|
+
hints: { readOnlyHint: true },
|
|
248
|
+
execute: ({ query }) => query,
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
expect(capability.config.hints).toEqual({ readOnlyHint: true });
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("flows readOnlyHint=false through to config", () => {
|
|
256
|
+
const capability = createToolCapability<{ pageId: string }, string>(
|
|
257
|
+
"writeTool",
|
|
258
|
+
{
|
|
259
|
+
title: "Write Tool",
|
|
260
|
+
description: "A write tool",
|
|
261
|
+
schema: j.object({ pageId: j.string() }),
|
|
262
|
+
hints: { readOnlyHint: false },
|
|
263
|
+
execute: ({ pageId }) => pageId,
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
expect(capability.config.hints).toEqual({ readOnlyHint: false });
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("does not affect execution behavior", async () => {
|
|
271
|
+
const capability = createToolCapability<{ x: number }, number>(
|
|
272
|
+
"doubleReadOnly",
|
|
273
|
+
{
|
|
274
|
+
title: "Double",
|
|
275
|
+
description: "Doubles a number",
|
|
276
|
+
schema: j.object({ x: j.number() }),
|
|
277
|
+
hints: { readOnlyHint: true },
|
|
278
|
+
execute: ({ x }) => x * 2,
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const result = await capability.handler({ x: 7 });
|
|
283
|
+
|
|
284
|
+
expect(result).toEqual({ _tag: "success", value: 14 });
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
225
288
|
it("invalid output with custom output schema", async () => {
|
|
226
289
|
const capability = createToolCapability<
|
|
227
290
|
{ value: number },
|
package/src/capabilities/tool.ts
CHANGED
|
@@ -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`
|
|
@@ -69,6 +70,26 @@ function validateRequiredProperties(
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Hints describing a tool's behavior. Advisory metadata read by the platform
|
|
75
|
+
* (not the model) to decide whether a call is safe to auto-execute or
|
|
76
|
+
* requires user confirmation.
|
|
77
|
+
*
|
|
78
|
+
* Field names mirror the MCP tool annotation shape
|
|
79
|
+
* (`McpServerToolAnnotations`) so worker tools flow through the same
|
|
80
|
+
* confirmation / auto-execute machinery as MCP tools.
|
|
81
|
+
*
|
|
82
|
+
* Only hints with active consumers in the Notion client are included.
|
|
83
|
+
*/
|
|
84
|
+
export interface ToolHints {
|
|
85
|
+
/**
|
|
86
|
+
* The tool only reads state and has no side effects. Safe to call
|
|
87
|
+
* repeatedly; safe to auto-execute under default policy. Tools without
|
|
88
|
+
* this hint are treated as write tools and prompt for confirmation.
|
|
89
|
+
*/
|
|
90
|
+
readOnlyHint?: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
72
93
|
export interface ToolConfiguration<
|
|
73
94
|
I extends JSONValue,
|
|
74
95
|
O extends JSONValue = JSONValue,
|
|
@@ -77,6 +98,7 @@ export interface ToolConfiguration<
|
|
|
77
98
|
description: string;
|
|
78
99
|
schema: SchemaBuilder<I>;
|
|
79
100
|
outputSchema?: SchemaBuilder<O>;
|
|
101
|
+
hints?: ToolHints;
|
|
80
102
|
execute: (input: I, context: CapabilityContext) => O | Promise<O>;
|
|
81
103
|
}
|
|
82
104
|
|
|
@@ -211,15 +233,13 @@ export function createToolCapability<
|
|
|
211
233
|
error: { name: error.name, message: error.message, trace: error.stack },
|
|
212
234
|
};
|
|
213
235
|
|
|
214
|
-
|
|
215
|
-
`\n<__notion_output__>${JSON.stringify(result)}</__notion_output__>\n`,
|
|
216
|
-
);
|
|
236
|
+
writeOutput(result);
|
|
217
237
|
|
|
218
238
|
return result;
|
|
219
239
|
}
|
|
220
240
|
|
|
221
241
|
try {
|
|
222
|
-
const capabilityContext = createCapabilityContext();
|
|
242
|
+
const capabilityContext = createCapabilityContext("tool");
|
|
223
243
|
const result = await config.execute(input, capabilityContext);
|
|
224
244
|
if (validateOutput && !validateOutput(result)) {
|
|
225
245
|
const error = new InvalidToolOutputError(
|
|
@@ -239,9 +259,7 @@ export function createToolCapability<
|
|
|
239
259
|
},
|
|
240
260
|
};
|
|
241
261
|
|
|
242
|
-
|
|
243
|
-
`\n<__notion_output__>${JSON.stringify(result)}</__notion_output__>\n`,
|
|
244
|
-
);
|
|
262
|
+
writeOutput(result);
|
|
245
263
|
|
|
246
264
|
return result;
|
|
247
265
|
}
|
|
@@ -250,9 +268,7 @@ export function createToolCapability<
|
|
|
250
268
|
return result;
|
|
251
269
|
}
|
|
252
270
|
|
|
253
|
-
|
|
254
|
-
`\n<__notion_output__>${JSON.stringify({ _tag: "success", value: result })}</__notion_output__>\n`,
|
|
255
|
-
);
|
|
271
|
+
writeOutput({ _tag: "success", value: result });
|
|
256
272
|
|
|
257
273
|
return {
|
|
258
274
|
_tag: "success" as const,
|
|
@@ -272,9 +288,7 @@ export function createToolCapability<
|
|
|
272
288
|
error: { name: error.name, message: error.message, trace: error.stack },
|
|
273
289
|
};
|
|
274
290
|
|
|
275
|
-
|
|
276
|
-
`\n<__notion_output__>${JSON.stringify(result)}</__notion_output__>\n`,
|
|
277
|
-
);
|
|
291
|
+
writeOutput(result);
|
|
278
292
|
|
|
279
293
|
return result;
|
|
280
294
|
}
|
|
@@ -288,6 +302,7 @@ export function createToolCapability<
|
|
|
288
302
|
description: config.description,
|
|
289
303
|
schema: inputSchema,
|
|
290
304
|
outputSchema: outputSchema,
|
|
305
|
+
hints: config.hints,
|
|
291
306
|
},
|
|
292
307
|
handler,
|
|
293
308
|
};
|
|
@@ -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("webhook");
|
|
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/error.ts
CHANGED
|
@@ -11,6 +11,46 @@ export class ExecutionError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Throw this error from your sync execute handler to signal that the
|
|
16
|
+
* external API returned a rate-limit response (e.g. HTTP 429).
|
|
17
|
+
*
|
|
18
|
+
* When the platform receives this error it applies exponential backoff
|
|
19
|
+
* instead of retrying immediately. If `retryAfter` is provided, the
|
|
20
|
+
* platform will wait at least that many seconds before the next attempt.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* worker.sync("issues", {
|
|
25
|
+
* database: issuesDb,
|
|
26
|
+
* execute: async (state, ctx) => {
|
|
27
|
+
* const res = await fetch("https://api.example.com/issues");
|
|
28
|
+
* if (res.status === 429) {
|
|
29
|
+
* const retryAfter = Number(res.headers.get("Retry-After"));
|
|
30
|
+
* throw new RateLimitError({
|
|
31
|
+
* retryAfter: Number.isFinite(retryAfter) ? retryAfter : undefined,
|
|
32
|
+
* });
|
|
33
|
+
* }
|
|
34
|
+
* // ...
|
|
35
|
+
* },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class RateLimitError extends ExecutionError {
|
|
40
|
+
/**
|
|
41
|
+
* Optional number of seconds the external API asked us to wait
|
|
42
|
+
* before retrying. The platform takes the max of this value and its
|
|
43
|
+
* own exponential-backoff delay.
|
|
44
|
+
*/
|
|
45
|
+
readonly retryAfter: number | undefined;
|
|
46
|
+
|
|
47
|
+
constructor(options?: { retryAfter?: number }) {
|
|
48
|
+
super("Rate limited by external API");
|
|
49
|
+
this.name = "RateLimitError";
|
|
50
|
+
this.retryAfter = options?.retryAfter;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
14
54
|
/**
|
|
15
55
|
* Helper for exhaustive switch statements. TypeScript will error if a case is not handled.
|
|
16
56
|
*/
|
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,
|
|
@@ -21,7 +37,18 @@ export type {
|
|
|
21
37
|
SyncExecutionResult,
|
|
22
38
|
SyncMode,
|
|
23
39
|
} from "./capabilities/sync.js";
|
|
24
|
-
export type {
|
|
40
|
+
export type {
|
|
41
|
+
ToolCapability,
|
|
42
|
+
ToolConfiguration,
|
|
43
|
+
ToolHints,
|
|
44
|
+
} from "./capabilities/tool.js";
|
|
45
|
+
export type {
|
|
46
|
+
WebhookCapability,
|
|
47
|
+
WebhookConfiguration,
|
|
48
|
+
WebhookEvent,
|
|
49
|
+
} from "./capabilities/webhook.js";
|
|
50
|
+
export { WebhookVerificationError } from "./capabilities/webhook.js";
|
|
51
|
+
export { RateLimitError } from "./error.js";
|
|
25
52
|
export type { AnyJSONSchema, JSONSchema } from "./json-schema.js";
|
|
26
53
|
export type { Infer, SchemaBuilder } from "./schema-builder.js";
|
|
27
54
|
export { getSchema, j } from "./schema-builder.js";
|
package/src/pacer_internal.ts
CHANGED
|
@@ -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
|
|
72
|
-
* This must match the
|
|
71
|
+
* The key of the related database declaration.
|
|
72
|
+
* This must match the key passed to `worker.database()`.
|
|
73
73
|
*/
|
|
74
|
-
|
|
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
|
|
207
|
-
* The related database must be
|
|
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
|
|
209
|
+
* @param relatedDatabaseKey - The key of the related database declaration.
|
|
210
210
|
* @example
|
|
211
211
|
* ```typescript
|
|
212
|
-
*
|
|
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
|
-
*
|
|
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("
|
|
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
|
-
|
|
243
|
+
relatedDatabaseKey: string,
|
|
227
244
|
config?: { twoWay: false } | { twoWay: true; relatedPropertyName: string },
|
|
228
245
|
): PropertyConfiguration {
|
|
229
246
|
return {
|
|
230
247
|
type: "relation",
|
|
231
|
-
|
|
248
|
+
relatedDatabaseKey,
|
|
232
249
|
config: config ?? { twoWay: false },
|
|
233
250
|
};
|
|
234
251
|
}
|