@pikku/core 0.12.27 → 0.12.29
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/function/function-runner.d.ts +1 -2
- package/dist/function/function-runner.js +2 -22
- package/dist/function/functions.types.d.ts +2 -0
- package/dist/services/meta-service.js +18 -7
- package/dist/types/core.types.d.ts +2 -0
- package/dist/wirings/channel/channel-common.js +9 -2
- package/dist/wirings/channel/local/local-channel-handler.d.ts +7 -4
- package/dist/wirings/channel/local/local-channel-handler.js +13 -5
- package/dist/wirings/channel/local/local-channel-runner.js +1 -1
- package/dist/wirings/http/pikku-fetch-http-request.js +42 -8
- package/dist/wirings/workflow/pikku-workflow-service.js +10 -1
- package/package.json +1 -1
- package/src/dev/hot-reload.test.ts +2 -0
- package/src/function/function-runner.test.ts +2 -28
- package/src/function/function-runner.ts +2 -35
- package/src/function/functions.types.ts +2 -0
- package/src/services/meta-service.ts +17 -7
- package/src/types/core.types.ts +2 -0
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +2 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +28 -4
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +3 -0
- package/src/wirings/channel/channel-common.ts +9 -2
- package/src/wirings/channel/local/local-channel-handler.ts +24 -9
- package/src/wirings/channel/local/local-channel-runner.ts +2 -1
- package/src/wirings/cli/cli-runner.test.ts +4 -0
- package/src/wirings/http/http-runner.test.ts +1 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +43 -8
- package/src/wirings/queue/queue-runner.test.ts +1 -0
- package/src/wirings/scheduler/scheduler-runner.test.ts +10 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +10 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -327,6 +327,7 @@ describe('streamAIAgent', () => {
|
|
|
327
327
|
description: 'Add a todo',
|
|
328
328
|
approvalRequired: true,
|
|
329
329
|
inputSchemaName: 'AddTodoInput',
|
|
330
|
+
sessionless: true,
|
|
330
331
|
}
|
|
331
332
|
pikkuState(null, 'misc', 'schemas').set('AddTodoInput', {
|
|
332
333
|
type: 'object',
|
|
@@ -428,6 +429,7 @@ describe('streamAIAgent', () => {
|
|
|
428
429
|
description: 'Add a todo',
|
|
429
430
|
approvalRequired: true,
|
|
430
431
|
inputSchemaName: 'AddTodoInput',
|
|
432
|
+
sessionless: true,
|
|
431
433
|
}
|
|
432
434
|
pikkuState('@test/addon-todos', 'misc', 'schemas').set('AddTodoInput', {
|
|
433
435
|
type: 'object',
|
|
@@ -1287,6 +1289,7 @@ describe('resumeAIAgent', () => {
|
|
|
1287
1289
|
pikkuState(null, 'function', 'meta').deploy = {
|
|
1288
1290
|
description: 'Deploy',
|
|
1289
1291
|
inputSchemaName: 'DeployInput',
|
|
1292
|
+
sessionless: true,
|
|
1290
1293
|
}
|
|
1291
1294
|
pikkuState(null, 'misc', 'schemas').set('DeployInput', {
|
|
1292
1295
|
type: 'object',
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
} from '../../types/core.types.js'
|
|
7
7
|
import type { CoreChannel, ChannelMessageMeta } from './channel.types.js'
|
|
8
8
|
import { combineMiddleware, runMiddleware } from '../../middleware-runner.js'
|
|
9
|
-
import {
|
|
9
|
+
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
10
10
|
import {
|
|
11
11
|
combineChannelMiddleware,
|
|
12
12
|
wrapChannelWithMiddleware,
|
|
@@ -79,7 +79,14 @@ export const runChannelLifecycleWithMiddleware = async ({
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const runLifecycle = async () => {
|
|
82
|
-
return await
|
|
82
|
+
return await runPikkuFunc('channel', channelConfig.name, meta.pikkuFuncId, {
|
|
83
|
+
singletonServices: services,
|
|
84
|
+
data: () => data as any,
|
|
85
|
+
wire,
|
|
86
|
+
tags: meta.tags ?? [],
|
|
87
|
+
inheritedPermissions: meta.permissions,
|
|
88
|
+
packageName: meta.packageName ?? null,
|
|
89
|
+
})
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
if (allMiddleware.length > 0) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Logger } from '../../../services/logger.js'
|
|
1
2
|
import type { BinaryData } from '../channel.types.js'
|
|
2
3
|
import { PikkuAbstractChannelHandler } from '../pikku-abstract-channel-handler.js'
|
|
3
4
|
|
|
@@ -7,19 +8,30 @@ export class PikkuLocalChannelHandler<
|
|
|
7
8
|
> extends PikkuAbstractChannelHandler<OpeningData, Out> {
|
|
8
9
|
private onMessageCallback?: (message: unknown) => void
|
|
9
10
|
private onBinaryMessageCallback?: (data: BinaryData) => void
|
|
10
|
-
private openCallBack?: () => void
|
|
11
|
-
private closeCallbacks: (() => void)[] = []
|
|
11
|
+
private openCallBack?: () => Promise<void> | void
|
|
12
|
+
private closeCallbacks: (() => Promise<void> | void)[] = []
|
|
12
13
|
private sendCallback?: (message: Out, isBinary?: boolean) => void
|
|
13
14
|
private sendBinaryCallback?: (data: BinaryData) => void
|
|
15
|
+
private logger?: Logger
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
constructor(
|
|
18
|
+
channelId: string,
|
|
19
|
+
channelName: string,
|
|
20
|
+
openingData: OpeningData,
|
|
21
|
+
logger?: Logger
|
|
22
|
+
) {
|
|
23
|
+
super(channelId, channelName, openingData)
|
|
24
|
+
this.logger = logger
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public registerOnOpen(callback: () => Promise<void> | void): void {
|
|
16
28
|
this.openCallBack = callback
|
|
17
29
|
}
|
|
18
30
|
|
|
19
|
-
public open() {
|
|
31
|
+
public async open(): Promise<void> {
|
|
20
32
|
this.getChannel().state = 'open'
|
|
21
33
|
if (this.openCallBack) {
|
|
22
|
-
this.openCallBack()
|
|
34
|
+
await this.openCallBack()
|
|
23
35
|
}
|
|
24
36
|
}
|
|
25
37
|
|
|
@@ -43,17 +55,20 @@ export class PikkuLocalChannelHandler<
|
|
|
43
55
|
return this.onBinaryMessageCallback?.(data)
|
|
44
56
|
}
|
|
45
57
|
|
|
46
|
-
public registerOnClose(callback: () => void): void {
|
|
58
|
+
public registerOnClose(callback: () => Promise<void> | void): void {
|
|
47
59
|
this.closeCallbacks.push(callback)
|
|
48
60
|
}
|
|
49
61
|
|
|
50
|
-
public close() {
|
|
62
|
+
public async close(): Promise<void> {
|
|
51
63
|
if (this.getChannel().state === 'closed') {
|
|
52
64
|
return
|
|
53
65
|
}
|
|
54
66
|
super.close()
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
const results = await Promise.allSettled(this.closeCallbacks.map((cb) => cb()))
|
|
68
|
+
for (const result of results) {
|
|
69
|
+
if (result.status === 'rejected') {
|
|
70
|
+
this.logger?.error('Error in channel close callback:', result.reason)
|
|
71
|
+
}
|
|
57
72
|
}
|
|
58
73
|
}
|
|
59
74
|
|
|
@@ -119,7 +119,8 @@ export const runLocalChannel = async ({
|
|
|
119
119
|
channelHandler = new PikkuLocalChannelHandler(
|
|
120
120
|
channelId,
|
|
121
121
|
channelConfig.name,
|
|
122
|
-
openingData
|
|
122
|
+
openingData,
|
|
123
|
+
singletonServices.logger
|
|
123
124
|
)
|
|
124
125
|
const channel = channelHandler.getChannel()
|
|
125
126
|
const wire: PikkuWire = {
|
|
@@ -128,6 +128,7 @@ describe('CLI Runner', () => {
|
|
|
128
128
|
pikkuFuncId: 'greetFunc',
|
|
129
129
|
inputSchemaName: null,
|
|
130
130
|
outputSchemaName: null,
|
|
131
|
+
sessionless: true,
|
|
131
132
|
},
|
|
132
133
|
})
|
|
133
134
|
|
|
@@ -192,6 +193,7 @@ describe('CLI Runner', () => {
|
|
|
192
193
|
pikkuFuncId: 'testFunc',
|
|
193
194
|
inputSchemaName: null,
|
|
194
195
|
outputSchemaName: null,
|
|
196
|
+
sessionless: true,
|
|
195
197
|
},
|
|
196
198
|
})
|
|
197
199
|
|
|
@@ -252,6 +254,7 @@ describe('CLI Runner', () => {
|
|
|
252
254
|
pikkuFuncId: 'greetFunc',
|
|
253
255
|
inputSchemaName: null,
|
|
254
256
|
outputSchemaName: null,
|
|
257
|
+
sessionless: true,
|
|
255
258
|
},
|
|
256
259
|
})
|
|
257
260
|
|
|
@@ -296,6 +299,7 @@ describe('CLI Runner', () => {
|
|
|
296
299
|
pikkuFuncId: 'secureFunc',
|
|
297
300
|
inputSchemaName: null,
|
|
298
301
|
outputSchemaName: null,
|
|
302
|
+
sessionless: true,
|
|
299
303
|
},
|
|
300
304
|
})
|
|
301
305
|
|
|
@@ -14,6 +14,9 @@ export class PikkuFetchHTTPRequest<
|
|
|
14
14
|
#cookies: Partial<Record<string, string>> | undefined
|
|
15
15
|
#params: Partial<Record<string, string | string[]>> = {}
|
|
16
16
|
#url: URL
|
|
17
|
+
#rawBodyText: string | undefined
|
|
18
|
+
#rawBodyBuffer: ArrayBuffer | undefined
|
|
19
|
+
#bodyRead = false
|
|
17
20
|
|
|
18
21
|
constructor(private request: Request) {
|
|
19
22
|
this.#url = new URL(request.url)
|
|
@@ -31,16 +34,47 @@ export class PikkuFetchHTTPRequest<
|
|
|
31
34
|
* Retrieves the request body.
|
|
32
35
|
* @returns A promise that resolves to the request body.
|
|
33
36
|
*/
|
|
34
|
-
public json(): Promise<In> {
|
|
35
|
-
|
|
37
|
+
public async json(): Promise<In> {
|
|
38
|
+
const text = await this.#readRawText()
|
|
39
|
+
return JSON.parse(text) as In
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
43
|
* Retrieves the raw request body as a Buffer.
|
|
40
44
|
* @returns A promise that resolves to the raw request body.
|
|
41
45
|
*/
|
|
42
|
-
public arrayBuffer(): Promise<ArrayBuffer> {
|
|
43
|
-
|
|
46
|
+
public async arrayBuffer(): Promise<ArrayBuffer> {
|
|
47
|
+
if (this.#rawBodyBuffer !== undefined) {
|
|
48
|
+
return this.#rawBodyBuffer
|
|
49
|
+
}
|
|
50
|
+
if (this.#bodyRead) {
|
|
51
|
+
if (this.#rawBodyText !== undefined) {
|
|
52
|
+
const buf = new TextEncoder().encode(this.#rawBodyText)
|
|
53
|
+
.buffer as ArrayBuffer
|
|
54
|
+
this.#rawBodyBuffer = buf
|
|
55
|
+
return buf
|
|
56
|
+
}
|
|
57
|
+
return new ArrayBuffer(0)
|
|
58
|
+
}
|
|
59
|
+
const buf = await this.request.arrayBuffer()
|
|
60
|
+
this.#rawBodyBuffer = buf
|
|
61
|
+
this.#bodyRead = true
|
|
62
|
+
return buf
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async #readRawText(): Promise<string> {
|
|
66
|
+
if (this.#rawBodyText !== undefined) {
|
|
67
|
+
return this.#rawBodyText
|
|
68
|
+
}
|
|
69
|
+
if (this.#rawBodyBuffer !== undefined) {
|
|
70
|
+
const text = new TextDecoder().decode(this.#rawBodyBuffer)
|
|
71
|
+
this.#rawBodyText = text
|
|
72
|
+
return text
|
|
73
|
+
}
|
|
74
|
+
const text = await this.request.text()
|
|
75
|
+
this.#rawBodyText = text
|
|
76
|
+
this.#bodyRead = true
|
|
77
|
+
return text
|
|
44
78
|
}
|
|
45
79
|
|
|
46
80
|
public headers(): Record<string, string> {
|
|
@@ -133,7 +167,8 @@ export class PikkuFetchHTTPRequest<
|
|
|
133
167
|
const contentType = this.header('content-type') || ''
|
|
134
168
|
try {
|
|
135
169
|
if (contentType.includes('application/json')) {
|
|
136
|
-
const
|
|
170
|
+
const text = await this.#readRawText()
|
|
171
|
+
const parsed = text ? JSON.parse(text) : null
|
|
137
172
|
body =
|
|
138
173
|
typeof parsed === 'object' &&
|
|
139
174
|
parsed !== null &&
|
|
@@ -141,13 +176,13 @@ export class PikkuFetchHTTPRequest<
|
|
|
141
176
|
? parsed
|
|
142
177
|
: { data: parsed }
|
|
143
178
|
} else if (contentType.includes('text/')) {
|
|
144
|
-
const text = await this
|
|
179
|
+
const text = await this.#readRawText()
|
|
145
180
|
body = { data: text }
|
|
146
181
|
} else if (contentType.includes('application/octet-stream')) {
|
|
147
|
-
const buffer = await this.
|
|
182
|
+
const buffer = await this.arrayBuffer()
|
|
148
183
|
body = { data: buffer }
|
|
149
184
|
} else if (contentType === 'application/x-www-form-urlencoded') {
|
|
150
|
-
const text = await this
|
|
185
|
+
const text = await this.#readRawText()
|
|
151
186
|
const params = new URLSearchParams(text)
|
|
152
187
|
let count = 0
|
|
153
188
|
for (const _ of params) {
|
|
@@ -157,6 +157,7 @@ describe('runScheduledTask', () => {
|
|
|
157
157
|
pikkuFuncId: 'scheduler_simple-task',
|
|
158
158
|
inputSchemaName: null,
|
|
159
159
|
outputSchemaName: null,
|
|
160
|
+
sessionless: true,
|
|
160
161
|
}
|
|
161
162
|
wireScheduler(mockTask)
|
|
162
163
|
|
|
@@ -199,6 +200,7 @@ describe('runScheduledTask', () => {
|
|
|
199
200
|
pikkuFuncId: 'scheduler_task-with-session',
|
|
200
201
|
inputSchemaName: null,
|
|
201
202
|
outputSchemaName: null,
|
|
203
|
+
sessionless: true,
|
|
202
204
|
}
|
|
203
205
|
wireScheduler(mockTask)
|
|
204
206
|
|
|
@@ -287,6 +289,7 @@ describe('runScheduledTask', () => {
|
|
|
287
289
|
pikkuFuncId: 'scheduler_skipped-task',
|
|
288
290
|
inputSchemaName: null,
|
|
289
291
|
outputSchemaName: null,
|
|
292
|
+
sessionless: true,
|
|
290
293
|
}
|
|
291
294
|
wireScheduler(mockTask)
|
|
292
295
|
|
|
@@ -332,6 +335,7 @@ describe('runScheduledTask', () => {
|
|
|
332
335
|
pikkuFuncId: 'scheduler_skipped-task-no-reason',
|
|
333
336
|
inputSchemaName: null,
|
|
334
337
|
outputSchemaName: null,
|
|
338
|
+
sessionless: true,
|
|
335
339
|
}
|
|
336
340
|
wireScheduler(mockTask)
|
|
337
341
|
|
|
@@ -380,6 +384,7 @@ describe('runScheduledTask', () => {
|
|
|
380
384
|
pikkuFuncId: 'scheduler_wire-task',
|
|
381
385
|
inputSchemaName: null,
|
|
382
386
|
outputSchemaName: null,
|
|
387
|
+
sessionless: true,
|
|
383
388
|
}
|
|
384
389
|
wireScheduler(mockTask)
|
|
385
390
|
|
|
@@ -420,6 +425,7 @@ describe('runScheduledTask', () => {
|
|
|
420
425
|
pikkuFuncId: 'scheduler_session-services-task',
|
|
421
426
|
inputSchemaName: null,
|
|
422
427
|
outputSchemaName: null,
|
|
428
|
+
sessionless: true,
|
|
423
429
|
}
|
|
424
430
|
wireScheduler(mockTask)
|
|
425
431
|
|
|
@@ -469,6 +475,7 @@ describe('runScheduledTask', () => {
|
|
|
469
475
|
pikkuFuncId: 'scheduler_cleanup-task',
|
|
470
476
|
inputSchemaName: null,
|
|
471
477
|
outputSchemaName: null,
|
|
478
|
+
sessionless: true,
|
|
472
479
|
}
|
|
473
480
|
wireScheduler(mockTask)
|
|
474
481
|
|
|
@@ -517,6 +524,7 @@ describe('runScheduledTask', () => {
|
|
|
517
524
|
pikkuFuncId: 'scheduler_error-cleanup-task',
|
|
518
525
|
inputSchemaName: null,
|
|
519
526
|
outputSchemaName: null,
|
|
527
|
+
sessionless: true,
|
|
520
528
|
}
|
|
521
529
|
wireScheduler(mockTask)
|
|
522
530
|
|
|
@@ -558,6 +566,7 @@ describe('runScheduledTask', () => {
|
|
|
558
566
|
pikkuFuncId: 'scheduler_error-task',
|
|
559
567
|
inputSchemaName: null,
|
|
560
568
|
outputSchemaName: null,
|
|
569
|
+
sessionless: true,
|
|
561
570
|
}
|
|
562
571
|
wireScheduler(mockTask)
|
|
563
572
|
|
|
@@ -607,6 +616,7 @@ describe('runScheduledTask', () => {
|
|
|
607
616
|
pikkuFuncId: 'scheduler_middleware-task',
|
|
608
617
|
inputSchemaName: null,
|
|
609
618
|
outputSchemaName: null,
|
|
619
|
+
sessionless: true,
|
|
610
620
|
}
|
|
611
621
|
wireScheduler(mockTask)
|
|
612
622
|
|
|
@@ -879,7 +879,16 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
879
879
|
data: unknown,
|
|
880
880
|
stepOptions?: WorkflowStepOptions
|
|
881
881
|
): Promise<boolean> {
|
|
882
|
-
if (
|
|
882
|
+
if (!getSingletonServices()?.queueService) {
|
|
883
|
+
return false
|
|
884
|
+
}
|
|
885
|
+
// Functions default to inline execution. Only dispatch via queue when the
|
|
886
|
+
// function explicitly sets inline: false.
|
|
887
|
+
const functionsMeta = pikkuState(null, 'function', 'meta')
|
|
888
|
+
const rpcFuncId = pikkuState(null, 'rpc', 'meta')[rpcName]
|
|
889
|
+
const rpcMeta = typeof rpcFuncId === 'string' ? functionsMeta[rpcFuncId] : undefined
|
|
890
|
+
const forceQueue = rpcMeta?.inline === false
|
|
891
|
+
if (!forceQueue && this.isInline(runId)) {
|
|
883
892
|
return false
|
|
884
893
|
}
|
|
885
894
|
const retries = stepOptions?.retries ?? 0
|