@pikku/core 0.9.5 → 0.9.6
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 +9 -0
- package/dist/function/function-runner.d.ts +2 -2
- package/dist/function/function-runner.js +3 -3
- package/dist/function/functions.types.d.ts +4 -0
- package/dist/function/functions.types.js +6 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/middleware-runner.d.ts +5 -4
- package/dist/middleware-runner.js +20 -6
- package/dist/permissions.d.ts +3 -3
- package/dist/permissions.js +30 -8
- package/dist/pikku-state.d.ts +2 -4
- package/dist/pikku-state.js +3 -2
- package/dist/schema.js +8 -6
- package/dist/types/core.types.d.ts +4 -0
- package/dist/types/core.types.js +6 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +17 -0
- package/dist/wirings/channel/channel-handler.js +2 -1
- package/dist/wirings/channel/channel-runner.d.ts +2 -0
- package/dist/wirings/channel/channel-runner.js +23 -25
- package/dist/wirings/channel/local/local-channel-runner.js +5 -3
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +4 -2
- package/dist/wirings/http/http-runner.js +36 -53
- package/dist/wirings/http/http.types.d.ts +1 -1
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
- package/dist/wirings/http/routers/http-router.d.ts +12 -0
- package/dist/wirings/http/routers/http-router.js +2 -0
- package/dist/wirings/http/routers/path-to-regex.d.ts +10 -0
- package/dist/wirings/http/routers/path-to-regex.js +118 -0
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.js +3 -2
- package/dist/wirings/queue/queue-runner.js +3 -2
- package/dist/wirings/rpc/rpc-runner.js +2 -1
- package/dist/wirings/scheduler/scheduler-runner.d.ts +1 -1
- package/dist/wirings/scheduler/scheduler-runner.js +3 -2
- package/package.json +1 -1
- package/src/factory-functions.test.ts +37 -0
- package/src/function/function-runner.test.ts +101 -52
- package/src/function/function-runner.ts +5 -2
- package/src/function/functions.types.ts +13 -0
- package/src/index.ts +3 -11
- package/src/middleware-runner.test.ts +71 -43
- package/src/middleware-runner.ts +43 -17
- package/src/permissions.test.ts +21 -17
- package/src/permissions.ts +68 -26
- package/src/pikku-state.ts +5 -3
- package/src/schema.ts +8 -6
- package/src/types/core.types.ts +12 -0
- package/src/utils.ts +19 -0
- package/src/wirings/channel/channel-handler.ts +17 -11
- package/src/wirings/channel/channel-runner.ts +31 -25
- package/src/wirings/channel/local/local-channel-runner.test.ts +4 -0
- package/src/wirings/channel/local/local-channel-runner.ts +5 -4
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +13 -11
- package/src/wirings/http/http-runner.test.ts +18 -6
- package/src/wirings/http/http-runner.ts +55 -74
- package/src/wirings/http/http.types.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-request.ts +3 -0
- package/src/wirings/http/routers/http-router.ts +15 -0
- package/src/wirings/http/routers/path-to-regex.test.ts +309 -0
- package/src/wirings/http/routers/path-to-regex.ts +162 -0
- package/src/wirings/mcp/mcp-runner.ts +18 -12
- package/src/wirings/queue/queue-runner.ts +15 -7
- package/src/wirings/rpc/rpc-runner.ts +21 -16
- package/src/wirings/scheduler/scheduler-runner.ts +18 -12
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/pikku-function.d.ts +0 -1
- package/dist/pikku-function.js +0 -1
- package/src/pikku-function.ts +0 -1
package/src/permissions.test.ts
CHANGED
|
@@ -6,7 +6,11 @@ import {
|
|
|
6
6
|
runPermissions,
|
|
7
7
|
} from './permissions.js'
|
|
8
8
|
import { resetPikkuState } from './pikku-state.js'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
CoreServices,
|
|
11
|
+
CoreUserSession,
|
|
12
|
+
PikkuWiringTypes,
|
|
13
|
+
} from './types/core.types.js'
|
|
10
14
|
import { CorePermissionGroup } from './function/functions.types.js'
|
|
11
15
|
|
|
12
16
|
beforeEach(() => {
|
|
@@ -160,7 +164,7 @@ describe('getPermissionsForTags', () => {
|
|
|
160
164
|
describe('runPermissions', () => {
|
|
161
165
|
test('should pass when no permissions are defined', async () => {
|
|
162
166
|
// Should not throw
|
|
163
|
-
await runPermissions({
|
|
167
|
+
await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
164
168
|
allServices: mockServices,
|
|
165
169
|
data: {},
|
|
166
170
|
session: mockSession,
|
|
@@ -176,7 +180,7 @@ describe('runPermissions', () => {
|
|
|
176
180
|
|
|
177
181
|
addPermission('wiringTag', [wiringTagPermission])
|
|
178
182
|
|
|
179
|
-
await runPermissions({
|
|
183
|
+
await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
180
184
|
wiringTags: ['wiringTag'],
|
|
181
185
|
allServices: mockServices,
|
|
182
186
|
data: {},
|
|
@@ -219,7 +223,7 @@ describe('runPermissions', () => {
|
|
|
219
223
|
],
|
|
220
224
|
}
|
|
221
225
|
|
|
222
|
-
await runPermissions({
|
|
226
|
+
await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
223
227
|
wiringTags: ['wiringTag'],
|
|
224
228
|
wiringPermissions,
|
|
225
229
|
funcTags: ['funcTag'],
|
|
@@ -245,7 +249,7 @@ describe('runPermissions', () => {
|
|
|
245
249
|
addPermission('atLeastOneTestTag', [failingPermission, passingPermission])
|
|
246
250
|
|
|
247
251
|
// Should not throw because at least one permission passes
|
|
248
|
-
await runPermissions({
|
|
252
|
+
await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
249
253
|
wiringTags: ['atLeastOneTestTag'],
|
|
250
254
|
allServices: mockServices,
|
|
251
255
|
data: {},
|
|
@@ -260,14 +264,14 @@ describe('runPermissions', () => {
|
|
|
260
264
|
addPermission('allFailTestTag', [failingPermission1, failingPermission2])
|
|
261
265
|
|
|
262
266
|
await assert.rejects(
|
|
263
|
-
runPermissions({
|
|
267
|
+
runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
264
268
|
wiringTags: ['allFailTestTag'],
|
|
265
269
|
allServices: mockServices,
|
|
266
270
|
data: {},
|
|
267
271
|
session: mockSession,
|
|
268
272
|
}),
|
|
269
273
|
{
|
|
270
|
-
message: 'Permission denied
|
|
274
|
+
message: 'Permission denied',
|
|
271
275
|
}
|
|
272
276
|
)
|
|
273
277
|
})
|
|
@@ -278,14 +282,14 @@ describe('runPermissions', () => {
|
|
|
278
282
|
}
|
|
279
283
|
|
|
280
284
|
await assert.rejects(
|
|
281
|
-
runPermissions({
|
|
285
|
+
runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
282
286
|
wiringPermissions,
|
|
283
287
|
allServices: mockServices,
|
|
284
288
|
data: {},
|
|
285
289
|
session: mockSession,
|
|
286
290
|
}),
|
|
287
291
|
{
|
|
288
|
-
message: 'Permission denied
|
|
292
|
+
message: 'Permission denied',
|
|
289
293
|
}
|
|
290
294
|
)
|
|
291
295
|
})
|
|
@@ -296,14 +300,14 @@ describe('runPermissions', () => {
|
|
|
296
300
|
addPermission('funcTag', [failingPermission])
|
|
297
301
|
|
|
298
302
|
await assert.rejects(
|
|
299
|
-
runPermissions({
|
|
303
|
+
runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
300
304
|
funcTags: ['funcTag'],
|
|
301
305
|
allServices: mockServices,
|
|
302
306
|
data: {},
|
|
303
307
|
session: mockSession,
|
|
304
308
|
}),
|
|
305
309
|
{
|
|
306
|
-
message: 'Permission denied
|
|
310
|
+
message: 'Permission denied',
|
|
307
311
|
}
|
|
308
312
|
)
|
|
309
313
|
})
|
|
@@ -314,14 +318,14 @@ describe('runPermissions', () => {
|
|
|
314
318
|
}
|
|
315
319
|
|
|
316
320
|
await assert.rejects(
|
|
317
|
-
runPermissions({
|
|
321
|
+
runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
318
322
|
funcPermissions,
|
|
319
323
|
allServices: mockServices,
|
|
320
324
|
data: {},
|
|
321
325
|
session: mockSession,
|
|
322
326
|
}),
|
|
323
327
|
{
|
|
324
|
-
message: 'Permission denied
|
|
328
|
+
message: 'Permission denied',
|
|
325
329
|
}
|
|
326
330
|
)
|
|
327
331
|
})
|
|
@@ -346,7 +350,7 @@ describe('runPermissions', () => {
|
|
|
346
350
|
}
|
|
347
351
|
|
|
348
352
|
await assert.rejects(
|
|
349
|
-
runPermissions({
|
|
353
|
+
runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
350
354
|
wiringTags: ['failingWiringTag'],
|
|
351
355
|
wiringPermissions,
|
|
352
356
|
allServices: mockServices,
|
|
@@ -365,7 +369,7 @@ describe('runPermissions', () => {
|
|
|
365
369
|
addPermission('arrayTestTag', arrayPermission)
|
|
366
370
|
|
|
367
371
|
// Should not throw because verifyPermissions handles array properly
|
|
368
|
-
await runPermissions({
|
|
372
|
+
await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
369
373
|
wiringTags: ['arrayTestTag'],
|
|
370
374
|
allServices: mockServices,
|
|
371
375
|
data: {},
|
|
@@ -381,7 +385,7 @@ describe('runPermissions', () => {
|
|
|
381
385
|
addPermission('objectTestTag', permissionGroup)
|
|
382
386
|
|
|
383
387
|
// Should not throw because verifyPermissions handles objects properly
|
|
384
|
-
await runPermissions({
|
|
388
|
+
await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
385
389
|
wiringTags: ['objectTestTag'],
|
|
386
390
|
allServices: mockServices,
|
|
387
391
|
data: {},
|
|
@@ -405,7 +409,7 @@ describe('runPermissions', () => {
|
|
|
405
409
|
|
|
406
410
|
addPermission('paramTestTag', [testPermission])
|
|
407
411
|
|
|
408
|
-
await runPermissions({
|
|
412
|
+
await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
|
|
409
413
|
wiringTags: ['paramTestTag'],
|
|
410
414
|
allServices: mockServices,
|
|
411
415
|
data: testData,
|
package/src/permissions.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CoreServices,
|
|
3
|
+
CoreUserSession,
|
|
4
|
+
PikkuWiringTypes,
|
|
5
|
+
} from './types/core.types.js'
|
|
2
6
|
import {
|
|
3
7
|
CorePermissionGroup,
|
|
4
8
|
CorePikkuPermission,
|
|
@@ -86,6 +90,8 @@ export const addPermission = (
|
|
|
86
90
|
permissionsStore[tag] = permissions
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
const EMPTY: readonly (CorePermissionGroup | CorePikkuPermission)[] = []
|
|
94
|
+
|
|
89
95
|
/**
|
|
90
96
|
* Retrieves permissions for a given set of tags.
|
|
91
97
|
*
|
|
@@ -103,9 +109,9 @@ export const addPermission = (
|
|
|
103
109
|
*/
|
|
104
110
|
export const getPermissionsForTags = (
|
|
105
111
|
tags?: string[]
|
|
106
|
-
):
|
|
112
|
+
): readonly (CorePermissionGroup | CorePikkuPermission)[] => {
|
|
107
113
|
if (!tags || tags.length === 0) {
|
|
108
|
-
return
|
|
114
|
+
return EMPTY
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
const permissionsStore = pikkuState('misc', 'permissions')
|
|
@@ -128,6 +134,26 @@ export const getPermissionsForTags = (
|
|
|
128
134
|
return applicablePermissions
|
|
129
135
|
}
|
|
130
136
|
|
|
137
|
+
const permissionCache: Record<
|
|
138
|
+
PikkuWiringTypes,
|
|
139
|
+
Partial<
|
|
140
|
+
Record<
|
|
141
|
+
string,
|
|
142
|
+
{
|
|
143
|
+
wiringTags: readonly (CorePermissionGroup | CorePikkuPermission)[]
|
|
144
|
+
funcTags: readonly (CorePermissionGroup | CorePikkuPermission)[]
|
|
145
|
+
}
|
|
146
|
+
>
|
|
147
|
+
>
|
|
148
|
+
> = {
|
|
149
|
+
[PikkuWiringTypes.http]: {},
|
|
150
|
+
[PikkuWiringTypes.rpc]: {},
|
|
151
|
+
[PikkuWiringTypes.channel]: {},
|
|
152
|
+
[PikkuWiringTypes.queue]: {},
|
|
153
|
+
[PikkuWiringTypes.scheduler]: {},
|
|
154
|
+
[PikkuWiringTypes.mcp]: {},
|
|
155
|
+
}
|
|
156
|
+
|
|
131
157
|
/**
|
|
132
158
|
* Runs permission checks in the specified order:
|
|
133
159
|
* 1) wiring tag permissions - at least one must pass if any exist
|
|
@@ -135,27 +161,39 @@ export const getPermissionsForTags = (
|
|
|
135
161
|
* 3) function tag permissions - at least one must pass if any exist
|
|
136
162
|
* 4) function permissions - must pass if defined
|
|
137
163
|
*/
|
|
138
|
-
export const runPermissions = async (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
export const runPermissions = async (
|
|
165
|
+
wireType: PikkuWiringTypes,
|
|
166
|
+
uid: string,
|
|
167
|
+
{
|
|
168
|
+
wiringTags,
|
|
169
|
+
wiringPermissions,
|
|
170
|
+
funcTags,
|
|
171
|
+
funcPermissions,
|
|
172
|
+
allServices,
|
|
173
|
+
data,
|
|
174
|
+
session,
|
|
175
|
+
}: {
|
|
176
|
+
wiringTags?: string[]
|
|
177
|
+
wiringPermissions?: CorePermissionGroup
|
|
178
|
+
funcTags?: string[]
|
|
179
|
+
funcPermissions?: CorePermissionGroup
|
|
180
|
+
allServices: CoreServices
|
|
181
|
+
data: any
|
|
182
|
+
session?: CoreUserSession
|
|
183
|
+
}
|
|
184
|
+
) => {
|
|
185
|
+
let cachedPermission = permissionCache[wireType][uid]
|
|
186
|
+
if (!cachedPermission) {
|
|
187
|
+
cachedPermission = {
|
|
188
|
+
wiringTags: getPermissionsForTags(wiringTags),
|
|
189
|
+
funcTags: getPermissionsForTags(funcTags),
|
|
190
|
+
}
|
|
191
|
+
permissionCache[wireType][uid] = cachedPermission
|
|
192
|
+
}
|
|
155
193
|
let permissioned = true
|
|
156
194
|
|
|
157
195
|
// 1. Wiring tag permissions - at least one must pass if any exist
|
|
158
|
-
const wiringTaggedPermissions =
|
|
196
|
+
const wiringTaggedPermissions = cachedPermission.wiringTags
|
|
159
197
|
if (wiringTaggedPermissions.length > 0) {
|
|
160
198
|
permissioned = false // Start false, need at least one to pass
|
|
161
199
|
for (const permissions of wiringTaggedPermissions) {
|
|
@@ -171,7 +209,8 @@ export const runPermissions = async ({
|
|
|
171
209
|
}
|
|
172
210
|
}
|
|
173
211
|
if (!permissioned) {
|
|
174
|
-
|
|
212
|
+
allServices.logger.debug('Permission denied - wiring tag permissions')
|
|
213
|
+
throw new ForbiddenError('Permission denied')
|
|
175
214
|
}
|
|
176
215
|
}
|
|
177
216
|
|
|
@@ -184,12 +223,13 @@ export const runPermissions = async ({
|
|
|
184
223
|
session
|
|
185
224
|
)
|
|
186
225
|
if (!permissioned) {
|
|
187
|
-
|
|
226
|
+
allServices.logger.debug('Permission denied - wiring permissions')
|
|
227
|
+
throw new ForbiddenError('Permission denied')
|
|
188
228
|
}
|
|
189
229
|
}
|
|
190
230
|
|
|
191
231
|
// 3. Function tag permissions - at least one must pass if any exist
|
|
192
|
-
const funcTaggedPermissions =
|
|
232
|
+
const funcTaggedPermissions = cachedPermission.funcTags
|
|
193
233
|
if (funcTaggedPermissions.length > 0) {
|
|
194
234
|
permissioned = false // Start false, need at least one to pass
|
|
195
235
|
for (const permissions of funcTaggedPermissions) {
|
|
@@ -205,7 +245,8 @@ export const runPermissions = async ({
|
|
|
205
245
|
}
|
|
206
246
|
}
|
|
207
247
|
if (!permissioned) {
|
|
208
|
-
|
|
248
|
+
allServices.logger.debug('Permission denied - function tag permissions')
|
|
249
|
+
throw new ForbiddenError('Permission denied')
|
|
209
250
|
}
|
|
210
251
|
}
|
|
211
252
|
|
|
@@ -218,7 +259,8 @@ export const runPermissions = async ({
|
|
|
218
259
|
session
|
|
219
260
|
)
|
|
220
261
|
if (!permissioned) {
|
|
221
|
-
|
|
262
|
+
allServices.logger.debug('Permission denied - function permissions')
|
|
263
|
+
throw new ForbiddenError('Permission denied')
|
|
222
264
|
}
|
|
223
265
|
}
|
|
224
266
|
}
|
package/src/pikku-state.ts
CHANGED
|
@@ -44,7 +44,8 @@ interface PikkuState {
|
|
|
44
44
|
>
|
|
45
45
|
}
|
|
46
46
|
http: {
|
|
47
|
-
middleware:
|
|
47
|
+
middleware: Map<string, CorePikkuMiddleware<any, any>[]>
|
|
48
|
+
permissions: Map<string, CorePermissionGroup | CorePikkuPermission[]>
|
|
48
49
|
routes: Map<HTTPMethod, Map<string, CoreHTTPFunctionWiring<any, any, any>>>
|
|
49
50
|
meta: HTTPWiringsMeta
|
|
50
51
|
}
|
|
@@ -87,9 +88,10 @@ export const resetPikkuState = () => {
|
|
|
87
88
|
files: new Map(),
|
|
88
89
|
},
|
|
89
90
|
http: {
|
|
90
|
-
|
|
91
|
+
permissions: new Map(),
|
|
92
|
+
middleware: new Map(),
|
|
91
93
|
routes: new Map(),
|
|
92
|
-
meta:
|
|
94
|
+
meta: {},
|
|
93
95
|
},
|
|
94
96
|
channel: {
|
|
95
97
|
channels: new Map(),
|
package/src/schema.ts
CHANGED
|
@@ -40,13 +40,15 @@ const validateAllSchemasLoaded = (
|
|
|
40
40
|
|
|
41
41
|
const missingSchemas: string[] = []
|
|
42
42
|
|
|
43
|
-
for (const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
for (const routePaths of Object.values(routesMeta)) {
|
|
44
|
+
for (const meta of Object.values(routePaths)) {
|
|
45
|
+
const inputs = pikkuState('function', 'meta')[meta.pikkuFuncName]?.inputs
|
|
46
|
+
const input = inputs?.[0]
|
|
47
|
+
if (!input || validators.has(input)) {
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
50
|
+
missingSchemas.push(input)
|
|
48
51
|
}
|
|
49
|
-
missingSchemas.push(input)
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
if (missingSchemas.length > 0) {
|
package/src/types/core.types.ts
CHANGED
|
@@ -137,6 +137,18 @@ export type CorePikkuMiddleware<
|
|
|
137
137
|
next: () => Promise<void>
|
|
138
138
|
) => Promise<void>
|
|
139
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Factory function for creating middleware with tree-shaking support
|
|
142
|
+
*/
|
|
143
|
+
export const pikkuMiddleware = <
|
|
144
|
+
SingletonServices extends CoreSingletonServices = CoreSingletonServices,
|
|
145
|
+
UserSession extends CoreUserSession = CoreUserSession,
|
|
146
|
+
>(
|
|
147
|
+
middleware: CorePikkuMiddleware<SingletonServices, UserSession>
|
|
148
|
+
): CorePikkuMiddleware<SingletonServices, UserSession> => {
|
|
149
|
+
return middleware
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
/**
|
|
141
153
|
* Represents the core services used by Pikku, including singleton services and the request/response interaction.
|
|
142
154
|
*/
|
package/src/utils.ts
CHANGED
|
@@ -38,3 +38,22 @@ export const isSerializable = (data: any): boolean => {
|
|
|
38
38
|
data instanceof Float64Array
|
|
39
39
|
)
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
const EMPTY_ARRAY = Object.freeze([])
|
|
43
|
+
|
|
44
|
+
export const freezeDedupe = <T extends Function>(
|
|
45
|
+
arr?: readonly T[] | T[] | undefined
|
|
46
|
+
): readonly T[] => {
|
|
47
|
+
if (!arr || arr.length === 0) return EMPTY_ARRAY
|
|
48
|
+
if (arr.length === 1) return Object.freeze([arr[0]!])
|
|
49
|
+
const seen = new Set<T>()
|
|
50
|
+
const out: T[] = []
|
|
51
|
+
for (let i = 0; i < arr.length; i++) {
|
|
52
|
+
const fn = arr[i]!
|
|
53
|
+
if (!seen.has(fn)) {
|
|
54
|
+
seen.add(fn)
|
|
55
|
+
out.push(fn)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return Object.freeze(out)
|
|
59
|
+
}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
CoreServices,
|
|
3
3
|
JSONValue,
|
|
4
4
|
CoreUserSession,
|
|
5
|
+
PikkuWiringTypes,
|
|
5
6
|
} from '../../types/core.types.js'
|
|
6
7
|
import {
|
|
7
8
|
ChannelMessageMeta,
|
|
@@ -98,17 +99,22 @@ export const processMessageHandlers = (
|
|
|
98
99
|
const middleware =
|
|
99
100
|
typeof onMessage === 'function' ? [] : onMessage.middleware
|
|
100
101
|
|
|
101
|
-
return await runPikkuFunc(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
return await runPikkuFunc(
|
|
103
|
+
PikkuWiringTypes.channel,
|
|
104
|
+
channelConfig.name,
|
|
105
|
+
pikkuFuncName,
|
|
106
|
+
{
|
|
107
|
+
getAllServices: () => ({
|
|
108
|
+
...services,
|
|
109
|
+
channel: channelHandler.getChannel(),
|
|
110
|
+
}),
|
|
111
|
+
data,
|
|
112
|
+
session,
|
|
113
|
+
permissions,
|
|
114
|
+
middleware,
|
|
115
|
+
tags: channelConfig.tags,
|
|
116
|
+
}
|
|
117
|
+
)
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
const onMessage = async (rawData): Promise<unknown> => {
|
|
@@ -3,13 +3,14 @@ import { addFunction } from '../../function/function-runner.js'
|
|
|
3
3
|
import { pikkuState } from '../../pikku-state.js'
|
|
4
4
|
import { coerceTopLevelDataFromSchema, validateSchema } from '../../schema.js'
|
|
5
5
|
import { UserSessionService } from '../../services/user-session-service.js'
|
|
6
|
+
import { CorePikkuMiddleware } from '../../types/core.types.js'
|
|
7
|
+
import { httpRouter } from '../http/routers/http-router.js'
|
|
6
8
|
import {
|
|
7
9
|
ChannelMeta,
|
|
8
10
|
CoreChannel,
|
|
9
11
|
RunChannelOptions,
|
|
10
12
|
RunChannelParams,
|
|
11
13
|
} from './channel.types.js'
|
|
12
|
-
import { match } from 'path-to-regexp'
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Adds a channel and registers all functions referenced in it using the
|
|
@@ -91,30 +92,33 @@ export const wireChannel = <
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
const getMatchingChannelConfig = (request: string) => {
|
|
95
|
+
const matchedPath = httpRouter.match('get', request)
|
|
96
|
+
if (!matchedPath) {
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const meta = pikkuState('channel', 'meta')
|
|
101
|
+
const channelMeta = Object.values(meta).find(
|
|
102
|
+
(channelConfig) => channelConfig.route === matchedPath.route
|
|
103
|
+
)
|
|
104
|
+
if (!channelMeta) {
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
|
|
94
108
|
const channels = pikkuState('channel', 'channels')
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
return {
|
|
109
|
-
matchedPath,
|
|
110
|
-
params: matchedPath.params,
|
|
111
|
-
channelConfig,
|
|
112
|
-
schemaName: channelMeta.input,
|
|
113
|
-
meta: channelMeta,
|
|
114
|
-
}
|
|
115
|
-
}
|
|
109
|
+
const channelConfig = channels.get(channelMeta.name)
|
|
110
|
+
if (!channelConfig) {
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
matchedPath,
|
|
116
|
+
params: matchedPath.params,
|
|
117
|
+
channelConfig,
|
|
118
|
+
schemaName: channelMeta.input,
|
|
119
|
+
meta: channelMeta,
|
|
120
|
+
httpMiddleware: matchedPath.middleware,
|
|
116
121
|
}
|
|
117
|
-
return null
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
export const openChannel = async ({
|
|
@@ -129,6 +133,7 @@ export const openChannel = async ({
|
|
|
129
133
|
openingData: unknown
|
|
130
134
|
channelConfig: CoreChannel<unknown, any>
|
|
131
135
|
meta: ChannelMeta
|
|
136
|
+
httpMiddleware: CorePikkuMiddleware[] | undefined
|
|
132
137
|
}> => {
|
|
133
138
|
const matchingChannel = getMatchingChannelConfig(route)
|
|
134
139
|
if (!matchingChannel) {
|
|
@@ -136,7 +141,8 @@ export const openChannel = async ({
|
|
|
136
141
|
throw new NotFoundError(`Channel not found: ${route}`)
|
|
137
142
|
}
|
|
138
143
|
|
|
139
|
-
const { params, channelConfig, schemaName, meta } =
|
|
144
|
+
const { params, channelConfig, schemaName, meta, httpMiddleware } =
|
|
145
|
+
matchingChannel
|
|
140
146
|
|
|
141
147
|
const requiresSession = channelConfig.auth !== false
|
|
142
148
|
request?.setParams(params)
|
|
@@ -159,5 +165,5 @@ export const openChannel = async ({
|
|
|
159
165
|
)
|
|
160
166
|
}
|
|
161
167
|
|
|
162
|
-
return { openingData, channelConfig, meta }
|
|
168
|
+
return { openingData, channelConfig, meta, httpMiddleware }
|
|
163
169
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
PikkuQuery,
|
|
11
11
|
} from '../../http/http.types.js'
|
|
12
12
|
import { SerializeOptions } from 'cookie'
|
|
13
|
+
import { httpRouter } from '../../http/routers/http-router.js'
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Minimal stubs for dependencies that runChannel expects.
|
|
@@ -139,6 +140,9 @@ test('runChannel should return a channel handler if channel matches and no auth
|
|
|
139
140
|
auth: false,
|
|
140
141
|
})
|
|
141
142
|
|
|
143
|
+
// Initialize router after adding channel (for tests)
|
|
144
|
+
httpRouter.initialize()
|
|
145
|
+
|
|
142
146
|
const result = await runLocalChannel({
|
|
143
147
|
singletonServices: mockSingletonServices,
|
|
144
148
|
channelId: 'test-channel-id',
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
RunChannelParams,
|
|
9
9
|
} from '../channel.types.js'
|
|
10
10
|
import { PikkuLocalChannelHandler } from './local-channel-handler.js'
|
|
11
|
-
import { SessionServices } from '../../../types/core.types.js'
|
|
11
|
+
import { PikkuWiringTypes, SessionServices } from '../../../types/core.types.js'
|
|
12
12
|
import { handleHTTPError } from '../../../handle-error.js'
|
|
13
13
|
import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js'
|
|
14
14
|
import { PikkuUserSessionService } from '../../../services/user-session-service.js'
|
|
@@ -42,9 +42,9 @@ export const runLocalChannel = async ({
|
|
|
42
42
|
route = http?.request?.path()
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
let openingData, channelConfig, meta
|
|
45
|
+
let openingData, channelConfig, meta, httpMiddleware
|
|
46
46
|
try {
|
|
47
|
-
;({ openingData, channelConfig, meta } = await openChannel({
|
|
47
|
+
;({ openingData, channelConfig, meta, httpMiddleware } = await openChannel({
|
|
48
48
|
channelId,
|
|
49
49
|
createSessionServices,
|
|
50
50
|
respondWith404,
|
|
@@ -146,9 +146,10 @@ export const runLocalChannel = async ({
|
|
|
146
146
|
userSession,
|
|
147
147
|
},
|
|
148
148
|
{ http },
|
|
149
|
-
combineMiddleware({
|
|
149
|
+
combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
|
|
150
150
|
wiringMiddleware: channelConfig.middleware,
|
|
151
151
|
wiringTags: channelConfig.tags,
|
|
152
|
+
httpMiddleware,
|
|
152
153
|
}),
|
|
153
154
|
main
|
|
154
155
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SessionServices } from '../../../types/core.types.js'
|
|
1
|
+
import { PikkuWiringTypes, SessionServices } from '../../../types/core.types.js'
|
|
2
2
|
import { closeSessionServices } from '../../../utils.js'
|
|
3
3
|
import { processMessageHandlers } from '../channel-handler.js'
|
|
4
4
|
import { openChannel } from '../channel-runner.js'
|
|
@@ -85,15 +85,16 @@ export const runChannelConnect = async ({
|
|
|
85
85
|
|
|
86
86
|
const userSession = new PikkuUserSessionService(channelStore, channelId)
|
|
87
87
|
|
|
88
|
-
const { channelConfig, openingData, meta } =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
const { channelConfig, openingData, meta, httpMiddleware } =
|
|
89
|
+
await openChannel({
|
|
90
|
+
channelId,
|
|
91
|
+
createSessionServices,
|
|
92
|
+
request,
|
|
93
|
+
route,
|
|
94
|
+
singletonServices,
|
|
95
|
+
coerceDataFromSchema,
|
|
96
|
+
userSession,
|
|
97
|
+
})
|
|
97
98
|
|
|
98
99
|
const main = async () => {
|
|
99
100
|
try {
|
|
@@ -146,9 +147,10 @@ export const runChannelConnect = async ({
|
|
|
146
147
|
userSession,
|
|
147
148
|
},
|
|
148
149
|
{ http },
|
|
149
|
-
combineMiddleware({
|
|
150
|
+
combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
|
|
150
151
|
wiringMiddleware: channelConfig.middleware,
|
|
151
152
|
wiringTags: channelConfig.tags,
|
|
153
|
+
httpMiddleware,
|
|
152
154
|
}),
|
|
153
155
|
main
|
|
154
156
|
)
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
PikkuMockResponse,
|
|
10
10
|
} from '../channel/local/local-channel-runner.test.js'
|
|
11
11
|
import { addFunction } from '../../function/function-runner.js'
|
|
12
|
+
import { httpRouter } from './routers/http-router.js'
|
|
12
13
|
|
|
13
14
|
const sessionMiddleware: CorePikkuMiddleware = async (services, _, next) => {
|
|
14
15
|
services.userSession.set({ userId: 'test' } as any)
|
|
@@ -22,13 +23,21 @@ const setHTTPFunctionMap = (func: any) => {
|
|
|
22
23
|
services: ['userSession'],
|
|
23
24
|
},
|
|
24
25
|
} as any)
|
|
25
|
-
pikkuState('http', 'meta',
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
pikkuState('http', 'meta', {
|
|
27
|
+
get: {
|
|
28
|
+
test: {
|
|
29
|
+
pikkuFuncName: 'pikku_func_name',
|
|
30
|
+
route: 'test',
|
|
31
|
+
method: 'get',
|
|
32
|
+
},
|
|
30
33
|
},
|
|
31
|
-
|
|
34
|
+
post: {},
|
|
35
|
+
delete: {},
|
|
36
|
+
patch: {},
|
|
37
|
+
head: {},
|
|
38
|
+
put: {},
|
|
39
|
+
options: {},
|
|
40
|
+
})
|
|
32
41
|
addFunction('pikku_func_name', { func })
|
|
33
42
|
}
|
|
34
43
|
|
|
@@ -85,6 +94,9 @@ describe('fetch', () => {
|
|
|
85
94
|
middleware: [sessionMiddleware],
|
|
86
95
|
})
|
|
87
96
|
|
|
97
|
+
// Initialize router after adding route (for tests)
|
|
98
|
+
httpRouter.initialize()
|
|
99
|
+
|
|
88
100
|
const result = await fetch(request, {
|
|
89
101
|
singletonServices,
|
|
90
102
|
createSessionServices,
|