@tinycloud/replica 0.3.0-beta.5 → 0.3.0-beta.7
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/kv-storage-testing.js.map +1 -1
- package/dist/kv-storage.js +7 -6
- package/dist/kv-storage.js.map +1 -1
- package/dist/{kv-storage.worker.67e46cebbda277040312a770449123633491901d6cceacea95754a843e01e9c9.js → kv-storage.worker.850a5e19b486939f03586f7d1294934f54f44cca0224450b1ba94dfb07912a75.js} +568 -560
- package/dist/kv-storage.worker.js +568 -560
- package/dist/kv-storage.worker.js.map +1 -1
- package/dist/replica.worker.js +562 -555
- package/dist/replica.worker.js.map +1 -1
- package/dist/sqlite.cjs +9 -6
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.js +9 -6
- package/dist/sqlite.js.map +1 -1
- package/package.json +2 -2
package/dist/replica.worker.js
CHANGED
|
@@ -6808,7 +6808,7 @@ var ErrorCodes = {
|
|
|
6808
6808
|
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
6809
6809
|
// Node retry signals (tinycloud-node #347). `meta.retryable` says whether a
|
|
6810
6810
|
// retry is safe; `meta.retryAfterMs` carries the node's Retry-After.
|
|
6811
|
-
/** HTTP 503 `sql_busy:<stage>` / `unavailable_retryable`: nothing was applied; retry after `meta.retryAfterMs`. */
|
|
6811
|
+
/** HTTP 503 `sql_busy:<stage>` / `unavailable_retryable`/`read_audit_busy`: nothing was applied; retry after `meta.retryAfterMs`. */
|
|
6812
6812
|
NODE_BUSY: "NODE_BUSY",
|
|
6813
6813
|
/** HTTP 503 `request_timeout`: the node's deadline passed before any write began; nothing was applied. */
|
|
6814
6814
|
REQUEST_TIMEOUT: "REQUEST_TIMEOUT",
|
|
@@ -6849,11 +6849,12 @@ var ErrorCodes = {
|
|
|
6849
6849
|
DUCKDB_READONLY_VIOLATION: "DUCKDB_READONLY_VIOLATION"
|
|
6850
6850
|
};
|
|
6851
6851
|
var defaultRetryPolicy = {
|
|
6852
|
-
maxAttempts:
|
|
6852
|
+
maxAttempts: 4,
|
|
6853
6853
|
backoff: "exponential",
|
|
6854
|
-
baseDelayMs:
|
|
6855
|
-
maxDelayMs:
|
|
6856
|
-
retryableErrors: [ErrorCodes.
|
|
6854
|
+
baseDelayMs: 250,
|
|
6855
|
+
maxDelayMs: 4e3,
|
|
6856
|
+
retryableErrors: [ErrorCodes.NODE_BUSY, ErrorCodes.REQUEST_TIMEOUT],
|
|
6857
|
+
maxTotalMs: 12e3
|
|
6857
6858
|
};
|
|
6858
6859
|
var TelemetryEvents = {
|
|
6859
6860
|
SPAN: "telemetry.span",
|
|
@@ -7273,7 +7274,9 @@ var RetryPolicySchema = external_exports.object({
|
|
|
7273
7274
|
/** Maximum delay in milliseconds between retries */
|
|
7274
7275
|
maxDelayMs: external_exports.number().nonnegative(),
|
|
7275
7276
|
/** Error codes that should trigger a retry */
|
|
7276
|
-
retryableErrors: external_exports.array(external_exports.string())
|
|
7277
|
+
retryableErrors: external_exports.array(external_exports.string()),
|
|
7278
|
+
/** Total time budget for all attempts and waits, in milliseconds */
|
|
7279
|
+
maxTotalMs: external_exports.number().positive().optional()
|
|
7277
7280
|
});
|
|
7278
7281
|
var ServiceSessionSchema = external_exports.object({
|
|
7279
7282
|
/** The delegation header containing the UCAN */
|
|
@@ -7289,345 +7292,6 @@ var ServiceSessionSchema = external_exports.object({
|
|
|
7289
7292
|
/** The session key JWK (required for invoke) */
|
|
7290
7293
|
jwk: external_exports.object({}).passthrough()
|
|
7291
7294
|
});
|
|
7292
|
-
var ServiceContext = class {
|
|
7293
|
-
constructor(config) {
|
|
7294
|
-
this._session = null;
|
|
7295
|
-
this._services = /* @__PURE__ */ new Map();
|
|
7296
|
-
this._eventHandlers = /* @__PURE__ */ new Map();
|
|
7297
|
-
this._abortController = new AbortController();
|
|
7298
|
-
this._retired = false;
|
|
7299
|
-
this._invoke = this.wrapInvoke(config.invoke);
|
|
7300
|
-
this._invokeAny = config.invokeAny ? this.wrapInvokeAny(config.invokeAny) : void 0;
|
|
7301
|
-
this._fetch = this.wrapFetch(config.fetch ?? globalThis.fetch.bind(globalThis));
|
|
7302
|
-
this._hosts = config.hosts;
|
|
7303
|
-
this._session = config.session ?? null;
|
|
7304
|
-
this._retryPolicy = {
|
|
7305
|
-
...defaultRetryPolicy,
|
|
7306
|
-
...config.retryPolicy
|
|
7307
|
-
};
|
|
7308
|
-
this._telemetryEnabled = typeof config.telemetry === "boolean" ? config.telemetry : config.telemetry?.enabled === true;
|
|
7309
|
-
this._telemetryHandler = typeof config.telemetry === "object" ? config.telemetry.onEvent : void 0;
|
|
7310
|
-
}
|
|
7311
|
-
// ============================================================
|
|
7312
|
-
// Session Management
|
|
7313
|
-
// ============================================================
|
|
7314
|
-
/**
|
|
7315
|
-
* Get the current session.
|
|
7316
|
-
*/
|
|
7317
|
-
get session() {
|
|
7318
|
-
return this._retired ? null : this._session;
|
|
7319
|
-
}
|
|
7320
|
-
/**
|
|
7321
|
-
* Check if the context has an authenticated session.
|
|
7322
|
-
*/
|
|
7323
|
-
get isAuthenticated() {
|
|
7324
|
-
return !this._retired && this._session !== null;
|
|
7325
|
-
}
|
|
7326
|
-
/**
|
|
7327
|
-
* Update the session and notify all registered services.
|
|
7328
|
-
*
|
|
7329
|
-
* @param session - New session or null to clear
|
|
7330
|
-
*/
|
|
7331
|
-
setSession(session2) {
|
|
7332
|
-
this.assertActive();
|
|
7333
|
-
this._session = session2;
|
|
7334
|
-
this.emit("session.changed", { authenticated: session2 !== null });
|
|
7335
|
-
for (const service of this._services.values()) {
|
|
7336
|
-
service.onSessionChange(session2);
|
|
7337
|
-
}
|
|
7338
|
-
}
|
|
7339
|
-
// ============================================================
|
|
7340
|
-
// Platform Dependencies
|
|
7341
|
-
// ============================================================
|
|
7342
|
-
/**
|
|
7343
|
-
* Get the invoke function for WASM operations.
|
|
7344
|
-
*/
|
|
7345
|
-
get invoke() {
|
|
7346
|
-
this.assertActive();
|
|
7347
|
-
return this._invoke;
|
|
7348
|
-
}
|
|
7349
|
-
/**
|
|
7350
|
-
* Get the multi-resource invoke function when available.
|
|
7351
|
-
*/
|
|
7352
|
-
get invokeAny() {
|
|
7353
|
-
this.assertActive();
|
|
7354
|
-
return this._invokeAny;
|
|
7355
|
-
}
|
|
7356
|
-
/**
|
|
7357
|
-
* Get the fetch function for HTTP requests.
|
|
7358
|
-
*/
|
|
7359
|
-
get fetch() {
|
|
7360
|
-
this.assertActive();
|
|
7361
|
-
return this._fetch;
|
|
7362
|
-
}
|
|
7363
|
-
/**
|
|
7364
|
-
* Get the list of TinyCloud host URLs.
|
|
7365
|
-
*/
|
|
7366
|
-
get hosts() {
|
|
7367
|
-
this.assertActive();
|
|
7368
|
-
return this._hosts;
|
|
7369
|
-
}
|
|
7370
|
-
// ============================================================
|
|
7371
|
-
// Service Registry
|
|
7372
|
-
// ============================================================
|
|
7373
|
-
/**
|
|
7374
|
-
* Register a service with the context.
|
|
7375
|
-
*
|
|
7376
|
-
* @param name - Service name (e.g., 'kv')
|
|
7377
|
-
* @param service - Service instance
|
|
7378
|
-
*/
|
|
7379
|
-
registerService(name, service) {
|
|
7380
|
-
this._services.set(name, service);
|
|
7381
|
-
}
|
|
7382
|
-
/**
|
|
7383
|
-
* Unregister a service from the context.
|
|
7384
|
-
*
|
|
7385
|
-
* @param name - Service name to remove
|
|
7386
|
-
*/
|
|
7387
|
-
unregisterService(name) {
|
|
7388
|
-
this._services.delete(name);
|
|
7389
|
-
}
|
|
7390
|
-
/**
|
|
7391
|
-
* Get a registered service by name.
|
|
7392
|
-
*
|
|
7393
|
-
* @param name - Service name
|
|
7394
|
-
* @returns The service instance or undefined if not registered
|
|
7395
|
-
*/
|
|
7396
|
-
getService(name) {
|
|
7397
|
-
return this._services.get(name);
|
|
7398
|
-
}
|
|
7399
|
-
// ============================================================
|
|
7400
|
-
// Event System (Telemetry)
|
|
7401
|
-
// ============================================================
|
|
7402
|
-
/**
|
|
7403
|
-
* Emit a telemetry event.
|
|
7404
|
-
*
|
|
7405
|
-
* @param event - Event name
|
|
7406
|
-
* @param data - Event data
|
|
7407
|
-
*/
|
|
7408
|
-
emit(event, data) {
|
|
7409
|
-
tinyCloudDebugLogger.log(event, data);
|
|
7410
|
-
if (this._telemetryEnabled && this._telemetryHandler) {
|
|
7411
|
-
try {
|
|
7412
|
-
this._telemetryHandler(event, projectDiagnosticData(data));
|
|
7413
|
-
} catch (error) {
|
|
7414
|
-
console.error(`Error in telemetry handler for "${event}":`, error);
|
|
7415
|
-
}
|
|
7416
|
-
}
|
|
7417
|
-
const handlers = this._eventHandlers.get(event);
|
|
7418
|
-
if (handlers) {
|
|
7419
|
-
for (const handler of handlers) {
|
|
7420
|
-
try {
|
|
7421
|
-
handler(data);
|
|
7422
|
-
} catch (error) {
|
|
7423
|
-
console.error(`Error in event handler for "${event}":`, error);
|
|
7424
|
-
}
|
|
7425
|
-
}
|
|
7426
|
-
}
|
|
7427
|
-
}
|
|
7428
|
-
/**
|
|
7429
|
-
* Subscribe to telemetry events.
|
|
7430
|
-
*
|
|
7431
|
-
* @param event - Event name to subscribe to
|
|
7432
|
-
* @param handler - Handler function
|
|
7433
|
-
* @returns Unsubscribe function
|
|
7434
|
-
*/
|
|
7435
|
-
on(event, handler) {
|
|
7436
|
-
if (!this._eventHandlers.has(event)) {
|
|
7437
|
-
this._eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
7438
|
-
}
|
|
7439
|
-
this._eventHandlers.get(event).add(handler);
|
|
7440
|
-
return () => {
|
|
7441
|
-
const handlers = this._eventHandlers.get(event);
|
|
7442
|
-
if (handlers) {
|
|
7443
|
-
handlers.delete(handler);
|
|
7444
|
-
if (handlers.size === 0) {
|
|
7445
|
-
this._eventHandlers.delete(event);
|
|
7446
|
-
}
|
|
7447
|
-
}
|
|
7448
|
-
};
|
|
7449
|
-
}
|
|
7450
|
-
/**
|
|
7451
|
-
* Remove all event handlers for an event.
|
|
7452
|
-
*
|
|
7453
|
-
* @param event - Event name (if omitted, clears all events)
|
|
7454
|
-
*/
|
|
7455
|
-
clearEventHandlers(event) {
|
|
7456
|
-
if (event) {
|
|
7457
|
-
this._eventHandlers.delete(event);
|
|
7458
|
-
} else {
|
|
7459
|
-
this._eventHandlers.clear();
|
|
7460
|
-
}
|
|
7461
|
-
}
|
|
7462
|
-
// ============================================================
|
|
7463
|
-
// Lifecycle
|
|
7464
|
-
// ============================================================
|
|
7465
|
-
/**
|
|
7466
|
-
* Get the abort signal for cancelling operations.
|
|
7467
|
-
*/
|
|
7468
|
-
get abortSignal() {
|
|
7469
|
-
return this._abortController.signal;
|
|
7470
|
-
}
|
|
7471
|
-
get operationAbortSignal() {
|
|
7472
|
-
return this._operationAbortSignal;
|
|
7473
|
-
}
|
|
7474
|
-
setOperationAbortSignal(signal) {
|
|
7475
|
-
this._operationAbortSignal = signal;
|
|
7476
|
-
}
|
|
7477
|
-
/**
|
|
7478
|
-
* Permanently retire this graph after its owner installs a replacement.
|
|
7479
|
-
* Unlike `abort()`, retirement never creates a fresh controller, so captured
|
|
7480
|
-
* services cannot resume calls under a superseded session.
|
|
7481
|
-
*/
|
|
7482
|
-
retire() {
|
|
7483
|
-
if (this._retired) return;
|
|
7484
|
-
this._retired = true;
|
|
7485
|
-
this._session = null;
|
|
7486
|
-
this._abortController.abort();
|
|
7487
|
-
for (const service of this._services.values()) {
|
|
7488
|
-
try {
|
|
7489
|
-
service.onSignOut();
|
|
7490
|
-
} catch (error) {
|
|
7491
|
-
console.error("Error retiring service:", error);
|
|
7492
|
-
}
|
|
7493
|
-
}
|
|
7494
|
-
}
|
|
7495
|
-
/**
|
|
7496
|
-
* Abort all pending operations and notify services.
|
|
7497
|
-
* Creates a new AbortController for future operations.
|
|
7498
|
-
*/
|
|
7499
|
-
abort() {
|
|
7500
|
-
if (this._retired) return;
|
|
7501
|
-
this._abortController.abort();
|
|
7502
|
-
this._abortController = new AbortController();
|
|
7503
|
-
for (const service of this._services.values()) {
|
|
7504
|
-
try {
|
|
7505
|
-
service.onSignOut();
|
|
7506
|
-
} catch (error) {
|
|
7507
|
-
console.error("Error signing out service:", error);
|
|
7508
|
-
}
|
|
7509
|
-
}
|
|
7510
|
-
}
|
|
7511
|
-
/**
|
|
7512
|
-
* Sign out - abort operations and clear session.
|
|
7513
|
-
*/
|
|
7514
|
-
signOut() {
|
|
7515
|
-
if (this._retired) return;
|
|
7516
|
-
this.abort();
|
|
7517
|
-
this.setSession(null);
|
|
7518
|
-
this.emit("session.expired", {});
|
|
7519
|
-
}
|
|
7520
|
-
// ============================================================
|
|
7521
|
-
// Retry Policy
|
|
7522
|
-
// ============================================================
|
|
7523
|
-
/**
|
|
7524
|
-
* Get the retry policy configuration.
|
|
7525
|
-
*/
|
|
7526
|
-
get retryPolicy() {
|
|
7527
|
-
return this._retryPolicy;
|
|
7528
|
-
}
|
|
7529
|
-
wrapInvoke(invoke) {
|
|
7530
|
-
return (session2, service, path, action, facts) => {
|
|
7531
|
-
this.assertActive();
|
|
7532
|
-
if (!tinyCloudDebugLogger.isEnabled()) {
|
|
7533
|
-
return invoke(session2, service, path, action, facts);
|
|
7534
|
-
}
|
|
7535
|
-
const timer = tinyCloudDebugLogger.startTimer("sdk.invoke", {
|
|
7536
|
-
service,
|
|
7537
|
-
path,
|
|
7538
|
-
action,
|
|
7539
|
-
facts
|
|
7540
|
-
});
|
|
7541
|
-
try {
|
|
7542
|
-
const result = invoke(session2, service, path, action, facts);
|
|
7543
|
-
timer.stop({ ok: true, service, path, action });
|
|
7544
|
-
return result;
|
|
7545
|
-
} catch (error) {
|
|
7546
|
-
timer.stop({ ok: false, service, path, action, error });
|
|
7547
|
-
throw error;
|
|
7548
|
-
}
|
|
7549
|
-
};
|
|
7550
|
-
}
|
|
7551
|
-
assertActive() {
|
|
7552
|
-
if (this._retired) {
|
|
7553
|
-
throw new Error("Service graph has been retired: its session was replaced or signed out.");
|
|
7554
|
-
}
|
|
7555
|
-
}
|
|
7556
|
-
wrapInvokeAny(invokeAny) {
|
|
7557
|
-
return (session2, entries, facts) => {
|
|
7558
|
-
this.assertActive();
|
|
7559
|
-
if (!tinyCloudDebugLogger.isEnabled()) {
|
|
7560
|
-
return invokeAny(session2, entries, facts);
|
|
7561
|
-
}
|
|
7562
|
-
const timer = tinyCloudDebugLogger.startTimer("sdk.invokeAny", {
|
|
7563
|
-
entries,
|
|
7564
|
-
facts
|
|
7565
|
-
});
|
|
7566
|
-
try {
|
|
7567
|
-
const result = invokeAny(session2, entries, facts);
|
|
7568
|
-
timer.stop({ ok: true, entries });
|
|
7569
|
-
return result;
|
|
7570
|
-
} catch (error) {
|
|
7571
|
-
timer.stop({ ok: false, entries, error });
|
|
7572
|
-
throw error;
|
|
7573
|
-
}
|
|
7574
|
-
};
|
|
7575
|
-
}
|
|
7576
|
-
wrapFetch(fetchFn) {
|
|
7577
|
-
return async (url, init) => {
|
|
7578
|
-
this.assertActive();
|
|
7579
|
-
const request = {
|
|
7580
|
-
...init,
|
|
7581
|
-
signal: this.combineSignals(init?.signal)
|
|
7582
|
-
};
|
|
7583
|
-
if (!tinyCloudDebugLogger.isEnabled()) {
|
|
7584
|
-
return fetchFn(url, request);
|
|
7585
|
-
}
|
|
7586
|
-
const timer = tinyCloudDebugLogger.startTimer("sdk.fetch", {
|
|
7587
|
-
url,
|
|
7588
|
-
method: request.method ?? "GET",
|
|
7589
|
-
init: request
|
|
7590
|
-
});
|
|
7591
|
-
try {
|
|
7592
|
-
const response = await fetchFn(url, request);
|
|
7593
|
-
timer.stop({
|
|
7594
|
-
ok: response.ok,
|
|
7595
|
-
url,
|
|
7596
|
-
method: request.method ?? "GET",
|
|
7597
|
-
status: response.status,
|
|
7598
|
-
statusText: response.statusText
|
|
7599
|
-
});
|
|
7600
|
-
return response;
|
|
7601
|
-
} catch (error) {
|
|
7602
|
-
timer.stop({
|
|
7603
|
-
ok: false,
|
|
7604
|
-
url,
|
|
7605
|
-
method: request.method ?? "GET",
|
|
7606
|
-
error
|
|
7607
|
-
});
|
|
7608
|
-
throw error;
|
|
7609
|
-
}
|
|
7610
|
-
};
|
|
7611
|
-
}
|
|
7612
|
-
combineSignals(signal) {
|
|
7613
|
-
if (!signal) return this._abortController.signal;
|
|
7614
|
-
const combined = new AbortController();
|
|
7615
|
-
const abort = (source) => combined.abort(source.reason);
|
|
7616
|
-
if (signal.aborted) {
|
|
7617
|
-
abort(signal);
|
|
7618
|
-
} else if (this._abortController.signal.aborted) {
|
|
7619
|
-
abort(this._abortController.signal);
|
|
7620
|
-
} else {
|
|
7621
|
-
signal.addEventListener("abort", () => abort(signal), { once: true });
|
|
7622
|
-
this._abortController.signal.addEventListener(
|
|
7623
|
-
"abort",
|
|
7624
|
-
() => abort(this._abortController.signal),
|
|
7625
|
-
{ once: true }
|
|
7626
|
-
);
|
|
7627
|
-
}
|
|
7628
|
-
return combined.signal;
|
|
7629
|
-
}
|
|
7630
|
-
};
|
|
7631
7295
|
function parseServiceErrorBody(errorText) {
|
|
7632
7296
|
try {
|
|
7633
7297
|
return JSON.parse(errorText);
|
|
@@ -7778,7 +7442,7 @@ function nodeRetryError(service, message, response, errorText) {
|
|
|
7778
7442
|
const body = errorText.trim();
|
|
7779
7443
|
const [, token, stage] = /^([a-z_]+)(?::\s*([a-z_]+):)?/.exec(body) ?? [];
|
|
7780
7444
|
let code;
|
|
7781
|
-
if (response.status === 503 && (token === "sql_busy" || token === "unavailable_retryable")) {
|
|
7445
|
+
if (response.status === 503 && (token === "sql_busy" || token === "unavailable_retryable" || token === "read_audit_busy")) {
|
|
7782
7446
|
code = ErrorCodes.NODE_BUSY;
|
|
7783
7447
|
} else if (response.status === 503 && token === "request_timeout") {
|
|
7784
7448
|
code = ErrorCodes.REQUEST_TIMEOUT;
|
|
@@ -7982,50 +7646,432 @@ function parseStorageRejection(responseText) {
|
|
|
7982
7646
|
} catch {
|
|
7983
7647
|
return parseStorageQuotaBytes(responseText);
|
|
7984
7648
|
}
|
|
7985
|
-
if (typeof body !== "object" || body === null) return void 0;
|
|
7986
|
-
const record = body;
|
|
7987
|
-
const message = typeof record.message === "string" ? record.message : "";
|
|
7988
|
-
const space = byteCountsOf(record.space) ?? parseStorageQuotaBytes(message);
|
|
7989
|
-
const isStorageBody = typeof record.error === "string" && STORAGE_REJECTION_BODY_ERRORS[record.error] === true;
|
|
7990
|
-
if (!space && !isStorageBody) {
|
|
7991
|
-
return void 0;
|
|
7649
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
7650
|
+
const record = body;
|
|
7651
|
+
const message = typeof record.message === "string" ? record.message : "";
|
|
7652
|
+
const space = byteCountsOf(record.space) ?? parseStorageQuotaBytes(message);
|
|
7653
|
+
const isStorageBody = typeof record.error === "string" && STORAGE_REJECTION_BODY_ERRORS[record.error] === true;
|
|
7654
|
+
if (!space && !isStorageBody) {
|
|
7655
|
+
return void 0;
|
|
7656
|
+
}
|
|
7657
|
+
const details = { ...space };
|
|
7658
|
+
const account = byteCountsOf(record.account);
|
|
7659
|
+
if (account) {
|
|
7660
|
+
const plan = record.account.plan;
|
|
7661
|
+
details.account = typeof plan === "string" && plan !== "" ? { ...account, plan } : account;
|
|
7662
|
+
}
|
|
7663
|
+
return details;
|
|
7664
|
+
}
|
|
7665
|
+
function storageRejectionError(service, status, meta, responseText) {
|
|
7666
|
+
const quotaMeta = { ...meta, ...parseStorageRejection(responseText) };
|
|
7667
|
+
return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
|
|
7668
|
+
}
|
|
7669
|
+
function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
7670
|
+
if (error instanceof Error) {
|
|
7671
|
+
if (isRequestTimeoutError(error)) {
|
|
7672
|
+
return timeoutError(service, error.timeoutMs, error);
|
|
7673
|
+
}
|
|
7674
|
+
if (error.name === "AbortError") {
|
|
7675
|
+
return abortedError(service);
|
|
7676
|
+
}
|
|
7677
|
+
if (error.name === "TimeoutError" || error.message.toLowerCase().includes("timeout")) {
|
|
7678
|
+
return timeoutError(service);
|
|
7679
|
+
}
|
|
7680
|
+
return {
|
|
7681
|
+
code: defaultCode,
|
|
7682
|
+
message: error.message,
|
|
7683
|
+
service,
|
|
7684
|
+
cause: error
|
|
7685
|
+
};
|
|
7686
|
+
}
|
|
7687
|
+
return {
|
|
7688
|
+
code: defaultCode,
|
|
7689
|
+
message: String(error),
|
|
7690
|
+
service
|
|
7691
|
+
};
|
|
7692
|
+
}
|
|
7693
|
+
var abortableSleep = (ms, signal, unref = false) => new Promise((resolve) => {
|
|
7694
|
+
const done = () => {
|
|
7695
|
+
clearTimeout(timer);
|
|
7696
|
+
signal.removeEventListener("abort", done);
|
|
7697
|
+
resolve();
|
|
7698
|
+
};
|
|
7699
|
+
const timer = setTimeout(done, ms);
|
|
7700
|
+
if (unref) timer.unref?.();
|
|
7701
|
+
signal.aborted ? done() : signal.addEventListener("abort", done);
|
|
7702
|
+
});
|
|
7703
|
+
var ServiceContext = class {
|
|
7704
|
+
constructor(config) {
|
|
7705
|
+
this._session = null;
|
|
7706
|
+
this._services = /* @__PURE__ */ new Map();
|
|
7707
|
+
this._eventHandlers = /* @__PURE__ */ new Map();
|
|
7708
|
+
this._abortController = new AbortController();
|
|
7709
|
+
this._retired = false;
|
|
7710
|
+
this._invoke = this.wrapInvoke(config.invoke);
|
|
7711
|
+
this._invokeAny = config.invokeAny ? this.wrapInvokeAny(config.invokeAny) : void 0;
|
|
7712
|
+
this._fetch = this.wrapFetch(config.fetch ?? globalThis.fetch.bind(globalThis));
|
|
7713
|
+
this._hosts = config.hosts;
|
|
7714
|
+
this._session = config.session ?? null;
|
|
7715
|
+
this._retryPolicy = {
|
|
7716
|
+
...defaultRetryPolicy,
|
|
7717
|
+
...config.retryPolicy
|
|
7718
|
+
};
|
|
7719
|
+
this._telemetryEnabled = typeof config.telemetry === "boolean" ? config.telemetry : config.telemetry?.enabled === true;
|
|
7720
|
+
this._telemetryHandler = typeof config.telemetry === "object" ? config.telemetry.onEvent : void 0;
|
|
7721
|
+
}
|
|
7722
|
+
// ============================================================
|
|
7723
|
+
// Session Management
|
|
7724
|
+
// ============================================================
|
|
7725
|
+
/**
|
|
7726
|
+
* Get the current session.
|
|
7727
|
+
*/
|
|
7728
|
+
get session() {
|
|
7729
|
+
return this._retired ? null : this._session;
|
|
7730
|
+
}
|
|
7731
|
+
/**
|
|
7732
|
+
* Check if the context has an authenticated session.
|
|
7733
|
+
*/
|
|
7734
|
+
get isAuthenticated() {
|
|
7735
|
+
return !this._retired && this._session !== null;
|
|
7736
|
+
}
|
|
7737
|
+
/**
|
|
7738
|
+
* Update the session and notify all registered services.
|
|
7739
|
+
*
|
|
7740
|
+
* @param session - New session or null to clear
|
|
7741
|
+
*/
|
|
7742
|
+
setSession(session2) {
|
|
7743
|
+
this.assertActive();
|
|
7744
|
+
this._session = session2;
|
|
7745
|
+
this.emit("session.changed", { authenticated: session2 !== null });
|
|
7746
|
+
for (const service of this._services.values()) {
|
|
7747
|
+
service.onSessionChange(session2);
|
|
7748
|
+
}
|
|
7749
|
+
}
|
|
7750
|
+
// ============================================================
|
|
7751
|
+
// Platform Dependencies
|
|
7752
|
+
// ============================================================
|
|
7753
|
+
/**
|
|
7754
|
+
* Get the invoke function for WASM operations.
|
|
7755
|
+
*/
|
|
7756
|
+
get invoke() {
|
|
7757
|
+
this.assertActive();
|
|
7758
|
+
return this._invoke;
|
|
7759
|
+
}
|
|
7760
|
+
/**
|
|
7761
|
+
* Get the multi-resource invoke function when available.
|
|
7762
|
+
*/
|
|
7763
|
+
get invokeAny() {
|
|
7764
|
+
this.assertActive();
|
|
7765
|
+
return this._invokeAny;
|
|
7766
|
+
}
|
|
7767
|
+
/**
|
|
7768
|
+
* Get the fetch function for HTTP requests.
|
|
7769
|
+
*/
|
|
7770
|
+
get fetch() {
|
|
7771
|
+
this.assertActive();
|
|
7772
|
+
return this._fetch;
|
|
7773
|
+
}
|
|
7774
|
+
/**
|
|
7775
|
+
* Get the list of TinyCloud host URLs.
|
|
7776
|
+
*/
|
|
7777
|
+
get hosts() {
|
|
7778
|
+
this.assertActive();
|
|
7779
|
+
return this._hosts;
|
|
7780
|
+
}
|
|
7781
|
+
// ============================================================
|
|
7782
|
+
// Service Registry
|
|
7783
|
+
// ============================================================
|
|
7784
|
+
/**
|
|
7785
|
+
* Register a service with the context.
|
|
7786
|
+
*
|
|
7787
|
+
* @param name - Service name (e.g., 'kv')
|
|
7788
|
+
* @param service - Service instance
|
|
7789
|
+
*/
|
|
7790
|
+
registerService(name, service) {
|
|
7791
|
+
this._services.set(name, service);
|
|
7792
|
+
}
|
|
7793
|
+
/**
|
|
7794
|
+
* Unregister a service from the context.
|
|
7795
|
+
*
|
|
7796
|
+
* @param name - Service name to remove
|
|
7797
|
+
*/
|
|
7798
|
+
unregisterService(name) {
|
|
7799
|
+
this._services.delete(name);
|
|
7800
|
+
}
|
|
7801
|
+
/**
|
|
7802
|
+
* Get a registered service by name.
|
|
7803
|
+
*
|
|
7804
|
+
* @param name - Service name
|
|
7805
|
+
* @returns The service instance or undefined if not registered
|
|
7806
|
+
*/
|
|
7807
|
+
getService(name) {
|
|
7808
|
+
return this._services.get(name);
|
|
7809
|
+
}
|
|
7810
|
+
// ============================================================
|
|
7811
|
+
// Event System (Telemetry)
|
|
7812
|
+
// ============================================================
|
|
7813
|
+
/**
|
|
7814
|
+
* Emit a telemetry event.
|
|
7815
|
+
*
|
|
7816
|
+
* @param event - Event name
|
|
7817
|
+
* @param data - Event data
|
|
7818
|
+
*/
|
|
7819
|
+
emit(event, data) {
|
|
7820
|
+
tinyCloudDebugLogger.log(event, data);
|
|
7821
|
+
if (this._telemetryEnabled && this._telemetryHandler) {
|
|
7822
|
+
try {
|
|
7823
|
+
this._telemetryHandler(event, projectDiagnosticData(data));
|
|
7824
|
+
} catch (error) {
|
|
7825
|
+
console.error(`Error in telemetry handler for "${event}":`, error);
|
|
7826
|
+
}
|
|
7827
|
+
}
|
|
7828
|
+
const handlers = this._eventHandlers.get(event);
|
|
7829
|
+
if (handlers) {
|
|
7830
|
+
for (const handler of handlers) {
|
|
7831
|
+
try {
|
|
7832
|
+
handler(data);
|
|
7833
|
+
} catch (error) {
|
|
7834
|
+
console.error(`Error in event handler for "${event}":`, error);
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
}
|
|
7839
|
+
/**
|
|
7840
|
+
* Subscribe to telemetry events.
|
|
7841
|
+
*
|
|
7842
|
+
* @param event - Event name to subscribe to
|
|
7843
|
+
* @param handler - Handler function
|
|
7844
|
+
* @returns Unsubscribe function
|
|
7845
|
+
*/
|
|
7846
|
+
on(event, handler) {
|
|
7847
|
+
if (!this._eventHandlers.has(event)) {
|
|
7848
|
+
this._eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
7849
|
+
}
|
|
7850
|
+
this._eventHandlers.get(event).add(handler);
|
|
7851
|
+
return () => {
|
|
7852
|
+
const handlers = this._eventHandlers.get(event);
|
|
7853
|
+
if (handlers) {
|
|
7854
|
+
handlers.delete(handler);
|
|
7855
|
+
if (handlers.size === 0) {
|
|
7856
|
+
this._eventHandlers.delete(event);
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7859
|
+
};
|
|
7860
|
+
}
|
|
7861
|
+
/**
|
|
7862
|
+
* Remove all event handlers for an event.
|
|
7863
|
+
*
|
|
7864
|
+
* @param event - Event name (if omitted, clears all events)
|
|
7865
|
+
*/
|
|
7866
|
+
clearEventHandlers(event) {
|
|
7867
|
+
if (event) {
|
|
7868
|
+
this._eventHandlers.delete(event);
|
|
7869
|
+
} else {
|
|
7870
|
+
this._eventHandlers.clear();
|
|
7871
|
+
}
|
|
7872
|
+
}
|
|
7873
|
+
// ============================================================
|
|
7874
|
+
// Lifecycle
|
|
7875
|
+
// ============================================================
|
|
7876
|
+
/**
|
|
7877
|
+
* Get the abort signal for cancelling operations.
|
|
7878
|
+
*/
|
|
7879
|
+
get abortSignal() {
|
|
7880
|
+
return this._abortController.signal;
|
|
7881
|
+
}
|
|
7882
|
+
get operationAbortSignal() {
|
|
7883
|
+
return this._operationAbortSignal;
|
|
7884
|
+
}
|
|
7885
|
+
setOperationAbortSignal(signal) {
|
|
7886
|
+
this._operationAbortSignal = signal;
|
|
7887
|
+
}
|
|
7888
|
+
/**
|
|
7889
|
+
* Permanently retire this graph after its owner installs a replacement.
|
|
7890
|
+
* Unlike `abort()`, retirement never creates a fresh controller, so captured
|
|
7891
|
+
* services cannot resume calls under a superseded session.
|
|
7892
|
+
*/
|
|
7893
|
+
retire() {
|
|
7894
|
+
if (this._retired) return;
|
|
7895
|
+
this._retired = true;
|
|
7896
|
+
this._session = null;
|
|
7897
|
+
this._abortController.abort();
|
|
7898
|
+
for (const service of this._services.values()) {
|
|
7899
|
+
try {
|
|
7900
|
+
service.onSignOut();
|
|
7901
|
+
} catch (error) {
|
|
7902
|
+
console.error("Error retiring service:", error);
|
|
7903
|
+
}
|
|
7904
|
+
}
|
|
7905
|
+
}
|
|
7906
|
+
/**
|
|
7907
|
+
* Abort all pending operations and notify services.
|
|
7908
|
+
* Creates a new AbortController for future operations.
|
|
7909
|
+
*/
|
|
7910
|
+
abort() {
|
|
7911
|
+
if (this._retired) return;
|
|
7912
|
+
this._abortController.abort();
|
|
7913
|
+
this._abortController = new AbortController();
|
|
7914
|
+
for (const service of this._services.values()) {
|
|
7915
|
+
try {
|
|
7916
|
+
service.onSignOut();
|
|
7917
|
+
} catch (error) {
|
|
7918
|
+
console.error("Error signing out service:", error);
|
|
7919
|
+
}
|
|
7920
|
+
}
|
|
7921
|
+
}
|
|
7922
|
+
/**
|
|
7923
|
+
* Sign out - abort operations and clear session.
|
|
7924
|
+
*/
|
|
7925
|
+
signOut() {
|
|
7926
|
+
if (this._retired) return;
|
|
7927
|
+
this.abort();
|
|
7928
|
+
this.setSession(null);
|
|
7929
|
+
this.emit("session.expired", {});
|
|
7992
7930
|
}
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7931
|
+
// ============================================================
|
|
7932
|
+
// Retry Policy
|
|
7933
|
+
// ============================================================
|
|
7934
|
+
/**
|
|
7935
|
+
* Get the retry policy configuration.
|
|
7936
|
+
*/
|
|
7937
|
+
get retryPolicy() {
|
|
7938
|
+
return this._retryPolicy;
|
|
7998
7939
|
}
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
}
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
7940
|
+
wrapInvoke(invoke) {
|
|
7941
|
+
return (session2, service, path, action, facts) => {
|
|
7942
|
+
this.assertActive();
|
|
7943
|
+
if (!tinyCloudDebugLogger.isEnabled()) {
|
|
7944
|
+
return invoke(session2, service, path, action, facts);
|
|
7945
|
+
}
|
|
7946
|
+
const timer = tinyCloudDebugLogger.startTimer("sdk.invoke", {
|
|
7947
|
+
service,
|
|
7948
|
+
path,
|
|
7949
|
+
action,
|
|
7950
|
+
facts
|
|
7951
|
+
});
|
|
7952
|
+
try {
|
|
7953
|
+
const result = invoke(session2, service, path, action, facts);
|
|
7954
|
+
timer.stop({ ok: true, service, path, action });
|
|
7955
|
+
return result;
|
|
7956
|
+
} catch (error) {
|
|
7957
|
+
timer.stop({ ok: false, service, path, action, error });
|
|
7958
|
+
throw error;
|
|
7959
|
+
}
|
|
7960
|
+
};
|
|
7961
|
+
}
|
|
7962
|
+
assertActive() {
|
|
7963
|
+
if (this._retired) {
|
|
7964
|
+
throw new Error("Service graph has been retired: its session was replaced or signed out.");
|
|
8012
7965
|
}
|
|
8013
|
-
|
|
8014
|
-
|
|
7966
|
+
}
|
|
7967
|
+
wrapInvokeAny(invokeAny) {
|
|
7968
|
+
return (session2, entries, facts) => {
|
|
7969
|
+
this.assertActive();
|
|
7970
|
+
if (!tinyCloudDebugLogger.isEnabled()) {
|
|
7971
|
+
return invokeAny(session2, entries, facts);
|
|
7972
|
+
}
|
|
7973
|
+
const timer = tinyCloudDebugLogger.startTimer("sdk.invokeAny", {
|
|
7974
|
+
entries,
|
|
7975
|
+
facts
|
|
7976
|
+
});
|
|
7977
|
+
try {
|
|
7978
|
+
const result = invokeAny(session2, entries, facts);
|
|
7979
|
+
timer.stop({ ok: true, entries });
|
|
7980
|
+
return result;
|
|
7981
|
+
} catch (error) {
|
|
7982
|
+
timer.stop({ ok: false, entries, error });
|
|
7983
|
+
throw error;
|
|
7984
|
+
}
|
|
7985
|
+
};
|
|
7986
|
+
}
|
|
7987
|
+
/** Retry `NODE_BUSY` / `REQUEST_TIMEOUT`; `build` must re-sign per attempt (the node 409s a repeated invocation). */
|
|
7988
|
+
async invokeWithRetry(url, build) {
|
|
7989
|
+
this.assertActive();
|
|
7990
|
+
const policy = this._retryPolicy;
|
|
7991
|
+
const startedAt = Date.now();
|
|
7992
|
+
let backoffMs = 0;
|
|
7993
|
+
let signal;
|
|
7994
|
+
for (let attempt = 1; ; attempt++) {
|
|
7995
|
+
const init = await build();
|
|
7996
|
+
signal ?? (signal = this.combineSignals(init.signal));
|
|
7997
|
+
const response = await this._fetch(url, init);
|
|
7998
|
+
const done = () => {
|
|
7999
|
+
try {
|
|
8000
|
+
response.retry = { attempts: attempt, backoffMs };
|
|
8001
|
+
} catch {
|
|
8002
|
+
}
|
|
8003
|
+
return response;
|
|
8004
|
+
};
|
|
8005
|
+
if (attempt >= policy.maxAttempts || response.status !== 503 || !response.clone) return done();
|
|
8006
|
+
const text = await response.clone().text().catch(() => "");
|
|
8007
|
+
const error = nodeRetryError("", "", response, text);
|
|
8008
|
+
if (!error || error.meta?.retryable !== true || !policy.retryableErrors.includes(error.code)) {
|
|
8009
|
+
return done();
|
|
8010
|
+
}
|
|
8011
|
+
const remaining = (policy.maxTotalMs ?? Infinity) - (Date.now() - startedAt);
|
|
8012
|
+
if (remaining <= 0) return done();
|
|
8013
|
+
const retryAfterMs = error.meta.retryAfterMs;
|
|
8014
|
+
const step = Math.min(
|
|
8015
|
+
policy.maxDelayMs,
|
|
8016
|
+
policy.backoff === "exponential" ? policy.baseDelayMs * 2 ** (attempt - 1) : policy.backoff === "linear" ? policy.baseDelayMs * attempt : 0
|
|
8017
|
+
);
|
|
8018
|
+
const delay = Math.min(
|
|
8019
|
+
typeof retryAfterMs === "number" ? retryAfterMs : Math.random() * step,
|
|
8020
|
+
remaining
|
|
8021
|
+
);
|
|
8022
|
+
response.body?.cancel?.()?.catch?.(() => {
|
|
8023
|
+
});
|
|
8024
|
+
await abortableSleep(delay, signal);
|
|
8025
|
+
if (signal.aborted) throw signal.reason;
|
|
8026
|
+
backoffMs += delay;
|
|
8015
8027
|
}
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8028
|
+
}
|
|
8029
|
+
wrapFetch(fetchFn) {
|
|
8030
|
+
return async (url, init) => {
|
|
8031
|
+
this.assertActive();
|
|
8032
|
+
const request = {
|
|
8033
|
+
...init,
|
|
8034
|
+
signal: this.combineSignals(init?.signal)
|
|
8035
|
+
};
|
|
8036
|
+
if (!tinyCloudDebugLogger.isEnabled()) {
|
|
8037
|
+
return fetchFn(url, request);
|
|
8038
|
+
}
|
|
8039
|
+
const meta = { url, method: request.method ?? "GET" };
|
|
8040
|
+
const timer = tinyCloudDebugLogger.startTimer("sdk.fetch", { ...meta, init: request });
|
|
8041
|
+
try {
|
|
8042
|
+
const response = await fetchFn(url, request);
|
|
8043
|
+
timer.stop({
|
|
8044
|
+
...meta,
|
|
8045
|
+
ok: response.ok,
|
|
8046
|
+
status: response.status,
|
|
8047
|
+
statusText: response.statusText
|
|
8048
|
+
});
|
|
8049
|
+
return response;
|
|
8050
|
+
} catch (error) {
|
|
8051
|
+
timer.stop({ ...meta, ok: false, error });
|
|
8052
|
+
throw error;
|
|
8053
|
+
}
|
|
8021
8054
|
};
|
|
8022
8055
|
}
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8056
|
+
combineSignals(signal) {
|
|
8057
|
+
if (!signal) return this._abortController.signal;
|
|
8058
|
+
const combined = new AbortController();
|
|
8059
|
+
const abort = (source) => combined.abort(source.reason);
|
|
8060
|
+
if (signal.aborted) {
|
|
8061
|
+
abort(signal);
|
|
8062
|
+
} else if (this._abortController.signal.aborted) {
|
|
8063
|
+
abort(this._abortController.signal);
|
|
8064
|
+
} else {
|
|
8065
|
+
signal.addEventListener("abort", () => abort(signal), { once: true });
|
|
8066
|
+
this._abortController.signal.addEventListener(
|
|
8067
|
+
"abort",
|
|
8068
|
+
() => abort(this._abortController.signal),
|
|
8069
|
+
{ once: true }
|
|
8070
|
+
);
|
|
8071
|
+
}
|
|
8072
|
+
return combined.signal;
|
|
8073
|
+
}
|
|
8074
|
+
};
|
|
8029
8075
|
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
8030
8076
|
var BaseService = class {
|
|
8031
8077
|
constructor() {
|
|
@@ -8200,6 +8246,18 @@ var BaseService = class {
|
|
|
8200
8246
|
}
|
|
8201
8247
|
return controller.signal;
|
|
8202
8248
|
}
|
|
8249
|
+
/** POST a signed invocation with retry; `mint` re-signs per attempt, `init.headers` add to the signed ones. */
|
|
8250
|
+
invokeWithRetry(url, mint, { headers: extra, ...init } = {}) {
|
|
8251
|
+
const build = () => {
|
|
8252
|
+
const signed = mint();
|
|
8253
|
+
return {
|
|
8254
|
+
method: "POST",
|
|
8255
|
+
...init,
|
|
8256
|
+
headers: Array.isArray(signed) ? [...signed, ...Object.entries(extra ?? {})] : { ...signed, ...extra }
|
|
8257
|
+
};
|
|
8258
|
+
};
|
|
8259
|
+
return this.context.invokeWithRetry ? this.context.invokeWithRetry(url, build) : this.context.fetch(url, build());
|
|
8260
|
+
}
|
|
8203
8261
|
/**
|
|
8204
8262
|
* Create the abort signal for one request, bounded by the request timeout.
|
|
8205
8263
|
*
|
|
@@ -8561,15 +8619,6 @@ var KVService = class extends BaseService {
|
|
|
8561
8619
|
get host() {
|
|
8562
8620
|
return this.context.hosts[0];
|
|
8563
8621
|
}
|
|
8564
|
-
withJsonContentType(headers) {
|
|
8565
|
-
if (Array.isArray(headers)) {
|
|
8566
|
-
return [...headers, ["content-type", "application/json"]];
|
|
8567
|
-
}
|
|
8568
|
-
return {
|
|
8569
|
-
...headers,
|
|
8570
|
-
"content-type": "application/json"
|
|
8571
|
-
};
|
|
8572
|
-
}
|
|
8573
8622
|
/**
|
|
8574
8623
|
* Execute an invoke operation.
|
|
8575
8624
|
*
|
|
@@ -8580,20 +8629,11 @@ var KVService = class extends BaseService {
|
|
|
8580
8629
|
* @returns Fetch response
|
|
8581
8630
|
*/
|
|
8582
8631
|
async invokeOperation(path, action, body, signal, extraHeaders) {
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
path,
|
|
8588
|
-
action
|
|
8632
|
+
return this.invokeWithRetry(
|
|
8633
|
+
`${this.host}/invoke`,
|
|
8634
|
+
() => this.context.invoke(this.context.session, "kv", path, action),
|
|
8635
|
+
{ headers: extraHeaders, body, signal }
|
|
8589
8636
|
);
|
|
8590
|
-
const requestHeaders = Array.isArray(headers) ? [...headers, ...Object.entries(extraHeaders ?? {})] : { ...headers, ...extraHeaders };
|
|
8591
|
-
return this.context.fetch(`${this.host}/invoke`, {
|
|
8592
|
-
method: "POST",
|
|
8593
|
-
headers: requestHeaders,
|
|
8594
|
-
body,
|
|
8595
|
-
signal
|
|
8596
|
-
});
|
|
8597
8637
|
}
|
|
8598
8638
|
/**
|
|
8599
8639
|
* Serialize a single put value into a fetch body.
|
|
@@ -8799,28 +8839,28 @@ var KVService = class extends BaseService {
|
|
|
8799
8839
|
}
|
|
8800
8840
|
const request = this.createRequestSignal(options?.signal, options?.timeout);
|
|
8801
8841
|
try {
|
|
8802
|
-
const session2 = this.context.session;
|
|
8803
|
-
const invocationHeaders = this.context.invokeAny(
|
|
8804
|
-
session2,
|
|
8805
|
-
paths.map((path) => ({
|
|
8806
|
-
spaceId: session2.spaceId,
|
|
8807
|
-
service: "kv",
|
|
8808
|
-
path,
|
|
8809
|
-
action
|
|
8810
|
-
}))
|
|
8811
|
-
);
|
|
8812
8842
|
const limitHeaders = {};
|
|
8813
8843
|
if (getOptions?.maxResponseBytes !== void 0) {
|
|
8814
8844
|
limitHeaders["x-tinycloud-max-response-bytes"] = String(
|
|
8815
8845
|
getOptions.maxResponseBytes
|
|
8816
8846
|
);
|
|
8817
8847
|
}
|
|
8818
|
-
const
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8848
|
+
const response = await this.invokeWithRetry(
|
|
8849
|
+
`${this.host}/invoke`,
|
|
8850
|
+
() => {
|
|
8851
|
+
const session2 = this.context.session;
|
|
8852
|
+
return this.context.invokeAny(
|
|
8853
|
+
session2,
|
|
8854
|
+
paths.map((path) => ({
|
|
8855
|
+
spaceId: session2.spaceId,
|
|
8856
|
+
service: "kv",
|
|
8857
|
+
path,
|
|
8858
|
+
action
|
|
8859
|
+
}))
|
|
8860
|
+
);
|
|
8861
|
+
},
|
|
8862
|
+
{ headers: limitHeaders, signal: request.signal }
|
|
8863
|
+
);
|
|
8824
8864
|
if (!response.ok) {
|
|
8825
8865
|
const errorText = response.status === 401 || response.status === 403 ? await this.readAuthorizationText(response) : await response.text();
|
|
8826
8866
|
if (response.status === 401 || response.status === 403) {
|
|
@@ -8866,10 +8906,10 @@ var KVService = class extends BaseService {
|
|
|
8866
8906
|
result: err(serviceError(item.error.code, item.error.message, "kv"))
|
|
8867
8907
|
};
|
|
8868
8908
|
}
|
|
8869
|
-
const
|
|
8909
|
+
const headers = item.headers;
|
|
8870
8910
|
const data = action === KVAction.HEAD ? void 0 : this.parseBatchValue(
|
|
8871
8911
|
item.dataBase64,
|
|
8872
|
-
|
|
8912
|
+
headers,
|
|
8873
8913
|
getOptions?.raw,
|
|
8874
8914
|
getOptions?.binary
|
|
8875
8915
|
);
|
|
@@ -8877,7 +8917,7 @@ var KVService = class extends BaseService {
|
|
|
8877
8917
|
key: keys[index],
|
|
8878
8918
|
result: ok({
|
|
8879
8919
|
data,
|
|
8880
|
-
headers: this.createBatchResponseHeaders(
|
|
8920
|
+
headers: this.createBatchResponseHeaders(headers)
|
|
8881
8921
|
})
|
|
8882
8922
|
};
|
|
8883
8923
|
});
|
|
@@ -9112,7 +9152,7 @@ var KVService = class extends BaseService {
|
|
|
9112
9152
|
));
|
|
9113
9153
|
}
|
|
9114
9154
|
const data = await this.parseResponse(response, options?.raw, options?.binary);
|
|
9115
|
-
return ok({ data, headers: this.createResponseHeaders(response.headers) });
|
|
9155
|
+
return ok({ data, headers: this.createResponseHeaders(response.headers), ...response.retry && { retry: response.retry } });
|
|
9116
9156
|
}
|
|
9117
9157
|
async batchGet(keys, options) {
|
|
9118
9158
|
return this.withTelemetry(
|
|
@@ -9202,7 +9242,8 @@ var KVService = class extends BaseService {
|
|
|
9202
9242
|
}
|
|
9203
9243
|
return ok({
|
|
9204
9244
|
data: void 0,
|
|
9205
|
-
headers: this.createResponseHeaders(response.headers)
|
|
9245
|
+
headers: this.createResponseHeaders(response.headers),
|
|
9246
|
+
...response.retry && { retry: response.retry }
|
|
9206
9247
|
});
|
|
9207
9248
|
}
|
|
9208
9249
|
/**
|
|
@@ -9275,25 +9316,23 @@ var KVService = class extends BaseService {
|
|
|
9275
9316
|
entries?.[index]?.body ?? this.serializeBatchPutValue(items[index])
|
|
9276
9317
|
);
|
|
9277
9318
|
}
|
|
9278
|
-
const
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9319
|
+
const response = await this.invokeWithRetry(
|
|
9320
|
+
`${this.host}/invoke`,
|
|
9321
|
+
() => {
|
|
9322
|
+
const headers = this.context.invokeAny(
|
|
9323
|
+
session2,
|
|
9324
|
+
paths.map((path) => ({
|
|
9325
|
+
spaceId: session2.spaceId,
|
|
9326
|
+
service: "kv",
|
|
9327
|
+
path,
|
|
9328
|
+
action: KVAction.PUT
|
|
9329
|
+
}))
|
|
9330
|
+
);
|
|
9331
|
+
requestMayHaveDispatched = true;
|
|
9332
|
+
return headers;
|
|
9333
|
+
},
|
|
9334
|
+
{ body, signal }
|
|
9286
9335
|
);
|
|
9287
|
-
const url = `${this.host}/invoke`;
|
|
9288
|
-
const init = {
|
|
9289
|
-
method: "POST",
|
|
9290
|
-
headers,
|
|
9291
|
-
body,
|
|
9292
|
-
signal
|
|
9293
|
-
};
|
|
9294
|
-
const fetchFn = this.context.fetch;
|
|
9295
|
-
requestMayHaveDispatched = true;
|
|
9296
|
-
const response = await fetchFn(url, init);
|
|
9297
9336
|
if (!response.ok) {
|
|
9298
9337
|
let errorText;
|
|
9299
9338
|
try {
|
|
@@ -9763,7 +9802,8 @@ var KVService = class extends BaseService {
|
|
|
9763
9802
|
}
|
|
9764
9803
|
return ok({
|
|
9765
9804
|
data: void 0,
|
|
9766
|
-
headers: this.createResponseHeaders(response.headers)
|
|
9805
|
+
headers: this.createResponseHeaders(response.headers),
|
|
9806
|
+
...response.retry && { retry: response.retry }
|
|
9767
9807
|
});
|
|
9768
9808
|
}
|
|
9769
9809
|
/**
|
|
@@ -9837,12 +9877,6 @@ var KVService = class extends BaseService {
|
|
|
9837
9877
|
}
|
|
9838
9878
|
const path = this.getFullPath(key, options?.prefix);
|
|
9839
9879
|
const session2 = this.context.session;
|
|
9840
|
-
const headers = this.context.invoke(
|
|
9841
|
-
session2,
|
|
9842
|
-
"kv",
|
|
9843
|
-
path,
|
|
9844
|
-
KVAction.GET
|
|
9845
|
-
);
|
|
9846
9880
|
const body = {
|
|
9847
9881
|
space: session2.spaceId,
|
|
9848
9882
|
path,
|
|
@@ -9856,12 +9890,15 @@ var KVService = class extends BaseService {
|
|
|
9856
9890
|
}
|
|
9857
9891
|
const request = this.createRequestSignal(options?.signal, options?.timeout);
|
|
9858
9892
|
try {
|
|
9859
|
-
const response = await this.
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9893
|
+
const response = await this.invokeWithRetry(
|
|
9894
|
+
`${this.host}/signed/kv`,
|
|
9895
|
+
() => this.context.invoke(session2, "kv", path, KVAction.GET),
|
|
9896
|
+
{
|
|
9897
|
+
headers: { "content-type": "application/json" },
|
|
9898
|
+
body: JSON.stringify(body),
|
|
9899
|
+
signal: request.signal
|
|
9900
|
+
}
|
|
9901
|
+
);
|
|
9865
9902
|
if (!response.ok) {
|
|
9866
9903
|
return await this.createSignedReadUrlError(response, key, path);
|
|
9867
9904
|
}
|
|
@@ -10287,18 +10324,19 @@ var SQLService = class extends BaseService {
|
|
|
10287
10324
|
}
|
|
10288
10325
|
// === Private helpers ===
|
|
10289
10326
|
async invokeSQL(dbName, actions, body, signal) {
|
|
10290
|
-
const session2 = this.context.session;
|
|
10291
10327
|
const actionList = Array.isArray(actions) ? actions : [actions];
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
"Content-Type": "application/json"
|
|
10328
|
+
return this.invokeWithRetry(
|
|
10329
|
+
`${this.host}/invoke`,
|
|
10330
|
+
() => {
|
|
10331
|
+
const session2 = this.context.session;
|
|
10332
|
+
return actionList.length === 1 ? this.context.invoke(session2, "sql", dbName, actionList[0]) : this.invokeSQLAny(session2, dbName, actionList);
|
|
10298
10333
|
},
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10334
|
+
{
|
|
10335
|
+
headers: { "Content-Type": "application/json" },
|
|
10336
|
+
body: JSON.stringify(body),
|
|
10337
|
+
signal
|
|
10338
|
+
}
|
|
10339
|
+
);
|
|
10302
10340
|
}
|
|
10303
10341
|
actionForSql(sql, fallback) {
|
|
10304
10342
|
const token = firstSqlToken(sql);
|
|
@@ -10752,22 +10790,20 @@ var DuckDbService = class extends BaseService {
|
|
|
10752
10790
|
}
|
|
10753
10791
|
const request = this.createRequestSignal(options?.signal);
|
|
10754
10792
|
try {
|
|
10755
|
-
const
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10793
|
+
const response = await this.invokeWithRetry(
|
|
10794
|
+
`${this.host}/invoke`,
|
|
10795
|
+
() => this.context.invoke(
|
|
10796
|
+
this.context.session,
|
|
10797
|
+
"duckdb",
|
|
10798
|
+
dbName,
|
|
10799
|
+
DuckDbAction.IMPORT
|
|
10800
|
+
),
|
|
10801
|
+
{
|
|
10802
|
+
headers: { "Content-Type": "application/x-duckdb" },
|
|
10803
|
+
body: new Blob([data]),
|
|
10804
|
+
signal: request.signal
|
|
10805
|
+
}
|
|
10761
10806
|
);
|
|
10762
|
-
const response = await this.context.fetch(`${this.host}/invoke`, {
|
|
10763
|
-
method: "POST",
|
|
10764
|
-
headers: {
|
|
10765
|
-
...headers,
|
|
10766
|
-
"Content-Type": "application/x-duckdb"
|
|
10767
|
-
},
|
|
10768
|
-
body: new Blob([data]),
|
|
10769
|
-
signal: request.signal
|
|
10770
|
-
});
|
|
10771
10807
|
if (!response.ok) {
|
|
10772
10808
|
return await this.handleErrorResponse(response, "import");
|
|
10773
10809
|
}
|
|
@@ -10781,18 +10817,15 @@ var DuckDbService = class extends BaseService {
|
|
|
10781
10817
|
}
|
|
10782
10818
|
// === Private helpers ===
|
|
10783
10819
|
async invokeDuckDb(dbName, action, body, signal, extraHeaders) {
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
|
|
10793
|
-
body: JSON.stringify(body),
|
|
10794
|
-
signal
|
|
10795
|
-
});
|
|
10820
|
+
return this.invokeWithRetry(
|
|
10821
|
+
`${this.host}/invoke`,
|
|
10822
|
+
() => this.context.invoke(this.context.session, "duckdb", dbName, action),
|
|
10823
|
+
{
|
|
10824
|
+
headers: { "Content-Type": "application/json", ...extraHeaders },
|
|
10825
|
+
body: JSON.stringify(body),
|
|
10826
|
+
signal
|
|
10827
|
+
}
|
|
10828
|
+
);
|
|
10796
10829
|
}
|
|
10797
10830
|
async handleErrorResponse(response, operation) {
|
|
10798
10831
|
const errorText = await response.text();
|
|
@@ -10977,26 +11010,24 @@ var HooksService = class extends BaseService {
|
|
|
10977
11010
|
);
|
|
10978
11011
|
}
|
|
10979
11012
|
try {
|
|
10980
|
-
const response = await this.
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
10998
|
-
})
|
|
10999
|
-
});
|
|
11013
|
+
const response = await this.invokeWithRetry(
|
|
11014
|
+
`${this.host}/hooks/webhooks`,
|
|
11015
|
+
() => this.createHookHeaders(
|
|
11016
|
+
"tinycloud.hooks/register",
|
|
11017
|
+
buildScopePath(webhook.service, webhook.pathPrefix)
|
|
11018
|
+
),
|
|
11019
|
+
{
|
|
11020
|
+
headers: { "content-type": "application/json" },
|
|
11021
|
+
body: JSON.stringify({
|
|
11022
|
+
space: webhook.space,
|
|
11023
|
+
service: webhook.service,
|
|
11024
|
+
pathPrefix: normalizePathPrefix(webhook.pathPrefix),
|
|
11025
|
+
abilities: webhook.abilities ?? [],
|
|
11026
|
+
callbackUrl: webhook.callbackUrl,
|
|
11027
|
+
secret: webhook.secret
|
|
11028
|
+
})
|
|
11029
|
+
}
|
|
11030
|
+
);
|
|
11000
11031
|
if (!response.ok) {
|
|
11001
11032
|
return err(
|
|
11002
11033
|
await responseError("hooks", "failed to register webhook", response)
|
|
@@ -11034,17 +11065,13 @@ var HooksService = class extends BaseService {
|
|
|
11034
11065
|
query.set("prefix", normalizedPrefix);
|
|
11035
11066
|
}
|
|
11036
11067
|
}
|
|
11037
|
-
const response = await this.
|
|
11068
|
+
const response = await this.invokeWithRetry(
|
|
11038
11069
|
`${this.host}/hooks/webhooks${query.size > 0 ? `?${query.toString()}` : ""}`,
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
options.service ? buildScopePath(options.service, options.pathPrefix) : "webhooks"
|
|
11045
|
-
)
|
|
11046
|
-
)
|
|
11047
|
-
}
|
|
11070
|
+
() => this.createHookHeaders(
|
|
11071
|
+
"tinycloud.hooks/list",
|
|
11072
|
+
options.service ? buildScopePath(options.service, options.pathPrefix) : "webhooks"
|
|
11073
|
+
),
|
|
11074
|
+
{ method: "GET" }
|
|
11048
11075
|
);
|
|
11049
11076
|
if (!response.ok) {
|
|
11050
11077
|
return err(
|
|
@@ -11071,20 +11098,16 @@ var HooksService = class extends BaseService {
|
|
|
11071
11098
|
return err(authRequiredError("hooks"));
|
|
11072
11099
|
}
|
|
11073
11100
|
try {
|
|
11074
|
-
const response = await this.
|
|
11101
|
+
const response = await this.invokeWithRetry(
|
|
11075
11102
|
`${this.host}/hooks/webhooks/${encodeURIComponent(id)}`,
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
) : `webhooks/${id}`
|
|
11085
|
-
)
|
|
11086
|
-
)
|
|
11087
|
-
}
|
|
11103
|
+
() => this.createHookHeaders(
|
|
11104
|
+
"tinycloud.hooks/unregister",
|
|
11105
|
+
options.target ? buildScopePath(
|
|
11106
|
+
options.target.service,
|
|
11107
|
+
options.target.pathPrefix
|
|
11108
|
+
) : `webhooks/${id}`
|
|
11109
|
+
),
|
|
11110
|
+
{ method: "DELETE" }
|
|
11088
11111
|
);
|
|
11089
11112
|
if (!response.ok) {
|
|
11090
11113
|
return err(
|
|
@@ -11540,23 +11563,7 @@ function readProperty(value, key) {
|
|
|
11540
11563
|
return void 0;
|
|
11541
11564
|
}
|
|
11542
11565
|
}
|
|
11543
|
-
|
|
11544
|
-
return new Promise((resolve) => {
|
|
11545
|
-
const finish = () => {
|
|
11546
|
-
clearTimeout(timer);
|
|
11547
|
-
signal.removeEventListener("abort", stop);
|
|
11548
|
-
resolve();
|
|
11549
|
-
};
|
|
11550
|
-
const timer = setTimeout(finish, delayMs);
|
|
11551
|
-
timer.unref?.();
|
|
11552
|
-
const stop = () => finish();
|
|
11553
|
-
if (signal.aborted) {
|
|
11554
|
-
finish();
|
|
11555
|
-
} else {
|
|
11556
|
-
signal.addEventListener("abort", stop, { once: true });
|
|
11557
|
-
}
|
|
11558
|
-
});
|
|
11559
|
-
}
|
|
11566
|
+
var sleep = (delayMs, _attempt, signal) => abortableSleep(delayMs, signal, true);
|
|
11560
11567
|
function abortPromise(signal) {
|
|
11561
11568
|
let resolve;
|
|
11562
11569
|
const promise = new Promise((done) => {
|