@zackbart/connecta 0.24.4 → 0.26.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/AGENTS.md +43 -12
- package/CHANGELOG.md +524 -0
- package/README.md +32 -15
- package/SECURITY.md +6 -5
- package/bin/connecta.mjs +5 -1
- package/dist/activity.d.ts +10 -3
- package/dist/activity.js +10 -1
- package/dist/advertised-schema.js +53 -0
- package/dist/artifacts/connector.d.ts +55 -0
- package/dist/artifacts/connector.js +724 -0
- package/dist/artifacts/document.js +76 -0
- package/dist/artifacts/guide.js +140 -0
- package/dist/artifacts/html-scan.js +329 -0
- package/dist/artifacts/json.js +68 -0
- package/dist/artifacts/kv-store.d.ts +20 -0
- package/dist/artifacts/kv-store.js +140 -0
- package/dist/artifacts/markdown.js +449 -0
- package/dist/artifacts/module.d.ts +37 -0
- package/dist/artifacts/module.js +60 -0
- package/dist/artifacts/operations.d.ts +195 -0
- package/dist/artifacts/operations.js +944 -0
- package/dist/artifacts/refresh.d.ts +53 -0
- package/dist/artifacts/refresh.js +235 -0
- package/dist/artifacts/routes.js +194 -0
- package/dist/artifacts/types.d.ts +214 -0
- package/dist/artifacts/types.js +22 -0
- package/dist/artifacts/validate.d.ts +62 -0
- package/dist/artifacts/validate.js +652 -0
- package/dist/artifacts.d.ts +6 -0
- package/dist/artifacts.js +9 -0
- package/dist/auth/downstream-oauth.js +490 -106
- package/dist/call-admission.js +139 -113
- package/dist/catalog-service.js +152 -106
- package/dist/catalog.js +297 -38
- package/dist/concurrency.js +24 -23
- package/dist/connector-access.d.ts +18 -4
- package/dist/connector-access.js +65 -20
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +19 -0
- package/dist/connector-scope.js +6 -37
- package/dist/connectors/api.d.ts +2 -2
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +330 -240
- package/dist/credential-contract.d.ts +9 -0
- package/dist/credential-rules.js +2 -2
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +45 -3
- package/dist/errors.d.ts +16 -1
- package/dist/errors.js +28 -0
- package/dist/execute.js +745 -282
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +145 -63
- package/dist/executors/quickjs-runtime.js +4 -2
- package/dist/executors/quickjs.js +255 -212
- package/dist/fix-prompt.js +37 -0
- package/dist/index.d.ts +72 -7
- package/dist/index.js +281 -26
- package/dist/invocation.js +371 -286
- package/dist/meta-tools.js +213 -94
- package/dist/module-contracts.d.ts +21 -1
- package/dist/oauth-callback-outcome.js +94 -0
- package/dist/oauth-sealing.js +29 -0
- package/dist/operator-ui/fix-prompts.js +87 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +103 -1
- package/dist/operator-ui/setup-commands.js +53 -0
- package/dist/operator-ui/view.js +128 -3
- package/dist/program-source.js +321 -0
- package/dist/providers/notion.js +2 -2
- package/dist/providers/revenuecat.js +32 -5
- package/dist/providers/stripe.d.ts +3 -2
- package/dist/providers/vercel.js +1 -1
- package/dist/registry.d.ts +43 -7
- package/dist/registry.js +434 -288
- package/dist/resumable.js +1291 -0
- package/dist/routes/activity.js +74 -154
- package/dist/routes/actor-labels.js +114 -0
- package/dist/routes/credentials.js +196 -163
- package/dist/routes/mcp.js +228 -112
- package/dist/routes/oauth-management.js +73 -71
- package/dist/routes/oauth.js +44 -16
- package/dist/routes/operator.js +83 -0
- package/dist/routes/shared.d.ts +25 -1
- package/dist/routes/shared.js +20 -2
- package/dist/routes/ui-data.js +211 -0
- package/dist/routes/ui.js +76 -35
- package/dist/run-journal.js +347 -0
- package/dist/runtime/admission.js +43 -0
- package/dist/runtime/call-admission.js +67 -0
- package/dist/runtime/connector-scope.js +72 -0
- package/dist/runtime/run.js +174 -0
- package/dist/runtime/services.js +130 -0
- package/dist/runtime/shared-read.js +106 -0
- package/dist/runtime/storage.js +66 -0
- package/dist/server.js +137 -89
- package/dist/skills.js +28 -9
- package/dist/storage/file.js +39 -2
- package/dist/storage/memory.js +28 -16
- package/dist/timeout.js +39 -43
- package/dist/tool-safety.d.ts +27 -1
- package/dist/tool-safety.js +31 -0
- package/dist/types.d.ts +45 -7
- package/dist/ui.d.ts +25 -4
- package/dist/ui.js +77 -167
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +367 -16
- package/documentation/auth.md +163 -2
- package/documentation/code-mode.md +274 -56
- package/documentation/meta-tools.md +200 -44
- package/ethos.md +46 -47
- package/examples/worker/AGENTS.md +6 -0
- package/examples/worker/README.md +224 -14
- package/examples/worker/src/d1-activity-row.ts +14 -2
- package/examples/worker/src/d1-activity.ts +4 -2
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +42 -7
- package/examples/worker/src/r2-artifact-blobs.ts +29 -0
- package/examples/worker/wrangler.jsonc +21 -5
- package/package.json +16 -4
- package/templates/node/AGENTS.md +9 -2
- package/templates/node/README.md +31 -3
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +27 -0
- package/dist/auth/downstream-oauth.d.ts +0 -200
- package/dist/catalog-fingerprint.d.ts +0 -19
- package/dist/catalog-limits.d.ts +0 -9
- package/dist/catalog-service.d.ts +0 -263
- package/dist/catalog.d.ts +0 -87
- package/dist/concurrency.d.ts +0 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -96
- package/dist/credential-rules.d.ts +0 -85
- package/dist/execute.d.ts +0 -154
- package/dist/executor-result.d.ts +0 -10
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -67
- package/dist/favicon.d.ts +0 -1
- package/dist/identity.d.ts +0 -4
- package/dist/invocation.d.ts +0 -74
- package/dist/mcp-result.d.ts +0 -8
- package/dist/meta-tools.d.ts +0 -129
- package/dist/operator-ui/generated.d.ts +0 -2
- package/dist/operator-ui/view.d.ts +0 -188
- package/dist/result-shapes.d.ts +0 -13
- package/dist/retired-toolkits.d.ts +0 -2
- package/dist/routes/activity.d.ts +0 -2
- package/dist/routes/credentials.d.ts +0 -2
- package/dist/routes/mcp.d.ts +0 -10
- package/dist/routes/oauth-management.d.ts +0 -2
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/ui.d.ts +0 -2
- package/dist/server.d.ts +0 -9
- package/dist/skills.d.ts +0 -44
- package/dist/timeout.d.ts +0 -21
|
@@ -23,6 +23,8 @@ export declare class ExecutorExecutionError extends ExecutorAdmissionError {
|
|
|
23
23
|
export interface AdmissionLease {
|
|
24
24
|
/** Time spent waiting behind active work. Zero for immediate admission. */
|
|
25
25
|
readonly waitMs: number;
|
|
26
|
+
/** Time left in the optional total admitted-request lifetime. */
|
|
27
|
+
remainingMs(): number | undefined;
|
|
26
28
|
release(): void;
|
|
27
29
|
}
|
|
28
30
|
export interface AdmissionControllerOptions {
|
|
@@ -31,6 +33,8 @@ export interface AdmissionControllerOptions {
|
|
|
31
33
|
queueTimeoutMs: number;
|
|
32
34
|
/** Suggested delay exposed with retryable overload failures. */
|
|
33
35
|
retryAfterMs?: number;
|
|
36
|
+
/** Hard admitted-request lifetime; omitted for executor admission. */
|
|
37
|
+
maxDurationMs?: number;
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
40
|
* Runtime-portable bounded admission. It deliberately owns no executor or
|
|
@@ -42,7 +46,9 @@ export declare class AdmissionController {
|
|
|
42
46
|
readonly maxQueueSize: number;
|
|
43
47
|
readonly queueTimeoutMs: number;
|
|
44
48
|
readonly retryAfterMs: number;
|
|
49
|
+
readonly maxDurationMs: number | undefined;
|
|
45
50
|
private active;
|
|
51
|
+
private readonly leases;
|
|
46
52
|
private closed;
|
|
47
53
|
private readonly waiters;
|
|
48
54
|
private admittedTotal;
|
|
@@ -63,7 +69,11 @@ export declare class AdmissionController {
|
|
|
63
69
|
close(): void;
|
|
64
70
|
private overloaded;
|
|
65
71
|
private makeLease;
|
|
66
|
-
private
|
|
72
|
+
private releaseSlot;
|
|
73
|
+
private fillSlots;
|
|
74
|
+
private pruneExpiredWaiters;
|
|
75
|
+
private reapExpired;
|
|
76
|
+
private nextReapDelay;
|
|
67
77
|
private remove;
|
|
68
78
|
private cleanup;
|
|
69
79
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Deferred, Duration, Effect } from "effect";
|
|
2
|
+
import { admit, provideAdmissionProgram } from "./runtime/admission.js";
|
|
3
|
+
import { fromSignal, runEdge } from "./runtime/run.js";
|
|
1
4
|
/**
|
|
2
5
|
* A stable, machine-readable admission failure. Overload is retryable; caller
|
|
3
6
|
* cancellation and shutdown are terminal for this invocation.
|
|
@@ -51,7 +54,9 @@ export class AdmissionController {
|
|
|
51
54
|
maxQueueSize;
|
|
52
55
|
queueTimeoutMs;
|
|
53
56
|
retryAfterMs;
|
|
57
|
+
maxDurationMs;
|
|
54
58
|
active = 0;
|
|
59
|
+
leases = new Map();
|
|
55
60
|
closed = false;
|
|
56
61
|
waiters = [];
|
|
57
62
|
admittedTotal = 0;
|
|
@@ -67,6 +72,12 @@ export class AdmissionController {
|
|
|
67
72
|
this.maxQueueSize = nonNegativeWhole(options.maxQueueSize, "maxQueueSize");
|
|
68
73
|
this.queueTimeoutMs = positiveWhole(options.queueTimeoutMs, "queueTimeoutMs");
|
|
69
74
|
this.retryAfterMs = nonNegativeWhole(options.retryAfterMs ?? this.queueTimeoutMs, "retryAfterMs");
|
|
75
|
+
this.maxDurationMs = options.maxDurationMs === undefined
|
|
76
|
+
? undefined
|
|
77
|
+
: positiveWhole(options.maxDurationMs, "maxDurationMs");
|
|
78
|
+
if (this.maxDurationMs !== undefined && this.maxDurationMs > 2_147_483_647) {
|
|
79
|
+
throw new TypeError("maxDurationMs must be at most 2,147,483,647 milliseconds.");
|
|
80
|
+
}
|
|
70
81
|
}
|
|
71
82
|
get activeCount() {
|
|
72
83
|
return this.active;
|
|
@@ -75,11 +86,13 @@ export class AdmissionController {
|
|
|
75
86
|
return this.waiters.length;
|
|
76
87
|
}
|
|
77
88
|
snapshot() {
|
|
89
|
+
this.reapExpired();
|
|
78
90
|
return {
|
|
79
91
|
concurrency: this.concurrency,
|
|
80
92
|
maxQueueSize: this.maxQueueSize,
|
|
81
93
|
queueTimeoutMs: this.queueTimeoutMs,
|
|
82
94
|
retryAfterMs: this.retryAfterMs,
|
|
95
|
+
...(this.maxDurationMs !== undefined ? { maxDurationMs: this.maxDurationMs } : {}),
|
|
83
96
|
active: this.active,
|
|
84
97
|
queued: this.waiters.length,
|
|
85
98
|
closed: this.closed,
|
|
@@ -98,57 +111,15 @@ export class AdmissionController {
|
|
|
98
111
|
};
|
|
99
112
|
}
|
|
100
113
|
acquire(options = {}) {
|
|
101
|
-
|
|
102
|
-
this.closedTotal++;
|
|
103
|
-
return Promise.reject(new ExecutorAdmissionError("executor_closed", "Executor is shutting down."));
|
|
104
|
-
}
|
|
105
|
-
if (options.signal?.aborted) {
|
|
106
|
-
this.cancelledTotal++;
|
|
107
|
-
return Promise.reject(new ExecutorAdmissionError("executor_cancelled", "Execution was cancelled before admission."));
|
|
108
|
-
}
|
|
109
|
-
if (this.active < this.concurrency) {
|
|
110
|
-
this.active++;
|
|
111
|
-
this.admittedTotal++;
|
|
112
|
-
return Promise.resolve(this.makeLease(0));
|
|
113
|
-
}
|
|
114
|
-
if (this.waiters.length >= this.maxQueueSize) {
|
|
115
|
-
this.rejectedTotal++;
|
|
116
|
-
return Promise.reject(this.overloaded("Executor queue is full."));
|
|
117
|
-
}
|
|
118
|
-
return new Promise((resolve, reject) => {
|
|
119
|
-
const waiter = {
|
|
120
|
-
queuedAt: Date.now(),
|
|
121
|
-
resolve,
|
|
122
|
-
reject,
|
|
123
|
-
...(options.signal ? { signal: options.signal } : {}),
|
|
124
|
-
};
|
|
125
|
-
waiter.onAbort = () => {
|
|
126
|
-
if (!this.remove(waiter))
|
|
127
|
-
return;
|
|
128
|
-
this.cleanup(waiter);
|
|
129
|
-
this.cancelledTotal++;
|
|
130
|
-
reject(new ExecutorAdmissionError("executor_cancelled", "Execution was cancelled while queued."));
|
|
131
|
-
};
|
|
132
|
-
options.signal?.addEventListener("abort", waiter.onAbort, { once: true });
|
|
133
|
-
waiter.timer = setTimeout(() => {
|
|
134
|
-
if (!this.remove(waiter))
|
|
135
|
-
return;
|
|
136
|
-
this.cleanup(waiter);
|
|
137
|
-
this.rejectedTotal++;
|
|
138
|
-
reject(this.overloaded(`Executor admission timed out after ${this.queueTimeoutMs}ms.`));
|
|
139
|
-
}, this.queueTimeoutMs);
|
|
140
|
-
this.waiters.push(waiter);
|
|
141
|
-
this.queuedTotal++;
|
|
142
|
-
});
|
|
114
|
+
return runEdge(admit(this, options));
|
|
143
115
|
}
|
|
144
116
|
close() {
|
|
145
117
|
if (this.closed)
|
|
146
118
|
return;
|
|
147
119
|
this.closed = true;
|
|
148
120
|
for (const waiter of this.waiters.splice(0)) {
|
|
149
|
-
this.cleanup(waiter);
|
|
150
121
|
this.closedTotal++;
|
|
151
|
-
waiter.
|
|
122
|
+
Deferred.doneUnsafe(waiter.outcome, Effect.fail(new ExecutorAdmissionError("executor_closed", "Executor is shutting down.")));
|
|
152
123
|
}
|
|
153
124
|
}
|
|
154
125
|
overloaded(message) {
|
|
@@ -157,33 +128,83 @@ export class AdmissionController {
|
|
|
157
128
|
});
|
|
158
129
|
}
|
|
159
130
|
makeLease(waitMs) {
|
|
160
|
-
|
|
131
|
+
const id = Symbol("admission lease");
|
|
132
|
+
const record = this.maxDurationMs !== undefined
|
|
133
|
+
? { expiresAt: Date.now() + this.maxDurationMs }
|
|
134
|
+
: {};
|
|
135
|
+
this.leases.set(id, record);
|
|
161
136
|
return {
|
|
162
137
|
waitMs,
|
|
138
|
+
remainingMs: () => !this.leases.has(id)
|
|
139
|
+
? 0
|
|
140
|
+
: record.expiresAt === undefined
|
|
141
|
+
? undefined
|
|
142
|
+
: Math.max(0, record.expiresAt - Date.now()),
|
|
163
143
|
release: () => {
|
|
164
|
-
if (
|
|
144
|
+
if (!this.leases.delete(id))
|
|
165
145
|
return;
|
|
166
|
-
|
|
167
|
-
this.release();
|
|
146
|
+
this.releaseSlot();
|
|
168
147
|
},
|
|
169
148
|
};
|
|
170
149
|
}
|
|
171
|
-
|
|
150
|
+
releaseSlot() {
|
|
172
151
|
if (this.active > 0)
|
|
173
152
|
this.active--;
|
|
174
153
|
if (this.closed)
|
|
175
154
|
return;
|
|
176
|
-
|
|
177
|
-
|
|
155
|
+
this.fillSlots();
|
|
156
|
+
}
|
|
157
|
+
fillSlots() {
|
|
158
|
+
this.pruneExpiredWaiters();
|
|
159
|
+
while (this.active < this.concurrency) {
|
|
160
|
+
const waiter = this.waiters.shift();
|
|
161
|
+
if (!waiter)
|
|
162
|
+
return;
|
|
163
|
+
const waitMs = Math.max(0, Date.now() - waiter.queuedAt);
|
|
164
|
+
this.admittedTotal++;
|
|
165
|
+
this.queueWaitCount++;
|
|
166
|
+
this.queueWaitTotalMs += waitMs;
|
|
167
|
+
this.queueWaitMaxMs = Math.max(this.queueWaitMaxMs, waitMs);
|
|
168
|
+
this.active++;
|
|
169
|
+
Deferred.doneUnsafe(waiter.outcome, Effect.succeed(this.makeLease(waitMs)));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
pruneExpiredWaiters() {
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
for (let index = this.waiters.length - 1; index >= 0; index--) {
|
|
175
|
+
const waiter = this.waiters[index];
|
|
176
|
+
if (now - waiter.queuedAt < this.queueTimeoutMs)
|
|
177
|
+
continue;
|
|
178
|
+
this.waiters.splice(index, 1);
|
|
179
|
+
this.rejectedTotal++;
|
|
180
|
+
Deferred.doneUnsafe(waiter.outcome, Effect.fail(this.overloaded(`Executor admission timed out after ${this.queueTimeoutMs}ms.`)));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
reapExpired() {
|
|
184
|
+
this.pruneExpiredWaiters();
|
|
185
|
+
if (this.maxDurationMs === undefined)
|
|
178
186
|
return;
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
const now = Date.now();
|
|
188
|
+
for (const [id, lease] of this.leases) {
|
|
189
|
+
if (lease.expiresAt !== undefined && lease.expiresAt <= now) {
|
|
190
|
+
this.leases.delete(id);
|
|
191
|
+
this.active--;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!this.closed)
|
|
195
|
+
this.fillSlots();
|
|
196
|
+
}
|
|
197
|
+
nextReapDelay() {
|
|
198
|
+
let next = Number.POSITIVE_INFINITY;
|
|
199
|
+
for (const lease of this.leases.values()) {
|
|
200
|
+
if (lease.expiresAt !== undefined)
|
|
201
|
+
next = Math.min(next, lease.expiresAt);
|
|
202
|
+
}
|
|
203
|
+
// A queued request's own timer reaps the earliest admitted lease. A
|
|
204
|
+
// fallback check covers a briefly empty lease map after a handoff.
|
|
205
|
+
return Number.isFinite(next)
|
|
206
|
+
? Math.max(1, next - Date.now())
|
|
207
|
+
: 250;
|
|
187
208
|
}
|
|
188
209
|
remove(waiter) {
|
|
189
210
|
const index = this.waiters.indexOf(waiter);
|
|
@@ -192,12 +213,73 @@ export class AdmissionController {
|
|
|
192
213
|
this.waiters.splice(index, 1);
|
|
193
214
|
return true;
|
|
194
215
|
}
|
|
216
|
+
// The waiting fiber was interrupted — an Effect caller's scope closed, not a
|
|
217
|
+
// signal. Withdraw the waiter if it is still queued; if release() handed it
|
|
218
|
+
// a slot in the same breath, give the slot back, because nobody is left to
|
|
219
|
+
// receive the lease. (A line comment, not JSDoc: TypeScript copies a private
|
|
220
|
+
// member's JSDoc into the published declaration.)
|
|
195
221
|
cleanup(waiter) {
|
|
196
|
-
if (waiter
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
waiter.signal?.removeEventListener("abort", waiter.onAbort);
|
|
222
|
+
if (this.remove(waiter)) {
|
|
223
|
+
this.cancelledTotal++;
|
|
224
|
+
return Effect.void;
|
|
200
225
|
}
|
|
226
|
+
if (!Deferred.isDoneUnsafe(waiter.outcome))
|
|
227
|
+
return Effect.void;
|
|
228
|
+
return Deferred.await(waiter.outcome).pipe(Effect.tap((lease) => Effect.sync(() => lease.release())), Effect.ignore);
|
|
229
|
+
}
|
|
230
|
+
// The admission program, handed to src/runtime/admission.ts. It lives in a
|
|
231
|
+
// static block because the class declaration ships, private member names
|
|
232
|
+
// and all: a new method or field would change the published `.d.ts`, and a
|
|
233
|
+
// static block is the one place outside an instance method that may read
|
|
234
|
+
// this bookkeeping while emitting nothing there.
|
|
235
|
+
static {
|
|
236
|
+
provideAdmissionProgram((controller, signal) => Effect.suspend(() => {
|
|
237
|
+
controller.reapExpired();
|
|
238
|
+
if (controller.closed) {
|
|
239
|
+
controller.closedTotal++;
|
|
240
|
+
return Effect.fail(new ExecutorAdmissionError("executor_closed", "Executor is shutting down."));
|
|
241
|
+
}
|
|
242
|
+
if (signal?.aborted) {
|
|
243
|
+
controller.cancelledTotal++;
|
|
244
|
+
return Effect.fail(new ExecutorAdmissionError("executor_cancelled", "Execution was cancelled before admission."));
|
|
245
|
+
}
|
|
246
|
+
if (controller.active < controller.concurrency) {
|
|
247
|
+
controller.active++;
|
|
248
|
+
controller.admittedTotal++;
|
|
249
|
+
return Effect.succeed(controller.makeLease(0));
|
|
250
|
+
}
|
|
251
|
+
if (controller.waiters.length >= controller.maxQueueSize) {
|
|
252
|
+
controller.rejectedTotal++;
|
|
253
|
+
return Effect.fail(controller.overloaded("Executor queue is full."));
|
|
254
|
+
}
|
|
255
|
+
const waiter = {
|
|
256
|
+
queuedAt: Date.now(),
|
|
257
|
+
outcome: Deferred.makeUnsafe(),
|
|
258
|
+
};
|
|
259
|
+
controller.waiters.push(waiter);
|
|
260
|
+
controller.queuedTotal++;
|
|
261
|
+
// A timeout or abort settles the waiter only if it takes it out of the
|
|
262
|
+
// queue first. Otherwise release() or close() got there in the same
|
|
263
|
+
// tick, and the outcome they recorded is the answer.
|
|
264
|
+
const giveUp = (count, error) => Effect.suspend(() => {
|
|
265
|
+
if (!controller.remove(waiter))
|
|
266
|
+
return Deferred.await(waiter.outcome);
|
|
267
|
+
count();
|
|
268
|
+
return Effect.fail(error());
|
|
269
|
+
});
|
|
270
|
+
// One flat race; each contender is a forked fiber.
|
|
271
|
+
const contenders = [
|
|
272
|
+
Deferred.await(waiter.outcome),
|
|
273
|
+
Effect.sleep(Duration.millis(controller.queueTimeoutMs)).pipe(Effect.andThen(giveUp(() => controller.rejectedTotal++, () => controller.overloaded(`Executor admission timed out after ${controller.queueTimeoutMs}ms.`)))),
|
|
274
|
+
];
|
|
275
|
+
if (controller.maxDurationMs !== undefined) {
|
|
276
|
+
contenders.push(Effect.forever(Effect.suspend(() => Effect.sleep(Duration.millis(controller.nextReapDelay()))).pipe(Effect.andThen(Effect.sync(() => controller.reapExpired())))));
|
|
277
|
+
}
|
|
278
|
+
if (signal) {
|
|
279
|
+
contenders.push(fromSignal(signal).pipe(Effect.catch(() => giveUp(() => controller.cancelledTotal++, () => new ExecutorAdmissionError("executor_cancelled", "Execution was cancelled while queued.")))));
|
|
280
|
+
}
|
|
281
|
+
return Effect.raceAllFirst(contenders).pipe(Effect.onInterrupt(() => controller.cleanup(waiter)));
|
|
282
|
+
}));
|
|
201
283
|
}
|
|
202
284
|
}
|
|
203
285
|
/** Preserve the one-method Workers seam while recognizing richer Node executors. */
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// provider args/results must be JSON-serializable.
|
|
10
10
|
import { getQuickJS, } from "quickjs-emscripten";
|
|
11
11
|
import { msg } from "../errors.js";
|
|
12
|
+
import { withoutProgramTerminator } from "../program-source.js";
|
|
12
13
|
import { hostCallLabel, MAX_QUICKJS_LOG_TRANSPORT_BYTES, serializedBytes, } from "./quickjs-protocol.js";
|
|
13
14
|
/** Load and compile the shared QuickJS WASM module before a run budget starts. */
|
|
14
15
|
export async function prepareQuickJs() {
|
|
@@ -70,8 +71,9 @@ export function normalizeCode(code) {
|
|
|
70
71
|
// lines — models routinely prefix their code with a `//` or `/* */` note,
|
|
71
72
|
// which would otherwise defeat the detection and wrap the whole thing.
|
|
72
73
|
const head = c.replace(/^(?:\s+|\/\/[^\n]*|\/\*[\s\S]*?\*\/)+/, "");
|
|
73
|
-
if (/^async\b/.test(head) || head.startsWith("("))
|
|
74
|
-
return c;
|
|
74
|
+
if (/^async\b/.test(head) || head.startsWith("(")) {
|
|
75
|
+
return withoutProgramTerminator(c);
|
|
76
|
+
}
|
|
75
77
|
return `async () => {\n${c}\n}`;
|
|
76
78
|
}
|
|
77
79
|
/**
|