@zackbart/connecta 0.26.3 → 0.27.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 +5 -5
- package/CHANGELOG.md +72 -1
- package/bin/connecta.mjs +9 -1
- package/dist/access-tokens.js +13 -2
- package/dist/artifacts/connector.d.ts +3 -0
- package/dist/artifacts/connector.js +6 -0
- package/dist/artifacts/document.js +6 -0
- package/dist/artifacts/module.js +7 -1
- package/dist/artifacts/operations.d.ts +3 -1
- package/dist/artifacts/operations.js +68 -10
- package/dist/artifacts/refresh.d.ts +2 -0
- package/dist/artifacts/refresh.js +3 -2
- package/dist/artifacts/routes.js +4 -15
- package/dist/artifacts/validate.js +1 -2
- package/dist/auth/downstream-oauth.js +95 -36
- package/dist/connector-scope.d.ts +2 -0
- package/dist/connector-scope.js +27 -0
- package/dist/connectors/api.js +8 -2
- package/dist/connectors/remote-mcp.js +57 -17
- package/dist/credentials.js +2 -1
- package/dist/execute.js +15 -0
- package/dist/index.js +1 -0
- package/dist/oauth-partition.js +32 -0
- package/dist/operator-ui/generated.js +1 -1
- package/dist/providers/notion.js +4 -1
- package/dist/providers/vercel.js +13 -9
- package/dist/registry.d.ts +1 -0
- package/dist/registry.js +58 -25
- package/dist/resumable.js +42 -10
- package/dist/routes/credentials.js +16 -8
- package/dist/routes/oauth.js +70 -64
- package/dist/runtime/run.js +7 -1
- package/dist/storage/file.js +57 -20
- package/dist/storage/memory.js +8 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +44 -3
- package/documentation/auth.md +7 -4
- package/examples/worker/README.md +4 -3
- package/examples/worker/src/d1-storage.ts +27 -5
- package/package.json +3 -3
- package/templates/node/package.json +2 -2
package/dist/storage/file.js
CHANGED
|
@@ -2,6 +2,23 @@ import { chmodSync, closeSync, existsSync, futimesSync, mkdirSync, openSync, rea
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
|
+
function validEntry(value) {
|
|
6
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) &&
|
|
7
|
+
"value" in value && typeof value.value === "string" &&
|
|
8
|
+
(!("exp" in value) || (typeof value.exp === "number" && Number.isFinite(value.exp)));
|
|
9
|
+
}
|
|
10
|
+
function makeEntry(value, ttlSeconds) {
|
|
11
|
+
if (ttlSeconds !== undefined && !Number.isFinite(ttlSeconds)) {
|
|
12
|
+
throw new RangeError("ttlSeconds must produce a finite expiration timestamp");
|
|
13
|
+
}
|
|
14
|
+
if (!ttlSeconds)
|
|
15
|
+
return { value };
|
|
16
|
+
const exp = Date.now() + ttlSeconds * 1000;
|
|
17
|
+
if (!Number.isFinite(exp)) {
|
|
18
|
+
throw new RangeError("ttlSeconds must produce a finite expiration timestamp");
|
|
19
|
+
}
|
|
20
|
+
return { value, exp };
|
|
21
|
+
}
|
|
5
22
|
const HEARTBEAT_MS = 15_000;
|
|
6
23
|
const STALE_LOCK_MS = 60_000;
|
|
7
24
|
const localLocks = new Map();
|
|
@@ -272,12 +289,19 @@ export function fileStorage(path, opts = {}) {
|
|
|
272
289
|
// Non-POSIX filesystem or a race on the file — leave the mode as-is.
|
|
273
290
|
}
|
|
274
291
|
};
|
|
275
|
-
let data =
|
|
292
|
+
let data = Object.create(null);
|
|
276
293
|
try {
|
|
277
294
|
if (existsSync(path)) {
|
|
278
295
|
tighten();
|
|
279
296
|
try {
|
|
280
|
-
|
|
297
|
+
const loaded = JSON.parse(readFileSync(path, "utf8"));
|
|
298
|
+
if (loaded === null || typeof loaded !== "object" || Array.isArray(loaded)) {
|
|
299
|
+
throw new TypeError("Expected a state object");
|
|
300
|
+
}
|
|
301
|
+
if (!Object.values(loaded).every(validEntry)) {
|
|
302
|
+
throw new TypeError("Expected string values and finite expiry timestamps");
|
|
303
|
+
}
|
|
304
|
+
data = Object.assign(Object.create(null), loaded);
|
|
281
305
|
}
|
|
282
306
|
catch (error) {
|
|
283
307
|
// Never let a damaged state file be silently replaced by an empty one:
|
|
@@ -299,7 +323,7 @@ export function fileStorage(path, opts = {}) {
|
|
|
299
323
|
`moved to ${quarantine}, starting from empty state. Downstream ` +
|
|
300
324
|
`OAuth connectors must be re-authorized and stored credentials ` +
|
|
301
325
|
`re-entered.`);
|
|
302
|
-
data =
|
|
326
|
+
data = Object.create(null);
|
|
303
327
|
}
|
|
304
328
|
}
|
|
305
329
|
}
|
|
@@ -315,13 +339,14 @@ export function fileStorage(path, opts = {}) {
|
|
|
315
339
|
// Reads only prune memory; they do not rewrite the state file.
|
|
316
340
|
const now = Date.now();
|
|
317
341
|
for (const [key, entry] of Object.entries(data)) {
|
|
318
|
-
if (entry.exp && now
|
|
342
|
+
if (entry.exp !== undefined && now >= entry.exp)
|
|
319
343
|
delete data[key];
|
|
320
344
|
}
|
|
321
345
|
const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
322
346
|
// 0o600 on the tmp file; the atomic rename below preserves it, so the live
|
|
323
347
|
// state file is never briefly world-readable.
|
|
324
348
|
const fd = openSync(tmp, "wx", 0o600);
|
|
349
|
+
let committed = false;
|
|
325
350
|
try {
|
|
326
351
|
try {
|
|
327
352
|
writeFileSync(fd, JSON.stringify(data));
|
|
@@ -331,9 +356,13 @@ export function fileStorage(path, opts = {}) {
|
|
|
331
356
|
}
|
|
332
357
|
lock.assertHeld();
|
|
333
358
|
renameSync(tmp, path);
|
|
359
|
+
committed = true;
|
|
334
360
|
}
|
|
335
361
|
finally {
|
|
336
|
-
|
|
362
|
+
// A successful rename already removed the temp path. Cleanup must not
|
|
363
|
+
// turn a committed snapshot into a failure that callers roll back.
|
|
364
|
+
if (!committed)
|
|
365
|
+
rmSync(tmp, { force: true });
|
|
337
366
|
}
|
|
338
367
|
tighten();
|
|
339
368
|
};
|
|
@@ -341,7 +370,7 @@ export function fileStorage(path, opts = {}) {
|
|
|
341
370
|
const e = data[key];
|
|
342
371
|
if (!e)
|
|
343
372
|
return null;
|
|
344
|
-
if (e.exp && Date.now()
|
|
373
|
+
if (e.exp !== undefined && Date.now() >= e.exp) {
|
|
345
374
|
delete data[key];
|
|
346
375
|
return null;
|
|
347
376
|
}
|
|
@@ -358,19 +387,32 @@ export function fileStorage(path, opts = {}) {
|
|
|
358
387
|
async set(key, value, opts) {
|
|
359
388
|
assertOpen();
|
|
360
389
|
lock.assertHeld();
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
390
|
+
const previous = fresh(key);
|
|
391
|
+
data[key] = makeEntry(value, opts?.ttlSeconds);
|
|
392
|
+
try {
|
|
393
|
+
persist();
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
if (previous)
|
|
397
|
+
data[key] = previous;
|
|
398
|
+
else
|
|
399
|
+
delete data[key];
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
368
402
|
},
|
|
369
403
|
async delete(key) {
|
|
370
404
|
assertOpen();
|
|
371
405
|
lock.assertHeld();
|
|
406
|
+
const previous = fresh(key);
|
|
372
407
|
delete data[key];
|
|
373
|
-
|
|
408
|
+
try {
|
|
409
|
+
persist();
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
if (previous)
|
|
413
|
+
data[key] = previous;
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
374
416
|
},
|
|
375
417
|
async list(prefix) {
|
|
376
418
|
assertOpen();
|
|
@@ -395,12 +437,7 @@ export function fileStorage(path, opts = {}) {
|
|
|
395
437
|
delete data[key];
|
|
396
438
|
}
|
|
397
439
|
else {
|
|
398
|
-
data[key] =
|
|
399
|
-
value: next,
|
|
400
|
-
...(opts?.ttlSeconds
|
|
401
|
-
? { exp: Date.now() + opts.ttlSeconds * 1000 }
|
|
402
|
-
: {}),
|
|
403
|
-
};
|
|
440
|
+
data[key] = makeEntry(next, opts?.ttlSeconds);
|
|
404
441
|
}
|
|
405
442
|
try {
|
|
406
443
|
persist();
|
package/dist/storage/memory.js
CHANGED
|
@@ -13,6 +13,13 @@ export function memoryStorage() {
|
|
|
13
13
|
return e;
|
|
14
14
|
};
|
|
15
15
|
const write = (key, value, ttlSeconds) => {
|
|
16
|
+
if (ttlSeconds !== undefined && !Number.isFinite(ttlSeconds)) {
|
|
17
|
+
throw new RangeError("ttlSeconds must produce a finite expiration timestamp");
|
|
18
|
+
}
|
|
19
|
+
const exp = ttlSeconds ? Date.now() + ttlSeconds * 1000 : undefined;
|
|
20
|
+
if (exp !== undefined && !Number.isFinite(exp)) {
|
|
21
|
+
throw new RangeError("ttlSeconds must produce a finite expiration timestamp");
|
|
22
|
+
}
|
|
16
23
|
// Rotate through at most 16 existing keys. Live entries cannot keep an
|
|
17
24
|
// expired tail resident forever, and no request starts a background job.
|
|
18
25
|
for (let i = 0; i < 16; i++) {
|
|
@@ -25,7 +32,7 @@ export function memoryStorage() {
|
|
|
25
32
|
}
|
|
26
33
|
map.set(key, {
|
|
27
34
|
value,
|
|
28
|
-
...(
|
|
35
|
+
...(exp !== undefined ? { exp } : {}),
|
|
29
36
|
});
|
|
30
37
|
};
|
|
31
38
|
return {
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -407,7 +407,8 @@ shape from building, in someone else's repository rather than this one.
|
|
|
407
407
|
|
|
408
408
|
## Effect inside
|
|
409
409
|
|
|
410
|
-
The core runs on Effect v4
|
|
410
|
+
The core runs on stable Effect v4, exact-pinned to `4.0.0`; no API a deployment
|
|
411
|
+
touches does. `createConnecta`,
|
|
411
412
|
`remoteMcp()`, `api()`, the `Connector` contract, and every shipped `.d.ts`
|
|
412
413
|
are Promise-shaped and name no Effect type, so a connector author never meets a
|
|
413
414
|
second async paradigm. The reason for the rewrite is the first section of this
|
|
@@ -569,8 +570,48 @@ the operator and activity routes cost about +100 KB gzip on `./ui` and +130 KB
|
|
|
569
570
|
on `./activity`, and matching the wire format meant opting out of most of what
|
|
570
571
|
it does: unowned paths fall through rather than 404, a wrong method is a JSON
|
|
571
572
|
405, and the content-type and size checks run before a body is read. The root
|
|
572
|
-
entry may import only `effect` itself (`test/purity.test.ts`)
|
|
573
|
-
`
|
|
573
|
+
entry may import only `effect` itself (`test/purity.test.ts`). Effect v4's
|
|
574
|
+
area imports, such as `effect/http-api` and `effect/ai`, would have to stay
|
|
575
|
+
behind a subpath. APIs tagged `@stability unstable` can still change in minor
|
|
576
|
+
releases, so stable v4 keeps the exact pin.
|
|
577
|
+
|
|
578
|
+
The stable `4.0.0` release was re-evaluated on 2026-10-01 with
|
|
579
|
+
[`scripts/probes/effect-v4-evaluation.mjs`](https://github.com/zackbart/connecta/blob/main/scripts/probes/effect-v4-evaluation.mjs).
|
|
580
|
+
Run `npm run build`, then `node scripts/probes/effect-v4-evaluation.mjs output.json`.
|
|
581
|
+
The [raw observations](https://github.com/zackbart/connecta/blob/main/scripts/probes/effect-v4-evaluation-2026-10-01.json)
|
|
582
|
+
are synthetic Node requests and neutral esbuild bundles, not live-client or
|
|
583
|
+
workerd lifetime verification. Bundle figures add representative prototypes
|
|
584
|
+
alongside the current code; they do not claim savings from removing old code.
|
|
585
|
+
|
|
586
|
+
Two native input schemas with validation and JSON Schema rendering add 62,148
|
|
587
|
+
bytes gzip to the root, taking it from 303,083 to 365,231 against a 339,526
|
|
588
|
+
cap. Effect can reject excess properties when both decoding and rendering use
|
|
589
|
+
`onExcessProperty: "error"`; its default strips them. The generated schemas
|
|
590
|
+
still differ from the exact meta-tool goldens, including record rendering and
|
|
591
|
+
composed numeric bounds. A Schema replacement would need to preserve that
|
|
592
|
+
contract, memoized rendering, and the raw-argument checks used by resumable
|
|
593
|
+
writes. Both MCP SDK packages still depend on Zod, so changing our inputs alone
|
|
594
|
+
does not remove Zod from an installation.
|
|
595
|
+
|
|
596
|
+
A one-endpoint `HttpApi` prototype adds 106,634 bytes gzip to `./ui` and 106,453
|
|
597
|
+
to `./activity`. Even plain `HttpRouter` adds 45,324 to `./ui`, taking it to
|
|
598
|
+
130,023 against a 128,983 cap. Both default handlers return an empty 404 for a
|
|
599
|
+
wrong method and an unowned path; our routes need a JSON 405 for the former
|
|
600
|
+
and module fallthrough for the latter. Their `toWebHandler` also builds its
|
|
601
|
+
layer immediately and owns a runner, so adopting it would require proving
|
|
602
|
+
global-scope safety and preserving our scheduler and response-lifetime rules.
|
|
603
|
+
The existing Effect route programs keep those rules without this routing layer.
|
|
604
|
+
|
|
605
|
+
Effect's MCP server has a separate compatibility gate. Its `2026-07-28`
|
|
606
|
+
adapter accepted a stateless `tools/list`, but its `2025-06-18` adapter refused
|
|
607
|
+
a fresh request with 400. Initialization issued a session, and that session's
|
|
608
|
+
next request returned 404 on a fresh handler; Connecta served the same fresh
|
|
609
|
+
legacy list with 200 JSON and no session. The stable package exports no MCP
|
|
610
|
+
client, so it also cannot replace our downstream SDK and OAuth implementation.
|
|
611
|
+
[Issue #622](https://github.com/zackbart/connecta/issues/622) records the server
|
|
612
|
+
parity requirements. These measurements support keeping the current MCP,
|
|
613
|
+
validation, and routing implementations; stable v4 alone does not establish a
|
|
614
|
+
replacement's benefit or compatibility.
|
|
574
615
|
|
|
575
616
|
The core's cost is recorded rather than guessed. Against 0.24.4 the root entry
|
|
576
617
|
grew from 235,346 to 279,526 bytes gzip, the Worker example from 263,949 to
|
package/documentation/auth.md
CHANGED
|
@@ -64,9 +64,10 @@ also work on older adapters without `compareAndSet`; **new issuance requires
|
|
|
64
64
|
atomic `compareAndSet`**, because counting records before writing admits too many
|
|
65
65
|
concurrent creates. Active capacity defaults to 100, configurable with
|
|
66
66
|
`accessTokens(storage, { maxActive: 200 })`, up to 1,000. A durable reservation
|
|
67
|
-
counts before a secret is written.
|
|
68
|
-
|
|
69
|
-
an
|
|
67
|
+
counts before a secret is written. Failures before lookup publication release
|
|
68
|
+
their reservation; an uncertain lookup write retains its metadata and capacity
|
|
69
|
+
so an operator can revoke it, even when creation returned no secret. Creation is
|
|
70
|
+
never automatically retried. Avoid creating new tokens through old-version instances once
|
|
70
71
|
new-version issuance has started, since those instances do not honor reservations.
|
|
71
72
|
|
|
72
73
|
Secrets contain 256 random bits and only their SHA-256 digests persist. Creation
|
|
@@ -285,7 +286,9 @@ remoteMcp("linear", { url: "https://mcp.linear.app/mcp", authScope: "personal",
|
|
|
285
286
|
A personal connector is absent — not refused — from any request without a
|
|
286
287
|
stable namespaced principal. For a principal that can see one, connecta
|
|
287
288
|
partitions connector storage, vault records, catalog caches, OAuth generations,
|
|
288
|
-
and observed result shapes under an opaque SHA-256 identity key.
|
|
289
|
+
and observed result shapes under an opaque SHA-256 identity key. Refreshes
|
|
290
|
+
coalesce within one owner; another owner's authorization epoch cannot retire
|
|
291
|
+
that refresh. Keep namespaces
|
|
289
292
|
and principal ids stable across upgrades; changing either selects different
|
|
290
293
|
partitions. Literal `auth: { type: "headers" }` cannot be personal, because its
|
|
291
294
|
secret lives in deployment code; `remoteMcp()` refuses that combination at
|
|
@@ -118,7 +118,7 @@ Keep this example's Worker on Wrangler. A copied deployment may use
|
|
|
118
118
|
[Alchemy](https://alchemy.run) to manage its KV namespace, optional D1 databases
|
|
119
119
|
and R2 bucket, and the Access application without adding Alchemy to connecta or
|
|
120
120
|
changing this template. In that deployment, install `alchemy@2.0.0-beta.79`
|
|
121
|
-
and `effect@4.0.0
|
|
121
|
+
and `effect@4.0.0` at exact versions. The latter is connecta's own
|
|
122
122
|
Effect pin; check `npm ls effect` for one resolved copy after installation.
|
|
123
123
|
The following `alchemy.run.ts` is a resource stack, not a Worker deployment:
|
|
124
124
|
|
|
@@ -193,8 +193,9 @@ deleting the state store loses Alchemy's record of what it owns.
|
|
|
193
193
|
|
|
194
194
|
Verification for this optional path stops at the API and types. The fenced
|
|
195
195
|
snippet was copied to an isolated `alchemy.run.ts` and passed `tsc --noEmit`
|
|
196
|
-
with TypeScript 5.9.3, Alchemy 2.0.0-beta.79, and Effect 4.0.0
|
|
197
|
-
Alchemy deploy or adoption
|
|
196
|
+
with TypeScript 5.9.3, Alchemy 2.0.0-beta.79, and Effect 4.0.0, with
|
|
197
|
+
`npm ls effect` confirming one resolved copy. No Alchemy deploy or adoption
|
|
198
|
+
was run.
|
|
198
199
|
[Alchemy's Worker resource](https://alchemy.run/cloudflare/compute/workers/)
|
|
199
200
|
can declare `WorkerLoader`, domains, Browser Rendering, and cron wiring, but
|
|
200
201
|
those properties are coupled to its script deploy. Its current full deploy
|
|
@@ -18,8 +18,17 @@ import type { KVStorage } from "@zackbart/connecta";
|
|
|
18
18
|
export function d1Storage(db: D1Database): KVStorage {
|
|
19
19
|
// Every statement that tests liveness binds the current time as ?2.
|
|
20
20
|
const live = "(expires_at_ms IS NULL OR expires_at_ms > ?2)";
|
|
21
|
-
const expiresAt = (now: number, ttlSeconds?: number) =>
|
|
22
|
-
ttlSeconds
|
|
21
|
+
const expiresAt = (now: number, ttlSeconds?: number): number | null => {
|
|
22
|
+
if (ttlSeconds !== undefined && !Number.isFinite(ttlSeconds)) {
|
|
23
|
+
throw new RangeError("ttlSeconds must produce a finite expiration timestamp");
|
|
24
|
+
}
|
|
25
|
+
if (!ttlSeconds) return null;
|
|
26
|
+
const expiry = now + ttlSeconds * 1000;
|
|
27
|
+
if (!Number.isFinite(expiry)) {
|
|
28
|
+
throw new RangeError("ttlSeconds must produce a finite expiration timestamp");
|
|
29
|
+
}
|
|
30
|
+
return expiry;
|
|
31
|
+
};
|
|
23
32
|
const changed = (result: D1Result) => result.meta.changes > 0;
|
|
24
33
|
return {
|
|
25
34
|
async get(key) {
|
|
@@ -31,6 +40,7 @@ export function d1Storage(db: D1Database): KVStorage {
|
|
|
31
40
|
},
|
|
32
41
|
async set(key, value, opts) {
|
|
33
42
|
const now = Date.now();
|
|
43
|
+
const expiry = expiresAt(now, opts?.ttlSeconds);
|
|
34
44
|
await db.batch([
|
|
35
45
|
db
|
|
36
46
|
.prepare(
|
|
@@ -46,7 +56,7 @@ export function d1Storage(db: D1Database): KVStorage {
|
|
|
46
56
|
ON CONFLICT (key) DO UPDATE SET
|
|
47
57
|
value = excluded.value, expires_at_ms = excluded.expires_at_ms`,
|
|
48
58
|
)
|
|
49
|
-
.bind(key, value,
|
|
59
|
+
.bind(key, value, expiry),
|
|
50
60
|
]);
|
|
51
61
|
},
|
|
52
62
|
async delete(key) {
|
|
@@ -56,7 +66,7 @@ export function d1Storage(db: D1Database): KVStorage {
|
|
|
56
66
|
const { results } = await db
|
|
57
67
|
.prepare(
|
|
58
68
|
`SELECT key FROM connecta_kv
|
|
59
|
-
WHERE key >= ?1 AND substr(key, 1, length(?1)) = ?1 AND ${live}`,
|
|
69
|
+
WHERE key >= ?1 AND substr(CAST(key AS BLOB), 1, length(CAST(?1 AS BLOB))) = CAST(?1 AS BLOB) AND ${live}`,
|
|
60
70
|
)
|
|
61
71
|
.bind(prefix, Date.now())
|
|
62
72
|
.all<{ key: string }>();
|
|
@@ -84,7 +94,19 @@ export function d1Storage(db: D1Database): KVStorage {
|
|
|
84
94
|
.run(),
|
|
85
95
|
);
|
|
86
96
|
}
|
|
87
|
-
|
|
97
|
+
let expiry: number | null;
|
|
98
|
+
try {
|
|
99
|
+
expiry = expiresAt(now, opts?.ttlSeconds);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
// Invalid write options do not change a failed comparison's result.
|
|
102
|
+
// Ordinary claims still use one atomic statement without this read.
|
|
103
|
+
const current = await db
|
|
104
|
+
.prepare(`SELECT value FROM connecta_kv WHERE key = ?1 AND ${live}`)
|
|
105
|
+
.bind(key, now)
|
|
106
|
+
.first<{ value: string }>();
|
|
107
|
+
if ((current?.value ?? null) !== expected) return false;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
88
110
|
if (expected === null) {
|
|
89
111
|
// Insert, or take over a row that has expired. A live row makes the
|
|
90
112
|
// upsert's WHERE false, so nothing changes and the claim is refused.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zackbart/connecta",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of eight meta-tools.",
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
"@cfworker/json-schema": "^4.1.1",
|
|
155
155
|
"@modelcontextprotocol/client": "2.0.0",
|
|
156
156
|
"@modelcontextprotocol/server": "2.0.0",
|
|
157
|
-
"effect": "4.0.0
|
|
157
|
+
"effect": "4.0.0",
|
|
158
158
|
"zod": "^4.4.3"
|
|
159
159
|
},
|
|
160
160
|
"peerDependencies": {
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
"preact": "^10.29.8",
|
|
187
187
|
"quickjs-emscripten": "^0.32.0",
|
|
188
188
|
"tsx": "^4.23.1",
|
|
189
|
-
"typescript": "^5.
|
|
189
|
+
"typescript": "^5.9.3",
|
|
190
190
|
"vitest": "^4.1.6",
|
|
191
191
|
"wrangler": "^4.114.0"
|
|
192
192
|
}
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@zackbart/connecta": "0.
|
|
18
|
+
"@zackbart/connecta": "0.27.0",
|
|
19
19
|
"quickjs-emscripten": "0.32.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^22.0.0",
|
|
23
23
|
"tsx": "^4.23.1",
|
|
24
|
-
"typescript": "^5.
|
|
24
|
+
"typescript": "^5.9.3"
|
|
25
25
|
}
|
|
26
26
|
}
|