@pikku/inspector 0.9.6-next.0 → 0.10.0
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 +6 -0
- package/dist/add/add-channel.d.ts +5 -1
- package/dist/add/add-channel.js +51 -32
- package/dist/add/add-cli.d.ts +4 -0
- package/dist/add/add-cli.js +128 -23
- package/dist/add/add-file-extends-core-type.js +3 -2
- package/dist/add/add-file-with-factory.d.ts +2 -2
- package/dist/add/add-file-with-factory.js +34 -1
- package/dist/add/add-functions.js +52 -5
- package/dist/add/add-http-route.js +19 -12
- package/dist/add/add-mcp-prompt.js +20 -13
- package/dist/add/add-mcp-resource.js +24 -14
- package/dist/add/add-mcp-tool.js +23 -13
- package/dist/add/add-middleware.js +51 -12
- package/dist/add/add-permission.d.ts +1 -2
- package/dist/add/add-permission.js +275 -19
- package/dist/add/add-queue-worker.js +10 -12
- package/dist/add/add-schedule.js +9 -10
- package/dist/error-codes.d.ts +35 -0
- package/dist/error-codes.js +40 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/inspector.js +20 -1
- package/dist/types.d.ts +31 -3
- package/dist/utils/ensure-function-metadata.d.ts +6 -0
- package/dist/utils/ensure-function-metadata.js +18 -0
- package/dist/utils/extract-function-name.d.ts +2 -2
- package/dist/utils/extract-function-name.js +13 -8
- package/dist/utils/filter-inspector-state.d.ts +6 -0
- package/dist/utils/filter-inspector-state.js +382 -0
- package/dist/utils/filter-utils.d.ts +10 -0
- package/dist/utils/filter-utils.js +66 -2
- package/dist/utils/find-root-dir.d.ts +23 -0
- package/dist/utils/find-root-dir.js +55 -0
- package/dist/utils/get-files-and-methods.d.ts +2 -1
- package/dist/utils/get-files-and-methods.js +2 -1
- package/dist/utils/get-property-value.d.ts +9 -0
- package/dist/utils/get-property-value.js +20 -0
- package/dist/utils/middleware.d.ts +1 -1
- package/dist/utils/middleware.js +7 -7
- package/dist/utils/permissions.d.ts +43 -0
- package/dist/utils/permissions.js +178 -0
- package/dist/utils/post-process.d.ts +16 -0
- package/dist/utils/post-process.js +132 -0
- package/dist/utils/serialize-inspector-state.d.ts +179 -0
- package/dist/utils/serialize-inspector-state.js +170 -0
- package/dist/visit.js +3 -2
- package/package.json +4 -4
- package/src/add/add-channel.ts +92 -40
- package/src/add/add-cli.ts +188 -29
- package/src/add/add-file-extends-core-type.ts +5 -2
- package/src/add/add-file-with-factory.ts +45 -2
- package/src/add/add-functions.ts +60 -5
- package/src/add/add-http-route.ts +46 -21
- package/src/add/add-mcp-prompt.ts +42 -21
- package/src/add/add-mcp-prompt.ts.tmp +0 -0
- package/src/add/add-mcp-resource.ts +50 -24
- package/src/add/add-mcp-resource.ts.tmp +0 -0
- package/src/add/add-mcp-tool.ts +48 -21
- package/src/add/add-middleware.ts +74 -15
- package/src/add/add-permission.ts +364 -22
- package/src/add/add-queue-worker.ts +22 -25
- package/src/add/add-schedule.ts +19 -20
- package/src/error-codes.ts +43 -0
- package/src/index.ts +7 -0
- package/src/inspector.ts +22 -1
- package/src/types.ts +38 -3
- package/src/utils/ensure-function-metadata.ts +24 -0
- package/src/utils/extract-function-name.ts +20 -8
- package/src/utils/filter-inspector-state.test.ts +1433 -0
- package/src/utils/filter-inspector-state.ts +526 -0
- package/src/utils/filter-utils.test.ts +350 -1
- package/src/utils/filter-utils.ts +82 -2
- package/src/utils/find-root-dir.ts +68 -0
- package/src/utils/get-files-and-methods.ts +8 -0
- package/src/utils/get-property-value.ts +27 -0
- package/src/utils/middleware.ts +14 -7
- package/src/utils/permissions.test.ts +327 -0
- package/src/utils/permissions.ts +262 -0
- package/src/utils/post-process.ts +178 -0
- package/src/utils/serialize-inspector-state.ts +375 -0
- package/src/utils/test-data/inspector-state.json +1680 -0
- package/src/visit.ts +4 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,49 +1,391 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
2
|
import { AddWiring } from '../types.js'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
extractFunctionName,
|
|
5
|
+
isNamedExport,
|
|
6
|
+
} from '../utils/extract-function-name.js'
|
|
4
7
|
import { extractServicesFromFunction } from '../utils/extract-services.js'
|
|
8
|
+
import { extractPermissionPikkuNames } from '../utils/permissions.js'
|
|
9
|
+
import { getPropertyValue } from '../utils/get-property-value.js'
|
|
10
|
+
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
5
11
|
|
|
6
12
|
/**
|
|
7
|
-
* Inspect pikkuPermission calls and
|
|
8
|
-
* for tree shaking optimization.
|
|
13
|
+
* Inspect pikkuPermission calls, addPermission calls, and addHTTPPermission calls
|
|
9
14
|
*/
|
|
10
15
|
export const addPermission: AddWiring = (logger, node, checker, state) => {
|
|
11
16
|
if (!ts.isCallExpression(node)) return
|
|
12
17
|
|
|
13
18
|
const { expression, arguments: args } = node
|
|
14
19
|
|
|
15
|
-
// only handle
|
|
20
|
+
// only handle specific function calls
|
|
16
21
|
if (!ts.isIdentifier(expression)) {
|
|
17
22
|
return
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
// Handle pikkuPermission(...) - individual permission function definition
|
|
26
|
+
if (expression.text === 'pikkuPermission') {
|
|
27
|
+
const arg = args[0]
|
|
28
|
+
if (!arg) return
|
|
29
|
+
|
|
30
|
+
let actualHandler: ts.ArrowFunction | ts.FunctionExpression
|
|
31
|
+
let name: string | undefined
|
|
32
|
+
let description: string | undefined
|
|
33
|
+
|
|
34
|
+
// Check if using object syntax: pikkuPermission({ func: ..., name: '...', description: '...' })
|
|
35
|
+
if (ts.isObjectLiteralExpression(arg)) {
|
|
36
|
+
// Extract name and description metadata
|
|
37
|
+
const nameValue = getPropertyValue(arg, 'name')
|
|
38
|
+
const descValue = getPropertyValue(arg, 'description')
|
|
39
|
+
name = typeof nameValue === 'string' ? nameValue : undefined
|
|
40
|
+
description = typeof descValue === 'string' ? descValue : undefined
|
|
41
|
+
|
|
42
|
+
// Extract the func property
|
|
43
|
+
const fnProp = getPropertyAssignmentInitializer(
|
|
44
|
+
arg,
|
|
45
|
+
'func',
|
|
46
|
+
true,
|
|
47
|
+
checker
|
|
48
|
+
)
|
|
49
|
+
if (
|
|
50
|
+
!fnProp ||
|
|
51
|
+
(!ts.isArrowFunction(fnProp) && !ts.isFunctionExpression(fnProp))
|
|
52
|
+
) {
|
|
53
|
+
logger.error(
|
|
54
|
+
`• pikkuPermission object missing required 'func' property.`
|
|
55
|
+
)
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
actualHandler = fnProp
|
|
59
|
+
} else if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
|
|
60
|
+
actualHandler = arg
|
|
61
|
+
} else {
|
|
62
|
+
logger.error(`• Handler for pikkuPermission is not a function.`)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const services = extractServicesFromFunction(actualHandler)
|
|
67
|
+
const { pikkuFuncName, exportedName } = extractFunctionName(
|
|
68
|
+
node,
|
|
69
|
+
checker,
|
|
70
|
+
state.rootDir
|
|
71
|
+
)
|
|
72
|
+
state.permissions.meta[pikkuFuncName] = {
|
|
73
|
+
services,
|
|
74
|
+
sourceFile: node.getSourceFile().fileName,
|
|
75
|
+
position: node.getStart(),
|
|
76
|
+
exportedName,
|
|
77
|
+
name,
|
|
78
|
+
description,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
logger.debug(
|
|
82
|
+
`• Found permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}`
|
|
83
|
+
)
|
|
21
84
|
return
|
|
22
85
|
}
|
|
23
86
|
|
|
24
|
-
|
|
25
|
-
if (
|
|
87
|
+
// Handle pikkuPermissionFactory(...) - permission factory function
|
|
88
|
+
if (expression.text === 'pikkuPermissionFactory') {
|
|
89
|
+
const factoryNode = args[0]
|
|
90
|
+
if (!factoryNode) return
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
!ts.isArrowFunction(factoryNode) &&
|
|
94
|
+
!ts.isFunctionExpression(factoryNode)
|
|
95
|
+
) {
|
|
96
|
+
logger.error(`• Handler for pikkuPermissionFactory is not a function.`)
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Extract services by looking inside the factory function body
|
|
101
|
+
// The factory should return pikkuPermission(...), so we need to find that call
|
|
102
|
+
// If no wrapper is found, extract from the factory's returned function directly
|
|
103
|
+
let services = { optimized: false, services: [] as string[] }
|
|
104
|
+
|
|
105
|
+
const findPikkuPermissionCall = (
|
|
106
|
+
node: ts.Node
|
|
107
|
+
): ts.CallExpression | undefined => {
|
|
108
|
+
if (ts.isCallExpression(node)) {
|
|
109
|
+
const expr = node.expression
|
|
110
|
+
if (ts.isIdentifier(expr) && expr.text === 'pikkuPermission') {
|
|
111
|
+
return node
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return ts.forEachChild(node, findPikkuPermissionCall)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const pikkuPermissionCall = findPikkuPermissionCall(factoryNode)
|
|
118
|
+
if (pikkuPermissionCall && pikkuPermissionCall.arguments[0]) {
|
|
119
|
+
const permissionHandler = pikkuPermissionCall.arguments[0]
|
|
120
|
+
if (
|
|
121
|
+
ts.isArrowFunction(permissionHandler) ||
|
|
122
|
+
ts.isFunctionExpression(permissionHandler)
|
|
123
|
+
) {
|
|
124
|
+
services = extractServicesFromFunction(permissionHandler)
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
// No pikkuPermission wrapper found - extract from factory's return value directly
|
|
128
|
+
// Factory pattern: (config) => (services, data, session) => { ... }
|
|
129
|
+
if (
|
|
130
|
+
ts.isArrowFunction(factoryNode) ||
|
|
131
|
+
ts.isFunctionExpression(factoryNode)
|
|
132
|
+
) {
|
|
133
|
+
const factoryBody = factoryNode.body
|
|
134
|
+
// Check if the body is an arrow function (direct return)
|
|
135
|
+
if (
|
|
136
|
+
ts.isArrowFunction(factoryBody) ||
|
|
137
|
+
ts.isFunctionExpression(factoryBody)
|
|
138
|
+
) {
|
|
139
|
+
services = extractServicesFromFunction(factoryBody)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const { pikkuFuncName, exportedName } = extractFunctionName(
|
|
145
|
+
node,
|
|
146
|
+
checker,
|
|
147
|
+
state.rootDir
|
|
148
|
+
)
|
|
149
|
+
state.permissions.meta[pikkuFuncName] = {
|
|
150
|
+
services,
|
|
151
|
+
sourceFile: node.getSourceFile().fileName,
|
|
152
|
+
position: node.getStart(),
|
|
153
|
+
exportedName,
|
|
154
|
+
factory: true,
|
|
155
|
+
}
|
|
26
156
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
) {
|
|
31
|
-
logger.error(`• Handler for pikkuPermission is not a function.`)
|
|
157
|
+
logger.debug(
|
|
158
|
+
`• Found permission factory with services: ${services.services.join(', ')}`
|
|
159
|
+
)
|
|
32
160
|
return
|
|
33
161
|
}
|
|
34
162
|
|
|
35
|
-
|
|
163
|
+
// Handle addPermission('tag', [permission1, permission2])
|
|
164
|
+
// Supports two patterns:
|
|
165
|
+
// 1. export const x = () => addPermission('tag', [...]) (factory - tree-shakeable)
|
|
166
|
+
// 2. export const x = addPermission('tag', [...]) (direct - no tree-shaking)
|
|
167
|
+
if (expression.text === 'addPermission') {
|
|
168
|
+
const tagArg = args[0]
|
|
169
|
+
const permissionsArrayArg = args[1]
|
|
170
|
+
|
|
171
|
+
if (!tagArg || !permissionsArrayArg) return
|
|
172
|
+
|
|
173
|
+
// Extract tag name
|
|
174
|
+
let tag: string | undefined
|
|
175
|
+
if (ts.isStringLiteral(tagArg)) {
|
|
176
|
+
tag = tagArg.text
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!tag) {
|
|
180
|
+
logger.warn(`• addPermission call without valid tag string`)
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Check if permissions is a literal array or object
|
|
185
|
+
if (
|
|
186
|
+
!ts.isArrayLiteralExpression(permissionsArrayArg) &&
|
|
187
|
+
!ts.isObjectLiteralExpression(permissionsArrayArg)
|
|
188
|
+
) {
|
|
189
|
+
logger.error(
|
|
190
|
+
`• addPermission('${tag}', ...) must have a literal array or object as second argument`
|
|
191
|
+
)
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Extract permission pikkuFuncNames from array
|
|
196
|
+
const permissionNames = extractPermissionPikkuNames(
|
|
197
|
+
permissionsArrayArg,
|
|
198
|
+
checker,
|
|
199
|
+
state.rootDir
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if (permissionNames.length === 0) {
|
|
203
|
+
logger.warn(`• addPermission('${tag}', ...) has empty permissions array`)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Collect services from all permissions in the group
|
|
208
|
+
const allServices = new Set<string>()
|
|
209
|
+
for (const permissionName of permissionNames) {
|
|
210
|
+
const permissionMeta = state.permissions.meta[permissionName]
|
|
211
|
+
if (permissionMeta && permissionMeta.services) {
|
|
212
|
+
for (const service of permissionMeta.services.services) {
|
|
213
|
+
allServices.add(service)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Check if this call is wrapped in a factory function
|
|
219
|
+
// We need to walk up the tree to see if the parent is: const x = () => addPermission(...)
|
|
220
|
+
let isFactory = false
|
|
221
|
+
let exportedName: string | null = null
|
|
222
|
+
let parent = node.parent
|
|
223
|
+
|
|
224
|
+
// Check if parent is arrow function: () => addPermission(...)
|
|
225
|
+
if (parent && ts.isArrowFunction(parent)) {
|
|
226
|
+
// Check if arrow function has no parameters
|
|
227
|
+
if (parent.parameters.length === 0) {
|
|
228
|
+
isFactory = true
|
|
229
|
+
|
|
230
|
+
// For factories, we need to check the arrow function's parent for the export name
|
|
231
|
+
// const apiTagPermissions = () => addPermission(...)
|
|
232
|
+
const arrowParent = parent.parent
|
|
233
|
+
if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
|
|
234
|
+
if (ts.isIdentifier(arrowParent.name)) {
|
|
235
|
+
// Check if it's exported
|
|
236
|
+
if (isNamedExport(arrowParent)) {
|
|
237
|
+
exportedName = arrowParent.name.text
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
36
243
|
|
|
37
|
-
|
|
244
|
+
// If not a factory, get export name from the call expression itself
|
|
245
|
+
if (!isFactory) {
|
|
246
|
+
const extracted = extractFunctionName(node, checker, state.rootDir)
|
|
247
|
+
exportedName = extracted.exportedName
|
|
248
|
+
}
|
|
38
249
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
250
|
+
// Log warning if not using factory pattern
|
|
251
|
+
if (!isFactory && exportedName) {
|
|
252
|
+
logger.warn(
|
|
253
|
+
`• Permission group '${exportedName}' for tag '${tag}' is not wrapped in a factory function. ` +
|
|
254
|
+
`For tree-shaking, use: export const ${exportedName} = () => addPermission('${tag}', [...])`
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Store group metadata
|
|
259
|
+
state.permissions.tagPermissions.set(tag, {
|
|
260
|
+
exportName: exportedName,
|
|
261
|
+
sourceFile: node.getSourceFile().fileName,
|
|
262
|
+
position: node.getStart(),
|
|
263
|
+
services: {
|
|
264
|
+
optimized: false,
|
|
265
|
+
services: Array.from(allServices),
|
|
266
|
+
},
|
|
267
|
+
permissionCount: permissionNames.length,
|
|
268
|
+
isFactory,
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
logger.debug(
|
|
272
|
+
`• Found tag permission group: ${tag} -> [${permissionNames.join(', ')}] (${isFactory ? 'factory' : 'direct'})`
|
|
273
|
+
)
|
|
274
|
+
return
|
|
44
275
|
}
|
|
45
276
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
277
|
+
// Handle addHTTPPermission(pattern, [permission1, permission2])
|
|
278
|
+
// Supports two patterns:
|
|
279
|
+
// 1. export const x = () => addHTTPPermission('*', [...]) (factory - tree-shakeable)
|
|
280
|
+
// 2. export const x = addHTTPPermission('*', [...]) (direct - no tree-shaking)
|
|
281
|
+
if (expression.text === 'addHTTPPermission') {
|
|
282
|
+
const patternArg = args[0]
|
|
283
|
+
const permissionsArrayArg = args[1]
|
|
284
|
+
|
|
285
|
+
if (!patternArg || !permissionsArrayArg) return
|
|
286
|
+
|
|
287
|
+
// Extract route pattern
|
|
288
|
+
let pattern: string | undefined
|
|
289
|
+
if (ts.isStringLiteral(patternArg)) {
|
|
290
|
+
pattern = patternArg.text
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!pattern) {
|
|
294
|
+
logger.warn(`• addHTTPPermission call without valid pattern string`)
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Check if permissions is a literal array or object
|
|
299
|
+
if (
|
|
300
|
+
!ts.isArrayLiteralExpression(permissionsArrayArg) &&
|
|
301
|
+
!ts.isObjectLiteralExpression(permissionsArrayArg)
|
|
302
|
+
) {
|
|
303
|
+
logger.error(
|
|
304
|
+
`• addHTTPPermission('${pattern}', ...) must have a literal array or object as second argument`
|
|
305
|
+
)
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Extract permission pikkuFuncNames from array
|
|
310
|
+
const permissionNames = extractPermissionPikkuNames(
|
|
311
|
+
permissionsArrayArg,
|
|
312
|
+
checker,
|
|
313
|
+
state.rootDir
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if (permissionNames.length === 0) {
|
|
317
|
+
logger.warn(
|
|
318
|
+
`• addHTTPPermission('${pattern}', ...) has empty permissions array`
|
|
319
|
+
)
|
|
320
|
+
return
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Collect services from all permissions in the group
|
|
324
|
+
const allServices = new Set<string>()
|
|
325
|
+
for (const permissionName of permissionNames) {
|
|
326
|
+
const permissionMeta = state.permissions.meta[permissionName]
|
|
327
|
+
if (permissionMeta && permissionMeta.services) {
|
|
328
|
+
for (const service of permissionMeta.services.services) {
|
|
329
|
+
allServices.add(service)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Check if this call is wrapped in a factory function
|
|
335
|
+
let isFactory = false
|
|
336
|
+
let exportedName: string | null = null
|
|
337
|
+
let parent = node.parent
|
|
338
|
+
|
|
339
|
+
// Check if parent is arrow function: () => addHTTPPermission(...)
|
|
340
|
+
if (parent && ts.isArrowFunction(parent)) {
|
|
341
|
+
// Check if arrow function has no parameters
|
|
342
|
+
if (parent.parameters.length === 0) {
|
|
343
|
+
isFactory = true
|
|
344
|
+
|
|
345
|
+
// For factories, we need to check the arrow function's parent for the export name
|
|
346
|
+
// const apiRoutePermissions = () => addHTTPPermission(...)
|
|
347
|
+
const arrowParent = parent.parent
|
|
348
|
+
if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
|
|
349
|
+
if (ts.isIdentifier(arrowParent.name)) {
|
|
350
|
+
// Check if it's exported
|
|
351
|
+
if (isNamedExport(arrowParent)) {
|
|
352
|
+
exportedName = arrowParent.name.text
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// If not a factory, get export name from the call expression itself
|
|
360
|
+
if (!isFactory) {
|
|
361
|
+
const extracted = extractFunctionName(node, checker, state.rootDir)
|
|
362
|
+
exportedName = extracted.exportedName
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Log warning if not using factory pattern
|
|
366
|
+
if (!isFactory && exportedName) {
|
|
367
|
+
logger.warn(
|
|
368
|
+
`• HTTP permission group '${exportedName}' for pattern '${pattern}' is not wrapped in a factory function. ` +
|
|
369
|
+
`For tree-shaking, use: export const ${exportedName} = () => addHTTPPermission('${pattern}', [...])`
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Store group metadata
|
|
374
|
+
state.http.routePermissions.set(pattern, {
|
|
375
|
+
exportName: exportedName,
|
|
376
|
+
sourceFile: node.getSourceFile().fileName,
|
|
377
|
+
position: node.getStart(),
|
|
378
|
+
services: {
|
|
379
|
+
optimized: false,
|
|
380
|
+
services: Array.from(allServices),
|
|
381
|
+
},
|
|
382
|
+
permissionCount: permissionNames.length,
|
|
383
|
+
isFactory,
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
logger.debug(
|
|
387
|
+
`• Found HTTP route permission group: ${pattern} -> [${permissionNames.join(', ')}] (${isFactory ? 'factory' : 'direct'})`
|
|
388
|
+
)
|
|
389
|
+
return
|
|
390
|
+
}
|
|
49
391
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
getPropertyValue,
|
|
4
|
+
getPropertyTags,
|
|
5
|
+
} from '../utils/get-property-value.js'
|
|
6
|
+
import { PikkuDocs } from '@pikku/core'
|
|
4
7
|
import { AddWiring } from '../types.js'
|
|
5
8
|
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
6
9
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
7
|
-
import { matchesFilters } from '../utils/filter-utils.js'
|
|
8
10
|
import { resolveMiddleware } from '../utils/middleware.js'
|
|
11
|
+
import { extractWireNames } from '../utils/post-process.js'
|
|
12
|
+
import { ErrorCode } from '../error-codes.js'
|
|
9
13
|
|
|
10
14
|
export const addQueueWorker: AddWiring = (
|
|
11
15
|
logger,
|
|
@@ -36,7 +40,7 @@ export const addQueueWorker: AddWiring = (
|
|
|
36
40
|
|
|
37
41
|
const queueName = getPropertyValue(obj, 'queueName') as string | null
|
|
38
42
|
const docs = (getPropertyValue(obj, 'docs') as PikkuDocs) || undefined
|
|
39
|
-
const tags = (
|
|
43
|
+
const tags = getPropertyTags(obj, 'Queue worker', queueName, logger)
|
|
40
44
|
|
|
41
45
|
// --- find the referenced function ---
|
|
42
46
|
const funcInitializer = getPropertyAssignmentInitializer(
|
|
@@ -46,36 +50,23 @@ export const addQueueWorker: AddWiring = (
|
|
|
46
50
|
checker
|
|
47
51
|
)
|
|
48
52
|
if (!funcInitializer) {
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
logger.critical(
|
|
54
|
+
ErrorCode.MISSING_FUNC,
|
|
55
|
+
`No valid 'func' property for queue processor '${queueName}'.`
|
|
51
56
|
)
|
|
52
57
|
return
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
const pikkuFuncName = extractFunctionName(
|
|
56
61
|
funcInitializer,
|
|
57
|
-
checker
|
|
62
|
+
checker,
|
|
63
|
+
state.rootDir
|
|
58
64
|
).pikkuFuncName
|
|
59
65
|
|
|
60
66
|
if (!queueName) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const filePath = node.getSourceFile().fileName
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
!matchesFilters(
|
|
71
|
-
options.filters || {},
|
|
72
|
-
{ tags },
|
|
73
|
-
{ type: PikkuWiringTypes.queue, name: queueName, filePath },
|
|
74
|
-
logger
|
|
75
|
-
)
|
|
76
|
-
) {
|
|
77
|
-
console.info(
|
|
78
|
-
`• Skipping queue processor '${pikkuFuncName}' for queue '${queueName}' due to filter mismatch.`
|
|
67
|
+
logger.critical(
|
|
68
|
+
ErrorCode.MISSING_QUEUE_NAME,
|
|
69
|
+
`No 'queueName' provided for queue processor function '${pikkuFuncName}'.`
|
|
79
70
|
)
|
|
80
71
|
return
|
|
81
72
|
}
|
|
@@ -83,6 +74,12 @@ export const addQueueWorker: AddWiring = (
|
|
|
83
74
|
// --- resolve middleware ---
|
|
84
75
|
const middleware = resolveMiddleware(state, obj, tags, checker)
|
|
85
76
|
|
|
77
|
+
// --- track used functions/middleware for service aggregation ---
|
|
78
|
+
state.serviceAggregation.usedFunctions.add(pikkuFuncName)
|
|
79
|
+
extractWireNames(middleware).forEach((name) =>
|
|
80
|
+
state.serviceAggregation.usedMiddleware.add(name)
|
|
81
|
+
)
|
|
82
|
+
|
|
86
83
|
state.queueWorkers.files.add(node.getSourceFile().fileName)
|
|
87
84
|
state.queueWorkers.meta[queueName] = {
|
|
88
85
|
pikkuFuncName,
|
package/src/add/add-schedule.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
getPropertyValue,
|
|
4
|
+
getPropertyTags,
|
|
5
|
+
} from '../utils/get-property-value.js'
|
|
6
|
+
import { PikkuDocs } from '@pikku/core'
|
|
4
7
|
import { AddWiring } from '../types.js'
|
|
5
8
|
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
6
9
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
7
|
-
import { matchesFilters } from '../utils/filter-utils.js'
|
|
8
10
|
import { resolveMiddleware } from '../utils/middleware.js'
|
|
11
|
+
import { extractWireNames } from '../utils/post-process.js'
|
|
9
12
|
|
|
13
|
+
import { ErrorCode } from '../error-codes.js'
|
|
10
14
|
export const addSchedule: AddWiring = (
|
|
11
15
|
logger,
|
|
12
16
|
node,
|
|
@@ -37,7 +41,7 @@ export const addSchedule: AddWiring = (
|
|
|
37
41
|
const nameValue = getPropertyValue(obj, 'name') as string | null
|
|
38
42
|
const scheduleValue = getPropertyValue(obj, 'schedule') as string | null
|
|
39
43
|
const docs = (getPropertyValue(obj, 'docs') as PikkuDocs) || undefined
|
|
40
|
-
const tags = (
|
|
44
|
+
const tags = getPropertyTags(obj, 'Scheduler', nameValue, logger)
|
|
41
45
|
|
|
42
46
|
const funcInitializer = getPropertyAssignmentInitializer(
|
|
43
47
|
obj,
|
|
@@ -46,37 +50,32 @@ export const addSchedule: AddWiring = (
|
|
|
46
50
|
checker
|
|
47
51
|
)
|
|
48
52
|
if (!funcInitializer) {
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
logger.critical(
|
|
54
|
+
ErrorCode.MISSING_FUNC,
|
|
55
|
+
`No valid 'func' property for scheduled task '${nameValue}'.`
|
|
51
56
|
)
|
|
52
57
|
return
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
const pikkuFuncName = extractFunctionName(
|
|
56
61
|
funcInitializer,
|
|
57
|
-
checker
|
|
62
|
+
checker,
|
|
63
|
+
state.rootDir
|
|
58
64
|
).pikkuFuncName
|
|
59
65
|
|
|
60
66
|
if (!nameValue || !scheduleValue) {
|
|
61
67
|
return
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
const filePath = node.getSourceFile().fileName
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
!matchesFilters(
|
|
68
|
-
options.filters || {},
|
|
69
|
-
{ tags },
|
|
70
|
-
{ type: PikkuWiringTypes.scheduler, name: nameValue, filePath },
|
|
71
|
-
logger
|
|
72
|
-
)
|
|
73
|
-
) {
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
|
|
77
70
|
// --- resolve middleware ---
|
|
78
71
|
const middleware = resolveMiddleware(state, obj, tags, checker)
|
|
79
72
|
|
|
73
|
+
// --- track used functions/middleware for service aggregation ---
|
|
74
|
+
state.serviceAggregation.usedFunctions.add(pikkuFuncName)
|
|
75
|
+
extractWireNames(middleware).forEach((name) =>
|
|
76
|
+
state.serviceAggregation.usedMiddleware.add(name)
|
|
77
|
+
)
|
|
78
|
+
|
|
80
79
|
state.scheduledTasks.files.add(node.getSourceFile().fileName)
|
|
81
80
|
state.scheduledTasks.meta[nameValue] = {
|
|
82
81
|
pikkuFuncName,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error code system for Pikku CLI and Inspector
|
|
3
|
+
*
|
|
4
|
+
* Each error has a unique code and links to documentation at pikku.dev
|
|
5
|
+
*
|
|
6
|
+
* Error codes use random 3-digit numbers to avoid implying a sequential order.
|
|
7
|
+
* Each code links to detailed documentation and troubleshooting steps.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export enum ErrorCode {
|
|
11
|
+
// Validation errors
|
|
12
|
+
MISSING_NAME = 'PKU111',
|
|
13
|
+
MISSING_DESCRIPTION = 'PKU123',
|
|
14
|
+
MISSING_URI = 'PKU220',
|
|
15
|
+
MISSING_FUNC = 'PKU236',
|
|
16
|
+
INVALID_TAGS_TYPE = 'PKU247',
|
|
17
|
+
INVALID_HANDLER = 'PKU300',
|
|
18
|
+
MISSING_TITLE = 'PKU370',
|
|
19
|
+
MISSING_QUEUE_NAME = 'PKU384',
|
|
20
|
+
MISSING_CHANNEL_NAME = 'PKU400',
|
|
21
|
+
CLI_CLIENTSIDE_RENDERER_HAS_SERVICES = 'PKU672',
|
|
22
|
+
|
|
23
|
+
// Configuration errors
|
|
24
|
+
CONFIG_TYPE_NOT_FOUND = 'PKU426',
|
|
25
|
+
CONFIG_TYPE_UNDEFINED = 'PKU427',
|
|
26
|
+
SCHEMA_NO_ROOT = 'PKU431',
|
|
27
|
+
SCHEMA_GENERATION_ERROR = 'PKU456',
|
|
28
|
+
SCHEMA_LOAD_ERROR = 'PKU488',
|
|
29
|
+
|
|
30
|
+
// Function errors
|
|
31
|
+
FUNCTION_METADATA_NOT_FOUND = 'PKU559',
|
|
32
|
+
HANDLER_NOT_RESOLVED = 'PKU568',
|
|
33
|
+
|
|
34
|
+
// Middleware/Permission errors
|
|
35
|
+
MIDDLEWARE_HANDLER_INVALID = 'PKU685',
|
|
36
|
+
MIDDLEWARE_TAG_INVALID = 'PKU715',
|
|
37
|
+
MIDDLEWARE_EMPTY_ARRAY = 'PKU736',
|
|
38
|
+
MIDDLEWARE_PATTERN_INVALID = 'PKU787',
|
|
39
|
+
PERMISSION_HANDLER_INVALID = 'PKU835',
|
|
40
|
+
PERMISSION_TAG_INVALID = 'PKU836',
|
|
41
|
+
PERMISSION_EMPTY_ARRAY = 'PKU937',
|
|
42
|
+
PERMISSION_PATTERN_INVALID = 'PKU975',
|
|
43
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -7,3 +7,10 @@ export type {
|
|
|
7
7
|
FilesAndMethods,
|
|
8
8
|
FilesAndMethodsErrors,
|
|
9
9
|
} from './utils/get-files-and-methods.js'
|
|
10
|
+
export { ErrorCode } from './error-codes.js'
|
|
11
|
+
export {
|
|
12
|
+
serializeInspectorState,
|
|
13
|
+
deserializeInspectorState,
|
|
14
|
+
} from './utils/serialize-inspector-state.js'
|
|
15
|
+
export type { SerializableInspectorState } from './utils/serialize-inspector-state.js'
|
|
16
|
+
export { filterInspectorState } from './utils/filter-inspector-state.js'
|