@notionhq/workers 0.7.0 → 0.8.1
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/alpha/workflow.d.ts +30 -2
- package/dist/alpha/workflow.d.ts.map +1 -1
- package/dist/alpha/workflow.js +51 -1
- package/dist/capabilities/custom-block.d.ts +124 -0
- package/dist/capabilities/custom-block.d.ts.map +1 -0
- package/dist/capabilities/custom-block.js +22 -0
- package/dist/credential.d.ts +26 -0
- package/dist/credential.d.ts.map +1 -0
- package/dist/credential.js +176 -0
- package/dist/credential.test.d.ts +2 -0
- package/dist/credential.test.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/worker.d.ts +48 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +78 -1
- package/package.json +1 -1
- package/src/alpha/workflow.test.ts +124 -2
- package/src/alpha/workflow.ts +92 -3
- package/src/capabilities/custom-block.ts +181 -0
- package/src/credential.test.ts +324 -0
- package/src/credential.ts +238 -0
- package/src/index.ts +15 -0
- package/src/worker.test.ts +300 -2
- package/src/worker.ts +102 -2
- package/dist/block.d.ts +0 -321
- package/dist/block.d.ts.map +0 -1
- package/dist/block.js +0 -0
- package/src/block.ts +0 -525
package/src/worker.test.ts
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
|
+
import { CREDENTIAL_VALUE } from "./credential.js";
|
|
3
4
|
import * as Schema from "./schema.js";
|
|
4
5
|
import { Worker } from "./worker.js";
|
|
5
6
|
|
|
6
7
|
describe("Worker", () => {
|
|
7
|
-
it("includes sdkVersion, databases, and capabilities in manifest", () => {
|
|
8
|
+
it("includes sdkVersion, credentials, databases, and capabilities in manifest", () => {
|
|
8
9
|
const worker = new Worker();
|
|
10
|
+
worker.credential("LINEAR_API_TOKEN", {
|
|
11
|
+
network: [
|
|
12
|
+
{
|
|
13
|
+
domain: "api.linear.com",
|
|
14
|
+
transform: [
|
|
15
|
+
{
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `Bearer ${CREDENTIAL_VALUE}`,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
});
|
|
9
24
|
const tasks = worker.database("tasks", {
|
|
10
25
|
type: "managed",
|
|
11
26
|
initialTitle: "Tasks",
|
|
@@ -26,6 +41,23 @@ describe("Worker", () => {
|
|
|
26
41
|
});
|
|
27
42
|
|
|
28
43
|
expect(worker.manifest.sdkVersion).toMatch(/^\d+\.\d+\.\d+/);
|
|
44
|
+
expect(worker.manifest.credentials).toEqual([
|
|
45
|
+
{
|
|
46
|
+
key: "LINEAR_API_TOKEN",
|
|
47
|
+
network: [
|
|
48
|
+
{
|
|
49
|
+
domain: "api.linear.com",
|
|
50
|
+
transform: [
|
|
51
|
+
{
|
|
52
|
+
headers: {
|
|
53
|
+
Authorization: "Bearer {{credential}}",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
29
61
|
expect(worker.manifest.databases).toEqual([
|
|
30
62
|
{
|
|
31
63
|
key: "tasks",
|
|
@@ -141,7 +173,154 @@ describe("Worker", () => {
|
|
|
141
173
|
]);
|
|
142
174
|
});
|
|
143
175
|
|
|
144
|
-
it("
|
|
176
|
+
it("registers a project custom block from a folder path (type defaults to project)", () => {
|
|
177
|
+
const worker = new Worker();
|
|
178
|
+
|
|
179
|
+
worker.customBlock("visualBlock", {
|
|
180
|
+
path: "./views/visual",
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
expect(worker.manifest.capabilities).toEqual([
|
|
184
|
+
{
|
|
185
|
+
_tag: "custom_block",
|
|
186
|
+
key: "visualBlock",
|
|
187
|
+
config: {
|
|
188
|
+
source: { type: "project", path: "./views/visual" },
|
|
189
|
+
manifest: {
|
|
190
|
+
version: 1,
|
|
191
|
+
dataSources: {},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
]);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("carries a project's custom build command and output dir", () => {
|
|
199
|
+
const worker = new Worker();
|
|
200
|
+
|
|
201
|
+
worker.customBlock("built", {
|
|
202
|
+
path: "./app",
|
|
203
|
+
command: "npm run build-prod",
|
|
204
|
+
output: "build",
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
expect(worker.manifest.capabilities).toEqual([
|
|
208
|
+
{
|
|
209
|
+
_tag: "custom_block",
|
|
210
|
+
key: "built",
|
|
211
|
+
config: {
|
|
212
|
+
source: {
|
|
213
|
+
type: "project",
|
|
214
|
+
path: "./app",
|
|
215
|
+
command: "npm run build-prod",
|
|
216
|
+
output: "build",
|
|
217
|
+
},
|
|
218
|
+
manifest: { version: 1, dataSources: {} },
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
]);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("registers a static custom block served as-is", () => {
|
|
225
|
+
const worker = new Worker();
|
|
226
|
+
|
|
227
|
+
worker.customBlock("prebuilt", { type: "static", path: "./dist" });
|
|
228
|
+
|
|
229
|
+
expect(worker.manifest.capabilities).toEqual([
|
|
230
|
+
{
|
|
231
|
+
_tag: "custom_block",
|
|
232
|
+
key: "prebuilt",
|
|
233
|
+
config: {
|
|
234
|
+
source: { type: "static", path: "./dist" },
|
|
235
|
+
manifest: { version: 1, dataSources: {} },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
]);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("carries the author-declared data-source schema verbatim into the manifest", () => {
|
|
242
|
+
const worker = new Worker();
|
|
243
|
+
|
|
244
|
+
worker.customBlock("issueBoard", {
|
|
245
|
+
path: "./views/issueBoard",
|
|
246
|
+
dataSources: {
|
|
247
|
+
issues: {
|
|
248
|
+
name: "Issues",
|
|
249
|
+
description: "The team's issues",
|
|
250
|
+
icon: { type: "emoji", emoji: "🐛" },
|
|
251
|
+
properties: {
|
|
252
|
+
title: { name: "Title", type: "title" },
|
|
253
|
+
status: {
|
|
254
|
+
name: "Status",
|
|
255
|
+
description: "Workflow state",
|
|
256
|
+
type: "status",
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
expect(worker.manifest.capabilities).toEqual([
|
|
264
|
+
{
|
|
265
|
+
_tag: "custom_block",
|
|
266
|
+
key: "issueBoard",
|
|
267
|
+
config: {
|
|
268
|
+
source: { type: "project", path: "./views/issueBoard" },
|
|
269
|
+
manifest: {
|
|
270
|
+
version: 1,
|
|
271
|
+
dataSources: {
|
|
272
|
+
issues: {
|
|
273
|
+
name: "Issues",
|
|
274
|
+
description: "The team's issues",
|
|
275
|
+
icon: { type: "emoji", emoji: "🐛" },
|
|
276
|
+
properties: {
|
|
277
|
+
title: { name: "Title", type: "title" },
|
|
278
|
+
status: {
|
|
279
|
+
name: "Status",
|
|
280
|
+
description: "Workflow state",
|
|
281
|
+
type: "status",
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
]);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("threads a top-level manifest version into the generated manifest", () => {
|
|
293
|
+
const worker = new Worker();
|
|
294
|
+
|
|
295
|
+
worker.customBlock("versioned", {
|
|
296
|
+
path: "./views/versioned",
|
|
297
|
+
version: 1,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
expect(worker.manifest.capabilities).toEqual([
|
|
301
|
+
{
|
|
302
|
+
_tag: "custom_block",
|
|
303
|
+
key: "versioned",
|
|
304
|
+
config: {
|
|
305
|
+
source: { type: "project", path: "./views/versioned" },
|
|
306
|
+
manifest: { version: 1, dataSources: {} },
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
]);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("cannot run a custom block capability", async () => {
|
|
313
|
+
const worker = new Worker();
|
|
314
|
+
worker.customBlock("visualBlock", {
|
|
315
|
+
path: "./views/visual",
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
await expect(worker.run("visualBlock", {})).rejects.toThrow(
|
|
319
|
+
'Cannot run custom block capability "visualBlock"',
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("rejects duplicate keys across credentials, databases, pacers, and capabilities", () => {
|
|
145
324
|
const worker = new Worker();
|
|
146
325
|
const db = worker.database("shared-key", {
|
|
147
326
|
type: "managed",
|
|
@@ -154,6 +333,23 @@ describe("Worker", () => {
|
|
|
154
333
|
},
|
|
155
334
|
});
|
|
156
335
|
|
|
336
|
+
expect(() =>
|
|
337
|
+
worker.credential("shared-key", {
|
|
338
|
+
network: [
|
|
339
|
+
{
|
|
340
|
+
domain: "api.example.com",
|
|
341
|
+
transform: [
|
|
342
|
+
{
|
|
343
|
+
headers: {
|
|
344
|
+
Authorization: CREDENTIAL_VALUE,
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
}),
|
|
351
|
+
).toThrow('Worker item with key "shared-key" already registered');
|
|
352
|
+
|
|
157
353
|
expect(() =>
|
|
158
354
|
worker.sync("shared-key", {
|
|
159
355
|
database: db,
|
|
@@ -164,4 +360,106 @@ describe("Worker", () => {
|
|
|
164
360
|
}),
|
|
165
361
|
).toThrow('Worker item with key "shared-key" already registered');
|
|
166
362
|
});
|
|
363
|
+
|
|
364
|
+
it("does not reserve a key when registration fails", () => {
|
|
365
|
+
const worker = new Worker();
|
|
366
|
+
|
|
367
|
+
expect(() =>
|
|
368
|
+
worker.credential("REUSABLE_KEY", {
|
|
369
|
+
network: [],
|
|
370
|
+
}),
|
|
371
|
+
).toThrow("must declare at least one network rule");
|
|
372
|
+
|
|
373
|
+
expect(() =>
|
|
374
|
+
worker.pacer("REUSABLE_KEY", {
|
|
375
|
+
allowedRequests: 10,
|
|
376
|
+
intervalMs: 1_000,
|
|
377
|
+
}),
|
|
378
|
+
).not.toThrow();
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it("rejects credentials that target the same domain and header", () => {
|
|
382
|
+
const worker = new Worker();
|
|
383
|
+
worker.credential("FIRST_TOKEN", {
|
|
384
|
+
network: [
|
|
385
|
+
{
|
|
386
|
+
domain: "api.example.com",
|
|
387
|
+
transform: [{ headers: { Authorization: `Bearer ${CREDENTIAL_VALUE}` } }],
|
|
388
|
+
},
|
|
389
|
+
],
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
expect(() =>
|
|
393
|
+
worker.credential("SECOND_TOKEN", {
|
|
394
|
+
network: [
|
|
395
|
+
{
|
|
396
|
+
domain: "API.EXAMPLE.COM",
|
|
397
|
+
transform: [{ headers: { authorization: CREDENTIAL_VALUE } }],
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
}),
|
|
401
|
+
).toThrow('conflicts with credential "FIRST_TOKEN"');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it("limits the number of credential declarations", () => {
|
|
405
|
+
const worker = new Worker();
|
|
406
|
+
for (let index = 0; index < 100; index += 1) {
|
|
407
|
+
worker.credential(`TOKEN_${index}`, {
|
|
408
|
+
network: [
|
|
409
|
+
{
|
|
410
|
+
domain: `api${index}.example.com`,
|
|
411
|
+
transform: [{ headers: { Authorization: CREDENTIAL_VALUE } }],
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
expect(() =>
|
|
418
|
+
worker.credential("ONE_TOO_MANY", {
|
|
419
|
+
network: [
|
|
420
|
+
{
|
|
421
|
+
domain: "overflow.example.com",
|
|
422
|
+
transform: [{ headers: { Authorization: CREDENTIAL_VALUE } }],
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
}),
|
|
426
|
+
).toThrow("cannot declare more than 100 credentials");
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it("matches the service limit for the credential declaration payload", () => {
|
|
430
|
+
const worker = new Worker();
|
|
431
|
+
for (let index = 0; index < 32; index += 1) {
|
|
432
|
+
worker.credential(`TOKEN_${index}`, {
|
|
433
|
+
network: [
|
|
434
|
+
{
|
|
435
|
+
domain: `api${index}.example.com`,
|
|
436
|
+
transform: [
|
|
437
|
+
{
|
|
438
|
+
headers: {
|
|
439
|
+
Authorization: `${"x".repeat(8_000)}${CREDENTIAL_VALUE}`,
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
expect(() =>
|
|
449
|
+
worker.credential("TOKEN_32", {
|
|
450
|
+
network: [
|
|
451
|
+
{
|
|
452
|
+
domain: "api32.example.com",
|
|
453
|
+
transform: [
|
|
454
|
+
{
|
|
455
|
+
headers: {
|
|
456
|
+
Authorization: `${"x".repeat(8_000)}${CREDENTIAL_VALUE}`,
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
}),
|
|
463
|
+
).toThrow("credential declarations must be at most 262144 bytes");
|
|
464
|
+
});
|
|
167
465
|
});
|
package/src/worker.ts
CHANGED
|
@@ -11,6 +11,11 @@ import type {
|
|
|
11
11
|
} from "./capabilities/automation.js";
|
|
12
12
|
import { createAutomationCapability } from "./capabilities/automation.js";
|
|
13
13
|
import type { CapabilityContext } from "./capabilities/context.js";
|
|
14
|
+
import type {
|
|
15
|
+
CustomBlockCapability,
|
|
16
|
+
CustomBlockConfiguration,
|
|
17
|
+
} from "./capabilities/custom-block.js";
|
|
18
|
+
import { createCustomBlockCapability } from "./capabilities/custom-block.js";
|
|
14
19
|
import type {
|
|
15
20
|
NotionManagedOAuthConfiguration,
|
|
16
21
|
OAuthCapability,
|
|
@@ -34,6 +39,17 @@ import type {
|
|
|
34
39
|
WorkflowEvent,
|
|
35
40
|
} from "./capabilities/workflow.js";
|
|
36
41
|
import { createWorkflowCapability } from "./capabilities/workflow.js";
|
|
42
|
+
import type {
|
|
43
|
+
CredentialConfiguration,
|
|
44
|
+
CredentialDeclaration,
|
|
45
|
+
CredentialKey,
|
|
46
|
+
} from "./credential.js";
|
|
47
|
+
import {
|
|
48
|
+
createCredentialDeclaration,
|
|
49
|
+
MAX_CREDENTIAL_DECLARATIONS_BYTES,
|
|
50
|
+
MAX_CREDENTIALS,
|
|
51
|
+
validateCredentialDeclarationConflicts,
|
|
52
|
+
} from "./credential.js";
|
|
37
53
|
import type { PacerDeclaration } from "./pacer_internal.js";
|
|
38
54
|
import { pacerWait } from "./pacer_internal.js";
|
|
39
55
|
import type { Schema } from "./schema.js";
|
|
@@ -69,7 +85,8 @@ type Capability =
|
|
|
69
85
|
| AutomationCapability
|
|
70
86
|
| WorkflowCapability
|
|
71
87
|
| OAuthCapability
|
|
72
|
-
| WebhookCapability
|
|
88
|
+
| WebhookCapability
|
|
89
|
+
| CustomBlockCapability;
|
|
73
90
|
|
|
74
91
|
export type CapabilityType = Capability["_tag"];
|
|
75
92
|
|
|
@@ -105,6 +122,7 @@ type StoredCapability =
|
|
|
105
122
|
| AutomationCapability
|
|
106
123
|
| StoredWorkflowCapability
|
|
107
124
|
| OAuthCapability
|
|
125
|
+
| CustomBlockCapability
|
|
108
126
|
| WebhookCapability;
|
|
109
127
|
|
|
110
128
|
/**
|
|
@@ -205,6 +223,7 @@ type CapabilityManifestEntry = Pick<Capability, "_tag" | "key" | "config">;
|
|
|
205
223
|
*/
|
|
206
224
|
export type WorkerManifest = {
|
|
207
225
|
readonly sdkVersion: string;
|
|
226
|
+
readonly credentials: readonly CredentialDeclaration[];
|
|
208
227
|
readonly databases: readonly RegisteredDatabase[];
|
|
209
228
|
readonly pacers: readonly PacerDeclaration[];
|
|
210
229
|
readonly capabilities: readonly CapabilityManifestEntry[];
|
|
@@ -236,9 +255,33 @@ const SDK_VERSION = (() => {
|
|
|
236
255
|
|
|
237
256
|
export class Worker {
|
|
238
257
|
#capabilities: Map<string, StoredCapability> = new Map();
|
|
258
|
+
#credentials: Map<string, CredentialDeclaration> = new Map();
|
|
239
259
|
#databases: Map<string, RegisteredDatabase> = new Map();
|
|
240
260
|
#pacers: Map<string, PacerDeclaration> = new Map();
|
|
241
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Declare a credential that is injected into matching outbound HTTPS
|
|
264
|
+
* requests without exposing its value to Worker code.
|
|
265
|
+
*/
|
|
266
|
+
credential(key: CredentialKey, config: CredentialConfiguration): void {
|
|
267
|
+
this.#validateUniqueKey(key);
|
|
268
|
+
if (this.#credentials.size >= MAX_CREDENTIALS) {
|
|
269
|
+
throw new Error(`Worker cannot declare more than ${MAX_CREDENTIALS} credentials`);
|
|
270
|
+
}
|
|
271
|
+
const declaration = createCredentialDeclaration(key, config);
|
|
272
|
+
validateCredentialDeclarationConflicts(this.#credentials.values(), declaration);
|
|
273
|
+
const declarations = [...this.#credentials.values(), declaration];
|
|
274
|
+
if (
|
|
275
|
+
new TextEncoder().encode(JSON.stringify(declarations)).byteLength >
|
|
276
|
+
MAX_CREDENTIAL_DECLARATIONS_BYTES
|
|
277
|
+
) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`Worker credential declarations must be at most ${MAX_CREDENTIAL_DECLARATIONS_BYTES} bytes`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
this.#credentials.set(key, declaration);
|
|
283
|
+
}
|
|
284
|
+
|
|
242
285
|
database<PK extends string, S extends Schema<PK>>(
|
|
243
286
|
key: string,
|
|
244
287
|
config: DatabaseConfig<PK, S>,
|
|
@@ -609,6 +652,51 @@ export class Worker {
|
|
|
609
652
|
return capability;
|
|
610
653
|
}
|
|
611
654
|
|
|
655
|
+
/**
|
|
656
|
+
* Register a custom block capability.
|
|
657
|
+
*
|
|
658
|
+
* A custom block is a front-end view that the deploy pipeline builds from
|
|
659
|
+
* the source entry into a deployable bundle. It can optionally declare a
|
|
660
|
+
* data-source *schema* (`name`, and optional `description`, `icon`, and
|
|
661
|
+
* `properties`) that the block reads.
|
|
662
|
+
*
|
|
663
|
+
* Example:
|
|
664
|
+
*
|
|
665
|
+
* ```ts
|
|
666
|
+
* const worker = new Worker();
|
|
667
|
+
* export default worker;
|
|
668
|
+
*
|
|
669
|
+
* worker.customBlock("issueBoard", {
|
|
670
|
+
* version: 1,
|
|
671
|
+
* path: "./views/issueBoard",
|
|
672
|
+
* dataSources: {
|
|
673
|
+
* issues: {
|
|
674
|
+
* name: "Issues",
|
|
675
|
+
* properties: {
|
|
676
|
+
* title: { name: "Title", type: "title" },
|
|
677
|
+
* status: { name: "Status", type: "status" },
|
|
678
|
+
* },
|
|
679
|
+
* },
|
|
680
|
+
* },
|
|
681
|
+
* });
|
|
682
|
+
* ```
|
|
683
|
+
*
|
|
684
|
+
* The manifest keys (`version`, `dataSources`) are top-level on the config;
|
|
685
|
+
* the SDK assembles the deploy-wire `CustomBlockManifest` from them. Binding
|
|
686
|
+
* each declared data source to a concrete managed database is instance-level
|
|
687
|
+
* and handled at deploy time — the worker does not emit bindings.
|
|
688
|
+
*
|
|
689
|
+
* @param key - The unique key for this block (the block declaration key).
|
|
690
|
+
* @param config - The custom block configuration (a `project`/`static` source folder + optional `version`/`dataSources` schema).
|
|
691
|
+
* @returns The custom block capability.
|
|
692
|
+
*/
|
|
693
|
+
customBlock(key: string, config: CustomBlockConfiguration): CustomBlockCapability {
|
|
694
|
+
this.#validateUniqueKey(key);
|
|
695
|
+
const capability = createCustomBlockCapability(key, config);
|
|
696
|
+
this.#capabilities.set(key, capability);
|
|
697
|
+
return capability;
|
|
698
|
+
}
|
|
699
|
+
|
|
612
700
|
/**
|
|
613
701
|
* Get all registered capabilities (for discovery) without their handlers.
|
|
614
702
|
*/
|
|
@@ -623,6 +711,7 @@ export class Worker {
|
|
|
623
711
|
get manifest(): WorkerManifest {
|
|
624
712
|
return {
|
|
625
713
|
sdkVersion: SDK_VERSION,
|
|
714
|
+
credentials: Array.from(this.#credentials.values()),
|
|
626
715
|
databases: Array.from(this.#databases.values()),
|
|
627
716
|
pacers: Array.from(this.#pacers.values()),
|
|
628
717
|
capabilities: this.capabilities,
|
|
@@ -650,6 +739,12 @@ export class Worker {
|
|
|
650
739
|
);
|
|
651
740
|
}
|
|
652
741
|
|
|
742
|
+
if (capability._tag === "custom_block") {
|
|
743
|
+
throw new Error(
|
|
744
|
+
`Cannot run custom block capability "${key}" - custom blocks only provide configuration`,
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
|
|
653
748
|
if (!capability.handler) {
|
|
654
749
|
throw new Error(`Capability "${key}" cannot be executed`);
|
|
655
750
|
}
|
|
@@ -665,7 +760,12 @@ export class Worker {
|
|
|
665
760
|
if (!key || typeof key !== "string") {
|
|
666
761
|
throw new Error("Capability key must be a non-empty string");
|
|
667
762
|
}
|
|
668
|
-
if (
|
|
763
|
+
if (
|
|
764
|
+
this.#capabilities.has(key) ||
|
|
765
|
+
this.#credentials.has(key) ||
|
|
766
|
+
this.#databases.has(key) ||
|
|
767
|
+
this.#pacers.has(key)
|
|
768
|
+
) {
|
|
669
769
|
throw new Error(`Worker item with key "${key}" already registered`);
|
|
670
770
|
}
|
|
671
771
|
}
|