@pikku/core 0.12.4 → 0.12.5
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 +11 -1
- package/dist/dev/hot-reload.d.ts +10 -0
- package/dist/dev/hot-reload.js +158 -0
- package/dist/middleware-runner.d.ts +1 -0
- package/dist/middleware-runner.js +5 -0
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.js +5 -0
- package/dist/services/in-memory-workflow-service.js +7 -11
- package/dist/wirings/ai-agent/ai-agent-prepare.js +16 -1
- package/dist/wirings/channel/channel-middleware-runner.d.ts +1 -0
- package/dist/wirings/channel/channel-middleware-runner.js +5 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +7 -8
- package/package.json +3 -2
- package/src/crypto-utils.test.ts +214 -0
- package/src/crypto-utils.ts +213 -0
- package/src/dev/hot-reload.test.ts +484 -0
- package/src/dev/hot-reload.ts +212 -0
- package/src/errors/error-handler.ts +62 -0
- package/src/errors/error.test.ts +195 -0
- package/src/errors/errors.ts +374 -0
- package/src/errors/index.ts +2 -0
- package/src/factory-functions.test.ts +109 -0
- package/src/function/function-runner.test.ts +536 -0
- package/src/function/function-runner.ts +365 -0
- package/src/function/functions.types.ts +304 -0
- package/src/function/index.ts +5 -0
- package/src/handle-error.test.ts +424 -0
- package/src/handle-error.ts +69 -0
- package/src/index.ts +118 -0
- package/src/internal.ts +6 -0
- package/src/middleware/auth-apikey.test.ts +363 -0
- package/src/middleware/auth-apikey.ts +47 -0
- package/src/middleware/auth-bearer.test.ts +450 -0
- package/src/middleware/auth-bearer.ts +72 -0
- package/src/middleware/auth-cookie.test.ts +528 -0
- package/src/middleware/auth-cookie.ts +80 -0
- package/src/middleware/cors.test.ts +424 -0
- package/src/middleware/cors.ts +104 -0
- package/src/middleware/index.ts +5 -0
- package/src/middleware/remote-auth.test.ts +488 -0
- package/src/middleware/remote-auth.ts +68 -0
- package/src/middleware/timeout.ts +15 -0
- package/src/middleware-runner.test.ts +418 -0
- package/src/middleware-runner.ts +240 -0
- package/src/permissions.test.ts +434 -0
- package/src/permissions.ts +327 -0
- package/src/pikku-request.ts +23 -0
- package/src/pikku-response.ts +5 -0
- package/src/pikku-state.test.ts +224 -0
- package/src/pikku-state.ts +216 -0
- package/src/run-tests-script.test.ts +49 -0
- package/src/schema.test.ts +249 -0
- package/src/schema.ts +151 -0
- package/src/services/ai-agent-runner-service.ts +41 -0
- package/src/services/ai-run-state-service.ts +20 -0
- package/src/services/ai-storage-service.ts +39 -0
- package/src/services/content-service.ts +76 -0
- package/src/services/deployment-service.ts +22 -0
- package/src/services/gateway-service.ts +20 -0
- package/src/services/gopass-secrets.ts +98 -0
- package/src/services/in-memory-ai-run-state-service.ts +73 -0
- package/src/services/in-memory-trigger-service.ts +64 -0
- package/src/services/in-memory-workflow-service.test.ts +351 -0
- package/src/services/in-memory-workflow-service.ts +502 -0
- package/src/services/index.ts +56 -0
- package/src/services/jwt-service.ts +30 -0
- package/src/services/local-content.ts +120 -0
- package/src/services/local-gateway-service.ts +62 -0
- package/src/services/local-secrets.test.ts +80 -0
- package/src/services/local-secrets.ts +94 -0
- package/src/services/local-variables.test.ts +61 -0
- package/src/services/local-variables.ts +39 -0
- package/src/services/logger-console.test.ts +118 -0
- package/src/services/logger-console.ts +98 -0
- package/src/services/logger.ts +57 -0
- package/src/services/scheduler-service.ts +86 -0
- package/src/services/schema-service.ts +33 -0
- package/src/services/scoped-secret-service.test.ts +74 -0
- package/src/services/scoped-secret-service.ts +41 -0
- package/src/services/secret-service.ts +38 -0
- package/src/services/trigger-service.ts +17 -0
- package/src/services/typed-secret-service.test.ts +93 -0
- package/src/services/typed-secret-service.ts +70 -0
- package/src/services/typed-variables-service.test.ts +73 -0
- package/src/services/typed-variables-service.ts +81 -0
- package/src/services/user-session-service.test.ts +113 -0
- package/src/services/user-session-service.ts +86 -0
- package/src/services/variables-service.ts +11 -0
- package/src/services/workflow-service.ts +95 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/service-tests.ts +873 -0
- package/src/time-utils.test.ts +56 -0
- package/src/time-utils.ts +107 -0
- package/src/types/core.types.ts +478 -0
- package/src/types/state.types.ts +188 -0
- package/src/utils/hash.test.ts +68 -0
- package/src/utils/hash.ts +26 -0
- package/src/utils.test.ts +350 -0
- package/src/utils.ts +129 -0
- package/src/version.test.ts +80 -0
- package/src/version.ts +25 -0
- package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
- package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
- package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +310 -0
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
- package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +659 -0
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
- package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +319 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +773 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +859 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +1153 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +382 -0
- package/src/wirings/ai-agent/index.ts +37 -0
- package/src/wirings/channel/channel-common.ts +90 -0
- package/src/wirings/channel/channel-handler.ts +250 -0
- package/src/wirings/channel/channel-middleware-runner.ts +126 -0
- package/src/wirings/channel/channel-runner.ts +166 -0
- package/src/wirings/channel/channel-store.ts +29 -0
- package/src/wirings/channel/channel.types.ts +172 -0
- package/src/wirings/channel/define-channel-routes.ts +25 -0
- package/src/wirings/channel/eventhub-service.ts +34 -0
- package/src/wirings/channel/eventhub-store.ts +13 -0
- package/src/wirings/channel/index.ts +24 -0
- package/src/wirings/channel/local/index.ts +3 -0
- package/src/wirings/channel/local/local-channel-handler.ts +81 -0
- package/src/wirings/channel/local/local-channel-runner.test.ts +215 -0
- package/src/wirings/channel/local/local-channel-runner.ts +210 -0
- package/src/wirings/channel/local/local-eventhub-service.test.ts +95 -0
- package/src/wirings/channel/local/local-eventhub-service.ts +101 -0
- package/src/wirings/channel/log-channels.ts +20 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +54 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +45 -0
- package/src/wirings/channel/serverless/index.ts +5 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +289 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +136 -0
- package/src/wirings/cli/channel/index.ts +1 -0
- package/src/wirings/cli/cli-runner.test.ts +403 -0
- package/src/wirings/cli/cli-runner.ts +529 -0
- package/src/wirings/cli/cli.types.ts +358 -0
- package/src/wirings/cli/command-parser.test.ts +445 -0
- package/src/wirings/cli/command-parser.ts +504 -0
- package/src/wirings/cli/define-cli-commands.ts +24 -0
- package/src/wirings/cli/index.ts +19 -0
- package/src/wirings/gateway/gateway-runner.test.ts +474 -0
- package/src/wirings/gateway/gateway-runner.ts +411 -0
- package/src/wirings/gateway/gateway.types.ts +149 -0
- package/src/wirings/gateway/index.ts +13 -0
- package/src/wirings/http/http-routes.test.ts +322 -0
- package/src/wirings/http/http-routes.ts +197 -0
- package/src/wirings/http/http-runner.test.ts +144 -0
- package/src/wirings/http/http-runner.ts +588 -0
- package/src/wirings/http/http.types.ts +369 -0
- package/src/wirings/http/index.ts +28 -0
- package/src/wirings/http/log-http-routes.ts +22 -0
- package/src/wirings/http/pikku-fetch-http-request.test.ts +237 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +170 -0
- package/src/wirings/http/pikku-fetch-http-response.test.ts +82 -0
- package/src/wirings/http/pikku-fetch-http-response.ts +147 -0
- package/src/wirings/http/routers/http-router.ts +13 -0
- package/src/wirings/http/routers/path-to-regex.test.ts +319 -0
- package/src/wirings/http/routers/path-to-regex.ts +126 -0
- package/src/wirings/http/web-request.test.ts +236 -0
- package/src/wirings/http/web-request.ts +104 -0
- package/src/wirings/mcp/index.ts +27 -0
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +389 -0
- package/src/wirings/mcp/mcp-endpoint-registry.ts +159 -0
- package/src/wirings/mcp/mcp-runner.ts +323 -0
- package/src/wirings/mcp/mcp.types.ts +216 -0
- package/src/wirings/node/index.ts +2 -0
- package/src/wirings/node/node.types.ts +23 -0
- package/src/wirings/oauth2/index.ts +3 -0
- package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
- package/src/wirings/oauth2/oauth2-client.ts +335 -0
- package/src/wirings/oauth2/oauth2.types.ts +69 -0
- package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
- package/src/wirings/queue/index.ts +31 -0
- package/src/wirings/queue/queue-runner.test.ts +710 -0
- package/src/wirings/queue/queue-runner.ts +192 -0
- package/src/wirings/queue/queue.types.ts +189 -0
- package/src/wirings/queue/register-queue-helper.ts +60 -0
- package/src/wirings/queue/validate-worker-config.test.ts +108 -0
- package/src/wirings/queue/validate-worker-config.ts +116 -0
- package/src/wirings/rpc/index.ts +4 -0
- package/src/wirings/rpc/rpc-runner.ts +460 -0
- package/src/wirings/rpc/rpc-types.ts +56 -0
- package/src/wirings/rpc/wire-addon.ts +21 -0
- package/src/wirings/scheduler/index.ts +11 -0
- package/src/wirings/scheduler/log-schedulers.ts +20 -0
- package/src/wirings/scheduler/scheduler-runner.test.ts +660 -0
- package/src/wirings/scheduler/scheduler-runner.ts +133 -0
- package/src/wirings/scheduler/scheduler.types.ts +53 -0
- package/src/wirings/secret/index.ts +9 -0
- package/src/wirings/secret/secret.types.ts +32 -0
- package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
- package/src/wirings/secret/validate-secret-definitions.ts +82 -0
- package/src/wirings/trigger/index.ts +10 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +112 -0
- package/src/wirings/trigger/trigger-runner.test.ts +79 -0
- package/src/wirings/trigger/trigger-runner.ts +135 -0
- package/src/wirings/trigger/trigger.types.ts +178 -0
- package/src/wirings/variable/index.ts +8 -0
- package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
- package/src/wirings/variable/validate-variable-definitions.ts +69 -0
- package/src/wirings/variable/variable.types.ts +22 -0
- package/src/wirings/workflow/dsl/index.ts +31 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
- package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
- package/src/wirings/workflow/graph/graph-node.ts +222 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
- package/src/wirings/workflow/graph/graph-runner.ts +816 -0
- package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
- package/src/wirings/workflow/graph/graph-validation.ts +237 -0
- package/src/wirings/workflow/graph/index.ts +19 -0
- package/src/wirings/workflow/graph/template.test.ts +49 -0
- package/src/wirings/workflow/graph/template.ts +42 -0
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
- package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
- package/src/wirings/workflow/index.ts +82 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +1179 -0
- package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
- package/src/wirings/workflow/workflow-helpers.ts +79 -0
- package/src/wirings/workflow/workflow.types.ts +274 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/lcov.info +0 -18891
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { GatewayService } from './gateway-service.js'
|
|
2
|
+
import type { GatewayAdapter } from '../wirings/gateway/gateway.types.js'
|
|
3
|
+
import { pikkuState, getSingletonServices } from '../pikku-state.js'
|
|
4
|
+
import { createListenerMessageHandler } from '../wirings/gateway/gateway-runner.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Local GatewayService implementation.
|
|
8
|
+
*
|
|
9
|
+
* Starts all registered listener gateways unconditionally (single-process).
|
|
10
|
+
* For distributed deployments, implement `GatewayService` with leader
|
|
11
|
+
* election or similar coordination.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const gatewayService = new LocalGatewayService()
|
|
16
|
+
* await gatewayService.start()
|
|
17
|
+
*
|
|
18
|
+
* // On shutdown
|
|
19
|
+
* await gatewayService.stop()
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class LocalGatewayService implements GatewayService {
|
|
23
|
+
private activeAdapters = new Map<string, GatewayAdapter>()
|
|
24
|
+
|
|
25
|
+
async start(): Promise<void> {
|
|
26
|
+
const singletonServices = getSingletonServices()
|
|
27
|
+
const gateways = pikkuState(null, 'gateway', 'gateways')
|
|
28
|
+
|
|
29
|
+
for (const [name, config] of gateways) {
|
|
30
|
+
if (config.type !== 'listener') continue
|
|
31
|
+
if (this.activeAdapters.has(name)) continue
|
|
32
|
+
|
|
33
|
+
const handleMessage = createListenerMessageHandler(
|
|
34
|
+
name,
|
|
35
|
+
config,
|
|
36
|
+
singletonServices
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
await config.adapter.init(handleMessage)
|
|
40
|
+
this.activeAdapters.set(name, config.adapter)
|
|
41
|
+
singletonServices.logger.info(`Started listener gateway: ${name}`)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async stop(): Promise<void> {
|
|
46
|
+
for (const [name, adapter] of this.activeAdapters) {
|
|
47
|
+
try {
|
|
48
|
+
await adapter.close()
|
|
49
|
+
} catch (e: any) {
|
|
50
|
+
try {
|
|
51
|
+
getSingletonServices().logger.error(
|
|
52
|
+
`Error closing listener gateway '${name}':`,
|
|
53
|
+
e
|
|
54
|
+
)
|
|
55
|
+
} catch {
|
|
56
|
+
// logger may not be available during shutdown
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
this.activeAdapters.clear()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { LocalSecretService } from './local-secrets.js'
|
|
4
|
+
import { LocalVariablesService } from './local-variables.js'
|
|
5
|
+
|
|
6
|
+
describe('LocalSecretService', () => {
|
|
7
|
+
test('should get secret from local storage', async () => {
|
|
8
|
+
const service = new LocalSecretService()
|
|
9
|
+
await service.setSecretJSON('MY_KEY', { token: 'abc' })
|
|
10
|
+
const result = await service.getSecret('MY_KEY')
|
|
11
|
+
assert.strictEqual(result, '{"token":"abc"}')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('should get secret from environment variables', async () => {
|
|
15
|
+
const vars = new LocalVariablesService({ MY_SECRET: 'secret-value' })
|
|
16
|
+
const service = new LocalSecretService(vars)
|
|
17
|
+
const result = await service.getSecret('MY_SECRET')
|
|
18
|
+
assert.strictEqual(result, 'secret-value')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('should throw when secret not found', async () => {
|
|
22
|
+
const vars = new LocalVariablesService({})
|
|
23
|
+
const service = new LocalSecretService(vars)
|
|
24
|
+
await assert.rejects(() => service.getSecret('MISSING'), {
|
|
25
|
+
message: 'Secret Not Found: MISSING',
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('should get JSON secret from local storage', async () => {
|
|
30
|
+
const service = new LocalSecretService()
|
|
31
|
+
await service.setSecretJSON('JSON_KEY', { data: 42 })
|
|
32
|
+
const result = await service.getSecretJSON('JSON_KEY')
|
|
33
|
+
assert.deepStrictEqual(result, { data: 42 })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('should get JSON secret from environment variables', async () => {
|
|
37
|
+
const vars = new LocalVariablesService({ CONFIG: '{"port":3000}' })
|
|
38
|
+
const service = new LocalSecretService(vars)
|
|
39
|
+
const result = await service.getSecretJSON('CONFIG')
|
|
40
|
+
assert.deepStrictEqual(result, { port: 3000 })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('should throw when JSON secret not found', async () => {
|
|
44
|
+
const vars = new LocalVariablesService({})
|
|
45
|
+
const service = new LocalSecretService(vars)
|
|
46
|
+
await assert.rejects(() => service.getSecretJSON('MISSING'), {
|
|
47
|
+
message: 'Secret Not Found: MISSING',
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('should prefer local storage over env variables', async () => {
|
|
52
|
+
const vars = new LocalVariablesService({ KEY: 'env-value' })
|
|
53
|
+
const service = new LocalSecretService(vars)
|
|
54
|
+
await service.setSecretJSON('KEY', 'local-value')
|
|
55
|
+
const result = await service.getSecret('KEY')
|
|
56
|
+
assert.strictEqual(result, '"local-value"')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('should check hasSecret in local storage', async () => {
|
|
60
|
+
const vars = new LocalVariablesService({})
|
|
61
|
+
const service = new LocalSecretService(vars)
|
|
62
|
+
await service.setSecretJSON('EXISTS', 'yes')
|
|
63
|
+
assert.strictEqual(await service.hasSecret('EXISTS'), true)
|
|
64
|
+
assert.strictEqual(await service.hasSecret('NOPE'), false)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('should check hasSecret in environment', async () => {
|
|
68
|
+
const vars = new LocalVariablesService({ ENV_KEY: 'val' })
|
|
69
|
+
const service = new LocalSecretService(vars)
|
|
70
|
+
assert.strictEqual(await service.hasSecret('ENV_KEY'), true)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('should delete secret from local storage', async () => {
|
|
74
|
+
const service = new LocalSecretService()
|
|
75
|
+
await service.setSecretJSON('DEL_KEY', 'value')
|
|
76
|
+
assert.strictEqual(await service.hasSecret('DEL_KEY'), true)
|
|
77
|
+
await service.deleteSecret('DEL_KEY')
|
|
78
|
+
assert.strictEqual(await service.hasSecret('DEL_KEY'), false)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { LocalVariablesService } from './local-variables.js'
|
|
2
|
+
import type { SecretService } from './secret-service.js'
|
|
3
|
+
import type { VariablesService } from './variables-service.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Service for retrieving secrets from environment variables.
|
|
7
|
+
* Supports storing secrets locally in memory for CLI operations.
|
|
8
|
+
*/
|
|
9
|
+
export class LocalSecretService implements SecretService {
|
|
10
|
+
private localSecrets: Map<string, string> = new Map()
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates an instance of LocalSecretService.
|
|
14
|
+
*/
|
|
15
|
+
constructor(
|
|
16
|
+
private variables: VariablesService = new LocalVariablesService()
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves a secret by key.
|
|
21
|
+
* Checks local storage first, then falls back to environment variables.
|
|
22
|
+
* @param key - The key of the secret to retrieve.
|
|
23
|
+
* @returns A promise that resolves to the secret value.
|
|
24
|
+
* @throws {Error} If the secret is not found.
|
|
25
|
+
*/
|
|
26
|
+
public async getSecretJSON<R>(key: string): Promise<R> {
|
|
27
|
+
// Check local storage first
|
|
28
|
+
const localValue = this.localSecrets.get(key)
|
|
29
|
+
if (localValue) {
|
|
30
|
+
return JSON.parse(localValue)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fall back to environment variables
|
|
34
|
+
const value = await this.variables.get(key)
|
|
35
|
+
if (value) {
|
|
36
|
+
return JSON.parse(value)
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`Secret Not Found: ${key}`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Retrieves a secret by key.
|
|
43
|
+
* Checks local storage first, then falls back to environment variables.
|
|
44
|
+
* @param key - The key of the secret to retrieve.
|
|
45
|
+
* @returns A promise that resolves to the secret value.
|
|
46
|
+
* @throws {Error} If the secret is not found.
|
|
47
|
+
*/
|
|
48
|
+
public async getSecret(key: string): Promise<string> {
|
|
49
|
+
// Check local storage first
|
|
50
|
+
const localValue = this.localSecrets.get(key)
|
|
51
|
+
if (localValue) {
|
|
52
|
+
return localValue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Fall back to environment variables
|
|
56
|
+
const value = await this.variables.get(key)
|
|
57
|
+
if (value) {
|
|
58
|
+
return value
|
|
59
|
+
}
|
|
60
|
+
throw new Error(`Secret Not Found: ${key}`)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Stores a JSON value as a secret in local storage.
|
|
65
|
+
* @param key - The key to store the secret under.
|
|
66
|
+
* @param value - The JSON value to store.
|
|
67
|
+
* @returns A promise that resolves when the secret is stored.
|
|
68
|
+
*/
|
|
69
|
+
public async setSecretJSON(key: string, value: unknown): Promise<void> {
|
|
70
|
+
this.localSecrets.set(key, JSON.stringify(value))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Checks if a secret exists without throwing.
|
|
75
|
+
* @param key - The key of the secret to check.
|
|
76
|
+
* @returns A promise that resolves to true if the secret exists.
|
|
77
|
+
*/
|
|
78
|
+
public async hasSecret(key: string): Promise<boolean> {
|
|
79
|
+
if (this.localSecrets.has(key)) {
|
|
80
|
+
return true
|
|
81
|
+
}
|
|
82
|
+
const value = await this.variables.get(key)
|
|
83
|
+
return value !== undefined && value !== null && value !== ''
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Deletes a secret from local storage.
|
|
88
|
+
* @param key - The key of the secret to delete.
|
|
89
|
+
* @returns A promise that resolves when the secret is deleted.
|
|
90
|
+
*/
|
|
91
|
+
public async deleteSecret(key: string): Promise<void> {
|
|
92
|
+
this.localSecrets.delete(key)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { LocalVariablesService } from './local-variables.js'
|
|
4
|
+
|
|
5
|
+
describe('LocalVariablesService', () => {
|
|
6
|
+
test('should get a variable', () => {
|
|
7
|
+
const service = new LocalVariablesService({ MY_VAR: 'hello' })
|
|
8
|
+
assert.strictEqual(service.get('MY_VAR'), 'hello')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test('should return undefined for missing variable', () => {
|
|
12
|
+
const service = new LocalVariablesService({})
|
|
13
|
+
assert.strictEqual(service.get('MISSING'), undefined)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('should set a variable', () => {
|
|
17
|
+
const service = new LocalVariablesService({})
|
|
18
|
+
service.set('KEY', 'value')
|
|
19
|
+
assert.strictEqual(service.get('KEY'), 'value')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('should delete a variable', () => {
|
|
23
|
+
const service = new LocalVariablesService({ KEY: 'val' })
|
|
24
|
+
service.delete('KEY')
|
|
25
|
+
assert.strictEqual(service.get('KEY'), undefined)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('should check if variable exists', () => {
|
|
29
|
+
const service = new LocalVariablesService({ KEY: 'val' })
|
|
30
|
+
assert.strictEqual(service.has('KEY'), true)
|
|
31
|
+
assert.strictEqual(service.has('MISSING'), false)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('should get all variables', () => {
|
|
35
|
+
const vars = { A: '1', B: '2' }
|
|
36
|
+
const service = new LocalVariablesService(vars)
|
|
37
|
+
assert.deepStrictEqual(service.getAll(), vars)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('should get JSON variable', () => {
|
|
41
|
+
const service = new LocalVariablesService({ DATA: '{"key":"val"}' })
|
|
42
|
+
const result = service.getJSON('DATA')
|
|
43
|
+
assert.deepStrictEqual(result, { key: 'val' })
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('should return undefined for missing JSON variable', () => {
|
|
47
|
+
const service = new LocalVariablesService({})
|
|
48
|
+
assert.strictEqual(service.getJSON('MISSING'), undefined)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('should set JSON variable', () => {
|
|
52
|
+
const service = new LocalVariablesService({})
|
|
53
|
+
service.setJSON('DATA', { key: 'val' })
|
|
54
|
+
assert.strictEqual(service.get('DATA'), '{"key":"val"}')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('should handle has with undefined value', () => {
|
|
58
|
+
const service = new LocalVariablesService({ KEY: undefined })
|
|
59
|
+
assert.strictEqual(service.has('KEY'), false)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { VariablesService } from './variables-service.js'
|
|
2
|
+
|
|
3
|
+
export class LocalVariablesService implements VariablesService {
|
|
4
|
+
constructor(
|
|
5
|
+
private variables: Record<string, string | undefined> = process.env
|
|
6
|
+
) {}
|
|
7
|
+
|
|
8
|
+
public getAll():
|
|
9
|
+
| Promise<Record<string, string | undefined>>
|
|
10
|
+
| Record<string, string | undefined> {
|
|
11
|
+
return this.variables || {}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public get(name: string): Promise<string | undefined> | string | undefined {
|
|
15
|
+
return this.variables[name]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public getJSON<T = unknown>(name: string): T | undefined {
|
|
19
|
+
const value = this.variables[name]
|
|
20
|
+
if (value === undefined) return undefined
|
|
21
|
+
return JSON.parse(value)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public set(name: string, value: string): void {
|
|
25
|
+
this.variables[name] = value
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public setJSON(name: string, value: unknown): void {
|
|
29
|
+
this.variables[name] = JSON.stringify(value)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public has(name: string): boolean {
|
|
33
|
+
return name in this.variables && this.variables[name] !== undefined
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public delete(name: string): void {
|
|
37
|
+
delete this.variables[name]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, test, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { ConsoleLogger } from './logger-console.js'
|
|
4
|
+
import { LogLevel } from './logger.js'
|
|
5
|
+
|
|
6
|
+
describe('ConsoleLogger', () => {
|
|
7
|
+
let logger: ConsoleLogger
|
|
8
|
+
let captured: { method: string; args: any[] }[]
|
|
9
|
+
let origConsole: Record<string, any>
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
logger = new ConsoleLogger()
|
|
13
|
+
captured = []
|
|
14
|
+
origConsole = {
|
|
15
|
+
trace: console.trace,
|
|
16
|
+
debug: console.debug,
|
|
17
|
+
info: console.info,
|
|
18
|
+
warn: console.warn,
|
|
19
|
+
error: console.error,
|
|
20
|
+
log: console.log,
|
|
21
|
+
}
|
|
22
|
+
console.trace = (...args: any[]) => captured.push({ method: 'trace', args })
|
|
23
|
+
console.debug = (...args: any[]) => captured.push({ method: 'debug', args })
|
|
24
|
+
console.info = (...args: any[]) => captured.push({ method: 'info', args })
|
|
25
|
+
console.warn = (...args: any[]) => captured.push({ method: 'warn', args })
|
|
26
|
+
console.error = (...args: any[]) => captured.push({ method: 'error', args })
|
|
27
|
+
console.log = (...args: any[]) => captured.push({ method: 'log', args })
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// Restore console after each test
|
|
31
|
+
test('should log info at default level', () => {
|
|
32
|
+
logger.info('test message')
|
|
33
|
+
assert.strictEqual(captured.length, 1)
|
|
34
|
+
assert.strictEqual(captured[0].method, 'info')
|
|
35
|
+
assert.ok(captured[0].args.includes('test message'))
|
|
36
|
+
Object.assign(console, origConsole)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('should not log debug at default info level', () => {
|
|
40
|
+
logger.debug('debug message')
|
|
41
|
+
assert.strictEqual(captured.length, 0)
|
|
42
|
+
Object.assign(console, origConsole)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('should log debug when level is set to debug', () => {
|
|
46
|
+
logger.setLevel(LogLevel.debug)
|
|
47
|
+
logger.debug('debug message')
|
|
48
|
+
assert.strictEqual(captured.length, 1)
|
|
49
|
+
assert.strictEqual(captured[0].method, 'debug')
|
|
50
|
+
Object.assign(console, origConsole)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('should log warn at info level', () => {
|
|
54
|
+
logger.warn('warning')
|
|
55
|
+
assert.strictEqual(captured.length, 1)
|
|
56
|
+
assert.strictEqual(captured[0].method, 'warn')
|
|
57
|
+
Object.assign(console, origConsole)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('should log error at info level', () => {
|
|
61
|
+
logger.error('error message')
|
|
62
|
+
assert.strictEqual(captured.length, 1)
|
|
63
|
+
assert.strictEqual(captured[0].method, 'error')
|
|
64
|
+
Object.assign(console, origConsole)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('should log Error instance with stack trace', () => {
|
|
68
|
+
const err = new Error('test error')
|
|
69
|
+
logger.error(err)
|
|
70
|
+
assert.strictEqual(captured.length, 2) // error message + stack
|
|
71
|
+
assert.ok(captured[0].args.includes('test error'))
|
|
72
|
+
assert.strictEqual(captured[1].args[0], 'STACK:')
|
|
73
|
+
Object.assign(console, origConsole)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('should not log info when level is error', () => {
|
|
77
|
+
logger.setLevel(LogLevel.error)
|
|
78
|
+
logger.info('info')
|
|
79
|
+
logger.warn('warn')
|
|
80
|
+
logger.debug('debug')
|
|
81
|
+
assert.strictEqual(captured.length, 0)
|
|
82
|
+
Object.assign(console, origConsole)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('should log error when level is error', () => {
|
|
86
|
+
logger.setLevel(LogLevel.error)
|
|
87
|
+
logger.error('critical')
|
|
88
|
+
assert.strictEqual(captured.length, 1)
|
|
89
|
+
Object.assign(console, origConsole)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('should support trace level when enabled', () => {
|
|
93
|
+
logger.setLevel(LogLevel.trace)
|
|
94
|
+
logger.trace!('trace message')
|
|
95
|
+
assert.strictEqual(captured.length, 1)
|
|
96
|
+
assert.strictEqual(captured[0].method, 'trace')
|
|
97
|
+
Object.assign(console, origConsole)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('should log with additional metadata', () => {
|
|
101
|
+
logger.info('message', { extra: 'data' })
|
|
102
|
+
assert.ok(captured[0].args.includes('message'))
|
|
103
|
+
Object.assign(console, origConsole)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('should handle object messages in info', () => {
|
|
107
|
+
logger.info({ key: 'value' })
|
|
108
|
+
assert.strictEqual(captured.length, 1)
|
|
109
|
+
Object.assign(console, origConsole)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('log method should respect level', () => {
|
|
113
|
+
logger.setLevel(LogLevel.error)
|
|
114
|
+
logger.log('info', 'should not show')
|
|
115
|
+
assert.strictEqual(captured.length, 0)
|
|
116
|
+
Object.assign(console, origConsole)
|
|
117
|
+
})
|
|
118
|
+
})
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Logger } from './logger.js'
|
|
2
|
+
import { LogLevel } from './logger.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A logger implementation that logs messages to the console.
|
|
6
|
+
*/
|
|
7
|
+
export class ConsoleLogger implements Logger {
|
|
8
|
+
/**
|
|
9
|
+
* The current logging level.
|
|
10
|
+
*/
|
|
11
|
+
private level: LogLevel = LogLevel.info
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Sets the logging level.
|
|
15
|
+
* @param level - The logging level to set.
|
|
16
|
+
*/
|
|
17
|
+
setLevel(level: LogLevel): void {
|
|
18
|
+
this.level = level
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Logs a trace message.
|
|
23
|
+
* @param message - The message to log.
|
|
24
|
+
* @param meta - Additional metadata to log.
|
|
25
|
+
*/
|
|
26
|
+
trace?(message: string, ...meta: any[]): void {
|
|
27
|
+
if (this.level <= LogLevel.trace) {
|
|
28
|
+
console.trace('TRACE:', message, ...meta)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Logs a debug message.
|
|
34
|
+
* @param message - The message to log.
|
|
35
|
+
* @param meta - Additional metadata to log.
|
|
36
|
+
*/
|
|
37
|
+
debug(message: string, ...meta: any[]): void {
|
|
38
|
+
if (this.level <= LogLevel.debug) {
|
|
39
|
+
console.debug('DEBUG:', message, ...meta)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Logs an informational message.
|
|
45
|
+
* @param messageOrObj - The message or object to log.
|
|
46
|
+
* @param meta - Additional metadata to log.
|
|
47
|
+
*/
|
|
48
|
+
info(messageOrObj: string | Record<string, any>, ...meta: any[]): void {
|
|
49
|
+
if (this.level <= LogLevel.info) {
|
|
50
|
+
console.info('INFO:', messageOrObj, ...meta)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Logs a warning message.
|
|
56
|
+
* @param messageOrObj - The message or object to log.
|
|
57
|
+
* @param meta - Additional metadata to log.
|
|
58
|
+
*/
|
|
59
|
+
warn(messageOrObj: string | Record<string, any>, ...meta: any[]): void {
|
|
60
|
+
if (this.level <= LogLevel.warn) {
|
|
61
|
+
console.warn('WARN:', messageOrObj, ...meta)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Logs an error message.
|
|
67
|
+
* @param messageOrObj - The message, object, or error to log.
|
|
68
|
+
* @param meta - Additional metadata to log.
|
|
69
|
+
*/
|
|
70
|
+
error(
|
|
71
|
+
messageOrObj: string | Record<string, any> | Error,
|
|
72
|
+
...meta: any[]
|
|
73
|
+
): void {
|
|
74
|
+
if (this.level <= LogLevel.error) {
|
|
75
|
+
console.error(
|
|
76
|
+
'ERROR:',
|
|
77
|
+
messageOrObj instanceof Error ? messageOrObj.message : messageOrObj,
|
|
78
|
+
...meta
|
|
79
|
+
)
|
|
80
|
+
if (messageOrObj instanceof Error) {
|
|
81
|
+
console.error('STACK:', messageOrObj.stack)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Logs a message at a specified level.
|
|
88
|
+
* @param level - The logging level.
|
|
89
|
+
* @param message - The message to log.
|
|
90
|
+
* @param meta - Additional metadata to log.
|
|
91
|
+
*/
|
|
92
|
+
log(level: string, message: string, ...meta: any[]): void {
|
|
93
|
+
const logLevel = LogLevel[level as keyof typeof LogLevel]
|
|
94
|
+
if (this.level <= logLevel) {
|
|
95
|
+
console.log(`${level.toUpperCase()}:`, message, ...meta)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export enum LogLevel {
|
|
2
|
+
'trace', // Add this level
|
|
3
|
+
'debug',
|
|
4
|
+
'info',
|
|
5
|
+
'warn',
|
|
6
|
+
'error',
|
|
7
|
+
'critical',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Interface for logging messages at various levels.
|
|
12
|
+
*/
|
|
13
|
+
export interface Logger {
|
|
14
|
+
/**
|
|
15
|
+
* Logs an informational message.
|
|
16
|
+
* @param messageOrObj - The message or object to log.
|
|
17
|
+
* @param meta - Additional metadata to log.
|
|
18
|
+
*/
|
|
19
|
+
info(messageOrObj: string | Record<string, any>, ...meta: any[]): void
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Logs a warning message.
|
|
23
|
+
* @param messageOrObj - The message or object to log.
|
|
24
|
+
* @param meta - Additional metadata to log.
|
|
25
|
+
*/
|
|
26
|
+
warn(messageOrObj: string | Record<string, any>, ...meta: any[]): void
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Logs an error message.
|
|
30
|
+
* @param messageOrObj - The message, object, or error to log.
|
|
31
|
+
* @param meta - Additional metadata to log.
|
|
32
|
+
*/
|
|
33
|
+
error(
|
|
34
|
+
messageOrObj: string | Record<string, any> | Error,
|
|
35
|
+
...meta: any[]
|
|
36
|
+
): void
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Logs a debug message.
|
|
40
|
+
* @param message - The message to log.
|
|
41
|
+
* @param meta - Additional metadata to log.
|
|
42
|
+
*/
|
|
43
|
+
debug(message: string, ...meta: any[]): void
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Logs a trace message.
|
|
47
|
+
* @param message - The message to log.
|
|
48
|
+
* @param meta - Additional metadata to log.
|
|
49
|
+
*/
|
|
50
|
+
trace?(message: string, ...meta: any[]): void
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Sets the logging level.
|
|
54
|
+
* @param level - The logging level to set.
|
|
55
|
+
*/
|
|
56
|
+
setLevel(level: LogLevel): void
|
|
57
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { CoreUserSession } from '../types/core.types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal metadata for listing scheduled tasks
|
|
5
|
+
*/
|
|
6
|
+
export interface ScheduledTaskSummary {
|
|
7
|
+
/** Unique task ID */
|
|
8
|
+
taskId: string
|
|
9
|
+
/** RPC function name to invoke */
|
|
10
|
+
rpcName: string
|
|
11
|
+
/** Scheduled execution time */
|
|
12
|
+
scheduledFor: Date
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Full metadata for a scheduled task retrieved from the queue
|
|
17
|
+
*/
|
|
18
|
+
export interface ScheduledTaskInfo extends ScheduledTaskSummary {
|
|
19
|
+
/** Data to pass to the RPC function */
|
|
20
|
+
data?: any
|
|
21
|
+
/** User session */
|
|
22
|
+
session?: CoreUserSession
|
|
23
|
+
/** Task status */
|
|
24
|
+
status?: 'scheduled' | 'active' | 'completed' | 'failed'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Abstract scheduler service for persistent scheduled task management
|
|
29
|
+
* Implementations use queue backends (pg-boss, BullMQ) for distributed scheduling
|
|
30
|
+
*/
|
|
31
|
+
export abstract class SchedulerService {
|
|
32
|
+
/**
|
|
33
|
+
* Initialize the scheduler service
|
|
34
|
+
*/
|
|
35
|
+
abstract init(): Promise<void>
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Schedule a one-off delayed RPC call
|
|
39
|
+
* @param delay - Delay before execution (number in milliseconds or duration string like "5h", "30m")
|
|
40
|
+
* @param rpcName - RPC function name to invoke
|
|
41
|
+
* @param data - Data to pass to the RPC function
|
|
42
|
+
* @param session - Optional user session
|
|
43
|
+
* @returns Task ID
|
|
44
|
+
*/
|
|
45
|
+
abstract scheduleRPC(
|
|
46
|
+
delay: number | string,
|
|
47
|
+
rpcName: string,
|
|
48
|
+
data?: any,
|
|
49
|
+
session?: CoreUserSession
|
|
50
|
+
): Promise<string>
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Unschedule a task by ID
|
|
54
|
+
* @param taskId - Task ID
|
|
55
|
+
* @returns Success status
|
|
56
|
+
*/
|
|
57
|
+
abstract unschedule(taskId: string): Promise<boolean>
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get a scheduled task by ID with full details
|
|
61
|
+
* @param taskId - Task ID
|
|
62
|
+
* @returns Task info or null if not found
|
|
63
|
+
*/
|
|
64
|
+
abstract getTask(taskId: string): Promise<ScheduledTaskInfo | null>
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get all scheduled tasks with minimal info
|
|
68
|
+
* @returns Array of task summaries with taskId, rpcName, and schedule
|
|
69
|
+
*/
|
|
70
|
+
abstract getAllTasks(): Promise<ScheduledTaskSummary[]>
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Close any open connections
|
|
74
|
+
*/
|
|
75
|
+
abstract close(): Promise<void>
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Start recurring scheduled tasks
|
|
79
|
+
*/
|
|
80
|
+
async start(): Promise<void> {}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Stop recurring scheduled tasks
|
|
84
|
+
*/
|
|
85
|
+
async stop(): Promise<void> {}
|
|
86
|
+
}
|