@pikku/inspector 0.12.41 → 0.12.43
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 +104 -0
- package/dist/add/add-addon-bans.js +1 -0
- package/dist/add/add-ai-agent.d.ts +3 -1
- package/dist/add/add-ai-agent.js +82 -0
- package/dist/add/add-credential.js +3 -1
- package/dist/add/add-functions.js +10 -9
- package/dist/add/add-gateway.js +3 -0
- package/dist/add/add-http-route.js +2 -2
- package/dist/add/add-mcp-prompt.js +0 -1
- package/dist/add/add-mcp-resource.js +0 -1
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +3 -177
- package/dist/add/add-queue-worker.js +3 -0
- package/dist/add/add-schedule.js +3 -0
- package/dist/add/add-scope.d.ts +2 -0
- package/dist/add/add-scope.js +146 -0
- package/dist/add/add-trigger.js +3 -0
- package/dist/add/add-wire-remote-addon.d.ts +10 -0
- package/dist/add/add-wire-remote-addon.js +66 -0
- package/dist/add/add-workflow-graph.js +32 -3
- package/dist/error-codes.d.ts +3 -0
- package/dist/error-codes.js +4 -0
- package/dist/inspector.js +19 -7
- package/dist/types.d.ts +19 -13
- package/dist/utils/custom-types-generator.js +2 -1
- package/dist/utils/ensure-function-metadata.d.ts +15 -0
- package/dist/utils/ensure-function-metadata.js +24 -0
- package/dist/utils/filter-inspector-state.js +9 -0
- package/dist/utils/get-property-value.d.ts +7 -2
- package/dist/utils/get-property-value.js +48 -1
- package/dist/utils/load-addon-functions-meta.js +81 -6
- package/dist/utils/nested-project-filter.d.ts +1 -0
- package/dist/utils/nested-project-filter.js +29 -0
- package/dist/utils/permissions.d.ts +1 -21
- package/dist/utils/permissions.js +17 -108
- package/dist/utils/post-process.d.ts +30 -1
- package/dist/utils/post-process.js +150 -72
- package/dist/utils/serialize-inspector-state.d.ts +13 -8
- package/dist/utils/serialize-inspector-state.js +12 -6
- package/dist/utils/serialize-permissions-groups-meta.d.ts +0 -2
- package/dist/utils/serialize-permissions-groups-meta.js +0 -26
- package/dist/utils/workflow/derive-workflow-plan.js +9 -3
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +56 -1
- package/dist/utils/workflow/dsl/patterns.d.ts +10 -0
- package/dist/utils/workflow/dsl/patterns.js +24 -8
- package/dist/utils/workflow/dsl/validation.js +3 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +14 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +7 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +11 -2
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +4 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +6 -2
- package/dist/visit.js +4 -0
- package/package.json +3 -3
- package/src/add/add-addon-bans.ts +1 -0
- package/src/add/add-ai-agent.test.ts +87 -0
- package/src/add/add-ai-agent.ts +122 -0
- package/src/add/add-credential.ts +6 -0
- package/src/add/add-functions.ts +10 -12
- package/src/add/add-gateway.ts +11 -0
- package/src/add/add-http-route.ts +2 -8
- package/src/add/add-mcp-prompt.ts +0 -1
- package/src/add/add-mcp-resource.ts +0 -1
- package/src/add/add-permission.ts +4 -242
- package/src/add/add-queue-worker.ts +11 -0
- package/src/add/add-schedule.ts +11 -0
- package/src/add/add-scope.test.ts +346 -0
- package/src/add/add-scope.ts +225 -0
- package/src/add/add-trigger.ts +11 -0
- package/src/add/add-wire-remote-addon.ts +77 -0
- package/src/add/add-workflow-graph-input.test.ts +94 -0
- package/src/add/add-workflow-graph-notes.test.ts +74 -0
- package/src/add/add-workflow-graph.ts +35 -3
- package/src/add/inline-wiring-function.test.ts +104 -0
- package/src/add/validate-workflow-graph-addons.test.ts +102 -0
- package/src/error-codes.ts +5 -0
- package/src/inspector.ts +22 -6
- package/src/types.ts +16 -17
- package/src/utils/custom-types-generator.test.ts +26 -1
- package/src/utils/custom-types-generator.ts +2 -1
- package/src/utils/ensure-function-metadata.ts +42 -0
- package/src/utils/filter-inspector-state.test.ts +0 -2
- package/src/utils/filter-inspector-state.ts +9 -0
- package/src/utils/get-property-value.test.ts +141 -0
- package/src/utils/get-property-value.ts +62 -1
- package/src/utils/load-addon-functions-meta.test.ts +157 -0
- package/src/utils/load-addon-functions-meta.ts +94 -6
- package/src/utils/load-addon-scopes.test.ts +138 -0
- package/src/utils/nested-project-filter.test.ts +53 -0
- package/src/utils/nested-project-filter.ts +28 -0
- package/src/utils/permissions.test.ts +7 -232
- package/src/utils/permissions.ts +20 -160
- package/src/utils/post-process.test.ts +269 -4
- package/src/utils/post-process.ts +216 -95
- package/src/utils/serialize-inspector-state.ts +22 -25
- package/src/utils/serialize-permissions-groups-meta.ts +0 -28
- package/src/utils/workflow/derive-workflow-plan.test.ts +41 -3
- package/src/utils/workflow/derive-workflow-plan.ts +11 -4
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +66 -0
- package/src/utils/workflow/dsl/patterns.ts +34 -8
- package/src/utils/workflow/dsl/validation.ts +3 -4
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +15 -0
- package/src/utils/workflow/graph/finalize-workflows.test.ts +50 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +13 -2
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +6 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +8 -0
- package/src/visit.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import * as assert from 'node:assert'
|
|
3
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs'
|
|
4
|
+
import { tmpdir } from 'os'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
import { createNestedProjectFilter } from './nested-project-filter.js'
|
|
7
|
+
|
|
8
|
+
test('createNestedProjectFilter', async (t) => {
|
|
9
|
+
const root = mkdtempSync(join(tmpdir(), 'pikku-nested-'))
|
|
10
|
+
t.after(() => rmSync(root, { recursive: true, force: true }))
|
|
11
|
+
|
|
12
|
+
writeFileSync(join(root, 'pikku.config.json'), '{}')
|
|
13
|
+
mkdirSync(join(root, 'packages/functions/src'), { recursive: true })
|
|
14
|
+
mkdirSync(join(root, 'packages/addon/src'), { recursive: true })
|
|
15
|
+
mkdirSync(join(root, 'packages/addon/types'), { recursive: true })
|
|
16
|
+
writeFileSync(join(root, 'packages/addon/pikku.config.json'), '{}')
|
|
17
|
+
|
|
18
|
+
const isNested = createNestedProjectFilter(root)
|
|
19
|
+
|
|
20
|
+
await t.test('keeps project source files', () => {
|
|
21
|
+
assert.strictEqual(
|
|
22
|
+
isNested(join(root, 'packages/functions/src/auth.ts')),
|
|
23
|
+
false
|
|
24
|
+
)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
await t.test('keeps files directly under rootDir', () => {
|
|
28
|
+
assert.strictEqual(isNested(join(root, 'services.ts')), false)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
await t.test('excludes files inside a nested pikku project', () => {
|
|
32
|
+
assert.strictEqual(
|
|
33
|
+
isNested(join(root, 'packages/addon/types/application-types.d.ts')),
|
|
34
|
+
true
|
|
35
|
+
)
|
|
36
|
+
assert.strictEqual(
|
|
37
|
+
isNested(join(root, 'packages/addon/src/index.ts')),
|
|
38
|
+
true
|
|
39
|
+
)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
await t.test('keeps files outside rootDir untouched', () => {
|
|
43
|
+
assert.strictEqual(isNested('/somewhere/else/file.ts'), false)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
await t.test('rootDir itself is not treated as nested', () => {
|
|
47
|
+
const addonScoped = createNestedProjectFilter(join(root, 'packages/addon'))
|
|
48
|
+
assert.strictEqual(
|
|
49
|
+
addonScoped(join(root, 'packages/addon/src/index.ts')),
|
|
50
|
+
false
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { existsSync } from 'fs'
|
|
2
|
+
import { dirname, join } from 'path'
|
|
3
|
+
|
|
4
|
+
export const createNestedProjectFilter = (rootDir: string) => {
|
|
5
|
+
const cache = new Map<string, boolean>()
|
|
6
|
+
return (fileName: string): boolean => {
|
|
7
|
+
const pending: string[] = []
|
|
8
|
+
let dir = dirname(fileName)
|
|
9
|
+
let nested = false
|
|
10
|
+
while (dir.startsWith(rootDir) && dir !== rootDir) {
|
|
11
|
+
const cached = cache.get(dir)
|
|
12
|
+
if (cached !== undefined) {
|
|
13
|
+
nested = cached
|
|
14
|
+
break
|
|
15
|
+
}
|
|
16
|
+
pending.push(dir)
|
|
17
|
+
if (existsSync(join(dir, 'pikku.config.json'))) {
|
|
18
|
+
nested = true
|
|
19
|
+
break
|
|
20
|
+
}
|
|
21
|
+
const parent = dirname(dir)
|
|
22
|
+
if (parent === dir) break
|
|
23
|
+
dir = parent
|
|
24
|
+
}
|
|
25
|
+
for (const d of pending) cache.set(d, nested)
|
|
26
|
+
return nested
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,193 +1,10 @@
|
|
|
1
1
|
import { describe, test } from 'node:test'
|
|
2
2
|
import assert from 'node:assert'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
resolveHTTPPermissions,
|
|
6
|
-
resolvePermissions,
|
|
7
|
-
} from './permissions.js'
|
|
8
|
-
import type { InspectorState, PermissionGroupMeta } from '../types.js'
|
|
9
|
-
|
|
10
|
-
describe('routeMatchesPattern', () => {
|
|
11
|
-
test('should match exact routes', () => {
|
|
12
|
-
assert.strictEqual(routeMatchesPattern('/api/users', '/api/users'), true)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
test('should not match different routes', () => {
|
|
16
|
-
assert.strictEqual(routeMatchesPattern('/api/users', '/api/posts'), false)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
test('should match wildcard patterns', () => {
|
|
20
|
-
assert.strictEqual(routeMatchesPattern('/api/users', '/api/*'), true)
|
|
21
|
-
assert.strictEqual(routeMatchesPattern('/api/users/123', '/api/*'), true)
|
|
22
|
-
assert.strictEqual(routeMatchesPattern('/api/posts', '/api/*'), true)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test('should match global wildcard', () => {
|
|
26
|
-
assert.strictEqual(routeMatchesPattern('/api/users', '*'), true)
|
|
27
|
-
assert.strictEqual(routeMatchesPattern('/any/path', '*'), true)
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test('should not match if pattern is more specific', () => {
|
|
31
|
-
assert.strictEqual(routeMatchesPattern('/users', '/api/*'), false)
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
describe('resolveHTTPPermissions', () => {
|
|
36
|
-
test('should return undefined when no permissions exist', () => {
|
|
37
|
-
const state: InspectorState = createMockState()
|
|
38
|
-
|
|
39
|
-
const result = resolveHTTPPermissions(
|
|
40
|
-
state,
|
|
41
|
-
'/api/test',
|
|
42
|
-
undefined,
|
|
43
|
-
undefined,
|
|
44
|
-
{} as any
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
assert.strictEqual(result, undefined)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('should resolve global HTTP permissions', () => {
|
|
51
|
-
const state: InspectorState = createMockState()
|
|
52
|
-
const globalPermission: PermissionGroupMeta = {
|
|
53
|
-
exportName: 'globalPermissions',
|
|
54
|
-
sourceFile: '/test.ts',
|
|
55
|
-
position: 0,
|
|
56
|
-
services: { services: [], singletons: [] },
|
|
57
|
-
count: 1,
|
|
58
|
-
instanceIds: [],
|
|
59
|
-
isFactory: true,
|
|
60
|
-
}
|
|
61
|
-
state.http.routePermissions.set('*', globalPermission)
|
|
62
|
-
|
|
63
|
-
const result = resolveHTTPPermissions(
|
|
64
|
-
state,
|
|
65
|
-
'/api/test',
|
|
66
|
-
undefined,
|
|
67
|
-
undefined,
|
|
68
|
-
{} as any
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
assert.ok(result)
|
|
72
|
-
assert.strictEqual(result.length, 1)
|
|
73
|
-
assert.deepEqual(result[0], { type: 'http', route: '*' })
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
test('should resolve route pattern permissions', () => {
|
|
77
|
-
const state: InspectorState = createMockState()
|
|
78
|
-
const apiPermission: PermissionGroupMeta = {
|
|
79
|
-
exportName: 'apiPermissions',
|
|
80
|
-
sourceFile: '/test.ts',
|
|
81
|
-
position: 0,
|
|
82
|
-
services: { services: [], singletons: [] },
|
|
83
|
-
count: 1,
|
|
84
|
-
instanceIds: [],
|
|
85
|
-
isFactory: true,
|
|
86
|
-
}
|
|
87
|
-
state.http.routePermissions.set('/api/*', apiPermission)
|
|
88
|
-
|
|
89
|
-
const result = resolveHTTPPermissions(
|
|
90
|
-
state,
|
|
91
|
-
'/api/users',
|
|
92
|
-
undefined,
|
|
93
|
-
undefined,
|
|
94
|
-
{} as any
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
assert.ok(result)
|
|
98
|
-
assert.strictEqual(result.length, 1)
|
|
99
|
-
assert.deepEqual(result[0], { type: 'http', route: '/api/*' })
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test('should resolve tag-based permissions', () => {
|
|
103
|
-
const state: InspectorState = createMockState()
|
|
104
|
-
const adminPermission: PermissionGroupMeta = {
|
|
105
|
-
exportName: 'adminPermissions',
|
|
106
|
-
sourceFile: '/test.ts',
|
|
107
|
-
position: 0,
|
|
108
|
-
services: { services: [], singletons: [] },
|
|
109
|
-
count: 1,
|
|
110
|
-
instanceIds: [],
|
|
111
|
-
isFactory: true,
|
|
112
|
-
}
|
|
113
|
-
state.permissions.tagPermissions.set('admin', adminPermission)
|
|
114
|
-
|
|
115
|
-
const result = resolveHTTPPermissions(
|
|
116
|
-
state,
|
|
117
|
-
'/api/admin',
|
|
118
|
-
['admin'],
|
|
119
|
-
undefined,
|
|
120
|
-
{} as any
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
assert.ok(result)
|
|
124
|
-
assert.strictEqual(result.length, 1)
|
|
125
|
-
assert.deepEqual(result[0], { type: 'tag', tag: 'admin' })
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
test('should combine multiple permission sources in correct order', () => {
|
|
129
|
-
const state: InspectorState = createMockState()
|
|
130
|
-
|
|
131
|
-
// Setup global HTTP permission
|
|
132
|
-
state.http.routePermissions.set('*', {
|
|
133
|
-
exportName: 'global',
|
|
134
|
-
sourceFile: '/test.ts',
|
|
135
|
-
position: 0,
|
|
136
|
-
services: { services: [], singletons: [] },
|
|
137
|
-
count: 1,
|
|
138
|
-
instanceIds: [],
|
|
139
|
-
isFactory: true,
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
// Setup route pattern permission
|
|
143
|
-
state.http.routePermissions.set('/api/*', {
|
|
144
|
-
exportName: 'apiRoute',
|
|
145
|
-
sourceFile: '/test.ts',
|
|
146
|
-
position: 0,
|
|
147
|
-
services: { services: [], singletons: [] },
|
|
148
|
-
count: 1,
|
|
149
|
-
instanceIds: [],
|
|
150
|
-
isFactory: true,
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
// Setup tag permission
|
|
154
|
-
state.permissions.tagPermissions.set('admin', {
|
|
155
|
-
exportName: 'adminTag',
|
|
156
|
-
sourceFile: '/test.ts',
|
|
157
|
-
position: 0,
|
|
158
|
-
services: { services: [], singletons: [] },
|
|
159
|
-
count: 1,
|
|
160
|
-
instanceIds: [],
|
|
161
|
-
isFactory: true,
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
// Setup explicit permission
|
|
165
|
-
state.permissions.definitions['testPermission'] = {
|
|
166
|
-
services: { services: [], singletons: [] },
|
|
167
|
-
sourceFile: '/test.ts',
|
|
168
|
-
position: 0,
|
|
169
|
-
exportedName: 'testPerm',
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const result = resolveHTTPPermissions(
|
|
173
|
-
state,
|
|
174
|
-
'/api/admin',
|
|
175
|
-
['admin'],
|
|
176
|
-
undefined, // We'd need a real TS node for this
|
|
177
|
-
{} as any
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
assert.ok(result)
|
|
181
|
-
// Should have: global HTTP, route pattern, tag
|
|
182
|
-
assert.strictEqual(result.length, 3)
|
|
183
|
-
assert.deepEqual(result[0], { type: 'http', route: '*' })
|
|
184
|
-
assert.deepEqual(result[1], { type: 'http', route: '/api/*' })
|
|
185
|
-
assert.deepEqual(result[2], { type: 'tag', tag: 'admin' })
|
|
186
|
-
})
|
|
187
|
-
})
|
|
3
|
+
import { resolvePermissions } from './permissions.js'
|
|
4
|
+
import type { InspectorState } from '../types.js'
|
|
188
5
|
|
|
189
6
|
describe('resolvePermissions', () => {
|
|
190
|
-
test('should return undefined when no permissions
|
|
7
|
+
test('should return undefined when the object declares no permissions', () => {
|
|
191
8
|
const state: InspectorState = createMockState()
|
|
192
9
|
const mockObj = createMockObjectLiteral()
|
|
193
10
|
|
|
@@ -196,47 +13,10 @@ describe('resolvePermissions', () => {
|
|
|
196
13
|
assert.strictEqual(result, undefined)
|
|
197
14
|
})
|
|
198
15
|
|
|
199
|
-
test('
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
exportName: 'mcpPermissions',
|
|
203
|
-
sourceFile: '/test.ts',
|
|
204
|
-
position: 0,
|
|
205
|
-
services: { services: [], singletons: [] },
|
|
206
|
-
count: 1,
|
|
207
|
-
instanceIds: [],
|
|
208
|
-
isFactory: true,
|
|
209
|
-
}
|
|
210
|
-
state.permissions.tagPermissions.set('mcp', mcpPermission)
|
|
211
|
-
const mockObj = createMockObjectLiteral()
|
|
212
|
-
|
|
213
|
-
const result = resolvePermissions(state, mockObj, ['mcp'], {} as any)
|
|
214
|
-
|
|
215
|
-
assert.ok(result)
|
|
216
|
-
assert.strictEqual(result.length, 1)
|
|
217
|
-
assert.deepEqual(result[0], { type: 'tag', tag: 'mcp' })
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
test('should handle multiple tags', () => {
|
|
16
|
+
test('tags no longer resolve to permissions', () => {
|
|
17
|
+
// Permissions are function-scoped only; tags are organizational and never
|
|
18
|
+
// contribute permission metadata.
|
|
221
19
|
const state: InspectorState = createMockState()
|
|
222
|
-
state.permissions.tagPermissions.set('mcp', {
|
|
223
|
-
exportName: 'mcpPerms',
|
|
224
|
-
sourceFile: '/test.ts',
|
|
225
|
-
position: 0,
|
|
226
|
-
services: { services: [], singletons: [] },
|
|
227
|
-
count: 1,
|
|
228
|
-
instanceIds: [],
|
|
229
|
-
isFactory: true,
|
|
230
|
-
})
|
|
231
|
-
state.permissions.tagPermissions.set('admin', {
|
|
232
|
-
exportName: 'adminPerms',
|
|
233
|
-
sourceFile: '/test.ts',
|
|
234
|
-
position: 0,
|
|
235
|
-
services: { services: [], singletons: [] },
|
|
236
|
-
count: 1,
|
|
237
|
-
instanceIds: [],
|
|
238
|
-
isFactory: true,
|
|
239
|
-
})
|
|
240
20
|
const mockObj = createMockObjectLiteral()
|
|
241
21
|
|
|
242
22
|
const result = resolvePermissions(
|
|
@@ -246,10 +26,7 @@ describe('resolvePermissions', () => {
|
|
|
246
26
|
{} as any
|
|
247
27
|
)
|
|
248
28
|
|
|
249
|
-
assert.
|
|
250
|
-
assert.strictEqual(result.length, 2)
|
|
251
|
-
assert.deepEqual(result[0], { type: 'tag', tag: 'mcp' })
|
|
252
|
-
assert.deepEqual(result[1], { type: 'tag', tag: 'admin' })
|
|
29
|
+
assert.strictEqual(result, undefined)
|
|
253
30
|
})
|
|
254
31
|
})
|
|
255
32
|
|
|
@@ -288,7 +65,6 @@ function createMockState(): InspectorState {
|
|
|
288
65
|
},
|
|
289
66
|
files: new Set(),
|
|
290
67
|
routeMiddleware: new Map(),
|
|
291
|
-
routePermissions: new Map(),
|
|
292
68
|
},
|
|
293
69
|
functions: {
|
|
294
70
|
typesMap: {} as any,
|
|
@@ -344,7 +120,6 @@ function createMockState(): InspectorState {
|
|
|
344
120
|
permissions: {
|
|
345
121
|
definitions: {},
|
|
346
122
|
instances: {},
|
|
347
|
-
tagPermissions: new Map(),
|
|
348
123
|
},
|
|
349
124
|
}
|
|
350
125
|
}
|
package/src/utils/permissions.ts
CHANGED
|
@@ -76,147 +76,34 @@ export function getPermissionsNode(
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
79
|
+
* Resolve the explicit permission references declared on a function (or agent).
|
|
80
|
+
* Permissions are function-scoped only: a `permissions: { group: [fn] }` object
|
|
81
|
+
* is flattened to `{ type: 'wire', name }` references used at filter time.
|
|
82
|
+
* Returns undefined if none are declared, otherwise an array with ≥1 item.
|
|
82
83
|
*/
|
|
83
|
-
|
|
84
|
-
if (route === pattern) return true
|
|
85
|
-
|
|
86
|
-
// Convert pattern to regex: replace * with .*
|
|
87
|
-
const regexPattern = pattern
|
|
88
|
-
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars except *
|
|
89
|
-
.replace(/\*/g, '.*') // Replace * with .*
|
|
90
|
-
|
|
91
|
-
const regex = new RegExp(`^${regexPattern}$`)
|
|
92
|
-
return regex.test(route)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Resolve permissions for an HTTP wiring based on:
|
|
97
|
-
* 1. Global HTTP permissions (addHTTPPermission('*', [...]))
|
|
98
|
-
* 2. Route-specific HTTP permissions (addHTTPPermission('/pattern', [...]))
|
|
99
|
-
* 3. Tag-based permissions (addPermission('tag', [...]))
|
|
100
|
-
* 4. Explicit wiring permissions (wireHTTP({ permissions: [...] }))
|
|
101
|
-
* Returns undefined if no permissions are found, otherwise returns array with at least one item
|
|
102
|
-
*/
|
|
103
|
-
export function resolveHTTPPermissions(
|
|
84
|
+
function resolveExplicitPermissions(
|
|
104
85
|
state: InspectorState,
|
|
105
|
-
route: string,
|
|
106
|
-
tags: string[] | undefined,
|
|
107
86
|
explicitPermissionsNode: ts.Expression | undefined,
|
|
108
87
|
checker: ts.TypeChecker
|
|
109
88
|
): PermissionMetadata[] | undefined {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// 1. HTTP route permission groups (includes '*' for global)
|
|
113
|
-
for (const [pattern, _groupMeta] of state.http.routePermissions.entries()) {
|
|
114
|
-
if (routeMatchesPattern(route, pattern)) {
|
|
115
|
-
// Just reference the group by route pattern
|
|
116
|
-
resolved.push({
|
|
117
|
-
type: 'http',
|
|
118
|
-
route: pattern,
|
|
119
|
-
})
|
|
120
|
-
}
|
|
89
|
+
if (!explicitPermissionsNode) {
|
|
90
|
+
return undefined
|
|
121
91
|
}
|
|
122
92
|
|
|
123
|
-
// 2. Tag-based permission groups
|
|
124
|
-
if (tags && tags.length > 0) {
|
|
125
|
-
for (const tag of tags) {
|
|
126
|
-
if (state.permissions.tagPermissions.has(tag)) {
|
|
127
|
-
// Just reference the group by tag
|
|
128
|
-
resolved.push({
|
|
129
|
-
type: 'tag',
|
|
130
|
-
tag,
|
|
131
|
-
})
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// 3. Explicit wire permissions (inline is OK here)
|
|
137
|
-
if (explicitPermissionsNode) {
|
|
138
|
-
const permissionNames = extractPermissionPikkuNames(
|
|
139
|
-
explicitPermissionsNode,
|
|
140
|
-
checker,
|
|
141
|
-
state.rootDir
|
|
142
|
-
)
|
|
143
|
-
for (const name of permissionNames) {
|
|
144
|
-
const def = state.permissions.definitions[name]
|
|
145
|
-
resolved.push({
|
|
146
|
-
type: 'wire',
|
|
147
|
-
name,
|
|
148
|
-
inline: def?.exportedName === null,
|
|
149
|
-
})
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return resolved.length > 0 ? resolved : undefined
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Resolve tag-based and explicit permissions (common logic for wires and functions)
|
|
158
|
-
* 1. Tag-based permissions (addPermission('tag', [...]))
|
|
159
|
-
* 2. Explicit permissions (wireHTTP/pikkuFunc({ permissions: [...] }))
|
|
160
|
-
*/
|
|
161
|
-
function resolveTagAndExplicitPermissions(
|
|
162
|
-
state: InspectorState,
|
|
163
|
-
tags: string[] | undefined,
|
|
164
|
-
explicitPermissionsNode: ts.Expression | undefined,
|
|
165
|
-
checker: ts.TypeChecker
|
|
166
|
-
): PermissionMetadata[] {
|
|
167
93
|
const resolved: PermissionMetadata[] = []
|
|
168
|
-
|
|
169
|
-
// 1. Tag-based permission groups
|
|
170
|
-
if (tags && tags.length > 0) {
|
|
171
|
-
for (const tag of tags) {
|
|
172
|
-
if (state.permissions.tagPermissions.has(tag)) {
|
|
173
|
-
// Just reference the group by tag
|
|
174
|
-
resolved.push({
|
|
175
|
-
type: 'tag',
|
|
176
|
-
tag,
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// 2. Explicit permissions (inline is OK here - used directly in wire/function)
|
|
183
|
-
if (explicitPermissionsNode) {
|
|
184
|
-
const permissionNames = extractPermissionPikkuNames(
|
|
185
|
-
explicitPermissionsNode,
|
|
186
|
-
checker,
|
|
187
|
-
state.rootDir
|
|
188
|
-
)
|
|
189
|
-
for (const name of permissionNames) {
|
|
190
|
-
const def = state.permissions.definitions[name]
|
|
191
|
-
resolved.push({
|
|
192
|
-
type: 'wire',
|
|
193
|
-
name,
|
|
194
|
-
inline: def?.exportedName === null,
|
|
195
|
-
})
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return resolved
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Resolve permissions for a function based on:
|
|
204
|
-
* 1. Tag-based permissions (addPermission('tag', [...]))
|
|
205
|
-
* 2. Explicit function permissions (pikkuFunc({ permissions: [...] }))
|
|
206
|
-
* Returns undefined if no permissions are found, otherwise returns array with at least one item
|
|
207
|
-
*/
|
|
208
|
-
function resolveFunctionPermissionsInternal(
|
|
209
|
-
state: InspectorState,
|
|
210
|
-
tags: string[] | undefined,
|
|
211
|
-
explicitPermissionsNode: ts.Expression | undefined,
|
|
212
|
-
checker: ts.TypeChecker
|
|
213
|
-
): PermissionMetadata[] | undefined {
|
|
214
|
-
const resolved = resolveTagAndExplicitPermissions(
|
|
215
|
-
state,
|
|
216
|
-
tags,
|
|
94
|
+
const permissionNames = extractPermissionPikkuNames(
|
|
217
95
|
explicitPermissionsNode,
|
|
218
|
-
checker
|
|
96
|
+
checker,
|
|
97
|
+
state.rootDir
|
|
219
98
|
)
|
|
99
|
+
for (const name of permissionNames) {
|
|
100
|
+
const def = state.permissions.definitions[name]
|
|
101
|
+
resolved.push({
|
|
102
|
+
type: 'wire',
|
|
103
|
+
name,
|
|
104
|
+
inline: def?.exportedName === null,
|
|
105
|
+
})
|
|
106
|
+
}
|
|
220
107
|
|
|
221
108
|
return resolved.length > 0 ? resolved : undefined
|
|
222
109
|
}
|
|
@@ -228,35 +115,8 @@ function resolveFunctionPermissionsInternal(
|
|
|
228
115
|
export function resolvePermissions(
|
|
229
116
|
state: InspectorState,
|
|
230
117
|
obj: ts.ObjectLiteralExpression,
|
|
231
|
-
|
|
118
|
+
_tags: string[] | undefined,
|
|
232
119
|
checker: ts.TypeChecker
|
|
233
120
|
): PermissionMetadata[] | undefined {
|
|
234
|
-
|
|
235
|
-
return resolveFunctionPermissionsInternal(
|
|
236
|
-
state,
|
|
237
|
-
tags,
|
|
238
|
-
explicitPermissionsNode,
|
|
239
|
-
checker
|
|
240
|
-
)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Convenience wrapper for HTTP: Extract permissions and resolve with HTTP-specific logic
|
|
245
|
-
* Use this in add-http-route.ts for cleaner code
|
|
246
|
-
*/
|
|
247
|
-
export function resolveHTTPPermissionsFromObject(
|
|
248
|
-
state: InspectorState,
|
|
249
|
-
route: string,
|
|
250
|
-
obj: ts.ObjectLiteralExpression,
|
|
251
|
-
tags: string[] | undefined,
|
|
252
|
-
checker: ts.TypeChecker
|
|
253
|
-
): PermissionMetadata[] | undefined {
|
|
254
|
-
const explicitPermissionsNode = getPermissionsNode(obj)
|
|
255
|
-
return resolveHTTPPermissions(
|
|
256
|
-
state,
|
|
257
|
-
route,
|
|
258
|
-
tags,
|
|
259
|
-
explicitPermissionsNode,
|
|
260
|
-
checker
|
|
261
|
-
)
|
|
121
|
+
return resolveExplicitPermissions(state, getPermissionsNode(obj), checker)
|
|
262
122
|
}
|