@fadhilp/stateql 0.8.0 → 0.8.1
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/README.md +5 -0
- package/dist/src/adapters.d.ts +1 -0
- package/dist/src/adapters.js +1 -0
- package/dist/src/stateql.d.ts +1 -0
- package/dist/src/stateql.js +35 -30
- package/dist/src/types.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -415,6 +415,7 @@ const stateql = StateQL.forActor({
|
|
|
415
415
|
home: "./.stql",
|
|
416
416
|
actor: "pi-session-id",
|
|
417
417
|
timeoutMs: 30_000,
|
|
418
|
+
credentialTimeoutMs: 120_000,
|
|
418
419
|
maxResultBytes: 16 * 1024 * 1024,
|
|
419
420
|
maxStateBytes: 256 * 1024 * 1024,
|
|
420
421
|
});
|
|
@@ -506,6 +507,10 @@ const stateql = StateQL.forActor({
|
|
|
506
507
|
});
|
|
507
508
|
```
|
|
508
509
|
|
|
510
|
+
Credential resolution has its own two-minute default deadline
|
|
511
|
+
(`credentialTimeoutMs`) and remains cancellable through `request.signal`.
|
|
512
|
+
The database-operation timeout begins after a credential is resolved.
|
|
513
|
+
|
|
509
514
|
When no custom resolver is configured, StateQL reads only `secret_env`
|
|
510
515
|
references from `process.env`; `credential_ref` never falls back to the
|
|
511
516
|
environment. A configured resolver is authoritative for both sources: returning
|
package/dist/src/adapters.d.ts
CHANGED
package/dist/src/adapters.js
CHANGED
package/dist/src/stateql.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class StateQL {
|
|
|
11
11
|
private readonly maxResultRows;
|
|
12
12
|
private readonly maxResultBytes;
|
|
13
13
|
private readonly timeoutMs;
|
|
14
|
+
private readonly credentialTimeoutMs;
|
|
14
15
|
private readonly signal?;
|
|
15
16
|
private readonly commandContexts;
|
|
16
17
|
private readonly credentialResolver?;
|
package/dist/src/stateql.js
CHANGED
|
@@ -13,6 +13,7 @@ import { StateStore, } from "./store.js";
|
|
|
13
13
|
import { compactRows, defaultHome, hash, isSqlParameters, parseJson, redact, } from "./util.js";
|
|
14
14
|
const DEFAULT_SNAPSHOT_HISTORY_LIMIT = 50;
|
|
15
15
|
const MAX_SNAPSHOT_HISTORY_LIMIT = 100;
|
|
16
|
+
const DEFAULT_CREDENTIAL_RESOLUTION_TIMEOUT_MS = 120_000;
|
|
16
17
|
export class StateQL {
|
|
17
18
|
static forActor(options) {
|
|
18
19
|
if (!options.actor.trim()) {
|
|
@@ -41,6 +42,7 @@ export class StateQL {
|
|
|
41
42
|
maxResultRows;
|
|
42
43
|
maxResultBytes;
|
|
43
44
|
timeoutMs;
|
|
45
|
+
credentialTimeoutMs;
|
|
44
46
|
signal;
|
|
45
47
|
commandContexts = new AsyncLocalStorage();
|
|
46
48
|
credentialResolver;
|
|
@@ -60,6 +62,7 @@ export class StateQL {
|
|
|
60
62
|
this.maxResultRows = positiveInteger(options.maxResultRows ?? 10_000, "maxResultRows");
|
|
61
63
|
this.maxResultBytes = positiveInteger(options.maxResultBytes ?? 16 * 1024 * 1024, "maxResultBytes");
|
|
62
64
|
this.timeoutMs = executionTimeout(options.timeoutMs ?? 30_000);
|
|
65
|
+
this.credentialTimeoutMs = executionTimeout(options.credentialTimeoutMs ?? DEFAULT_CREDENTIAL_RESOLUTION_TIMEOUT_MS, "credentialTimeoutMs");
|
|
63
66
|
const maxStateBytes = positiveInteger(options.maxStateBytes ?? 256 * 1024 * 1024, "maxStateBytes");
|
|
64
67
|
this.signal = options.signal;
|
|
65
68
|
this.credentialResolver = options.credentialResolver;
|
|
@@ -2067,39 +2070,41 @@ export class StateQL {
|
|
|
2067
2070
|
}
|
|
2068
2071
|
async resolveCredential(reference, source, session, operation, access, context, details = {}) {
|
|
2069
2072
|
const resolver = this.credentialResolver;
|
|
2073
|
+
const credentialContext = createAdapterContext(this.credentialTimeoutMs, context.signal);
|
|
2074
|
+
let value;
|
|
2070
2075
|
if (!resolver) {
|
|
2071
|
-
if (
|
|
2076
|
+
if (credentialContext.signal?.aborted) {
|
|
2072
2077
|
throw credentialStateQLError(reference, new CredentialResolutionError("cancelled"));
|
|
2073
2078
|
}
|
|
2074
|
-
if (
|
|
2079
|
+
if (credentialContext.deadline <= Date.now()) {
|
|
2075
2080
|
throw credentialStateQLError(reference, new CredentialResolutionError("timeout"));
|
|
2076
2081
|
}
|
|
2077
|
-
if (source === "secret_env")
|
|
2078
|
-
|
|
2079
|
-
if (value)
|
|
2080
|
-
return value;
|
|
2081
|
-
}
|
|
2082
|
-
throw credentialStateQLError(reference, new CredentialResolutionError("unavailable"));
|
|
2082
|
+
if (source === "secret_env")
|
|
2083
|
+
value = env[reference];
|
|
2083
2084
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2085
|
+
else {
|
|
2086
|
+
const request = {
|
|
2087
|
+
reference,
|
|
2088
|
+
source,
|
|
2089
|
+
actorId: this.actorId,
|
|
2090
|
+
session: { id: session.id, name: session.name },
|
|
2091
|
+
operation,
|
|
2092
|
+
access,
|
|
2093
|
+
...(context.signal ? { signal: context.signal } : {}),
|
|
2094
|
+
...details,
|
|
2095
|
+
};
|
|
2096
|
+
try {
|
|
2097
|
+
value = await resolveCredentialBeforeDeadline(resolver, request, credentialContext);
|
|
2098
|
+
}
|
|
2099
|
+
catch (error) {
|
|
2100
|
+
throw credentialStateQLError(reference, error);
|
|
2101
|
+
}
|
|
2099
2102
|
}
|
|
2100
|
-
|
|
2101
|
-
throw credentialStateQLError(reference,
|
|
2103
|
+
if (!value) {
|
|
2104
|
+
throw credentialStateQLError(reference, new CredentialResolutionError("unavailable"));
|
|
2102
2105
|
}
|
|
2106
|
+
context.deadline = Date.now() + (context.timeoutMs ?? this.timeoutMs);
|
|
2107
|
+
return value;
|
|
2103
2108
|
}
|
|
2104
2109
|
async openAdapter(connection, context, source) {
|
|
2105
2110
|
try {
|
|
@@ -2435,19 +2440,19 @@ function credentialStateQLError(reference, error) {
|
|
|
2435
2440
|
case "cancelled":
|
|
2436
2441
|
return new StateQLError("OPERATION_CANCELLED", "Credential resolution was cancelled.", { retryable: true });
|
|
2437
2442
|
case "timeout":
|
|
2438
|
-
return new StateQLError("DEADLINE_EXCEEDED", "Credential resolution exceeded the
|
|
2443
|
+
return new StateQLError("DEADLINE_EXCEEDED", "Credential resolution exceeded the credential deadline.", { retryable: true });
|
|
2439
2444
|
}
|
|
2440
2445
|
}
|
|
2441
2446
|
return new StateQLError("CREDENTIAL_RESOLUTION_FAILED", `Credential reference "${reference}" could not be resolved.`, { retryable: true });
|
|
2442
2447
|
}
|
|
2443
2448
|
function safeCredentialErrorMessage(error, source) {
|
|
2444
2449
|
return redact(errorMessage(error).split(source).join("[credential redacted]"))
|
|
2445
|
-
.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s
|
|
2450
|
+
.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s\/@]+(?::[^\s\/@]*)?@/giu, "$1***@");
|
|
2446
2451
|
}
|
|
2447
|
-
function executionTimeout(value) {
|
|
2448
|
-
const timeout = positiveInteger(value,
|
|
2452
|
+
function executionTimeout(value, name = "timeoutMs") {
|
|
2453
|
+
const timeout = positiveInteger(value, name);
|
|
2449
2454
|
if (timeout > 2_147_483_647) {
|
|
2450
|
-
throw new StateQLError("INVALID_COMMAND",
|
|
2455
|
+
throw new StateQLError("INVALID_COMMAND", `${name} cannot exceed 2147483647 milliseconds.`);
|
|
2451
2456
|
}
|
|
2452
2457
|
return timeout;
|
|
2453
2458
|
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -213,6 +213,8 @@ export interface StateQLOptions extends ExecutionOptions {
|
|
|
213
213
|
maxResultBytes?: number;
|
|
214
214
|
maxStateBytes?: number;
|
|
215
215
|
credentialResolver?: CredentialResolver;
|
|
216
|
+
/** Maximum time allowed for one credential resolution; defaults to two minutes. */
|
|
217
|
+
credentialTimeoutMs?: number;
|
|
216
218
|
now?: () => Date;
|
|
217
219
|
}
|
|
218
220
|
export type StateQLActorOptions = Omit<StateQLOptions, "session" | "actor"> & {
|