@pikku/core 0.12.104 → 0.12.106
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 +14 -0
- package/dist/dev/module-runner.js +33 -1
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +1 -0
- package/dist/middleware/remote-jobs-secret.d.ts +14 -0
- package/dist/middleware/remote-jobs-secret.js +27 -0
- package/dist/services/credential-wire-service.d.ts +15 -0
- package/dist/services/credential-wire-service.js +30 -11
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/remote-jobs.d.ts +27 -0
- package/dist/services/remote-jobs.js +59 -0
- package/dist/wirings/scheduler/index.d.ts +1 -1
- package/dist/wirings/scheduler/index.js +1 -1
- package/dist/wirings/scheduler/scheduler-runner.d.ts +4 -0
- package/dist/wirings/scheduler/scheduler-runner.js +1 -1
- package/package.json +1 -1
- package/src/dev/module-runner.test.ts +23 -0
- package/src/dev/module-runner.ts +39 -1
- package/src/middleware/index.ts +1 -0
- package/src/middleware/remote-jobs-secret.ts +35 -0
- package/src/public-surface.json +61 -14
- package/src/services/credential-wire-service.test.ts +63 -0
- package/src/services/credential-wire-service.ts +27 -9
- package/src/services/index.ts +10 -0
- package/src/services/remote-jobs.test.ts +106 -0
- package/src/services/remote-jobs.ts +92 -0
- package/src/wirings/scheduler/index.ts +1 -0
- package/src/wirings/scheduler/scheduler-runner.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/classification/data-lock.d.ts +0 -80
- package/dist/classification/data-lock.js +0 -146
- package/dist/column-form.d.ts +0 -32
- package/dist/column-form.js +0 -42
- package/dist/data-classification.d.ts +0 -64
- package/dist/data-classification.js +0 -1
- package/dist/ecosystem/agent-scorer.d.ts +0 -12
- package/dist/ecosystem/agent-scorer.js +0 -5
- package/dist/ecosystem/agent.d.ts +0 -21
- package/dist/ecosystem/agent.js +0 -6
- package/dist/ecosystem/channel/local.d.ts +0 -3
- package/dist/ecosystem/channel/local.js +0 -3
- package/dist/ecosystem/channel/serverless.d.ts +0 -9
- package/dist/ecosystem/channel/serverless.js +0 -1
- package/dist/ecosystem/channel.d.ts +0 -15
- package/dist/ecosystem/channel.js +0 -5
- package/dist/ecosystem/cli/channel.d.ts +0 -16
- package/dist/ecosystem/cli/channel.js +0 -2
- package/dist/ecosystem/cli.d.ts +0 -9
- package/dist/ecosystem/cli.js +0 -2
- package/dist/ecosystem/credential.d.ts +0 -10
- package/dist/ecosystem/credential.js +0 -1
- package/dist/ecosystem/crypto-utils.d.ts +0 -8
- package/dist/ecosystem/crypto-utils.js +0 -1
- package/dist/ecosystem/dev.d.ts +0 -9
- package/dist/ecosystem/dev.js +0 -2
- package/dist/ecosystem/errors.d.ts +0 -8
- package/dist/ecosystem/errors.js +0 -2
- package/dist/ecosystem/function.d.ts +0 -13
- package/dist/ecosystem/function.js +0 -2
- package/dist/ecosystem/gateway.d.ts +0 -1
- package/dist/ecosystem/gateway.js +0 -1
- package/dist/ecosystem/hmac.d.ts +0 -1
- package/dist/ecosystem/hmac.js +0 -1
- package/dist/ecosystem/http.d.ts +0 -13
- package/dist/ecosystem/http.js +0 -5
- package/dist/ecosystem/mcp.d.ts +0 -3
- package/dist/ecosystem/mcp.js +0 -2
- package/dist/ecosystem/middleware.d.ts +0 -18
- package/dist/ecosystem/middleware.js +0 -7
- package/dist/ecosystem/node-host-resolver.d.ts +0 -1
- package/dist/ecosystem/node-host-resolver.js +0 -1
- package/dist/ecosystem/node.d.ts +0 -1
- package/dist/ecosystem/node.js +0 -1
- package/dist/ecosystem/oauth2.d.ts +0 -1
- package/dist/ecosystem/oauth2.js +0 -1
- package/dist/ecosystem/persona.d.ts +0 -17
- package/dist/ecosystem/persona.js +0 -5
- package/dist/ecosystem/queue.d.ts +0 -13
- package/dist/ecosystem/queue.js +0 -4
- package/dist/ecosystem/remote.d.ts +0 -8
- package/dist/ecosystem/remote.js +0 -1
- package/dist/ecosystem/role.d.ts +0 -4
- package/dist/ecosystem/role.js +0 -2
- package/dist/ecosystem/rpc.d.ts +0 -6
- package/dist/ecosystem/rpc.js +0 -3
- package/dist/ecosystem/safe-fetch.d.ts +0 -7
- package/dist/ecosystem/safe-fetch.js +0 -1
- package/dist/ecosystem/scenario.d.ts +0 -21
- package/dist/ecosystem/scenario.js +0 -8
- package/dist/ecosystem/scheduler.d.ts +0 -3
- package/dist/ecosystem/scheduler.js +0 -2
- package/dist/ecosystem/schema.d.ts +0 -2
- package/dist/ecosystem/schema.js +0 -1
- package/dist/ecosystem/scope.d.ts +0 -5
- package/dist/ecosystem/scope.js +0 -3
- package/dist/ecosystem/secret.d.ts +0 -11
- package/dist/ecosystem/secret.js +0 -2
- package/dist/ecosystem/services/istanbul-coverage.d.ts +0 -1
- package/dist/ecosystem/services/istanbul-coverage.js +0 -1
- package/dist/ecosystem/services/local-content-request-handler.d.ts +0 -9
- package/dist/ecosystem/services/local-content-request-handler.js +0 -1
- package/dist/ecosystem/services/local-content.d.ts +0 -2
- package/dist/ecosystem/services/local-content.js +0 -1
- package/dist/ecosystem/services/local-meta.d.ts +0 -1
- package/dist/ecosystem/services/local-meta.js +0 -1
- package/dist/ecosystem/services/v8-coverage.d.ts +0 -1
- package/dist/ecosystem/services/v8-coverage.js +0 -1
- package/dist/ecosystem/services.d.ts +0 -36
- package/dist/ecosystem/services.js +0 -12
- package/dist/ecosystem/testing.d.ts +0 -7
- package/dist/ecosystem/testing.js +0 -1
- package/dist/ecosystem/trigger.d.ts +0 -2
- package/dist/ecosystem/trigger.js +0 -1
- package/dist/ecosystem/types.d.ts +0 -12
- package/dist/ecosystem/types.js +0 -6
- package/dist/ecosystem/variable.d.ts +0 -9
- package/dist/ecosystem/variable.js +0 -1
- package/dist/ecosystem/virtual-user.d.ts +0 -24
- package/dist/ecosystem/virtual-user.js +0 -8
- package/dist/ecosystem/workflow.d.ts +0 -20
- package/dist/ecosystem/workflow.js +0 -6
- package/dist/ecosystem.d.ts +0 -27
- package/dist/ecosystem.js +0 -26
- package/dist/index.d.ts +0 -60
- package/dist/index.js +0 -25
- package/dist/middleware/require-unlocked.d.ts +0 -23
- package/dist/middleware/require-unlocked.js +0 -21
- package/dist/pikku-request.d.ts +0 -8
- package/dist/pikku-request.js +0 -15
- package/dist/secret-value.d.ts +0 -56
- package/dist/secret-value.js +0 -46
- package/dist/wirings/data-lock/data-lock-wiring.d.ts +0 -40
- package/dist/wirings/data-lock/data-lock-wiring.js +0 -77
- package/dist/wirings/data-lock/index.d.ts +0 -9
- package/dist/wirings/data-lock/index.js +0 -8
- package/dist/wirings/rpc/addon-runner.d.ts +0 -17
- package/dist/wirings/rpc/addon-runner.js +0 -150
- package/dist/wirings/rpc/remote-addon-auth.d.ts +0 -16
- package/dist/wirings/rpc/remote-addon-auth.js +0 -39
- package/dist/wirings/rpc/wire-addon.d.ts +0 -62
- package/dist/wirings/rpc/wire-addon.js +0 -88
- package/dist/wirings/rpc/wire-remote-addon.d.ts +0 -22
- package/dist/wirings/rpc/wire-remote-addon.js +0 -11
|
@@ -122,6 +122,69 @@ describe('PikkuCredentialWireService', () => {
|
|
|
122
122
|
assert.deepStrictEqual(r1, r2)
|
|
123
123
|
})
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* The regression the `loaded` flag was written to avoid.
|
|
127
|
+
*
|
|
128
|
+
* `get` answers synchronously once `loaded` is set, so setting the flag
|
|
129
|
+
* before the fetch resolved gave every caller that arrived during the load a
|
|
130
|
+
* confident `null` for a credential that exists. `getAll` hid it: it hands
|
|
131
|
+
* back the live `credentials` object, which is filled in before anyone
|
|
132
|
+
* asserts on it, so only a reader that copies a VALUE out sees the gap.
|
|
133
|
+
*/
|
|
134
|
+
test('should not answer null while a lazy load is still in flight', async () => {
|
|
135
|
+
const credService = mockCredentialService({ stripe: { key: 'sk_live' } })
|
|
136
|
+
const wire: PikkuWire = { session: { userId: 'user-1' } as any }
|
|
137
|
+
const service = new PikkuCredentialWireService(credService, wire)
|
|
138
|
+
|
|
139
|
+
// Issued in one tick, exactly as `Promise.all` over sibling RPC calls does.
|
|
140
|
+
const results = await Promise.all([
|
|
141
|
+
service.get('stripe'),
|
|
142
|
+
service.get('stripe'),
|
|
143
|
+
service.get('stripe'),
|
|
144
|
+
])
|
|
145
|
+
|
|
146
|
+
for (const [index, result] of results.entries()) {
|
|
147
|
+
assert.deepStrictEqual(
|
|
148
|
+
result,
|
|
149
|
+
{ key: 'sk_live' },
|
|
150
|
+
`concurrent reader ${index} lost the credential`
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('should not answer null during a slow lazy load', async () => {
|
|
156
|
+
const credService: CredentialService = {
|
|
157
|
+
get: async () => null,
|
|
158
|
+
set: async () => {},
|
|
159
|
+
delete: async () => {},
|
|
160
|
+
has: async () => true,
|
|
161
|
+
getAll: async () => {
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
163
|
+
return { stripe: { key: 'sk_live' } }
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
const wire: PikkuWire = { session: { userId: 'user-1' } as any }
|
|
167
|
+
const service = new PikkuCredentialWireService(credService, wire)
|
|
168
|
+
|
|
169
|
+
const first = service.get('stripe')
|
|
170
|
+
const second = service.get('stripe')
|
|
171
|
+
assert.deepStrictEqual(await first, { key: 'sk_live' })
|
|
172
|
+
assert.deepStrictEqual(await second, { key: 'sk_live' })
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('should still take the sync path when there is nothing to load', async () => {
|
|
176
|
+
// Both early returns are settled states, so they must set `loaded` too.
|
|
177
|
+
const service = new PikkuCredentialWireService(
|
|
178
|
+
mockCredentialService({ stripe: { key: '1' } }),
|
|
179
|
+
{}
|
|
180
|
+
)
|
|
181
|
+
await service.getAll()
|
|
182
|
+
assert.ok(
|
|
183
|
+
!(service.getAll() instanceof Promise),
|
|
184
|
+
'expected sync return after an unresolvable user'
|
|
185
|
+
)
|
|
186
|
+
})
|
|
187
|
+
|
|
125
188
|
test('getScoped should only return allowed names', async () => {
|
|
126
189
|
const credService = mockCredentialService({
|
|
127
190
|
stripe: { key: '1' },
|
|
@@ -62,17 +62,35 @@ export class PikkuCredentialWireService {
|
|
|
62
62
|
return this.loadPromise
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* `loaded` means "the credentials are in hand", never "a load has started".
|
|
67
|
+
*
|
|
68
|
+
* The distinction matters because `get`, `getAll` and `getScoped` all answer
|
|
69
|
+
* SYNCHRONOUSLY once it is set. Setting it before the await let every caller
|
|
70
|
+
* that arrived while the fetch was still in flight take that synchronous path
|
|
71
|
+
* over an empty map and receive `null` for a credential that exists — the
|
|
72
|
+
* first of N concurrent readers got the value and the rest silently did not.
|
|
73
|
+
* `loadPromise` is what guards against loading twice; this flag is only ever
|
|
74
|
+
* about whether the fast path is safe to take.
|
|
75
|
+
*
|
|
76
|
+
* `finally` rather than a trailing assignment: the two early returns (no
|
|
77
|
+
* service or wire, no resolvable user) are settled states as well — there is
|
|
78
|
+
* nothing to fetch — so they should reach the fast path too.
|
|
79
|
+
*/
|
|
65
80
|
private async doLoad(): Promise<void> {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
try {
|
|
82
|
+
if (!this.credentialService || !this.wire) return
|
|
83
|
+
const userId = defaultPikkuUserIdResolver(this.wire)
|
|
84
|
+
if (!userId) return
|
|
85
|
+
this.wire.pikkuUserId = userId
|
|
86
|
+
const allCreds = await this.credentialService.getAll(userId)
|
|
87
|
+
for (const [name, value] of Object.entries(allCreds)) {
|
|
88
|
+
if (!(name in this.credentials)) {
|
|
89
|
+
this.credentials[name] = value
|
|
90
|
+
}
|
|
75
91
|
}
|
|
92
|
+
} finally {
|
|
93
|
+
this.loaded = true
|
|
76
94
|
}
|
|
77
95
|
}
|
|
78
96
|
}
|
package/src/services/index.ts
CHANGED
|
@@ -18,6 +18,16 @@ export {
|
|
|
18
18
|
QueueWebhookService,
|
|
19
19
|
pikkuWebhookWorkerFunc,
|
|
20
20
|
} from './queue-webhook-service.js'
|
|
21
|
+
export {
|
|
22
|
+
REMOTE_JOBS_SECRET_HEADER,
|
|
23
|
+
REMOTE_JOBS_SECRET_VARIABLE,
|
|
24
|
+
REMOTE_QUEUE_JOB_PATH,
|
|
25
|
+
REMOTE_SCHEDULER_JOB_PATH,
|
|
26
|
+
pikkuRemoteQueueJobFunc,
|
|
27
|
+
pikkuRemoteScheduledJobFunc,
|
|
28
|
+
type RemoteQueueJobData,
|
|
29
|
+
type RemoteScheduledJobData,
|
|
30
|
+
} from './remote-jobs.js'
|
|
21
31
|
export { InMemoryQueueService } from './in-memory-queue-service.js'
|
|
22
32
|
export { InMemoryTriggerService } from './in-memory-trigger-service.js'
|
|
23
33
|
export { InMemoryAgentRunStateService } from './in-memory-agent-run-state-service.js'
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { BadRequestError, UnauthorizedError } from '../errors/errors.js'
|
|
4
|
+
import { resetPikkuState, setSingletonServices } from '../pikku-state.js'
|
|
5
|
+
import { remoteJobsSecret } from '../middleware/remote-jobs-secret.js'
|
|
6
|
+
import {
|
|
7
|
+
REMOTE_JOBS_SECRET_HEADER,
|
|
8
|
+
pikkuRemoteScheduledJobFunc,
|
|
9
|
+
} from './remote-jobs.js'
|
|
10
|
+
|
|
11
|
+
const silentLogger = {
|
|
12
|
+
info: () => {},
|
|
13
|
+
warn: () => {},
|
|
14
|
+
error: () => {},
|
|
15
|
+
debug: () => {},
|
|
16
|
+
} as any
|
|
17
|
+
|
|
18
|
+
const wire = (header?: string) =>
|
|
19
|
+
({
|
|
20
|
+
http: { request: { header: () => header ?? null } },
|
|
21
|
+
}) as any
|
|
22
|
+
|
|
23
|
+
const services = (secret?: string) =>
|
|
24
|
+
({ variables: { get: async () => secret } }) as any
|
|
25
|
+
|
|
26
|
+
describe('remoteJobsSecret', () => {
|
|
27
|
+
test('lets a caller holding the secret through', async () => {
|
|
28
|
+
let reached = false
|
|
29
|
+
await remoteJobsSecret(services('s3cret'), wire('s3cret'), async () => {
|
|
30
|
+
reached = true
|
|
31
|
+
})
|
|
32
|
+
assert.equal(reached, true)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('rejects a caller presenting the wrong secret', async () => {
|
|
36
|
+
await assert.rejects(
|
|
37
|
+
remoteJobsSecret(services('s3cret'), wire('nope'), async () => {}),
|
|
38
|
+
UnauthorizedError
|
|
39
|
+
)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('rejects when the deployment configured no secret at all', async () => {
|
|
43
|
+
await assert.rejects(
|
|
44
|
+
remoteJobsSecret(services(undefined), wire('anything'), async () => {}),
|
|
45
|
+
UnauthorizedError
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('rejects a caller presenting no header', async () => {
|
|
50
|
+
await assert.rejects(
|
|
51
|
+
remoteJobsSecret(services('s3cret'), wire(), async () => {}),
|
|
52
|
+
UnauthorizedError
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('reads the header the dispatchers already send', async () => {
|
|
57
|
+
const seen: string[] = []
|
|
58
|
+
await assert.rejects(
|
|
59
|
+
remoteJobsSecret(
|
|
60
|
+
services('s3cret'),
|
|
61
|
+
{
|
|
62
|
+
http: {
|
|
63
|
+
request: {
|
|
64
|
+
header: (name: string) => {
|
|
65
|
+
seen.push(name)
|
|
66
|
+
return null
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
} as any,
|
|
71
|
+
async () => {}
|
|
72
|
+
),
|
|
73
|
+
UnauthorizedError
|
|
74
|
+
)
|
|
75
|
+
assert.deepEqual(seen, [REMOTE_JOBS_SECRET_HEADER])
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('awaits a variables service that resolves asynchronously', async () => {
|
|
79
|
+
let reached = false
|
|
80
|
+
await remoteJobsSecret(
|
|
81
|
+
{
|
|
82
|
+
variables: {
|
|
83
|
+
get: () => new Promise((resolve) => setTimeout(() => resolve('s3cret'), 1)),
|
|
84
|
+
},
|
|
85
|
+
} as any,
|
|
86
|
+
wire('s3cret'),
|
|
87
|
+
async () => {
|
|
88
|
+
reached = true
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
assert.equal(reached, true)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe('pikkuRemoteScheduledJobFunc', () => {
|
|
96
|
+
test('acks a task this deployment does not have rather than asking for a retry', async () => {
|
|
97
|
+
resetPikkuState()
|
|
98
|
+
setSingletonServices({ logger: silentLogger } as any)
|
|
99
|
+
await assert.rejects(
|
|
100
|
+
pikkuRemoteScheduledJobFunc({ logger: silentLogger }, {
|
|
101
|
+
taskName: 'not-a-task',
|
|
102
|
+
}),
|
|
103
|
+
BadRequestError
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { BadRequestError, PikkuMissingMetaError } from '../errors/errors.js'
|
|
2
|
+
import { QueueJobDiscardedError, runQueueJob } from '../wirings/queue/queue-runner.js'
|
|
3
|
+
import type { QueueJob, QueueJobStatus } from '../wirings/queue/queue.types.js'
|
|
4
|
+
import {
|
|
5
|
+
ScheduledTaskNotFoundError,
|
|
6
|
+
runScheduledTask,
|
|
7
|
+
} from '../wirings/scheduler/scheduler-runner.js'
|
|
8
|
+
import type { Logger } from './logger.js'
|
|
9
|
+
|
|
10
|
+
/** The variable holding the shared secret every remote job must present. */
|
|
11
|
+
export const REMOTE_JOBS_SECRET_VARIABLE = 'PIKKU_DISPATCH_SECRET'
|
|
12
|
+
|
|
13
|
+
export const REMOTE_JOBS_SECRET_HEADER = 'x-pikku-dispatch'
|
|
14
|
+
|
|
15
|
+
export const REMOTE_QUEUE_JOB_PATH = '/__pikku/queue-job'
|
|
16
|
+
|
|
17
|
+
export const REMOTE_SCHEDULER_JOB_PATH = '/__pikku/scheduler-job'
|
|
18
|
+
|
|
19
|
+
export interface RemoteQueueJobData {
|
|
20
|
+
queueName: string
|
|
21
|
+
data?: unknown
|
|
22
|
+
jobId?: string
|
|
23
|
+
traceId?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RemoteScheduledJobData {
|
|
27
|
+
taskName: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A dispatched job is delivered, not owned: the sender retries, so a permanent
|
|
32
|
+
* failure has to read differently from a transient one. An unknown worker or a
|
|
33
|
+
* discarded job answers 4xx so the sender stops; anything else propagates and
|
|
34
|
+
* is delivered again.
|
|
35
|
+
*/
|
|
36
|
+
export async function pikkuRemoteQueueJobFunc(
|
|
37
|
+
{ logger }: { logger: Logger },
|
|
38
|
+
{ queueName, data, jobId, traceId }: RemoteQueueJobData
|
|
39
|
+
): Promise<void> {
|
|
40
|
+
const job: QueueJob = {
|
|
41
|
+
queueName,
|
|
42
|
+
data,
|
|
43
|
+
id: jobId ?? traceId ?? `remote-${Date.now()}`,
|
|
44
|
+
status: async (): Promise<QueueJobStatus> => 'active',
|
|
45
|
+
metadata: () => ({
|
|
46
|
+
processedAt: new Date(),
|
|
47
|
+
attemptsMade: 0,
|
|
48
|
+
maxAttempts: undefined,
|
|
49
|
+
result: undefined,
|
|
50
|
+
progress: 0,
|
|
51
|
+
createdAt: new Date(),
|
|
52
|
+
completedAt: undefined,
|
|
53
|
+
failedAt: undefined,
|
|
54
|
+
error: undefined,
|
|
55
|
+
}),
|
|
56
|
+
waitForCompletion: async () => {
|
|
57
|
+
throw new Error('remote jobs do not support waitForCompletion')
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await runQueueJob({ job, traceId })
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (
|
|
65
|
+
error instanceof PikkuMissingMetaError ||
|
|
66
|
+
error instanceof QueueJobDiscardedError
|
|
67
|
+
) {
|
|
68
|
+
throw new BadRequestError(error.message)
|
|
69
|
+
}
|
|
70
|
+
logger.error(
|
|
71
|
+
`remote-jobs: queue '${queueName}' failed: ${error instanceof Error ? error.message : String(error)}`
|
|
72
|
+
)
|
|
73
|
+
throw error
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function pikkuRemoteScheduledJobFunc(
|
|
78
|
+
{ logger }: { logger: Logger },
|
|
79
|
+
{ taskName }: RemoteScheduledJobData
|
|
80
|
+
): Promise<void> {
|
|
81
|
+
try {
|
|
82
|
+
await runScheduledTask({ name: taskName, traceId: `cron-${taskName}` })
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error instanceof ScheduledTaskNotFoundError) {
|
|
85
|
+
throw new BadRequestError(error.message)
|
|
86
|
+
}
|
|
87
|
+
logger.error(
|
|
88
|
+
`remote-jobs: task '${taskName}' failed: ${error instanceof Error ? error.message : String(error)}`
|
|
89
|
+
)
|
|
90
|
+
throw error
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -52,7 +52,7 @@ export const wireScheduler = <
|
|
|
52
52
|
tasks.set(scheduledTask.name, scheduledTask)
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
class ScheduledTaskNotFoundError extends PikkuError {
|
|
55
|
+
export class ScheduledTaskNotFoundError extends PikkuError {
|
|
56
56
|
constructor(title: string) {
|
|
57
57
|
super(`Scheduled task not found: ${title}`)
|
|
58
58
|
}
|