@zackbart/connecta 0.28.0 → 0.29.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/CHANGELOG.md +961 -5
- package/README.md +62 -39
- package/bin/connecta.mjs +110 -139
- package/bin/meta-tool-names.d.mts +2 -0
- package/bin/meta-tool-names.mjs +9 -0
- package/bin/version.mjs +3 -0
- package/dist/access-tokens.js +53 -63
- package/dist/activity-request.js +8 -0
- package/dist/activity.d.ts +76 -42
- package/dist/activity.js +110 -42
- package/dist/auth/clerk-sdk/client.d.ts +65 -0
- package/dist/auth/clerk-sdk/client.js +5 -0
- package/dist/auth/clerk-sdk/generated.js +1845 -0
- package/dist/auth/clerk-transport.js +19 -0
- package/dist/auth/clerk.d.ts +17 -7
- package/dist/auth/clerk.js +189 -107
- package/dist/auth/cloudflare-access.js +25 -26
- package/dist/auth/downstream-client-metadata.d.ts +29 -0
- package/dist/auth/downstream-client-metadata.js +133 -0
- package/dist/auth/downstream-oauth.js +1632 -1509
- package/dist/auth/downstream-revocation.js +106 -0
- package/dist/auth/oauth-token-response.js +231 -0
- package/dist/auth/oauth-v2-migration.js +116 -0
- package/dist/auth/static-oauth.js +526 -0
- package/dist/auth-elicitation.js +283 -0
- package/dist/branding.d.ts +8 -16
- package/dist/branding.js +26 -59
- package/dist/byte-read-response.js +39 -0
- package/dist/call-admission.js +12 -16
- package/dist/catalog-cache.js +739 -0
- package/dist/catalog-drift.d.ts +50 -15
- package/dist/catalog-drift.js +365 -128
- package/dist/catalog-fingerprint.js +2 -4
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-limits.js +2 -2
- package/dist/catalog-service.js +368 -224
- package/dist/catalog.js +43 -84
- package/dist/concurrency.js +3 -4
- package/dist/config-defaults.d.ts +39 -0
- package/dist/config-defaults.js +46 -0
- package/dist/config-schema.d.ts +181 -0
- package/dist/config-schema.js +435 -0
- package/dist/config-value-sources.js +102 -0
- package/dist/config.d.ts +631 -0
- package/dist/config.js +503 -0
- package/dist/connector-access.d.ts +1 -0
- package/dist/connector-access.js +13 -15
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +15 -6
- package/dist/connector-scope.js +1 -1
- package/dist/connectors/api-connector.d.ts +220 -0
- package/dist/connectors/api-connector.js +214 -0
- package/dist/connectors/api.d.ts +8 -71
- package/dist/connectors/api.js +13 -99
- package/dist/connectors/guarded-fetch.d.ts +144 -0
- package/dist/connectors/guarded-fetch.js +108 -25
- package/dist/connectors/negotiation-cache.js +46 -0
- package/dist/connectors/option-shapes.js +27 -0
- package/dist/connectors/remote-mcp.d.ts +30 -4
- package/dist/connectors/remote-mcp.js +1780 -351
- package/dist/connectors/resource-uri.js +335 -0
- package/dist/credential-contract.d.ts +10 -0
- package/dist/credential-rules.js +1 -3
- package/dist/credential-url.js +108 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +27 -18
- package/dist/d1.d.ts +130 -0
- package/dist/d1.js +290 -0
- package/dist/describe-config.d.ts +165 -0
- package/dist/describe-config.js +315 -0
- package/dist/described.js +106 -0
- package/dist/downstream-input-context.js +119 -0
- package/dist/downstream-input.js +323 -0
- package/dist/errors.d.ts +97 -20
- package/dist/errors.js +158 -80
- package/dist/execute.js +453 -305
- package/dist/executor-admission.d.ts +1 -0
- package/dist/executor-admission.js +11 -15
- package/dist/executor-contract.d.ts +20 -0
- package/dist/executor-contract.js +48 -0
- package/dist/executor-result.js +24 -9
- package/dist/executors/quickjs-child.js +3 -4
- package/dist/executors/quickjs-runtime.js +147 -59
- package/dist/executors/quickjs.js +29 -35
- package/dist/favicon.js +1 -2
- package/dist/fix-prompt.js +1 -3
- package/dist/guest-runtime.js +188 -0
- package/dist/guest-types.d.ts +176 -0
- package/dist/guest-types.js +1 -0
- package/dist/html-security.js +24 -0
- package/dist/identity.js +1 -3
- package/dist/inbound-credential.js +52 -0
- package/dist/index.d.ts +25 -257
- package/dist/index.js +65 -478
- package/dist/invocation-auth.js +69 -0
- package/dist/invocation.js +230 -73
- package/dist/json-schema.d.ts +1 -1
- package/dist/mcp-client-context.js +35 -0
- package/dist/mcp-result.js +11 -7
- package/dist/meta-output.js +76 -0
- package/dist/meta-tool-names.d.ts +2 -0
- package/dist/meta-tool-names.js +3 -0
- package/dist/meta-tools.js +285 -209
- package/dist/module-contracts.d.ts +8 -23
- package/dist/node.d.ts +0 -2
- package/dist/node.js +2 -2
- package/dist/oauth-callback-outcome.js +1 -3
- package/dist/oauth-handoff.js +117 -0
- package/dist/oauth-partition.js +17 -0
- package/dist/oauth-sealing.js +20 -0
- package/dist/operator-record.d.ts +137 -0
- package/dist/operator-record.js +420 -0
- package/dist/operator-ui/assets.js +31 -0
- package/dist/operator-ui/contract.d.ts +72 -0
- package/dist/operator-ui/contract.js +1 -0
- package/dist/operator-ui/fix-prompts.d.ts +18 -0
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/model.d.ts +12 -87
- package/dist/operator-ui/model.js +2 -9
- package/dist/operator-ui/setup-commands.js +1 -3
- package/dist/operator-ui/view.d.ts +323 -0
- package/dist/operator-ui/view.js +62 -189
- package/dist/page-styles.js +2 -3
- package/dist/program-source.js +14 -2
- package/dist/{exempt-writes.js → program-writes.js} +55 -18
- package/dist/provider.d.ts +142 -0
- package/dist/provider.js +187 -0
- package/dist/providers/_shared/google/result-size.js +57 -0
- package/dist/providers/_shared/google/service-account.d.ts +58 -0
- package/dist/providers/_shared/google/service-account.js +500 -0
- package/dist/providers/_shared/google/workspace.d.ts +276 -0
- package/dist/providers/_shared/google/workspace.js +518 -0
- package/dist/providers/basecamp/index.d.ts +43 -0
- package/dist/providers/basecamp/index.js +997 -0
- package/dist/providers/basecamp/skill.generated.js +9 -0
- package/dist/providers/breeze/index.d.ts +34 -0
- package/dist/providers/breeze/index.js +1446 -0
- package/dist/providers/breeze/skill.generated.js +9 -0
- package/dist/providers/ccb/index.d.ts +103 -0
- package/dist/providers/ccb/index.js +1725 -0
- package/dist/providers/ccb/skill.generated.js +9 -0
- package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
- package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
- package/dist/providers/cloudflare/skill.generated.js +10 -0
- package/dist/providers/docs/index.d.ts +54 -0
- package/dist/providers/docs/index.js +1237 -0
- package/dist/providers/docs/skill.generated.js +9 -0
- package/dist/providers/drive/index.d.ts +53 -0
- package/dist/providers/drive/index.js +1523 -0
- package/dist/providers/drive/skill.generated.js +9 -0
- package/dist/providers/forms/index.d.ts +54 -0
- package/dist/providers/forms/index.js +1217 -0
- package/dist/providers/forms/skill.generated.js +11 -0
- package/dist/providers/github/auth.d.ts +32 -0
- package/dist/providers/github/auth.js +361 -0
- package/dist/providers/github/hosted.js +218 -0
- package/dist/providers/github/index.d.ts +13 -0
- package/dist/providers/github/index.js +140 -0
- package/dist/providers/github/key.js +100 -0
- package/dist/providers/github/rest.js +321 -0
- package/dist/providers/github/scope.d.ts +29 -0
- package/dist/providers/github/scope.js +124 -0
- package/dist/providers/github/skill.generated.js +8 -0
- package/dist/providers/gmail/index.d.ts +51 -0
- package/dist/providers/gmail/index.js +1436 -0
- package/dist/providers/gmail/skill.generated.js +9 -0
- package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
- package/dist/providers/linear/index.js +352 -0
- package/dist/providers/linear/skill.generated.js +8 -0
- package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
- package/dist/providers/mixpanel/index.js +416 -0
- package/dist/providers/mixpanel/skill.generated.js +9 -0
- package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
- package/dist/providers/{notion.js → notion/index.js} +226 -239
- package/dist/providers/notion/skill.generated.js +10 -0
- package/dist/providers/overflow/index.d.ts +60 -0
- package/dist/providers/overflow/index.js +1535 -0
- package/dist/providers/overflow/skill.generated.js +8 -0
- package/dist/providers/planning-center/index.d.ts +59 -0
- package/dist/providers/planning-center/index.js +2199 -0
- package/dist/providers/planning-center/skill.generated.js +10 -0
- package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
- package/dist/providers/revenuecat/index.js +557 -0
- package/dist/providers/revenuecat/skill.generated.js +11 -0
- package/dist/providers/sheets/index.d.ts +54 -0
- package/dist/providers/sheets/index.js +1237 -0
- package/dist/providers/sheets/skill.generated.js +10 -0
- package/dist/providers/slides/index.d.ts +53 -0
- package/dist/providers/slides/index.js +2645 -0
- package/dist/providers/slides/skill.generated.js +9 -0
- package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
- package/dist/providers/stripe/index.js +266 -0
- package/dist/providers/stripe/skill.generated.js +11 -0
- package/dist/providers/tithely/index.d.ts +49 -0
- package/dist/providers/tithely/index.js +1003 -0
- package/dist/providers/tithely/skill.generated.js +8 -0
- package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
- package/dist/providers/vercel/index.js +1038 -0
- package/dist/providers/vercel/mcp-ownership.js +212 -0
- package/dist/providers/vercel/skill.generated.js +11 -0
- package/dist/registry.d.ts +108 -171
- package/dist/registry.js +513 -925
- package/dist/request-state.js +21 -0
- package/dist/resource-read.js +100 -0
- package/dist/result-shapes.js +17 -49
- package/dist/retired-toolkits.js +1 -1
- package/dist/routes/access-tokens.js +3 -6
- package/dist/routes/activity-context.js +21 -0
- package/dist/routes/activity-disclosure.js +40 -0
- package/dist/routes/activity.js +59 -21
- package/dist/routes/actor-labels.js +3 -50
- package/dist/routes/connect.js +221 -0
- package/dist/routes/credentials.js +15 -27
- package/dist/routes/mcp.js +356 -130
- package/dist/routes/oauth-client-metadata.js +15 -0
- package/dist/routes/oauth-management.js +45 -107
- package/dist/routes/oauth.js +136 -57
- package/dist/routes/operator.js +3 -3
- package/dist/routes/shared.d.ts +25 -64
- package/dist/routes/shared.js +160 -80
- package/dist/routes/skills.js +30 -0
- package/dist/routes/ui-config.js +225 -0
- package/dist/routes/ui-data.js +40 -35
- package/dist/routes/ui.js +66 -56
- package/dist/runtime/admission.js +4 -2
- package/dist/runtime/call-admission.js +3 -1
- package/dist/runtime/connector-scope.js +1 -4
- package/dist/runtime/run.js +10 -39
- package/dist/runtime/services.js +12 -117
- package/dist/runtime/shared-read.js +1 -3
- package/dist/runtime/storage.js +8 -1
- package/dist/sent-secrets.d.ts +52 -0
- package/dist/sent-secrets.js +474 -0
- package/dist/server.js +28 -71
- package/dist/skills.js +406 -174
- package/dist/sqlite.d.ts +48 -0
- package/dist/sqlite.js +166 -0
- package/dist/storage/keys.js +428 -0
- package/dist/storage/memory.js +7 -0
- package/dist/storage/sql.d.ts +66 -0
- package/dist/storage/sql.js +534 -0
- package/dist/timeout.js +4 -2
- package/dist/tool-name.js +17 -0
- package/dist/tool-safety.d.ts +9 -35
- package/dist/tool-safety.js +20 -40
- package/dist/types.d.ts +320 -54
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +7 -19
- package/dist/ui.js +76 -90
- package/dist/url-safety.js +3 -6
- package/dist/usage-guide.js +137 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +309 -47
- package/dist/version.d.ts +1 -7
- package/dist/version.js +3 -7
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +434 -0
- package/documentation/architecture.md +665 -230
- package/documentation/auth.md +962 -458
- package/documentation/code-mode.md +501 -312
- package/documentation/meta-tools.md +449 -200
- package/documentation/operator-tests.md +93 -0
- package/documentation/provider-migration-0.29.md +60 -0
- package/examples/worker/AGENTS.md +34 -12
- package/examples/worker/README.md +286 -255
- package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
- package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
- package/examples/worker/src/connecta.config.ts +151 -0
- package/examples/worker/src/index.ts +14 -197
- package/examples/worker/wrangler.jsonc +15 -35
- package/package.json +125 -35
- package/templates/node/.dockerignore +1 -2
- package/templates/node/.env.example +20 -21
- package/templates/node/AGENTS.md +21 -17
- package/templates/node/Dockerfile +3 -5
- package/templates/node/README.md +133 -58
- package/templates/node/docker-compose.yml +11 -12
- package/templates/node/package.json +4 -2
- package/templates/node/src/connecta.config.ts +119 -0
- package/templates/node/src/index.ts +9 -154
- package/templates/node/src/provision-token.ts +17 -0
- package/AGENTS.md +0 -170
- package/dist/artifacts/connector.d.ts +0 -58
- package/dist/artifacts/connector.js +0 -730
- package/dist/artifacts/document.js +0 -86
- package/dist/artifacts/guide.js +0 -140
- package/dist/artifacts/html-scan.js +0 -329
- package/dist/artifacts/json.js +0 -78
- package/dist/artifacts/kv-store.d.ts +0 -20
- package/dist/artifacts/kv-store.js +0 -140
- package/dist/artifacts/markdown.js +0 -478
- package/dist/artifacts/module.d.ts +0 -37
- package/dist/artifacts/module.js +0 -66
- package/dist/artifacts/operations.d.ts +0 -197
- package/dist/artifacts/operations.js +0 -1002
- package/dist/artifacts/refresh.d.ts +0 -55
- package/dist/artifacts/refresh.js +0 -236
- package/dist/artifacts/routes.js +0 -206
- package/dist/artifacts/types.d.ts +0 -214
- package/dist/artifacts/types.js +0 -22
- package/dist/artifacts/validate.d.ts +0 -62
- package/dist/artifacts/validate.js +0 -651
- package/dist/artifacts.d.ts +0 -6
- package/dist/artifacts.js +0 -9
- package/dist/auth/bearer.d.ts +0 -11
- package/dist/auth/bearer.js +0 -50
- package/dist/providers/linear.js +0 -217
- package/dist/providers/mixpanel.js +0 -247
- package/dist/providers/revenuecat.js +0 -317
- package/dist/providers/stripe.js +0 -253
- package/dist/providers/vercel.js +0 -1253
- package/dist/storage/file.d.ts +0 -15
- package/dist/storage/file.js +0 -455
- package/ethos.md +0 -93
- package/examples/worker/src/cloudflare-kv.ts +0 -43
- package/examples/worker/src/d1-activity-row.ts +0 -155
- package/examples/worker/src/d1-activity.ts +0 -147
- package/examples/worker/src/d1-storage.ts +0 -138
- package/examples/worker/src/r2-artifact-blobs.ts +0 -29
- package/templates/node/src/file-activity.ts +0 -182
package/dist/worker.js
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
// The upstream sandbox, with request-owned loader/RPC handles. A guest's
|
|
2
|
+
// deadline cannot clean up after the parent response has already ended.
|
|
3
|
+
import { DynamicWorkerExecutor, normalizeCode } from "@cloudflare/codemode";
|
|
4
|
+
import { AdmissionController, ExecutorExecutionError } from "./executor-admission.js";
|
|
5
|
+
import { brandExecutor } from "./executor-contract.js";
|
|
6
|
+
import { InvocationFailure } from "./invocation.js";
|
|
7
|
+
import { parse } from "acorn";
|
|
8
|
+
import { guestInitializer, guestPrelude, guestPromiseInitializer, isolateGuestProgram } from "./guest-runtime.js";
|
|
9
|
+
/** Parse guest syntax before rewriting imports; strings and regexes are data. */
|
|
10
|
+
function routeGuestImports(source) {
|
|
11
|
+
const offsets = [];
|
|
12
|
+
const visit = (node) => {
|
|
13
|
+
if (node.type === "ImportExpression")
|
|
14
|
+
offsets.push(node.start - "export default (".length);
|
|
15
|
+
for (const value of Object.values(node)) {
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
for (const child of value)
|
|
18
|
+
if (child && typeof child === "object" && "type" in child)
|
|
19
|
+
visit(child);
|
|
20
|
+
}
|
|
21
|
+
else if (value && typeof value === "object" && "type" in value)
|
|
22
|
+
visit(value);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const module = parse(`export default (${source}\n);`, {
|
|
26
|
+
ecmaVersion: "latest",
|
|
27
|
+
sourceType: "module",
|
|
28
|
+
locations: true,
|
|
29
|
+
});
|
|
30
|
+
// Closing the parentheses and adding statements must never create module
|
|
31
|
+
// code outside this one expression. Check the assembled splice, including
|
|
32
|
+
// comments and all grouping parentheses, before the loader sees any source.
|
|
33
|
+
if (module.body.length !== 1 || module.body[0]?.type !== "ExportDefaultDeclaration") {
|
|
34
|
+
throw new SyntaxError("A Worker program must contain exactly one expression.");
|
|
35
|
+
}
|
|
36
|
+
visit(module);
|
|
37
|
+
for (const offset of offsets.sort((a, b) => b - a)) {
|
|
38
|
+
source = source.slice(0, offset) + "__connecta_import" + source.slice(offset + 6);
|
|
39
|
+
}
|
|
40
|
+
return source;
|
|
41
|
+
}
|
|
42
|
+
/** Loader-only upstream sandbox whose lease disposes handles at run end. */
|
|
43
|
+
export function workerExecutor(options) {
|
|
44
|
+
const admission = new AdmissionController(options.admission ?? {
|
|
45
|
+
concurrency: 2,
|
|
46
|
+
maxQueueSize: 8,
|
|
47
|
+
queueTimeoutMs: 5_000,
|
|
48
|
+
retryAfterMs: 1_000,
|
|
49
|
+
});
|
|
50
|
+
const active = new Set();
|
|
51
|
+
return brandExecutor({
|
|
52
|
+
name: "DynamicWorkerExecutor",
|
|
53
|
+
async acquire(request = {}) {
|
|
54
|
+
const permit = await admission.acquire(request);
|
|
55
|
+
const disposers = [];
|
|
56
|
+
let released = false;
|
|
57
|
+
let executed = false;
|
|
58
|
+
let runEnded = false;
|
|
59
|
+
let cancel;
|
|
60
|
+
let hostProviders;
|
|
61
|
+
const hostFailures = new Map();
|
|
62
|
+
// An upstream evaluation can remain pending after disposal. Its
|
|
63
|
+
// dispatchers retain only these forwarders, detached on lease release.
|
|
64
|
+
const forward = (index, name) => async (...args) => {
|
|
65
|
+
const fn = hostProviders?.[index]?.fns[name];
|
|
66
|
+
try {
|
|
67
|
+
if (!fn)
|
|
68
|
+
throw new Error("The Worker run ended.");
|
|
69
|
+
return { ok: true, value: await fn(...args) };
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const message = error instanceof Error ? error.message : "Host call failed.";
|
|
73
|
+
if (error instanceof InvocationFailure && !released && !runEnded) {
|
|
74
|
+
const failureId = crypto.randomUUID();
|
|
75
|
+
hostFailures.set(failureId, error.details);
|
|
76
|
+
// The codec may expose the copy to guest hooks. The authoritative
|
|
77
|
+
// record never leaves this host-side map.
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
error: {
|
|
81
|
+
message,
|
|
82
|
+
code: error.details.code,
|
|
83
|
+
retryable: error.details.retryable,
|
|
84
|
+
details: structuredClone(error.details),
|
|
85
|
+
},
|
|
86
|
+
failureId,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return { ok: false, error: { message } };
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
let rpcOutcome;
|
|
93
|
+
// Keep each native method's receiver and make upstream's later finally
|
|
94
|
+
// harmless. Release RPC stubs before the Worker Loader handle.
|
|
95
|
+
const track = (resource, entrypoint = false) => {
|
|
96
|
+
let disposed = false;
|
|
97
|
+
const dispose = () => {
|
|
98
|
+
if (disposed)
|
|
99
|
+
return;
|
|
100
|
+
disposed = true;
|
|
101
|
+
const method = Reflect.get(resource, Symbol.dispose);
|
|
102
|
+
if (typeof method === "function")
|
|
103
|
+
method.call(resource);
|
|
104
|
+
};
|
|
105
|
+
disposers.push(dispose);
|
|
106
|
+
return new Proxy(resource, {
|
|
107
|
+
get(target, key) {
|
|
108
|
+
if (key === Symbol.dispose)
|
|
109
|
+
return dispose;
|
|
110
|
+
if (key === "getEntrypoint" && !entrypoint) {
|
|
111
|
+
return (...args) => track(Reflect.apply(Reflect.get(target, key), target, args), true);
|
|
112
|
+
}
|
|
113
|
+
if (key === "evaluate" && entrypoint)
|
|
114
|
+
return async (...args) => {
|
|
115
|
+
const result = (await Reflect.apply(Reflect.get(target, key), target, args));
|
|
116
|
+
// Only the native RPC return channel supplies executor facts.
|
|
117
|
+
rpcOutcome = result;
|
|
118
|
+
return result;
|
|
119
|
+
};
|
|
120
|
+
const value = Reflect.get(target, key);
|
|
121
|
+
return typeof value === "function" ? (...args) => Reflect.apply(value, target, args) : value;
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
let executableCode;
|
|
126
|
+
let guestProgram;
|
|
127
|
+
let normalizationLines = 0;
|
|
128
|
+
const loader = new Proxy(options.loader, {
|
|
129
|
+
get(target, key) {
|
|
130
|
+
if (key === "load")
|
|
131
|
+
return (...args) => {
|
|
132
|
+
if (released)
|
|
133
|
+
throw new Error("Executor lease was already released.");
|
|
134
|
+
const definition = args[0];
|
|
135
|
+
const source = definition?.modules?.["executor.js"];
|
|
136
|
+
// Keep guest source in its own module, outside the lexical scope of
|
|
137
|
+
// RPC dispatchers and retained host-failure identities.
|
|
138
|
+
if (typeof source !== "string" || !executableCode || !hostProviders)
|
|
139
|
+
throw new Error("Worker executable module was unavailable.");
|
|
140
|
+
const index = source.indexOf(executableCode);
|
|
141
|
+
if (index < 0)
|
|
142
|
+
throw new Error("Worker executable module did not contain the program.");
|
|
143
|
+
const guest = guestProgram;
|
|
144
|
+
if (guest)
|
|
145
|
+
normalizationLines = 0;
|
|
146
|
+
const names = hostProviders.map((provider) => provider.name).join(", ");
|
|
147
|
+
const globals = hostProviders
|
|
148
|
+
.map((provider) => `globalThis[${JSON.stringify(provider.name)}] = ${provider.name};`)
|
|
149
|
+
.join("\n");
|
|
150
|
+
let isolated = source.slice(0, index) + "__connecta_program" + source.slice(index + executableCode.length);
|
|
151
|
+
const bridge = `if (data.error) throw new Error(data.error);\n return data.result;`;
|
|
152
|
+
if (hostProviders.length > 0 && !isolated.includes(bridge))
|
|
153
|
+
throw new Error("Worker bridge contract changed.");
|
|
154
|
+
isolated = isolated.replaceAll(bridge, `if (data.error) throw new NativeError(data.error);
|
|
155
|
+
const reply = data.result;
|
|
156
|
+
if (!reply.ok) {
|
|
157
|
+
const error = new NativeError(reply.error.message);
|
|
158
|
+
if (typeof reply.failureId === "string") {
|
|
159
|
+
retain(failures, error, reply.failureId);
|
|
160
|
+
defineProperties(error, {
|
|
161
|
+
code: { value: reply.error.code, enumerable: true },
|
|
162
|
+
retryable: { value: reply.error.retryable, enumerable: true },
|
|
163
|
+
details: { value: freeze(clone(reply.error.details)), enumerable: true }
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
return reply.value;`);
|
|
169
|
+
isolated = isolated.replace(" const __logs = [];", ` if (initialized) throw new NativeError("Worker initialization is single-use.");
|
|
170
|
+
initialized = true;
|
|
171
|
+
const failures = new NativeWeakMap();
|
|
172
|
+
const timeoutError = new NativeError("Execution timed out");
|
|
173
|
+
const __logs = [];`);
|
|
174
|
+
isolated = isolated.replace('new Error("Execution timed out")', "timeoutError");
|
|
175
|
+
isolated = isolated.replace("setTimeout(() => reject(timeoutError)", "nativeSetTimeout(() => reject(timeoutError)");
|
|
176
|
+
isolated = isolated.replace("return { result: undefined, error: err.message, logs: __logs };", `
|
|
177
|
+
const failureId = lookup(failures, err);
|
|
178
|
+
let name = "Error", message = "Program threw a value.", stack = "";
|
|
179
|
+
try {
|
|
180
|
+
if (typeof err === "string") message = slice(err, 0, 1000);
|
|
181
|
+
else if (err) {
|
|
182
|
+
if (typeof err.name === "string") name = slice(err.name, 0, 64);
|
|
183
|
+
if (typeof err.message === "string") message = slice(err.message, 0, 1000);
|
|
184
|
+
if (typeof err.stack === "string") stack = slice(err.stack, 0, 1000);
|
|
185
|
+
}
|
|
186
|
+
} catch {}
|
|
187
|
+
const location = exec(/connecta-guest\\.js:(\\d+):\\d+/, stack);
|
|
188
|
+
return { result: undefined, error: message, logs: __logs, failure: {
|
|
189
|
+
name, ...(failureId ? { failureId } : {}), ...(location ? { line: toNumber(location[1]) - 1 } : {}),
|
|
190
|
+
...(err === timeoutError ? { timeout: { elapsedMs: ${options.timeout ?? 60_000}, deadlineMs: ${options.timeout ?? 60_000} } } : {})
|
|
191
|
+
} };`);
|
|
192
|
+
const preludes = [];
|
|
193
|
+
let firstInitializer;
|
|
194
|
+
for (const provider of hostProviders) {
|
|
195
|
+
if (!provider.prelude)
|
|
196
|
+
continue;
|
|
197
|
+
if (!isolated.includes(provider.prelude))
|
|
198
|
+
throw new Error("Worker provider setup was unavailable.");
|
|
199
|
+
preludes.push(provider.prelude);
|
|
200
|
+
const call = firstInitializer ? "" : `__connecta_initialize(${names});`;
|
|
201
|
+
isolated = isolated.replace(provider.prelude, call);
|
|
202
|
+
firstInitializer ||= call;
|
|
203
|
+
}
|
|
204
|
+
// Successive preludes share lexical bindings in both executors.
|
|
205
|
+
const captureGuest = preludes.includes(guestPrelude());
|
|
206
|
+
const initializer = preludes.length
|
|
207
|
+
? `const __connecta_initialize = (${names}) => {\n${preludes.map((prelude) => (prelude === guestPrelude() ? "__connecta_guest_initialize(connecta);" : prelude)).join("\n")}\n};\n`
|
|
208
|
+
: "";
|
|
209
|
+
// Runner modules are inaccessible to guest imports. The callback has
|
|
210
|
+
// no lexical access to initialization or RPC dispatchers.
|
|
211
|
+
const programName = guest ? "__connecta_user_program" : "__connecta_program";
|
|
212
|
+
const imports = 'import Runner from "./connecta-runner.js";\n' + `import program from "./connecta-guest.js";\n`;
|
|
213
|
+
const wrapper = guest ? ` const __connecta_program = (${guest.wrapper});\n` : "";
|
|
214
|
+
const hardening = `
|
|
215
|
+
const builtinAllowed = name => typeof name === "string" && name !== "module" && name !== "node:module" && name !== "process" && name !== "node:process";
|
|
216
|
+
if (typeof process !== "undefined" && typeof process.getBuiltinModule === "function") {
|
|
217
|
+
const getBuiltin = process.getBuiltinModule.bind(process);
|
|
218
|
+
Object.defineProperty(process, "getBuiltinModule", { configurable: false, writable: false,
|
|
219
|
+
value: name => builtinAllowed(name) ? getBuiltin(name) : undefined });
|
|
220
|
+
}
|
|
221
|
+
`;
|
|
222
|
+
// ESM evaluates the first dependency completely before the next.
|
|
223
|
+
// This module has no guest dependency; its references and retained
|
|
224
|
+
// failures stay private, and guest imports cannot reach its export.
|
|
225
|
+
const captures = `
|
|
226
|
+
const {
|
|
227
|
+
NativeError, NativeWeakMap, NativeProxy, NativePromise, NativeUint8Array, NativeArrayBuffer,
|
|
228
|
+
defineProperties, freeze, clone, retain, lookup, slice, nativeSetTimeout, toNumber, exec,
|
|
229
|
+
hasOwn, push, map, join, toString, fromCharCode, min, subarray, charCodeAt, bufferSlice,
|
|
230
|
+
isView, nativeBtoa, nativeAtob, race, jsonParse, jsonStringify, nativeConsole
|
|
231
|
+
} = Object.freeze({
|
|
232
|
+
NativeError: Error, NativeWeakMap: WeakMap, NativeProxy: Proxy, NativePromise: Promise,
|
|
233
|
+
NativeUint8Array: Uint8Array, NativeArrayBuffer: ArrayBuffer,
|
|
234
|
+
defineProperties: Object.defineProperties, freeze: Object.freeze, clone: structuredClone,
|
|
235
|
+
retain: Function.prototype.call.bind(WeakMap.prototype.set),
|
|
236
|
+
lookup: Function.prototype.call.bind(WeakMap.prototype.get),
|
|
237
|
+
slice: Function.prototype.call.bind(String.prototype.slice),
|
|
238
|
+
nativeSetTimeout: setTimeout, toNumber: Number,
|
|
239
|
+
exec: Function.prototype.call.bind(RegExp.prototype.exec),
|
|
240
|
+
hasOwn: Function.prototype.call.bind(Object.prototype.hasOwnProperty),
|
|
241
|
+
push: Function.prototype.call.bind(Array.prototype.push),
|
|
242
|
+
map: Function.prototype.call.bind(Array.prototype.map),
|
|
243
|
+
join: Function.prototype.call.bind(Array.prototype.join), toString: String,
|
|
244
|
+
fromCharCode: String.fromCharCode, min: Math.min,
|
|
245
|
+
subarray: Function.prototype.call.bind(Uint8Array.prototype.subarray),
|
|
246
|
+
charCodeAt: Function.prototype.call.bind(String.prototype.charCodeAt),
|
|
247
|
+
bufferSlice: Function.prototype.call.bind(ArrayBuffer.prototype.slice),
|
|
248
|
+
isView: ArrayBuffer.isView, nativeBtoa: btoa, nativeAtob: atob,
|
|
249
|
+
race: Promise.race.bind(Promise), jsonParse: JSON.parse, jsonStringify: JSON.stringify,
|
|
250
|
+
nativeConsole: console
|
|
251
|
+
});
|
|
252
|
+
`;
|
|
253
|
+
const binaryTagCheck = "(__CODEMODE_BINARY_TAG in value)";
|
|
254
|
+
const binaryDataCheck = 'typeof value.data !== "string"';
|
|
255
|
+
if (!isolated.includes(binaryTagCheck) || !isolated.includes(binaryDataCheck)) {
|
|
256
|
+
throw new Error("Worker binary codec contract changed.");
|
|
257
|
+
}
|
|
258
|
+
isolated = isolated
|
|
259
|
+
.replace("async evaluate(__dispatchers = {}, __connectors = {}) {", `async evaluate(__dispatchers = {}, __connectors = {}, ${programName}) {\n${wrapper}`)
|
|
260
|
+
.replaceAll("JSON.parse(", "jsonParse(")
|
|
261
|
+
.replaceAll("JSON.stringify(", "jsonStringify(")
|
|
262
|
+
.replaceAll(binaryTagCheck, "hasOwn(value, __CODEMODE_BINARY_TAG)")
|
|
263
|
+
.replaceAll(binaryDataCheck, '!hasOwn(value, "data") || typeof value.data !== "string"')
|
|
264
|
+
.replaceAll("console.", "nativeConsole.")
|
|
265
|
+
.replaceAll("new Proxy(", "new NativeProxy(")
|
|
266
|
+
.replaceAll("Object.prototype.hasOwnProperty.call(target, toolName)", "hasOwn(target, toolName)")
|
|
267
|
+
.replaceAll("String(toolName)", "toString(toolName)")
|
|
268
|
+
.replaceAll('a.map(String).join(" ")', 'join(map(a, toString), " ")')
|
|
269
|
+
.replaceAll("__logs.push(", "push(__logs, ")
|
|
270
|
+
.replaceAll("String.fromCharCode(", "fromCharCode(")
|
|
271
|
+
.replaceAll("bytes.subarray(i, Math.min(i + chunkSize, bytes.byteLength))", "subarray(bytes, i, min(i + chunkSize, bytes.byteLength))")
|
|
272
|
+
.replaceAll("btoa(binary)", "nativeBtoa(binary)")
|
|
273
|
+
.replaceAll("atob(b64)", "nativeAtob(b64)")
|
|
274
|
+
.replaceAll("new Uint8Array(", "new NativeUint8Array(")
|
|
275
|
+
.replaceAll("instanceof Uint8Array", "instanceof NativeUint8Array")
|
|
276
|
+
.replaceAll("instanceof ArrayBuffer", "instanceof NativeArrayBuffer")
|
|
277
|
+
.replaceAll("ArrayBuffer.isView(", "isView(")
|
|
278
|
+
.replaceAll("binary.charCodeAt(i)", "charCodeAt(binary, i)")
|
|
279
|
+
.replaceAll("bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)", "bufferSlice(bytes.buffer, bytes.byteOffset, bytes.byteOffset + bytes.byteLength)")
|
|
280
|
+
.replaceAll("Promise.race(", "race(")
|
|
281
|
+
.replaceAll("new Promise(", "new NativePromise(");
|
|
282
|
+
// Recompute the insertion point after changing the generated runner.
|
|
283
|
+
const setup = firstInitializer
|
|
284
|
+
? isolated.indexOf(firstInitializer)
|
|
285
|
+
: isolated.indexOf(" try {\n const result = await race");
|
|
286
|
+
if (setup < 0)
|
|
287
|
+
throw new Error("Worker provider setup was unavailable.");
|
|
288
|
+
const runner = hardening +
|
|
289
|
+
(captureGuest
|
|
290
|
+
? `const __connecta_guest_initialize = ${guestInitializer()};\n`
|
|
291
|
+
: `${guestPromiseInitializer()};\n`) +
|
|
292
|
+
captures +
|
|
293
|
+
"let initialized = false;\n" +
|
|
294
|
+
initializer +
|
|
295
|
+
isolated.slice(0, setup) +
|
|
296
|
+
globals +
|
|
297
|
+
"\n" +
|
|
298
|
+
isolated.slice(setup);
|
|
299
|
+
const main = imports +
|
|
300
|
+
`export default class CodeExecutor extends Runner {
|
|
301
|
+
evaluate(dispatchers, connectors) { return super.evaluate(dispatchers, connectors, program); }
|
|
302
|
+
}`;
|
|
303
|
+
const routed = routeGuestImports(guest?.program ?? executableCode);
|
|
304
|
+
return track(Reflect.apply(Reflect.get(target, key), target, [
|
|
305
|
+
{
|
|
306
|
+
...definition,
|
|
307
|
+
modules: {
|
|
308
|
+
...definition.modules,
|
|
309
|
+
"executor.js": main,
|
|
310
|
+
"connecta-runner.js": runner,
|
|
311
|
+
"connecta-guest.js": `const startsWith = Function.prototype.call.bind(String.prototype.startsWith); const reject = Promise.reject.bind(Promise); const NativeError = Error; const __connecta_import = specifier => typeof specifier === "string" && ((startsWith(specifier, "node:") && specifier !== "node:module" && specifier !== "node:process") || specifier === "cloudflare:workers") ? import(specifier) : reject(new NativeError("Imports of runner modules are outside the guest API."));\nexport default (${routed}\n);`,
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
...args.slice(1),
|
|
315
|
+
]));
|
|
316
|
+
};
|
|
317
|
+
const value = Reflect.get(target, key);
|
|
318
|
+
return typeof value === "function" ? (...args) => Reflect.apply(value, target, args) : value;
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
const lease = {
|
|
322
|
+
waitMs: permit.waitMs,
|
|
323
|
+
async execute(code, providers) {
|
|
324
|
+
if (released)
|
|
325
|
+
throw new Error("Executor lease was already released.");
|
|
326
|
+
if (executed)
|
|
327
|
+
throw new Error("Executor lease may execute only once.");
|
|
328
|
+
executed = true;
|
|
329
|
+
hostProviders = providers;
|
|
330
|
+
// Extract the fixed wrapper from the original source. Upstream may
|
|
331
|
+
// wrap invalid syntax as a bare body; that must not turn an attempted
|
|
332
|
+
// module escape into runnable statements or shift guest locations.
|
|
333
|
+
guestProgram = isolateGuestProgram(code.trim());
|
|
334
|
+
executableCode = normalizeCode(code);
|
|
335
|
+
const original = executableCode.indexOf(code.trim());
|
|
336
|
+
if (original >= 0)
|
|
337
|
+
normalizationLines = executableCode.slice(0, original).split("\n").length - 1;
|
|
338
|
+
const detached = providers.map((provider, index) => ({
|
|
339
|
+
name: provider.name,
|
|
340
|
+
...(provider.prelude ? { prelude: provider.prelude } : {}),
|
|
341
|
+
fns: Object.fromEntries(Object.keys(provider.fns).map((name) => [name, forward(index, name)])),
|
|
342
|
+
}));
|
|
343
|
+
const stopped = new Promise((_resolve, reject) => {
|
|
344
|
+
cancel = () => reject(new ExecutorExecutionError("executor_cancelled", "Worker executor lease was released during execution."));
|
|
345
|
+
});
|
|
346
|
+
const started = Date.now();
|
|
347
|
+
try {
|
|
348
|
+
const result = await Promise.race([
|
|
349
|
+
new DynamicWorkerExecutor({
|
|
350
|
+
loader,
|
|
351
|
+
...(options.timeout !== undefined ? { timeout: options.timeout } : {}),
|
|
352
|
+
}).execute(code, detached),
|
|
353
|
+
stopped,
|
|
354
|
+
]);
|
|
355
|
+
const outcome = rpcOutcome ?? result;
|
|
356
|
+
if (outcome.failure) {
|
|
357
|
+
const failure = { ...outcome.failure };
|
|
358
|
+
const call = typeof failure.failureId === "string" ? hostFailures.get(failure.failureId) : undefined;
|
|
359
|
+
// Neither an unknown id nor a guest/RPC-supplied call record
|
|
360
|
+
// supplies typed host details. Strip the private id at the edge.
|
|
361
|
+
delete failure.call;
|
|
362
|
+
delete failure.failureId;
|
|
363
|
+
outcome.failure = { ...failure, ...(call ? { call } : {}) };
|
|
364
|
+
}
|
|
365
|
+
if (outcome.failure?.timeout)
|
|
366
|
+
outcome.failure.timeout.elapsedMs = Date.now() - started;
|
|
367
|
+
return outcome;
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
const message = error instanceof Error ? error.message : "";
|
|
371
|
+
// Worker Loader erases the native SyntaxError type at its RPC edge.
|
|
372
|
+
// Match its startup diagnostic only; guest/downstream failures return normally.
|
|
373
|
+
const syntax = /^Failed to start Worker:\nUncaught SyntaxError: ([^\n]*)\n\s+at connecta-guest\.js:(\d+):\d+$/.exec(message);
|
|
374
|
+
if (error instanceof SyntaxError || syntax) {
|
|
375
|
+
const location = error instanceof SyntaxError && "loc" in error ? error.loc : undefined;
|
|
376
|
+
const line = syntax
|
|
377
|
+
? Math.max(1, Number(syntax[2]) - normalizationLines - 1)
|
|
378
|
+
: location?.line !== undefined
|
|
379
|
+
? Math.max(1, location.line)
|
|
380
|
+
: undefined;
|
|
381
|
+
return {
|
|
382
|
+
result: undefined,
|
|
383
|
+
error: (syntax?.[1] ?? message) || "Invalid JavaScript program.",
|
|
384
|
+
failure: { name: "SyntaxError", ...(line !== undefined ? { line } : {}) },
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
throw error;
|
|
388
|
+
}
|
|
389
|
+
finally {
|
|
390
|
+
runEnded = true;
|
|
391
|
+
cancel = undefined;
|
|
392
|
+
hostFailures.clear();
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
release() {
|
|
396
|
+
if (released)
|
|
397
|
+
return;
|
|
398
|
+
released = true;
|
|
399
|
+
hostProviders = undefined;
|
|
400
|
+
hostFailures.clear();
|
|
401
|
+
cancel?.();
|
|
402
|
+
for (const dispose of disposers.reverse()) {
|
|
403
|
+
try {
|
|
404
|
+
dispose();
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
/* Release every handle and the permit. */
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
disposers.length = 0;
|
|
411
|
+
active.delete(lease);
|
|
412
|
+
permit.release();
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
active.add(lease);
|
|
416
|
+
return lease;
|
|
417
|
+
},
|
|
418
|
+
async execute(code, providers) {
|
|
419
|
+
const lease = await this.acquire();
|
|
420
|
+
try {
|
|
421
|
+
return await lease.execute(code, providers);
|
|
422
|
+
}
|
|
423
|
+
finally {
|
|
424
|
+
lease.release();
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
admissionSnapshot: () => admission.snapshot(),
|
|
428
|
+
close() {
|
|
429
|
+
admission.close();
|
|
430
|
+
for (const lease of active)
|
|
431
|
+
lease.release();
|
|
432
|
+
},
|
|
433
|
+
}, "leased");
|
|
434
|
+
}
|