@pikku/core 0.12.20 → 0.12.21
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 -0
- package/dist/dev/hot-reload.js +20 -6
- package/dist/errors/error-handler.d.ts +1 -1
- package/dist/errors/error-handler.js +2 -2
- package/dist/function/functions.types.d.ts +3 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/middleware/index.d.ts +2 -2
- package/dist/middleware/index.js +2 -2
- package/dist/middleware/telemetry.d.ts +2 -2
- package/dist/middleware/telemetry.js +2 -2
- package/dist/middleware-runner.d.ts +15 -27
- package/dist/middleware-runner.js +25 -30
- package/dist/permissions.d.ts +15 -22
- package/dist/permissions.js +30 -25
- package/dist/pikku-request.js +1 -1
- package/dist/pikku-state.js +2 -3
- package/dist/services/ai-agent-runner-service.d.ts +1 -0
- package/dist/services/in-memory-queue-service.js +1 -1
- package/dist/services/in-memory-workflow-service.d.ts +15 -13
- package/dist/services/in-memory-workflow-service.js +31 -13
- package/dist/services/local-content.js +8 -1
- package/dist/services/user-session-service.d.ts +1 -1
- package/dist/testing/service-tests.js +24 -0
- package/dist/types/core.types.d.ts +4 -0
- package/dist/types/state.types.d.ts +2 -18
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +24 -5
- package/dist/wirings/ai-agent/ai-agent-memory.js +128 -23
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +10 -1
- package/dist/wirings/ai-agent/ai-agent-model-config.js +15 -35
- package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +45 -31
- package/dist/wirings/ai-agent/ai-agent-stream.js +63 -34
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +20 -0
- package/dist/wirings/channel/channel-handler.js +1 -1
- package/dist/wirings/channel/channel-store.d.ts +13 -0
- package/dist/wirings/channel/channel.types.d.ts +3 -0
- package/dist/wirings/channel/local/local-channel-runner.js +23 -5
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +8 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +9 -0
- package/dist/wirings/cli/cli-runner.js +24 -5
- package/dist/wirings/cli/command-parser.js +19 -4
- package/dist/wirings/http/http-runner.js +72 -36
- package/dist/wirings/http/http.types.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -1
- package/dist/wirings/http/web-request.js +32 -0
- package/dist/wirings/rpc/rpc-runner.js +13 -3
- package/dist/wirings/workflow/graph/graph-node.d.ts +8 -0
- package/dist/wirings/workflow/graph/graph-node.js +4 -0
- package/dist/wirings/workflow/graph/graph-runner.js +12 -10
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +4 -0
- package/dist/wirings/workflow/index.d.ts +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +81 -16
- package/dist/wirings/workflow/pikku-workflow-service.js +266 -45
- package/dist/wirings/workflow/workflow.types.d.ts +34 -0
- package/package.json +1 -1
- package/run-tests.sh +4 -1
- package/src/dev/hot-reload.test.ts +62 -11
- package/src/dev/hot-reload.ts +28 -7
- package/src/errors/error-handler.ts +8 -2
- package/src/errors/error.test.ts +2 -0
- package/src/function/function-runner.test.ts +500 -10
- package/src/function/functions.types.ts +3 -2
- package/src/index.ts +12 -2
- package/src/middleware/index.ts +11 -2
- package/src/middleware/telemetry.ts +2 -2
- package/src/middleware-runner.test.ts +16 -16
- package/src/middleware-runner.ts +42 -30
- package/src/permissions.test.ts +27 -24
- package/src/permissions.ts +41 -25
- package/src/pikku-request.test.ts +35 -0
- package/src/pikku-request.ts +1 -1
- package/src/pikku-state.ts +2 -3
- package/src/run-tests-script.test.ts +18 -0
- package/src/services/ai-agent-runner-service.ts +1 -0
- package/src/services/in-memory-queue-service.ts +1 -1
- package/src/services/in-memory-session-store.ts +1 -2
- package/src/services/in-memory-workflow-service.test.ts +33 -11
- package/src/services/in-memory-workflow-service.ts +49 -13
- package/src/services/local-content.test.ts +54 -0
- package/src/services/local-content.ts +12 -1
- package/src/services/typed-credential-service.ts +3 -3
- package/src/services/typed-secret-service.ts +3 -3
- package/src/services/typed-variables-service.ts +3 -3
- package/src/services/user-session-service.ts +4 -4
- package/src/testing/service-tests.ts +30 -0
- package/src/types/core.types.ts +6 -2
- package/src/types/state.types.ts +2 -13
- package/src/wirings/ai-agent/ai-agent-memory.test.ts +324 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +187 -36
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +12 -90
- package/src/wirings/ai-agent/ai-agent-model-config.ts +14 -38
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +292 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +4 -1
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +230 -3
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +625 -6
- package/src/wirings/ai-agent/ai-agent-runner.ts +65 -50
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +544 -5
- package/src/wirings/ai-agent/ai-agent-stream.ts +71 -69
- package/src/wirings/ai-agent/ai-agent.types.ts +24 -0
- package/src/wirings/channel/channel-handler.test.ts +272 -0
- package/src/wirings/channel/channel-handler.ts +1 -1
- package/src/wirings/channel/channel-middleware-runner.test.ts +163 -0
- package/src/wirings/channel/channel-store.ts +19 -0
- package/src/wirings/channel/channel.types.ts +4 -0
- package/src/wirings/channel/local/local-channel-runner.test.ts +63 -0
- package/src/wirings/channel/local/local-channel-runner.ts +41 -5
- package/src/wirings/channel/local/local-eventhub-service.ts +3 -3
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +9 -2
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +23 -5
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +7 -2
- package/src/wirings/cli/cli-runner.test.ts +255 -2
- package/src/wirings/cli/cli-runner.ts +31 -10
- package/src/wirings/cli/cli.types.ts +4 -8
- package/src/wirings/cli/command-parser.test.ts +83 -0
- package/src/wirings/cli/command-parser.ts +23 -4
- package/src/wirings/http/http-runner.test.ts +296 -1
- package/src/wirings/http/http-runner.ts +87 -57
- package/src/wirings/http/http.types.ts +11 -26
- package/src/wirings/http/pikku-fetch-http-request.ts +8 -4
- package/src/wirings/http/pikku-fetch-http-response.test.ts +41 -0
- package/src/wirings/http/web-request.test.ts +115 -0
- package/src/wirings/http/web-request.ts +43 -5
- package/src/wirings/mcp/mcp-runner.test.ts +367 -0
- package/src/wirings/queue/queue.types.ts +2 -5
- package/src/wirings/rpc/rpc-runner.test.ts +511 -0
- package/src/wirings/rpc/rpc-runner.ts +15 -3
- package/src/wirings/rpc/wire-addon.test.ts +57 -0
- package/src/wirings/workflow/graph/graph-node.ts +12 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +98 -0
- package/src/wirings/workflow/graph/graph-runner.ts +28 -10
- package/src/wirings/workflow/graph/workflow-graph.types.ts +4 -0
- package/src/wirings/workflow/index.ts +1 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +19 -2
- package/src/wirings/workflow/pikku-workflow-service.ts +370 -71
- package/src/wirings/workflow/workflow-queue-workers.test.ts +131 -0
- package/src/wirings/workflow/workflow.types.ts +68 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { beforeEach, describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { pikkuState, resetPikkuState } from '../../pikku-state.js'
|
|
5
|
+
import {
|
|
6
|
+
addChannelMiddleware,
|
|
7
|
+
clearChannelMiddlewareCache,
|
|
8
|
+
combineChannelMiddleware,
|
|
9
|
+
wrapChannelWithMiddleware,
|
|
10
|
+
} from './channel-middleware-runner.js'
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
resetPikkuState()
|
|
14
|
+
clearChannelMiddlewareCache()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe('addChannelMiddleware', () => {
|
|
18
|
+
test('registers tag-scoped channel middleware for a package', () => {
|
|
19
|
+
const middleware = [async () => {}]
|
|
20
|
+
|
|
21
|
+
const result = addChannelMiddleware(
|
|
22
|
+
'chat:outbound',
|
|
23
|
+
middleware,
|
|
24
|
+
'@addon/pkg'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
assert.strictEqual(result, middleware)
|
|
28
|
+
assert.strictEqual(
|
|
29
|
+
pikkuState('@addon/pkg', 'channelMiddleware', 'tagGroup')[
|
|
30
|
+
'chat:outbound'
|
|
31
|
+
],
|
|
32
|
+
middleware
|
|
33
|
+
)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('combineChannelMiddleware', () => {
|
|
38
|
+
test('combines tag, named wire middleware, and inline channel middleware', () => {
|
|
39
|
+
const execution: string[] = []
|
|
40
|
+
const tagMiddleware = async () => {
|
|
41
|
+
execution.push('tag')
|
|
42
|
+
}
|
|
43
|
+
const namedMiddleware = async () => {
|
|
44
|
+
execution.push('named')
|
|
45
|
+
}
|
|
46
|
+
const inlineMiddleware = async () => {
|
|
47
|
+
execution.push('inline')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addChannelMiddleware('chat:outbound', [tagMiddleware], '@addon/pkg')
|
|
51
|
+
pikkuState(null, 'misc', 'channelMiddleware').named = [
|
|
52
|
+
namedMiddleware,
|
|
53
|
+
] as never
|
|
54
|
+
|
|
55
|
+
const combined = combineChannelMiddleware('channel', 'combined-1', {
|
|
56
|
+
packageName: '@addon/pkg',
|
|
57
|
+
wireInheritedChannelMiddleware: [
|
|
58
|
+
{ type: 'tag', tag: 'chat:outbound' },
|
|
59
|
+
{ type: 'wire', name: 'named' },
|
|
60
|
+
],
|
|
61
|
+
wireChannelMiddleware: [inlineMiddleware],
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
assert.deepEqual(combined, [
|
|
65
|
+
tagMiddleware,
|
|
66
|
+
namedMiddleware,
|
|
67
|
+
inlineMiddleware,
|
|
68
|
+
])
|
|
69
|
+
void execution
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('deduplicates middleware and returns cached results until the cache is cleared', () => {
|
|
73
|
+
const shared = async () => {}
|
|
74
|
+
addChannelMiddleware('chat:outbound', [shared])
|
|
75
|
+
|
|
76
|
+
const first = combineChannelMiddleware('channel', 'cached-1', {
|
|
77
|
+
wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
|
|
78
|
+
wireChannelMiddleware: [shared],
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
assert.deepEqual(first, [shared])
|
|
82
|
+
|
|
83
|
+
addChannelMiddleware('chat:outbound', [async () => {}])
|
|
84
|
+
|
|
85
|
+
const cached = combineChannelMiddleware('channel', 'cached-1', {
|
|
86
|
+
wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
|
|
87
|
+
})
|
|
88
|
+
assert.strictEqual(cached, first)
|
|
89
|
+
|
|
90
|
+
clearChannelMiddlewareCache()
|
|
91
|
+
|
|
92
|
+
const refreshed = combineChannelMiddleware('channel', 'cached-1', {
|
|
93
|
+
wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
|
|
94
|
+
})
|
|
95
|
+
assert.notStrictEqual(refreshed, first)
|
|
96
|
+
assert.equal(refreshed.length, 1)
|
|
97
|
+
assert.notStrictEqual(refreshed[0], shared)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('ignores missing named middleware references', () => {
|
|
101
|
+
const combined = combineChannelMiddleware('channel', 'missing-wire', {
|
|
102
|
+
wireInheritedChannelMiddleware: [{ type: 'wire', name: 'missing' }],
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
assert.deepEqual(combined, [])
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('wrapChannelWithMiddleware', () => {
|
|
110
|
+
test('returns the original wire when there is no channel or middleware', () => {
|
|
111
|
+
const wireWithoutChannel = { traceId: 'trace-1' }
|
|
112
|
+
const wireWithChannel = {
|
|
113
|
+
channel: {
|
|
114
|
+
send: async () => {},
|
|
115
|
+
sendBinary: async () => {},
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
assert.strictEqual(
|
|
120
|
+
wrapChannelWithMiddleware(wireWithoutChannel as never, {} as never, [
|
|
121
|
+
async () => {},
|
|
122
|
+
]),
|
|
123
|
+
wireWithoutChannel
|
|
124
|
+
)
|
|
125
|
+
assert.strictEqual(
|
|
126
|
+
wrapChannelWithMiddleware(wireWithChannel as never, {} as never, []),
|
|
127
|
+
wireWithChannel
|
|
128
|
+
)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('wraps outbound channel sends and supports transformed arrays and null drops', async () => {
|
|
132
|
+
const sent: string[] = []
|
|
133
|
+
const wrapped = wrapChannelWithMiddleware(
|
|
134
|
+
{
|
|
135
|
+
channel: {
|
|
136
|
+
send: async (data: string) => {
|
|
137
|
+
sent.push(data)
|
|
138
|
+
},
|
|
139
|
+
sendBinary: async () => {},
|
|
140
|
+
},
|
|
141
|
+
} as never,
|
|
142
|
+
{} as never,
|
|
143
|
+
[
|
|
144
|
+
async (_services, event: string, next) => {
|
|
145
|
+
if (event === 'drop') {
|
|
146
|
+
await next(null)
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
await next([`${event}:a`, `${event}:b`])
|
|
150
|
+
},
|
|
151
|
+
async (_services, event: string, next) => {
|
|
152
|
+
await next(event.toUpperCase())
|
|
153
|
+
},
|
|
154
|
+
]
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
await wrapped.channel!.send('hello')
|
|
158
|
+
await wrapped.channel!.send('drop')
|
|
159
|
+
|
|
160
|
+
assert.deepEqual(sent, ['HELLO:A', 'HELLO:B'])
|
|
161
|
+
assert.strictEqual(wrapped.channel!.sendBinary, wrapped.channel!.sendBinary)
|
|
162
|
+
})
|
|
163
|
+
})
|
|
@@ -23,4 +23,23 @@ export abstract class ChannelStore<
|
|
|
23
23
|
):
|
|
24
24
|
| Promise<TypedChannel & { pikkuUserId?: string }>
|
|
25
25
|
| (TypedChannel & { pikkuUserId?: string })
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Persist per-socket scratch state scoped to a single channel (keyed by
|
|
29
|
+
* channelId). This is intentionally separate from `SessionStore` (which is
|
|
30
|
+
* keyed by `pikkuUserId` and holds the user session — shared across HTTP
|
|
31
|
+
* and channel transports).
|
|
32
|
+
*
|
|
33
|
+
* Use this for ephemeral, channel-local data: a per-socket subscription
|
|
34
|
+
* filter, a step in a connection-bound state machine, the last command sent
|
|
35
|
+
* on this socket, etc. It is cleared when the channel is removed.
|
|
36
|
+
*/
|
|
37
|
+
public abstract setState(
|
|
38
|
+
channelId: string,
|
|
39
|
+
state: unknown
|
|
40
|
+
): Promise<void> | void
|
|
41
|
+
public abstract getState(
|
|
42
|
+
channelId: string
|
|
43
|
+
): Promise<unknown | undefined> | unknown | undefined
|
|
44
|
+
public abstract clearState(channelId: string): Promise<void> | void
|
|
26
45
|
}
|
|
@@ -157,6 +157,10 @@ export interface PikkuChannel<OpeningData, out Out> {
|
|
|
157
157
|
close(): Promise<void> | void
|
|
158
158
|
// The current state of the channel
|
|
159
159
|
state: 'initial' | 'open' | 'closed'
|
|
160
|
+
// Per-socket ephemeral state scoped to this channel connection.
|
|
161
|
+
setState<T>(state: T): Promise<void> | void
|
|
162
|
+
getState<T>(): Promise<T | undefined> | T | undefined
|
|
163
|
+
clearState(): Promise<void> | void
|
|
160
164
|
}
|
|
161
165
|
|
|
162
166
|
export interface PikkuChannelHandler<OpeningData = unknown, Out = unknown> {
|
|
@@ -3,6 +3,7 @@ import * as assert from 'node:assert/strict'
|
|
|
3
3
|
import { runLocalChannel } from './local-channel-runner.js'
|
|
4
4
|
import { pikkuState, resetPikkuState } from '../../../pikku-state.js'
|
|
5
5
|
import { wireChannel } from '../channel-runner.js'
|
|
6
|
+
import { addHTTPMiddleware } from '../../http/http-runner.js'
|
|
6
7
|
import type {
|
|
7
8
|
HTTPMethod,
|
|
8
9
|
PikkuHTTPRequest,
|
|
@@ -11,6 +12,7 @@ import type {
|
|
|
11
12
|
} from '../../http/http.types.js'
|
|
12
13
|
import type { SerializeOptions } from 'cookie'
|
|
13
14
|
import { httpRouter } from '../../http/routers/http-router.js'
|
|
15
|
+
import { pikkuMiddleware } from '../../../types/core.types.js'
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Minimal stubs for dependencies that runChannel expects.
|
|
@@ -217,3 +219,64 @@ test('runChannel should close wire services once when channel closes', async ()
|
|
|
217
219
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
218
220
|
assert.equal(closeCount, 1)
|
|
219
221
|
})
|
|
222
|
+
|
|
223
|
+
test('runChannel should run HTTP middleware on websocket upgrade and establish session', async () => {
|
|
224
|
+
resetPikkuState()
|
|
225
|
+
pikkuState(null, 'package', 'singletonServices', mockSingletonServices as any)
|
|
226
|
+
pikkuState(null, 'package', 'factories', {
|
|
227
|
+
createWireServices: mockCreateWireServices,
|
|
228
|
+
} as any)
|
|
229
|
+
|
|
230
|
+
addHTTPMiddleware(
|
|
231
|
+
'*',
|
|
232
|
+
[
|
|
233
|
+
pikkuMiddleware(async (_services, { setSession }, next) => {
|
|
234
|
+
setSession?.({ userId: 'user-1' } as any)
|
|
235
|
+
await next()
|
|
236
|
+
}),
|
|
237
|
+
],
|
|
238
|
+
null
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
pikkuState(null, 'channel', 'meta', {
|
|
242
|
+
test: {
|
|
243
|
+
name: 'test',
|
|
244
|
+
route: '/test-channel',
|
|
245
|
+
messageWirings: {
|
|
246
|
+
action: {
|
|
247
|
+
ping: {
|
|
248
|
+
pikkuFuncId: 'test:ping',
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
} as any)
|
|
254
|
+
wireChannel({
|
|
255
|
+
name: 'test',
|
|
256
|
+
route: '/test-channel',
|
|
257
|
+
onMessageWiring: {
|
|
258
|
+
action: {
|
|
259
|
+
ping: {
|
|
260
|
+
func: async (_services, data) => data,
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
} as any)
|
|
265
|
+
|
|
266
|
+
httpRouter.initialize()
|
|
267
|
+
|
|
268
|
+
const result = await runLocalChannel({
|
|
269
|
+
channelId: 'test-channel-id',
|
|
270
|
+
request: new PikkuMockRequest('/test-channel', 'get'),
|
|
271
|
+
response: new PikkuMockResponse(),
|
|
272
|
+
route: '/test-channel',
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
assert.ok(result)
|
|
276
|
+
let sent: unknown
|
|
277
|
+
result.registerOnSend((message) => {
|
|
278
|
+
sent = message
|
|
279
|
+
})
|
|
280
|
+
await result.message(JSON.stringify({ action: 'ping' }))
|
|
281
|
+
assert.deepEqual(sent, { action: 'ping' })
|
|
282
|
+
})
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
PikkuSessionService,
|
|
13
13
|
createMiddlewareSessionWireProps,
|
|
14
14
|
} from '../../../services/user-session-service.js'
|
|
15
|
+
import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js'
|
|
15
16
|
import type {
|
|
16
17
|
PikkuHTTP,
|
|
17
18
|
PikkuHTTPRequest,
|
|
@@ -21,8 +22,38 @@ import { runChannelLifecycleWithMiddleware } from '../channel-common.js'
|
|
|
21
22
|
import {
|
|
22
23
|
getSingletonServices,
|
|
23
24
|
getCreateWireServices,
|
|
25
|
+
pikkuState,
|
|
24
26
|
} from '../../../pikku-state.js'
|
|
25
27
|
|
|
28
|
+
const runUpgradeMiddleware = async (
|
|
29
|
+
singletonServices: ReturnType<typeof getSingletonServices>,
|
|
30
|
+
request: PikkuHTTPRequest,
|
|
31
|
+
response: PikkuHTTPResponse | undefined,
|
|
32
|
+
userSession: PikkuSessionService<any>
|
|
33
|
+
) => {
|
|
34
|
+
const http = createHTTPWire(request, response)
|
|
35
|
+
const httpGroups = pikkuState(null, 'middleware', 'httpGroup')
|
|
36
|
+
const wireInheritedMiddleware = httpGroups['*']
|
|
37
|
+
? [{ type: 'http' as const, route: '*' }]
|
|
38
|
+
: undefined
|
|
39
|
+
const middleware = combineMiddleware('channel', `upgrade:${request.path()}`, {
|
|
40
|
+
wireInheritedMiddleware,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
if (middleware.length > 0) {
|
|
44
|
+
await runMiddleware(
|
|
45
|
+
singletonServices,
|
|
46
|
+
{
|
|
47
|
+
http,
|
|
48
|
+
...createMiddlewareSessionWireProps(userSession),
|
|
49
|
+
},
|
|
50
|
+
middleware
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return http
|
|
55
|
+
}
|
|
56
|
+
|
|
26
57
|
export const runLocalChannel = async ({
|
|
27
58
|
channelId,
|
|
28
59
|
request,
|
|
@@ -48,13 +79,18 @@ export const runLocalChannel = async ({
|
|
|
48
79
|
const userSession = new PikkuSessionService(singletonServices.sessionStore)
|
|
49
80
|
|
|
50
81
|
let http: PikkuHTTP | undefined
|
|
51
|
-
if (request) {
|
|
52
|
-
http = createHTTPWire(request, response)
|
|
53
|
-
route = http?.request?.path()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
82
|
let openingData, channelConfig, meta
|
|
57
83
|
try {
|
|
84
|
+
if (request) {
|
|
85
|
+
http = await runUpgradeMiddleware(
|
|
86
|
+
singletonServices,
|
|
87
|
+
request,
|
|
88
|
+
response,
|
|
89
|
+
userSession
|
|
90
|
+
)
|
|
91
|
+
route = request.path()
|
|
92
|
+
}
|
|
93
|
+
|
|
58
94
|
;({ openingData, channelConfig, meta } = await openChannel({
|
|
59
95
|
channelId,
|
|
60
96
|
respondWith404,
|
|
@@ -5,9 +5,9 @@ import type { EventHubService } from '../eventhub-service.js'
|
|
|
5
5
|
* Implementation of the SubscriptionService interface.
|
|
6
6
|
* Manages subscriptions and publishes messages to subscribers.
|
|
7
7
|
*/
|
|
8
|
-
export class LocalEventHubService<
|
|
9
|
-
|
|
10
|
-
{
|
|
8
|
+
export class LocalEventHubService<
|
|
9
|
+
Data extends Record<string, any> = {},
|
|
10
|
+
> implements EventHubService<Data> {
|
|
11
11
|
private channels = new Map<string, PikkuChannelHandler>()
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -7,8 +7,7 @@ import type {
|
|
|
7
7
|
export abstract class PikkuAbstractChannelHandler<
|
|
8
8
|
OpeningData = unknown,
|
|
9
9
|
Out = unknown,
|
|
10
|
-
> implements PikkuChannelHandler<OpeningData, Out>
|
|
11
|
-
{
|
|
10
|
+
> implements PikkuChannelHandler<OpeningData, Out> {
|
|
12
11
|
protected channel?: PikkuChannel<OpeningData, Out>
|
|
13
12
|
|
|
14
13
|
constructor(
|
|
@@ -23,6 +22,7 @@ export abstract class PikkuAbstractChannelHandler<
|
|
|
23
22
|
|
|
24
23
|
public getChannel(): PikkuChannel<OpeningData, Out> {
|
|
25
24
|
if (!this.channel) {
|
|
25
|
+
let channelState: unknown
|
|
26
26
|
this.channel = {
|
|
27
27
|
channelId: this.channelId,
|
|
28
28
|
openingData: this.openingData,
|
|
@@ -30,6 +30,13 @@ export abstract class PikkuAbstractChannelHandler<
|
|
|
30
30
|
sendBinary: this.sendBinary.bind(this),
|
|
31
31
|
close: this.close.bind(this),
|
|
32
32
|
state: 'initial',
|
|
33
|
+
setState: (s) => {
|
|
34
|
+
channelState = s
|
|
35
|
+
},
|
|
36
|
+
getState: () => channelState as any,
|
|
37
|
+
clearState: () => {
|
|
38
|
+
channelState = undefined
|
|
39
|
+
},
|
|
33
40
|
}
|
|
34
41
|
}
|
|
35
42
|
return this.channel
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
CoreUserSession,
|
|
3
|
+
PikkuWire,
|
|
4
|
+
WireServices,
|
|
5
|
+
} from '../../../types/core.types.js'
|
|
2
6
|
import { closeWireServices } from '../../../utils.js'
|
|
3
7
|
import { processMessageHandlers } from '../channel-handler.js'
|
|
4
8
|
import { openChannel } from '../channel-runner.js'
|
|
@@ -24,8 +28,9 @@ import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js'
|
|
|
24
28
|
import type { PikkuHTTP } from '../../http/http.types.js'
|
|
25
29
|
import { runChannelLifecycleWithMiddleware } from '../channel-common.js'
|
|
26
30
|
|
|
27
|
-
export interface RunServerlessChannelParams<
|
|
28
|
-
|
|
31
|
+
export interface RunServerlessChannelParams<
|
|
32
|
+
ChannelData,
|
|
33
|
+
> extends RunChannelParams<ChannelData> {
|
|
29
34
|
channelStore: ChannelStore
|
|
30
35
|
channelHandlerFactory: PikkuChannelHandlerFactory
|
|
31
36
|
channelObject?: unknown
|
|
@@ -111,6 +116,9 @@ export const runChannelConnect = async ({
|
|
|
111
116
|
channelHandlerFactory,
|
|
112
117
|
channelName: channelConfig.name,
|
|
113
118
|
})
|
|
119
|
+
channel.setState = (state) => channelStore.setState(channelId, state)
|
|
120
|
+
channel.getState = () => channelStore.getState(channelId) as any
|
|
121
|
+
channel.clearState = () => channelStore.clearState(channelId)
|
|
114
122
|
|
|
115
123
|
const wire: PikkuWire = {
|
|
116
124
|
channel,
|
|
@@ -193,8 +201,13 @@ export const runChannelDisconnect = async ({
|
|
|
193
201
|
openingData,
|
|
194
202
|
channelName,
|
|
195
203
|
})
|
|
204
|
+
channel.setState = (state) => channelStore.setState(channelId, state)
|
|
205
|
+
channel.getState = () => channelStore.getState(channelId) as any
|
|
206
|
+
channel.clearState = () => channelStore.clearState(channelId)
|
|
196
207
|
|
|
197
|
-
const userSession = new PikkuSessionService(
|
|
208
|
+
const userSession = new PikkuSessionService<CoreUserSession>(
|
|
209
|
+
singletonServices.sessionStore
|
|
210
|
+
)
|
|
198
211
|
if (pikkuUserId) {
|
|
199
212
|
userSession.setPikkuUserId(pikkuUserId)
|
|
200
213
|
const session = await singletonServices.sessionStore?.get(pikkuUserId)
|
|
@@ -260,8 +273,13 @@ export const runChannelMessage = async (
|
|
|
260
273
|
openingData,
|
|
261
274
|
channelName,
|
|
262
275
|
})
|
|
276
|
+
channel.setState = (state) => channelStore.setState(channelId, state)
|
|
277
|
+
channel.getState = () => channelStore.getState(channelId) as any
|
|
278
|
+
channel.clearState = () => channelStore.clearState(channelId)
|
|
263
279
|
|
|
264
|
-
const userSession = new PikkuSessionService(
|
|
280
|
+
const userSession = new PikkuSessionService<CoreUserSession>(
|
|
281
|
+
singletonServices.sessionStore
|
|
282
|
+
)
|
|
265
283
|
if (pikkuUserId) {
|
|
266
284
|
userSession.setPikkuUserId(pikkuUserId)
|
|
267
285
|
const session = await singletonServices.sessionStore?.get(pikkuUserId)
|
|
@@ -2,7 +2,10 @@ import { pikkuState } from '../../../pikku-state.js'
|
|
|
2
2
|
import type { CLIMeta } from '../cli.types.js'
|
|
3
3
|
import { parseCLIArguments, generateCommandHelp } from '../command-parser.js'
|
|
4
4
|
import { runCLICommand } from '../cli-runner.js'
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
CoreSingletonServices,
|
|
7
|
+
CreateWireServices,
|
|
8
|
+
} from '../../../types/core.types.js'
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Handles raw CLI input from a WebSocket channel.
|
|
@@ -57,7 +60,9 @@ export async function handleRawCLI({
|
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
// Check if the resolved command has a pikkuFuncId (is executable)
|
|
60
|
-
let current:
|
|
63
|
+
let current:
|
|
64
|
+
| { pikkuFuncId?: string; subcommands?: Record<string, any> }
|
|
65
|
+
| undefined = programMeta.commands[parsed.commandPath[0]]
|
|
61
66
|
for (let i = 1; i < parsed.commandPath.length; i++) {
|
|
62
67
|
current = current?.subcommands?.[parsed.commandPath[i]]
|
|
63
68
|
}
|