@pikku/core 0.12.74 → 0.12.78
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/CHANGELOG.md +397 -0
- package/dist/column-form.d.ts +32 -0
- package/dist/column-form.js +42 -0
- package/dist/crypto-utils.d.ts +15 -4
- package/dist/crypto-utils.js +18 -2
- package/dist/data-classification.d.ts +44 -0
- package/dist/function/functions.types.d.ts +23 -10
- package/dist/function/index.d.ts +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +3 -1
- package/dist/middleware/auth-bearer.js +2 -1
- package/dist/middleware/remote-auth.js +1 -1
- package/dist/remote.js +1 -1
- package/dist/secret-value.d.ts +56 -0
- package/dist/secret-value.js +46 -0
- package/dist/services/audit-service.d.ts +74 -4
- package/dist/services/audit-service.js +7 -5
- package/dist/services/credential-wire-service.d.ts +5 -0
- package/dist/services/credential-wire-service.js +9 -1
- package/dist/services/email-service.d.ts +2 -1
- package/dist/services/in-memory-workflow-service.d.ts +1 -0
- package/dist/services/in-memory-workflow-service.js +20 -0
- package/dist/services/index.d.ts +3 -3
- package/dist/services/index.js +1 -1
- package/dist/services/local-content-request-handler.d.ts +29 -0
- package/dist/services/local-content-request-handler.js +176 -0
- package/dist/services/local-secrets.d.ts +4 -3
- package/dist/services/local-secrets.js +7 -3
- package/dist/services/logger.d.ts +22 -5
- package/dist/services/queue-webhook-service.js +1 -1
- package/dist/services/scoped-secret-service.d.ts +4 -3
- package/dist/services/secret-service.d.ts +8 -3
- package/dist/services/typed-secret-service.d.ts +5 -4
- package/dist/services/webhook-service.d.ts +2 -1
- package/dist/testing/service-tests.js +6 -6
- package/dist/types/core.types.d.ts +25 -4
- package/dist/wirings/ai-agent/ai-agent-agui.js +13 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +7 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +14 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +27 -1
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +40 -0
- package/dist/wirings/ai-agent/index.d.ts +1 -1
- package/dist/wirings/ai-agent/index.js +1 -1
- package/dist/wirings/ai-agent/voice-input.d.ts +20 -0
- package/dist/wirings/ai-agent/voice-input.js +44 -9
- package/dist/wirings/ai-agent/voice-output.d.ts +15 -0
- package/dist/wirings/ai-agent/voice-output.js +10 -1
- package/dist/wirings/cli/channel/cli-raw-client-runner.d.ts +21 -3
- package/dist/wirings/cli/channel/cli-raw-client-runner.js +13 -5
- package/dist/wirings/cli/channel/index.d.ts +1 -0
- package/dist/wirings/persona/define-personas.d.ts +4 -0
- package/dist/wirings/persona/define-personas.js +4 -0
- package/dist/wirings/persona/persona.types.d.ts +11 -0
- package/dist/wirings/queue/queue-identity.js +2 -1
- package/dist/wirings/queue/queue.types.d.ts +2 -1
- package/dist/wirings/queue/signed-queue-service.d.ts +2 -1
- package/dist/wirings/rpc/remote-addon-auth.d.ts +2 -1
- package/dist/wirings/rpc/remote-addon-auth.js +6 -2
- package/dist/wirings/virtual-user/index.d.ts +3 -0
- package/dist/wirings/virtual-user/index.js +2 -0
- package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +54 -0
- package/dist/wirings/virtual-user/prepare-virtual-user-run.js +49 -0
- package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +90 -0
- package/dist/wirings/virtual-user/virtual-user-run-store.js +1 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +14 -10
- package/dist/wirings/workflow/pikku-scenario-service.js +1 -2
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +35 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +51 -0
- package/dist/wirings/workflow/scenario-prose.js +1 -1
- package/dist/wirings/workflow/scenario-step.types.d.ts +13 -7
- package/dist/wirings/workflow/workflow.types.d.ts +7 -0
- package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +48 -0
- package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +84 -0
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +9 -2
- package/knowledge/decisions/internals/index.md +4 -0
- package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +53 -0
- package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +48 -0
- package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +43 -0
- package/package.json +3 -2
- package/src/column-form.test.ts +97 -0
- package/src/column-form.ts +58 -0
- package/src/crypto-utils.ts +25 -6
- package/src/data-classification.ts +44 -0
- package/src/function/functions.types.ts +47 -10
- package/src/function/index.ts +1 -0
- package/src/index.ts +24 -2
- package/src/middleware/auth-bearer.test.ts +3 -2
- package/src/middleware/auth-bearer.ts +2 -1
- package/src/middleware/remote-auth.test.ts +2 -1
- package/src/middleware/remote-auth.ts +1 -1
- package/src/remote.test.ts +2 -1
- package/src/remote.ts +1 -1
- package/src/secret-value.test.ts +204 -0
- package/src/secret-value.ts +111 -0
- package/src/services/audit-service.ts +87 -9
- package/src/services/credential-wire-service.ts +9 -1
- package/src/services/email-service.ts +3 -1
- package/src/services/in-memory-workflow-service.ts +29 -0
- package/src/services/index.ts +3 -3
- package/src/services/local-content-request-handler.test.ts +202 -0
- package/src/services/local-content-request-handler.ts +267 -0
- package/src/services/local-secrets.test.ts +20 -5
- package/src/services/local-secrets.ts +15 -7
- package/src/services/logger.ts +27 -7
- package/src/services/queue-webhook-service.test.ts +2 -1
- package/src/services/queue-webhook-service.ts +1 -1
- package/src/services/scoped-secret-service.ts +4 -3
- package/src/services/secret-service.ts +8 -3
- package/src/services/typed-secret-service.ts +11 -7
- package/src/services/webhook-service.ts +4 -1
- package/src/testing/service-tests.ts +6 -6
- package/src/types/core.types.ts +25 -4
- package/src/wirings/ai-agent/ai-agent-agui.test.ts +16 -0
- package/src/wirings/ai-agent/ai-agent-agui.ts +14 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +7 -1
- package/src/wirings/ai-agent/ai-agent-runner.ts +18 -2
- package/src/wirings/ai-agent/ai-agent-stream.ts +32 -1
- package/src/wirings/ai-agent/ai-agent.types.ts +45 -1
- package/src/wirings/ai-agent/index.ts +2 -0
- package/src/wirings/ai-agent/voice-input.test.ts +65 -0
- package/src/wirings/ai-agent/voice-input.ts +48 -9
- package/src/wirings/ai-agent/voice-output.test.ts +91 -1
- package/src/wirings/ai-agent/voice-output.ts +28 -1
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +39 -9
- package/src/wirings/cli/channel/index.ts +4 -0
- package/src/wirings/persona/define-personas.ts +4 -0
- package/src/wirings/persona/persona.types.ts +11 -0
- package/src/wirings/queue/queue-identity.test.ts +2 -1
- package/src/wirings/queue/queue-identity.ts +4 -1
- package/src/wirings/queue/queue.types.ts +6 -1
- package/src/wirings/queue/signed-queue-service.ts +2 -1
- package/src/wirings/rpc/remote-addon-auth.ts +8 -3
- package/src/wirings/rpc/rpc-runner.test.ts +6 -4
- package/src/wirings/virtual-user/index.ts +12 -0
- package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +115 -0
- package/src/wirings/virtual-user/prepare-virtual-user-run.ts +95 -0
- package/src/wirings/virtual-user/virtual-user-run-store.ts +98 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +14 -16
- package/src/wirings/workflow/pikku-scenario-service.ts +1 -10
- package/src/wirings/workflow/pikku-workflow-service.ts +67 -0
- package/src/wirings/workflow/scenario-prose.test.ts +5 -7
- package/src/wirings/workflow/scenario-prose.ts +1 -1
- package/src/wirings/workflow/scenario-service.test.ts +0 -1
- package/src/wirings/workflow/scenario-step.test.ts +4 -5
- package/src/wirings/workflow/scenario-step.types.ts +13 -7
- package/src/wirings/workflow/scenario-surface.test.ts +6 -5
- package/src/wirings/workflow/workflow-stalled-recovery.test.ts +106 -0
- package/src/wirings/workflow/workflow.types.ts +7 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/tsconfig.type-tests.json +12 -0
|
@@ -4,13 +4,14 @@ import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
|
4
4
|
import type { PikkuError } from '../errors/error-handler.js';
|
|
5
5
|
import type { CoreNodeConfig } from '../wirings/node/node.types.js';
|
|
6
6
|
import type { ScenarioSurface } from '../wirings/workflow/scenario-step.types.js';
|
|
7
|
+
import type { Safe } from '../secret-value.js';
|
|
7
8
|
/**
|
|
8
9
|
* @deprecated Use StandardSchemaV1 from @standard-schema/spec instead.
|
|
9
10
|
* This alias exists only for backward compatibility with generated code.
|
|
10
11
|
*/
|
|
11
12
|
export type ZodLike<T = any> = StandardSchemaV1<T, T>;
|
|
12
|
-
export type CorePikkuFunction<In, Out, Services extends CoreSecretlessSingletonServices = SecretlessServices<CoreServices>, Session extends CoreUserSession = CoreUserSession, Wire extends PikkuWire<In, Out, true, Session, PikkuRPC, null, string> = PikkuWire<In, Out, true, Session>> = (services: Services, data: In, wire: Wire) => Wire['channel'] extends null ? Promise<Out
|
|
13
|
-
export type CorePikkuFunctionSessionless<In, Out, Services extends CoreSecretlessSingletonServices = SecretlessServices<CoreServices>, Session extends CoreUserSession = CoreUserSession, Wire extends PikkuWire<In, Out, false, Session, PikkuRPC, null, string> = PikkuWire<In, Out, false, Session>> = (services: Services, data: In, wire: Wire) => Wire['channel'] extends null ? Promise<Out
|
|
13
|
+
export type CorePikkuFunction<In, Out, Services extends CoreSecretlessSingletonServices = SecretlessServices<CoreServices>, Session extends CoreUserSession = CoreUserSession, Wire extends PikkuWire<In, Out, true, Session, PikkuRPC, null, string> = PikkuWire<In, Out, true, Session>> = (services: Services, data: In, wire: Wire) => Wire['channel'] extends null ? Promise<Safe<Out>> : Promise<Safe<Out>> | Promise<void>;
|
|
14
|
+
export type CorePikkuFunctionSessionless<In, Out, Services extends CoreSecretlessSingletonServices = SecretlessServices<CoreServices>, Session extends CoreUserSession = CoreUserSession, Wire extends PikkuWire<In, Out, false, Session, PikkuRPC, null, string> = PikkuWire<In, Out, false, Session>> = (services: Services, data: In, wire: Wire) => Wire['channel'] extends null ? Promise<Safe<Out>> : Promise<Safe<Out>> | Promise<void>;
|
|
14
15
|
export type CorePikkuPermission<In = any, Services extends CoreSecretlessSingletonServices = SecretlessServices<CoreServices>, Wire extends PikkuWire<In, never, false, any, PikkuRPC, never, never> = PikkuWire<In, never, false, any, PikkuRPC, never, never>> = (services: Services, data: In, wire: Wire) => Promise<boolean>;
|
|
15
16
|
export type CorePikkuPermissionConfig<In = any, Services extends CoreSecretlessSingletonServices = SecretlessServices<CoreServices>, Wire extends PikkuWire<In, never, false, any, PikkuRPC, never, never> = PikkuWire<In, never, false, any, PikkuRPC, never, never>> = {
|
|
16
17
|
func: CorePikkuPermission<In, Services, Wire>;
|
|
@@ -49,16 +50,18 @@ export type CorePikkuFunctionConfig<PikkuFunction extends CorePikkuFunction<any,
|
|
|
49
50
|
tags?: string[];
|
|
50
51
|
expose?: boolean;
|
|
51
52
|
/**
|
|
52
|
-
* The
|
|
53
|
+
* The permission check for this function lives in its body — verifying a
|
|
53
54
|
* signed token, checking a webhook signature, matching an invite code — so
|
|
54
|
-
* it is not open despite
|
|
55
|
+
* it is not open despite declaring no session, scope or permission.
|
|
55
56
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
57
|
+
* A last resort. Prefer `permissions`, which are declared, inspectable, and
|
|
58
|
+
* reusable; reach for this only when the check cannot be expressed as one.
|
|
59
|
+
*
|
|
60
|
+
* Purely declarative — it grants nothing, and asserting it falsely disables
|
|
61
|
+
* the audit that would have caught the mistake. Requires
|
|
62
|
+
* `allow.permissionsInBody` in `pikku.config.json`.
|
|
60
63
|
*/
|
|
61
|
-
|
|
64
|
+
permissionsInBody?: boolean;
|
|
62
65
|
remote?: boolean;
|
|
63
66
|
mcp?: boolean;
|
|
64
67
|
readonly?: boolean;
|
|
@@ -103,7 +106,8 @@ export type CorePikkuFunctionConfig<PikkuFunction extends CorePikkuFunction<any,
|
|
|
103
106
|
* Scopes the session must hold; all are required (AND) and checked before
|
|
104
107
|
* `permissions`, which OR together — a scope can only narrow access.
|
|
105
108
|
* Narrowed to the generated `ScopeId` union, so an undeclared scope is a
|
|
106
|
-
* compile error.
|
|
109
|
+
* compile error. Requires a session — see
|
|
110
|
+
* {@link CorePikkuSessionlessFunctionConfig}.
|
|
107
111
|
*/
|
|
108
112
|
scopes?: Scope[];
|
|
109
113
|
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
@@ -113,3 +117,12 @@ export type CorePikkuFunctionConfig<PikkuFunction extends CorePikkuFunction<any,
|
|
|
113
117
|
node?: CoreNodeConfig;
|
|
114
118
|
errors?: Array<typeof PikkuError>;
|
|
115
119
|
};
|
|
120
|
+
/**
|
|
121
|
+
* {@link CorePikkuFunctionConfig} for a function that runs without a session.
|
|
122
|
+
*
|
|
123
|
+
* Identical, minus `scopes`. Scopes are AND-ed and `verifyScopes` fails closed,
|
|
124
|
+
* so an anonymous caller holds none and satisfies none — a sessionless function
|
|
125
|
+
* with scopes rejects every caller it exists to serve. Gate it with
|
|
126
|
+
* `permissions`, which receive the optional session and may pass anonymous.
|
|
127
|
+
*/
|
|
128
|
+
export type CorePikkuSessionlessFunctionConfig<PikkuFunction extends CorePikkuFunctionSessionless<any, any, any, any, any>, PikkuPermission extends CorePikkuPermission<any, any, any> = CorePikkuPermission<any>, PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<any, any>, InputSchema extends StandardSchemaV1 | undefined = undefined, OutputSchema extends StandardSchemaV1 | undefined = undefined> = Omit<CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware, InputSchema, OutputSchema>, 'scopes'>;
|
package/dist/function/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { addFunction, getAllFunctionNames } from './function-runner.js';
|
|
2
2
|
export { pikkuAuth, pikkuPermission, pikkuPermissionFactory, pikkuApprovalDescription, } from './functions.types.js';
|
|
3
|
-
export type { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuFunctionConfig, CorePikkuAuth, CorePikkuAuthConfig, CorePikkuPermission, } from './functions.types.js';
|
|
3
|
+
export type { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuFunctionConfig, CorePikkuSessionlessFunctionConfig, CorePikkuAuth, CorePikkuAuthConfig, CorePikkuPermission, } from './functions.types.js';
|
|
4
4
|
export type { ListInput, ListOutput, Filter, LeafFilter, LeafValue, } from './list.types.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -36,8 +36,8 @@ export type { GatewayService } from './services/gateway-service.js';
|
|
|
36
36
|
export type { TriggerService } from './services/trigger-service.js';
|
|
37
37
|
export type { SchemaService } from './services/schema-service.js';
|
|
38
38
|
export type { SessionService } from './services/user-session-service.js';
|
|
39
|
-
export { NoopAuditService, createInvocationAudit,
|
|
40
|
-
export type {
|
|
39
|
+
export { NoopAuditService, createInvocationAudit, resolveAuditConfig, resolveAuditUserIdentityFromWire, } from './services/audit-service.js';
|
|
40
|
+
export type { AuditConfig, AuditDurability, AuditEvent, AuditEventBatch, AuditFacets, AuditLog, AuditLogWriteInput, AuditOutcome, AuditQuery, AuditQueryResult, AuditService, AuditSource, AuditUserIdentity, ResolvedAuditConfig, } from './services/audit-service.js';
|
|
41
41
|
export type { AIAgentRunnerService, AIEmbedManyParams, AIEmbedManyResult, AIEmbedParams, AIEmbedResult, AIGenerateImageParams, AIGenerateImagePrompt, AIGenerateImageResult, AIGenerateSpeechParams, AIGenerateSpeechResult, AIProviderOptions, AIRerankParams, AIRerankResult, AITranscriptionParams, AITranscriptionResult, } from './services/ai-agent-runner-service.js';
|
|
42
42
|
export type { AIEmbeddingService } from './services/ai-embedding-service.js';
|
|
43
43
|
export type { AIRunStateService } from './services/ai-run-state-service.js';
|
|
@@ -59,4 +59,7 @@ export { getSingletonServices, getCreateWireServices, setSingletonServices, } fr
|
|
|
59
59
|
export { clearPikkuRuntimeState } from './test-utils.js';
|
|
60
60
|
export { type ScheduledTaskInfo, type ScheduledTaskSummary, } from './services/scheduler-service.js';
|
|
61
61
|
export { SchedulerService } from './services/scheduler-service.js';
|
|
62
|
-
export type { Private, Pii, Secret, Classification, AnonymizeStrategy, ColumnClassification, ClassificationManifest, } from './data-classification.js';
|
|
62
|
+
export type { Private, Pii, Secret, Classification, AnonymizeStrategy, ColumnClassification, ClassificationManifest, ColumnForm, WrappedValue, SealedValue, HashedValue, } from './data-classification.js';
|
|
63
|
+
export { hashToken, unsafeAsWrapped, unsafeAsSealed, unsafeAsHashed, } from './column-form.js';
|
|
64
|
+
export type { SecretValue, Safe } from './secret-value.js';
|
|
65
|
+
export { createSecretValue, isSecretValue, SecretCoercionError, REDACTED, } from './secret-value.js';
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { AIProviderAuthError, AIProviderNotConfiguredError, BadGatewayError, Bad
|
|
|
15
15
|
export { PikkuError, isExpectedError } from './errors/error-handler.js';
|
|
16
16
|
export { SecretAccessDeniedError, withoutSecrets, } from './services/secretless.js';
|
|
17
17
|
export { SecretHostNotAllowedError, assertSecretAllowedForHost, } from './services/secret-host-binding.js';
|
|
18
|
-
export { NoopAuditService, createInvocationAudit,
|
|
18
|
+
export { NoopAuditService, createInvocationAudit, resolveAuditConfig, resolveAuditUserIdentityFromWire, } from './services/audit-service.js';
|
|
19
19
|
export { createGraph } from './wirings/workflow/graph/graph-node.js';
|
|
20
20
|
export { wireAddon } from './wirings/rpc/wire-addon.js';
|
|
21
21
|
export { wireRemoteAddon } from './wirings/rpc/wire-remote-addon.js';
|
|
@@ -26,3 +26,5 @@ export { isSerializable, stopSingletonServices, pikkuServerLifecycle, } from './
|
|
|
26
26
|
export { getSingletonServices, getCreateWireServices, setSingletonServices, } from './pikku-state.js';
|
|
27
27
|
export { clearPikkuRuntimeState } from './test-utils.js';
|
|
28
28
|
export { SchedulerService } from './services/scheduler-service.js';
|
|
29
|
+
export { hashToken, unsafeAsWrapped, unsafeAsSealed, unsafeAsHashed, } from './column-form.js';
|
|
30
|
+
export { createSecretValue, isSecretValue, SecretCoercionError, REDACTED, } from './secret-value.js';
|
|
@@ -33,9 +33,10 @@ export const authBearer = pikkuMiddlewareFactory(({ token } = {}) => pikkuMiddle
|
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
35
|
// An unset secret means the feature is off — never a request error.
|
|
36
|
-
|
|
36
|
+
const stored = await secrets
|
|
37
37
|
?.getSecret(token.secretId)
|
|
38
38
|
.catch(() => undefined);
|
|
39
|
+
expected = stored?.reveal();
|
|
39
40
|
}
|
|
40
41
|
if (expected && constantTimeEqual(bearerToken, expected)) {
|
|
41
42
|
userSession = token.userSession;
|
|
@@ -7,7 +7,7 @@ export const pikkuRemoteAuthMiddleware = pikkuMiddleware(async ({ secrets, jwt }
|
|
|
7
7
|
}
|
|
8
8
|
let secret;
|
|
9
9
|
try {
|
|
10
|
-
secret = await secrets.getSecret('PIKKU_REMOTE_SECRET');
|
|
10
|
+
secret = (await secrets.getSecret('PIKKU_REMOTE_SECRET')).reveal();
|
|
11
11
|
}
|
|
12
12
|
catch {
|
|
13
13
|
if (http.request.path().startsWith('/remote/rpc/')) {
|
package/dist/remote.js
CHANGED
|
@@ -11,7 +11,7 @@ export async function buildRemoteHeaders(jwt, secrets, funcName, session, traceI
|
|
|
11
11
|
};
|
|
12
12
|
let secret;
|
|
13
13
|
try {
|
|
14
|
-
secret = await secrets?.getSecret('PIKKU_REMOTE_SECRET');
|
|
14
|
+
secret = (await secrets?.getSecret('PIKKU_REMOTE_SECRET'))?.reveal();
|
|
15
15
|
}
|
|
16
16
|
catch { }
|
|
17
17
|
if (secret && jwt) {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Secret } from './data-classification.js';
|
|
2
|
+
declare const secretValueBrand: unique symbol;
|
|
3
|
+
export declare const REDACTED = "[secret]";
|
|
4
|
+
/** Runtime marker, resilient to duplicate copies of core. */
|
|
5
|
+
declare const SECRET_VALUE: unique symbol;
|
|
6
|
+
declare const NODE_INSPECT: unique symbol;
|
|
7
|
+
export declare class SecretCoercionError extends Error {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A vault secret. Nominal, so it is not assignable to `string` and every
|
|
12
|
+
* concretely-typed sink rejects it; `.reveal()` is the one way out, and every
|
|
13
|
+
* call is a deliberate, greppable disclosure.
|
|
14
|
+
*
|
|
15
|
+
* The revealed value carries the erasable `Secret<T>` classification brand, so
|
|
16
|
+
* the inspector can still follow it one hop past the call.
|
|
17
|
+
*
|
|
18
|
+
* Structured serialization redacts to `[secret]` — an audit or log write must
|
|
19
|
+
* stay honest about the field without crashing the request. String coercion
|
|
20
|
+
* throws, because a template literal or concatenation is always a leak.
|
|
21
|
+
*/
|
|
22
|
+
export declare class SecretValue<T = string> {
|
|
23
|
+
#private;
|
|
24
|
+
readonly [secretValueBrand]: true;
|
|
25
|
+
readonly [SECRET_VALUE] = true;
|
|
26
|
+
constructor(value: T);
|
|
27
|
+
reveal(): Secret<T>;
|
|
28
|
+
toJSON(): string;
|
|
29
|
+
[NODE_INSPECT](): string;
|
|
30
|
+
toString(): never;
|
|
31
|
+
[Symbol.toPrimitive](): never;
|
|
32
|
+
}
|
|
33
|
+
export declare const createSecretValue: <T>(value: T) => SecretValue<T>;
|
|
34
|
+
export declare const isSecretValue: (value: unknown) => value is SecretValue<unknown>;
|
|
35
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
36
|
+
type Passthrough = Function | Date | RegExp | Error | ArrayBuffer | ArrayBufferView;
|
|
37
|
+
/**
|
|
38
|
+
* Rejects a `SecretValue` anywhere in `T`, however deeply nested, by collapsing
|
|
39
|
+
* it to `never`.
|
|
40
|
+
*
|
|
41
|
+
* For sinks whose parameters are `any`, `unknown` or a free generic — loggers,
|
|
42
|
+
* queue payloads, channel messages — where nominality alone cannot help.
|
|
43
|
+
* `any` is passed through untouched: it cannot be guarded, and collapsing it
|
|
44
|
+
* would reject every legitimate call.
|
|
45
|
+
*
|
|
46
|
+
* `Promise`, `Map` and `Set` are recursed into rather than passed through. A
|
|
47
|
+
* mapped type cannot reach what they hold — mapping their keys yields their
|
|
48
|
+
* methods, not their contents — so treating them as opaque let a secret ride
|
|
49
|
+
* through inside one. `Promise` is the case that bites: `getSecret()` returns
|
|
50
|
+
* `Promise<SecretValue<T>>`, so a forgotten `await` would otherwise log a
|
|
51
|
+
* secret.
|
|
52
|
+
*/
|
|
53
|
+
export type Safe<T> = IsAny<T> extends true ? T : [Extract<T, SecretValue<any>>] extends [never] ? T extends Promise<infer V> ? Promise<Safe<V>> : T extends Map<infer K, infer V> ? Map<Safe<K>, Safe<V>> : T extends Set<infer V> ? Set<Safe<V>> : T extends Passthrough ? T : T extends object ? {
|
|
54
|
+
[K in keyof T]: Safe<T[K]>;
|
|
55
|
+
} : T : never;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const REDACTED = '[secret]';
|
|
2
|
+
/** Runtime marker, resilient to duplicate copies of core. */
|
|
3
|
+
const SECRET_VALUE = Symbol.for('pikku.secretValue');
|
|
4
|
+
const NODE_INSPECT = Symbol.for('nodejs.util.inspect.custom');
|
|
5
|
+
export class SecretCoercionError extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(`A secret was coerced to a string, which would write it out in the clear. Unwrap it deliberately with .reveal() at the point it reaches the wire.`);
|
|
8
|
+
this.name = 'SecretCoercionError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A vault secret. Nominal, so it is not assignable to `string` and every
|
|
13
|
+
* concretely-typed sink rejects it; `.reveal()` is the one way out, and every
|
|
14
|
+
* call is a deliberate, greppable disclosure.
|
|
15
|
+
*
|
|
16
|
+
* The revealed value carries the erasable `Secret<T>` classification brand, so
|
|
17
|
+
* the inspector can still follow it one hop past the call.
|
|
18
|
+
*
|
|
19
|
+
* Structured serialization redacts to `[secret]` — an audit or log write must
|
|
20
|
+
* stay honest about the field without crashing the request. String coercion
|
|
21
|
+
* throws, because a template literal or concatenation is always a leak.
|
|
22
|
+
*/
|
|
23
|
+
export class SecretValue {
|
|
24
|
+
[SECRET_VALUE] = true;
|
|
25
|
+
#value;
|
|
26
|
+
constructor(value) {
|
|
27
|
+
this.#value = value;
|
|
28
|
+
}
|
|
29
|
+
reveal() {
|
|
30
|
+
return this.#value;
|
|
31
|
+
}
|
|
32
|
+
toJSON() {
|
|
33
|
+
return REDACTED;
|
|
34
|
+
}
|
|
35
|
+
[NODE_INSPECT]() {
|
|
36
|
+
return REDACTED;
|
|
37
|
+
}
|
|
38
|
+
toString() {
|
|
39
|
+
throw new SecretCoercionError();
|
|
40
|
+
}
|
|
41
|
+
[Symbol.toPrimitive]() {
|
|
42
|
+
throw new SecretCoercionError();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export const createSecretValue = (value) => new SecretValue(value);
|
|
46
|
+
export const isSecretValue = (value) => typeof value === 'object' && value !== null && SECRET_VALUE in value;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CoreUserSession, PikkuWire, PikkuWiringTypes } from '../types/core.types.js';
|
|
2
2
|
import type { Logger } from './logger.js';
|
|
3
|
+
import type { Safe } from '../secret-value.js';
|
|
3
4
|
export type AuditDurability = 'best-effort' | 'transactional';
|
|
4
5
|
export type AuditOutcome = 'success' | 'failed' | 'denied';
|
|
5
6
|
export type AuditSource = 'auto' | 'explicit';
|
|
@@ -9,7 +10,19 @@ export type AuditConfig = boolean | {
|
|
|
9
10
|
export type ResolvedAuditConfig = {
|
|
10
11
|
durability: AuditDurability;
|
|
11
12
|
};
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Who an event happened under.
|
|
15
|
+
*
|
|
16
|
+
* A user, not an "actor": in pikku an actor is a synthetic person a scenario
|
|
17
|
+
* drives, flagged `actor` on the user row, and the overwhelming majority of
|
|
18
|
+
* audited events are caused by ordinary customers. Naming this `actor` made the
|
|
19
|
+
* synthetic case unsayable — `actor.actor === true` — and implied every
|
|
20
|
+
* recorded action was a test.
|
|
21
|
+
*
|
|
22
|
+
* `pikkuUserId` is the identity pikku resolves for every wire, so it is the one
|
|
23
|
+
* field a signed-out caller still leaves behind.
|
|
24
|
+
*/
|
|
25
|
+
export type AuditUserIdentity = {
|
|
13
26
|
userId?: string;
|
|
14
27
|
orgId?: string;
|
|
15
28
|
pikkuUserId?: string;
|
|
@@ -26,26 +39,83 @@ export type AuditEvent = {
|
|
|
26
39
|
traceId?: string;
|
|
27
40
|
transactionId?: string | null;
|
|
28
41
|
queryId?: string | null;
|
|
29
|
-
|
|
42
|
+
userIdentity?: AuditUserIdentity;
|
|
30
43
|
input?: unknown;
|
|
31
44
|
metadata?: Record<string, unknown>;
|
|
32
45
|
};
|
|
33
46
|
export type AuditEventBatch = AuditEvent[];
|
|
47
|
+
/**
|
|
48
|
+
* A page of the trail, newest first, narrowed by the filters a reader picked.
|
|
49
|
+
*
|
|
50
|
+
* Every field is a conjunction, and an empty array is not a filter — it is
|
|
51
|
+
* "match nothing", which would otherwise read as "match everything" and quietly
|
|
52
|
+
* widen a scoped query.
|
|
53
|
+
*/
|
|
54
|
+
export type AuditQuery = {
|
|
55
|
+
/** Restrict to these users. */
|
|
56
|
+
userIds?: string[];
|
|
57
|
+
/** Restrict to these `AuditEvent['type']` values. */
|
|
58
|
+
types?: string[];
|
|
59
|
+
/** Restrict to one organisation. */
|
|
60
|
+
orgId?: string;
|
|
61
|
+
/** Inclusive lower bound on `occurredAt` (ISO 8601). */
|
|
62
|
+
from?: string;
|
|
63
|
+
/** Exclusive upper bound on `occurredAt` (ISO 8601). */
|
|
64
|
+
to?: string;
|
|
65
|
+
limit?: number;
|
|
66
|
+
offset?: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* The distinct values present in the trail, for populating filter controls.
|
|
70
|
+
*
|
|
71
|
+
* Computed over the whole trail rather than the current page — a filter list
|
|
72
|
+
* that only offered what the current page happens to show could never be used
|
|
73
|
+
* to reach anything else.
|
|
74
|
+
*/
|
|
75
|
+
export type AuditFacets = {
|
|
76
|
+
userIds: string[];
|
|
77
|
+
types: string[];
|
|
78
|
+
};
|
|
79
|
+
export type AuditQueryResult = {
|
|
80
|
+
events: AuditEvent[];
|
|
81
|
+
/** Offset of the next page, or `null` at the end. */
|
|
82
|
+
nextCursor: number | null;
|
|
83
|
+
/** Present only when the caller asked for it — it costs two extra scans. */
|
|
84
|
+
facets?: AuditFacets;
|
|
85
|
+
};
|
|
34
86
|
export interface AuditService {
|
|
35
87
|
audit(event: AuditEvent): Promise<void>;
|
|
36
88
|
write?(batch: AuditEventBatch): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* The read side. Optional because a sink can legitimately be write-only — a
|
|
91
|
+
* queue producer that hands events to another system has nothing to read
|
|
92
|
+
* back. A reader that finds this absent should say the trail is not readable
|
|
93
|
+
* here rather than that it is empty; the two are very different answers.
|
|
94
|
+
*/
|
|
95
|
+
query?(query: AuditQuery): Promise<AuditQueryResult>;
|
|
96
|
+
/** Distinct users and types across the whole trail. Paired with {@link query}. */
|
|
97
|
+
facets?(): Promise<AuditFacets>;
|
|
37
98
|
}
|
|
38
99
|
export declare class NoopAuditService implements AuditService {
|
|
39
100
|
audit(_event: AuditEvent): Promise<void>;
|
|
40
101
|
write(_batch: AuditEventBatch): Promise<void>;
|
|
41
102
|
}
|
|
42
103
|
export type AuditLogWriteInput = Omit<AuditEvent, 'occurredAt'>;
|
|
104
|
+
/**
|
|
105
|
+
* The audit an invocation writes to. `write` is `Safe<>`-guarded like the
|
|
106
|
+
* logger: an audit event carries `input` and `metadata` as `unknown`, so
|
|
107
|
+
* nominality alone cannot stop a `SecretValue` landing in one. A secret
|
|
108
|
+
* anywhere in the event, however deeply nested, collapses to `never`.
|
|
109
|
+
*
|
|
110
|
+
* An unrevealed `SecretValue` would serialize as `[secret]` anyway; the guard
|
|
111
|
+
* is what makes that an explicit choice rather than a near miss.
|
|
112
|
+
*/
|
|
43
113
|
export interface AuditLog {
|
|
44
114
|
readonly config: ResolvedAuditConfig | undefined;
|
|
45
|
-
write(event:
|
|
115
|
+
write<E extends AuditLogWriteInput>(event: Safe<E>): Promise<void>;
|
|
46
116
|
flush(): Promise<void>;
|
|
47
117
|
close(): Promise<void>;
|
|
48
118
|
}
|
|
49
119
|
export declare const resolveAuditConfig: (config?: AuditConfig) => ResolvedAuditConfig | undefined;
|
|
50
120
|
export declare const createInvocationAudit: (service: AuditService, wire: PikkuWire<any, any, any, CoreUserSession>, logger?: Logger) => AuditLog;
|
|
51
|
-
export declare const
|
|
121
|
+
export declare const resolveAuditUserIdentityFromWire: (wire: PikkuWire<any, any, any, CoreUserSession>) => AuditUserIdentity | undefined;
|
|
@@ -75,7 +75,7 @@ class InvocationAuditLog {
|
|
|
75
75
|
wireType: this.wire.wireType,
|
|
76
76
|
wireId: this.wire.wireId,
|
|
77
77
|
traceId: this.wire.traceId,
|
|
78
|
-
|
|
78
|
+
userIdentity: event.userIdentity ?? resolveAuditUserIdentityFromWire(this.wire),
|
|
79
79
|
...event,
|
|
80
80
|
occurredAt: new Date().toISOString(),
|
|
81
81
|
};
|
|
@@ -98,15 +98,17 @@ export const createInvocationAudit = (service, wire, logger) => {
|
|
|
98
98
|
}
|
|
99
99
|
return new InvocationAuditLog(wire.audit, service, wire, logger);
|
|
100
100
|
};
|
|
101
|
-
export const
|
|
101
|
+
export const resolveAuditUserIdentityFromWire = (wire) => {
|
|
102
102
|
const session = wire.session;
|
|
103
|
-
const
|
|
103
|
+
const userIdentity = {
|
|
104
104
|
userId: session?.userId,
|
|
105
105
|
orgId: session?.orgId,
|
|
106
106
|
pikkuUserId: wire.pikkuUserId,
|
|
107
107
|
};
|
|
108
|
-
if (!
|
|
108
|
+
if (!userIdentity.userId &&
|
|
109
|
+
!userIdentity.orgId &&
|
|
110
|
+
!userIdentity.pikkuUserId) {
|
|
109
111
|
return undefined;
|
|
110
112
|
}
|
|
111
|
-
return
|
|
113
|
+
return userIdentity;
|
|
112
114
|
};
|
|
@@ -9,6 +9,11 @@ export declare class PikkuCredentialWireService {
|
|
|
9
9
|
private loadPromise;
|
|
10
10
|
constructor(credentialService?: CredentialService | undefined, wire?: PikkuRawWire | undefined, aliases?: Record<string, string> | undefined);
|
|
11
11
|
private resolveName;
|
|
12
|
+
/**
|
|
13
|
+
* A credential is one of the few places vault material is meant to end up, so
|
|
14
|
+
* a `SecretValue` is unwrapped here rather than rejected — `get` promises the
|
|
15
|
+
* raw material, and storing the wrapper would make that a lie.
|
|
16
|
+
*/
|
|
12
17
|
set(name: string, value: unknown): void;
|
|
13
18
|
get<T = unknown>(name: string): T | null | Promise<T | null>;
|
|
14
19
|
getAll(): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isSecretValue } from '../secret-value.js';
|
|
1
2
|
import { defaultPikkuUserIdResolver } from './pikku-user-id.js';
|
|
2
3
|
export class PikkuCredentialWireService {
|
|
3
4
|
credentialService;
|
|
@@ -14,8 +15,15 @@ export class PikkuCredentialWireService {
|
|
|
14
15
|
resolveName(name) {
|
|
15
16
|
return this.aliases?.[name] ?? name;
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* A credential is one of the few places vault material is meant to end up, so
|
|
20
|
+
* a `SecretValue` is unwrapped here rather than rejected — `get` promises the
|
|
21
|
+
* raw material, and storing the wrapper would make that a lie.
|
|
22
|
+
*/
|
|
17
23
|
set(name, value) {
|
|
18
|
-
this.credentials[this.resolveName(name)] = value
|
|
24
|
+
this.credentials[this.resolveName(name)] = isSecretValue(value)
|
|
25
|
+
? value.reveal()
|
|
26
|
+
: value;
|
|
19
27
|
}
|
|
20
28
|
get(name) {
|
|
21
29
|
const key = this.resolveName(name);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Safe } from '../secret-value.js';
|
|
1
2
|
export interface EmailTemplateReference {
|
|
2
3
|
name: string;
|
|
3
4
|
locale?: string;
|
|
@@ -32,5 +33,5 @@ export interface SendEmailResult {
|
|
|
32
33
|
messageId?: string;
|
|
33
34
|
}
|
|
34
35
|
export interface EmailService {
|
|
35
|
-
send(input:
|
|
36
|
+
send<T extends SendEmailInput>(input: Safe<T>): Promise<SendEmailResult>;
|
|
36
37
|
}
|
|
@@ -29,6 +29,7 @@ export declare class InMemoryWorkflowService extends PikkuWorkflowService implem
|
|
|
29
29
|
protected setStepErrorImpl(stepId: string, error: Error): Promise<void>;
|
|
30
30
|
protected setStepChildRunIdImpl(stepId: string, childRunId: string): Promise<void>;
|
|
31
31
|
protected createRetryAttemptImpl(failedStepId: string, status: 'pending' | 'running'): Promise<StepState>;
|
|
32
|
+
protected findStalledRunIds(before: Date, limit: number): Promise<string[]>;
|
|
32
33
|
listRuns(options?: {
|
|
33
34
|
workflowName?: string;
|
|
34
35
|
status?: string;
|
|
@@ -199,6 +199,26 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
|
|
|
199
199
|
this.stepHistory.set(runId, history);
|
|
200
200
|
return newStep;
|
|
201
201
|
}
|
|
202
|
+
async findStalledRunIds(before, limit) {
|
|
203
|
+
const stalled = [];
|
|
204
|
+
for (const [runId, run] of this.runs) {
|
|
205
|
+
if (run.status !== 'running')
|
|
206
|
+
continue;
|
|
207
|
+
const steps = this.stepHistory.get(runId) ?? [];
|
|
208
|
+
if (steps.some((step) => step.status === 'running' ||
|
|
209
|
+
step.status === 'scheduled' ||
|
|
210
|
+
step.status === 'suspended')) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const lastActivity = steps.reduce((latest, step) => (step.updatedAt > latest ? step.updatedAt : latest), run.updatedAt);
|
|
214
|
+
if (lastActivity >= before)
|
|
215
|
+
continue;
|
|
216
|
+
stalled.push(runId);
|
|
217
|
+
if (stalled.length >= limit)
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
return stalled;
|
|
221
|
+
}
|
|
202
222
|
async listRuns(options) {
|
|
203
223
|
let runs = Array.from(this.runs.values());
|
|
204
224
|
if (options?.workflowName) {
|
package/dist/services/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type { JWTService } from './jwt-service.js';
|
|
|
22
22
|
export type { EmailService, EmailTemplateReference, SendEmailInput, SendEmailResult, SendHTMLEmailInput, SendTemplateEmailInput, SendTextEmailInput, } from './email-service.js';
|
|
23
23
|
export { DEFAULT_WEBHOOK_RETRIES, DEFAULT_WEBHOOK_SIGNATURE_HEADER, PIKKU_OUTGOING_WEBHOOK_QUEUE_NAME, WebhookService, type SendWebhookInput, type SendWebhookResult, type WebhookAttemptRecord, type WebhookAttemptResult, type WebhookDeliveryRecord, type WebhookDeliveryWithAttempts, type WebhookJobData, type WebhookServiceConfig, } from './webhook-service.js';
|
|
24
24
|
export type { Logger } from './logger.js';
|
|
25
|
-
export type { SecretService } from './secret-service.js';
|
|
25
|
+
export type { SecretService, SecretValues } from './secret-service.js';
|
|
26
26
|
export type { VariablesService } from './variables-service.js';
|
|
27
27
|
export type { SchemaService } from './schema-service.js';
|
|
28
28
|
export type { SessionService } from './user-session-service.js';
|
|
@@ -44,8 +44,8 @@ export type { SessionStore } from './session-store.js';
|
|
|
44
44
|
export type { ScopeService, Role } from './scope-service.js';
|
|
45
45
|
export { assertRoleIsMutable, assertRoleNameAvailable, roleLockReason, } from './system-role-guard.js';
|
|
46
46
|
export type { IsSystemRole } from './system-role-guard.js';
|
|
47
|
-
export { NoopAuditService, createInvocationAudit,
|
|
48
|
-
export type {
|
|
47
|
+
export { NoopAuditService, createInvocationAudit, resolveAuditConfig, resolveAuditUserIdentityFromWire, } from './audit-service.js';
|
|
48
|
+
export type { AuditConfig, AuditDurability, AuditEvent, AuditEventBatch, AuditLog, AuditLogWriteInput, AuditOutcome, AuditService, AuditSource, AuditUserIdentity, ResolvedAuditConfig, } from './audit-service.js';
|
|
49
49
|
export { InMemorySessionStore } from './in-memory-session-store.js';
|
|
50
50
|
export type { MCPMeta, RPCMetaRecord, ServiceMeta, ServicesMetaRecord, MiddlewareDefinitionMeta, MiddlewareInstanceMeta, GroupMeta, MiddlewareGroupsMeta, PermissionDefinitionMeta, PermissionsGroupsMeta, FunctionsMeta, FunctionMeta, MiddlewareMeta, PermissionMeta, AgentsMeta, AgentMeta, EmailsMeta, EmailTemplateMeta, EmailTemplateLocaleMeta, EmailTemplateAssets, } from './meta-service.js';
|
|
51
51
|
export type { CoverageService, CoverageSnapshot, LineHits, ScriptCoverage, FunctionCoverage, CoverageRange, CoverageStatus, FunctionCoverageEntry, FunctionCoverageReport, CoverageFunctionMeta, } from './v8-coverage-service.js';
|
package/dist/services/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export { LocalGatewayService } from './local-gateway-service.js';
|
|
|
18
18
|
export { DEFAULT_WEBHOOK_RETRIES, DEFAULT_WEBHOOK_SIGNATURE_HEADER, PIKKU_OUTGOING_WEBHOOK_QUEUE_NAME, WebhookService, } from './webhook-service.js';
|
|
19
19
|
export { TypedCredentialService } from './typed-credential-service.js';
|
|
20
20
|
export { assertRoleIsMutable, assertRoleNameAvailable, roleLockReason, } from './system-role-guard.js';
|
|
21
|
-
export { NoopAuditService, createInvocationAudit,
|
|
21
|
+
export { NoopAuditService, createInvocationAudit, resolveAuditConfig, resolveAuditUserIdentityFromWire, } from './audit-service.js';
|
|
22
22
|
export { InMemorySessionStore } from './in-memory-session-store.js';
|
|
23
23
|
export { StubTracker, createStubProxy, getStubTracker, isTestRun, stub, spy, } from './stub-tracker.js';
|
|
24
24
|
export { SecretHostNotAllowedError, assertSecretAllowedForHost, } from './secret-host-binding.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { JWTService, Logger } from '@pikku/core/services';
|
|
2
|
+
import { type LocalContentConfig } from './local-content.js';
|
|
3
|
+
/**
|
|
4
|
+
* The server half of {@link LocalContent}.
|
|
5
|
+
*
|
|
6
|
+
* `LocalContent` hands out `PUT <uploadUrlPrefix>/<key>` upload URLs and signed
|
|
7
|
+
* `GET <assetUrlPrefix>/<key>` read URLs, but it cannot answer either: it is a
|
|
8
|
+
* `ContentService`, not a transport. Something in the serving path has to, and
|
|
9
|
+
* until now only `@pikku/node-http-server` did — so the very same project served
|
|
10
|
+
* under Bun handed the browser upload URLs that 404ed, with nothing naming the
|
|
11
|
+
* cause.
|
|
12
|
+
*
|
|
13
|
+
* Expressed in Web `Request`/`Response` so every runtime can share one
|
|
14
|
+
* implementation rather than each re-deriving the signature check. Returns
|
|
15
|
+
* `null` for anything that is not a content request, which is the caller's
|
|
16
|
+
* signal to carry on with its normal routing.
|
|
17
|
+
*/
|
|
18
|
+
export type LocalContentRequestHandler = (request: Request) => Promise<Response | null>;
|
|
19
|
+
export type LocalContentRequestHandlerOptions = {
|
|
20
|
+
content: LocalContentConfig;
|
|
21
|
+
logger: Logger;
|
|
22
|
+
/**
|
|
23
|
+
* Resolved per request rather than passed by value: a runtime may only be
|
|
24
|
+
* able to reach the signing service through `singletonServices`, which is not
|
|
25
|
+
* populated until after the server is constructed.
|
|
26
|
+
*/
|
|
27
|
+
getJWT: () => JWTService | undefined;
|
|
28
|
+
};
|
|
29
|
+
export declare const createLocalContentRequestHandler: ({ content, logger, getJWT, }: LocalContentRequestHandlerOptions) => LocalContentRequestHandler;
|