@forgezero/providers 0.1.5 → 0.1.6
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/binance.js +1 -1
- package/dist/chain.js +1 -1
- package/dist/database.d.ts +3 -19
- package/dist/database.js +11 -50
- package/dist/email.js +1 -1
- package/dist/http.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/pool.js +1 -1
- package/dist/storage.js +1 -1
- package/dist/translation.js +1 -1
- package/package.json +1 -1
package/dist/binance.js
CHANGED
package/dist/chain.js
CHANGED
package/dist/database.d.ts
CHANGED
|
@@ -43,11 +43,6 @@ export interface ArangoConfig {
|
|
|
43
43
|
url?: string;
|
|
44
44
|
/** Explicit authenticated private coordinators. Bounded to prevent config abuse. */
|
|
45
45
|
urls?: readonly string[];
|
|
46
|
-
/**
|
|
47
|
-
* Optional bootstrap Coordinators already proven to be in Arango `readonly`
|
|
48
|
-
* server mode. Prefer signed, probed membership for production.
|
|
49
|
-
*/
|
|
50
|
-
readOnlyUrls?: readonly string[];
|
|
51
46
|
/**
|
|
52
47
|
* Dynamic membership is accepted only as a signed/attested envelope verified
|
|
53
48
|
* by the host. A Worker/KV node list by itself is discovery data, not routing
|
|
@@ -65,8 +60,6 @@ export interface ArangoConfig {
|
|
|
65
60
|
maxCoordinatorAttempts?: number;
|
|
66
61
|
/** Local transport-failure quarantine. Primarily injectable for tests. */
|
|
67
62
|
unhealthyForMs?: number;
|
|
68
|
-
/** What an explicit read-only preference does when no read-only coordinator can serve it. */
|
|
69
|
-
readOnlyFallback?: 'authoritative' | 'error';
|
|
70
63
|
now?: () => number;
|
|
71
64
|
}
|
|
72
65
|
export interface Query {
|
|
@@ -77,24 +70,15 @@ export interface Query {
|
|
|
77
70
|
* caller assertions; this adapter never guesses AQL semantics from text.
|
|
78
71
|
*/
|
|
79
72
|
retrySafety?: 'read' | 'idempotent' | 'never';
|
|
80
|
-
/**
|
|
81
|
-
* `prefer-readonly` targets Coordinators whose actual Arango server mode is
|
|
82
|
-
* `readonly`. It is valid only with `retrySafety: 'read'`; ForgeZero does not
|
|
83
|
-
* infer AQL semantics. Community 3.11.14 dirty follower reads are not exposed:
|
|
84
|
-
* that server feature is Enterprise-only.
|
|
85
|
-
*/
|
|
86
|
-
readPreference?: 'authoritative' | 'prefer-readonly';
|
|
87
73
|
/** Required when `retrySafety` is `idempotent`; retained for audit correlation. */
|
|
88
74
|
idempotencyKey?: string;
|
|
89
75
|
}
|
|
90
76
|
export interface ArangoCoordinatorMember {
|
|
91
77
|
url: string;
|
|
92
|
-
/**
|
|
78
|
+
/** Clients address Coordinators; Community 3.11.14 has no read-only joiner role. */
|
|
93
79
|
role: 'coordinator';
|
|
94
80
|
/** Actual `/_admin/server/mode` state for this Coordinator. */
|
|
95
|
-
serverMode: 'default'
|
|
96
|
-
/** ForgeZero request-routing capability, required to agree with `serverMode`. */
|
|
97
|
-
access: 'read-write' | 'read-only';
|
|
81
|
+
serverMode: 'default';
|
|
98
82
|
capabilities: {
|
|
99
83
|
/** Always false for the exact Community 3.11.14 contract. */
|
|
100
84
|
dirtyFollowerReads: false;
|
|
@@ -122,7 +106,7 @@ export interface ArangoMembershipSnapshot {
|
|
|
122
106
|
export type ArangoMembershipVerifier = (snapshot: ArangoMembershipSnapshot, signal?: AbortSignal) => Promise<boolean>;
|
|
123
107
|
export declare const MAX_ARANGO_COORDINATORS = 16;
|
|
124
108
|
export declare const ARANGO_COMMUNITY_VERSION: "3.11.14";
|
|
125
|
-
/** Protected
|
|
109
|
+
/** Protected API used to attest cluster-wide `default`/maintenance mode. */
|
|
126
110
|
export declare const ARANGO_SERVER_MODE_PATH: "/_admin/server/mode";
|
|
127
111
|
/**
|
|
128
112
|
* Accept only an explicit, bounded set of private authenticated endpoints.
|
package/dist/database.js
CHANGED
|
@@ -144,7 +144,7 @@ function createRegistry(options) {
|
|
|
144
144
|
}
|
|
145
145
|
return { call };
|
|
146
146
|
}
|
|
147
|
-
var VERSION = "0.1.
|
|
147
|
+
var VERSION = "0.1.6";
|
|
148
148
|
|
|
149
149
|
// src/database.ts
|
|
150
150
|
var pools = new Map;
|
|
@@ -187,30 +187,18 @@ function arangoCoordinatorUrls(config) {
|
|
|
187
187
|
async function resolveMembership(config, signal) {
|
|
188
188
|
if (!config.membership) {
|
|
189
189
|
const urls = arangoCoordinatorUrls(config);
|
|
190
|
-
const readOnlyUrls = config.readOnlyUrls?.length ? arangoCoordinatorUrls({ urls: config.readOnlyUrls }) : [];
|
|
191
|
-
if (urls.length + readOnlyUrls.length > MAX_ARANGO_COORDINATORS || urls.some((url) => readOnlyUrls.includes(url))) {
|
|
192
|
-
throw new ProviderError("ARANGO_COORDINATORS_INVALID", `Writable and read-only ArangoDB coordinator URLs must be disjoint and total at most ${MAX_ARANGO_COORDINATORS}.`);
|
|
193
|
-
}
|
|
194
190
|
return {
|
|
195
|
-
members:
|
|
191
|
+
members: urls.map((url) => ({
|
|
196
192
|
url,
|
|
197
193
|
role: "coordinator",
|
|
198
194
|
serverMode: "default",
|
|
199
|
-
access: "read-write",
|
|
200
|
-
capabilities: { dirtyFollowerReads: false },
|
|
201
|
-
status: "healthy"
|
|
202
|
-
})), ...readOnlyUrls.map((url) => ({
|
|
203
|
-
url,
|
|
204
|
-
role: "coordinator",
|
|
205
|
-
serverMode: "readonly",
|
|
206
|
-
access: "read-only",
|
|
207
195
|
capabilities: { dirtyFollowerReads: false },
|
|
208
196
|
status: "healthy"
|
|
209
|
-
}))
|
|
210
|
-
identity: `static:${urls.join(",")}
|
|
197
|
+
})),
|
|
198
|
+
identity: `static:${urls.join(",")}`
|
|
211
199
|
};
|
|
212
200
|
}
|
|
213
|
-
if (config.url || config.urls
|
|
201
|
+
if (config.url || config.urls) {
|
|
214
202
|
throw new ProviderError("ARANGO_MEMBERSHIP_AMBIGUOUS", "Configure static coordinator URLs or verified membership, not both.");
|
|
215
203
|
}
|
|
216
204
|
if (!config.clusterId || config.membership.clusterId !== config.clusterId) {
|
|
@@ -228,7 +216,7 @@ async function resolveMembership(config, signal) {
|
|
|
228
216
|
throw new ProviderError("ARANGO_MEMBERSHIP_EXPIRED", "Dynamic ArangoDB membership is not currently valid.");
|
|
229
217
|
}
|
|
230
218
|
const validAuthority = snapshot.authority === "forgezero-platform-signed" || snapshot.authority === "node-attested";
|
|
231
|
-
const validNodes = snapshot.coordinators.every((node) => Number.isFinite(node.observedAt) && node.nodeKey.length > 0 && node.nodeKey === node.publicKeys?.ed25519 && Boolean(node.publicKeys.mlDsa) && (node.status === "healthy" || node.status === "degraded" || node.status === "unhealthy") && node.role === "coordinator" &&
|
|
219
|
+
const validNodes = snapshot.coordinators.every((node) => Number.isFinite(node.observedAt) && node.nodeKey.length > 0 && node.nodeKey === node.publicKeys?.ed25519 && Boolean(node.publicKeys.mlDsa) && (node.status === "healthy" || node.status === "degraded" || node.status === "unhealthy") && node.role === "coordinator" && node.serverMode === "default" && node.capabilities?.dirtyFollowerReads === false);
|
|
232
220
|
if (!validAuthority || !snapshot.revision || !validNodes) {
|
|
233
221
|
throw new ProviderError("ARANGO_MEMBERSHIP_INVALID", "Dynamic ArangoDB membership needs a revision and observed timestamps.");
|
|
234
222
|
}
|
|
@@ -291,20 +279,8 @@ function selectNode(pool, excluded, now, eligibleMember) {
|
|
|
291
279
|
}
|
|
292
280
|
return;
|
|
293
281
|
}
|
|
294
|
-
function
|
|
295
|
-
|
|
296
|
-
throw new ProviderError("ARANGO_READ_PREFERENCE_INVALID", "Unknown ArangoDB read preference.");
|
|
297
|
-
}
|
|
298
|
-
if (request.readPreference && request.retrySafety !== "read") {
|
|
299
|
-
throw new ProviderError("ARANGO_READ_PREFERENCE_UNSAFE", "ArangoDB read preference requires an explicit read retry-safety assertion.");
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
function nodeForRequest(pool, request, fallback, excluded, now) {
|
|
303
|
-
const writable = (node) => node.member.access === "read-write";
|
|
304
|
-
if (request.retrySafety === "read" && request.readPreference === "prefer-readonly") {
|
|
305
|
-
return selectNode(pool, excluded, now, (node) => node.member.access === "read-only") ?? (fallback === "error" ? undefined : selectNode(pool, excluded, now, writable));
|
|
306
|
-
}
|
|
307
|
-
return selectNode(pool, excluded, now, writable);
|
|
282
|
+
function nodeForRequest(pool, excluded, now) {
|
|
283
|
+
return selectNode(pool, excluded, now, () => true);
|
|
308
284
|
}
|
|
309
285
|
var arangodb = defineProvider({
|
|
310
286
|
id: "arangodb",
|
|
@@ -342,15 +318,6 @@ var arangodb = defineProvider({
|
|
|
342
318
|
title: "Coordinator URLs",
|
|
343
319
|
description: "Authenticated private coordinators in this one logical cluster."
|
|
344
320
|
},
|
|
345
|
-
readOnlyUrls: {
|
|
346
|
-
type: "array",
|
|
347
|
-
minItems: 1,
|
|
348
|
-
maxItems: MAX_ARANGO_COORDINATORS - 1,
|
|
349
|
-
uniqueItems: true,
|
|
350
|
-
items: { type: "string" },
|
|
351
|
-
title: "Read-only Coordinator URLs",
|
|
352
|
-
description: "Optional bootstrap endpoints whose protected Arango server mode was probed as readonly."
|
|
353
|
-
},
|
|
354
321
|
clusterId: {
|
|
355
322
|
type: "string",
|
|
356
323
|
title: "Cluster ID",
|
|
@@ -369,11 +336,6 @@ var arangodb = defineProvider({
|
|
|
369
336
|
minimum: 1000,
|
|
370
337
|
maximum: 300000,
|
|
371
338
|
title: "Local coordinator quarantine (ms)"
|
|
372
|
-
},
|
|
373
|
-
readOnlyFallback: {
|
|
374
|
-
type: "string",
|
|
375
|
-
enum: ["authoritative", "error"],
|
|
376
|
-
title: "Read-only coordinator fallback"
|
|
377
339
|
}
|
|
378
340
|
}
|
|
379
341
|
},
|
|
@@ -385,11 +347,10 @@ var arangodb = defineProvider({
|
|
|
385
347
|
const membership = await resolveMembership(config, context.signal);
|
|
386
348
|
const members = membership.members;
|
|
387
349
|
const urls = members.map(({ url }) => url);
|
|
388
|
-
assertReadPolicy(request);
|
|
389
350
|
const safe = requestIsSafe(request);
|
|
390
|
-
const available =
|
|
351
|
+
const available = members.length;
|
|
391
352
|
if (available === 0) {
|
|
392
|
-
throw new ProviderError("ARANGO_COORDINATORS_UNAVAILABLE",
|
|
353
|
+
throw new ProviderError("ARANGO_COORDINATORS_UNAVAILABLE", "No verified authoritative ArangoDB coordinator is available.");
|
|
393
354
|
}
|
|
394
355
|
const attempts = boundedAttempts(config, available, safe);
|
|
395
356
|
const key = `${membership.identity}/${config.database}/${config.username}`;
|
|
@@ -462,7 +423,7 @@ var arangodb = defineProvider({
|
|
|
462
423
|
throw context.signal.reason ?? new ProviderError("ARANGO_ABORTED", "ArangoDB request was cancelled.");
|
|
463
424
|
}
|
|
464
425
|
const now = (config.now ?? Date.now)();
|
|
465
|
-
const node = nodeForRequest(pool,
|
|
426
|
+
const node = nodeForRequest(pool, tried, now);
|
|
466
427
|
if (!node)
|
|
467
428
|
break;
|
|
468
429
|
tried.add(node.url);
|
package/dist/email.js
CHANGED
package/dist/http.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/pool.js
CHANGED
package/dist/storage.js
CHANGED
package/dist/translation.js
CHANGED