@pikku/inspector 0.12.106 → 0.12.107
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/dist/add/add-auth.d.ts +1 -9
- package/dist/add/add-auth.js +64 -17
- package/dist/add/add-cli.js +4 -0
- package/dist/add/add-functions.js +27 -31
- package/dist/add/add-keyed-wiring.js +22 -2
- package/dist/add/add-middleware.js +17 -16
- package/dist/add/add-permission.d.ts +0 -3
- package/dist/add/add-permission.js +213 -22
- package/dist/add/add-scope.d.ts +1 -7
- package/dist/add/add-scope.js +1 -21
- package/dist/add/check-indirect-registrations.d.ts +9 -0
- package/dist/add/check-indirect-registrations.js +114 -0
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +1 -0
- package/dist/finalize.d.ts +10 -0
- package/dist/finalize.js +102 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.js +6 -5
- package/dist/initial-state.d.ts +7 -0
- package/dist/initial-state.js +216 -0
- package/dist/inspector.d.ts +6 -4
- package/dist/inspector.js +28 -302
- package/dist/lite.d.ts +26 -0
- package/dist/lite.js +25 -0
- package/dist/source-file-cache.d.ts +11 -0
- package/dist/source-file-cache.js +34 -10
- package/dist/types-map.js +2 -1
- package/dist/types.d.ts +32 -0
- package/dist/utils/apply-declared-scopes.d.ts +8 -0
- package/dist/utils/apply-declared-scopes.js +21 -0
- package/dist/utils/canonical-type-string.d.ts +14 -0
- package/dist/utils/canonical-type-string.js +162 -0
- package/dist/utils/context-based-id.d.ts +7 -0
- package/dist/utils/context-based-id.js +9 -0
- package/dist/utils/custom-types-generator.d.ts +15 -1
- package/dist/utils/custom-types-generator.js +17 -5
- package/dist/utils/ensure-function-metadata.d.ts +0 -14
- package/dist/utils/ensure-function-metadata.js +4 -1
- package/dist/utils/extract-function-name.d.ts +1 -1
- package/dist/utils/extract-function-name.js +1 -3
- package/dist/utils/filter-inspector-state.js +31 -1
- package/dist/utils/get-property-value.d.ts +5 -0
- package/dist/utils/get-property-value.js +14 -0
- package/dist/utils/inspector-constants.d.ts +16 -0
- package/dist/utils/inspector-constants.js +16 -0
- package/dist/utils/post-process.d.ts +1 -2
- package/dist/utils/post-process.js +27 -27
- package/dist/utils/resolve-callee-name.d.ts +27 -0
- package/dist/utils/resolve-callee-name.js +147 -0
- package/dist/utils/resolve-service-types.d.ts +8 -0
- package/dist/utils/resolve-service-types.js +21 -0
- package/dist/utils/schema-generator.js +29 -2
- package/dist/utils/serialize-inspector-state.d.ts +7 -1
- package/dist/utils/serialize-inspector-state.js +16 -1
- package/dist/utils/workflow/dsl/patterns.d.ts +1 -8
- package/dist/utils/workflow/dsl/patterns.js +1 -7
- package/dist/visit.js +2 -0
- package/package.json +25 -9
- package/CHANGELOG.md +0 -4368
- package/OPTIMIZATION-PLAN.md +0 -201
- package/knowledge/decisions/a-channel-with-auth-false-marks-its-connect-and-disconnect-sessionless.md +0 -22
- package/knowledge/decisions/a-declared-scope-id-never-shadows-a-definition.md +0 -20
- package/knowledge/decisions/a-dsl-step-virtual-user-is-the-literal-name-in-its-options.md +0 -27
- package/knowledge/decisions/a-feature-flags-anyof-is-read-from-the-ast-and-an-empty-list-is-refused.md +0 -20
- package/knowledge/decisions/a-non-literal-string-array-reads-as-absent.md +0 -25
- package/knowledge/decisions/a-permission-with-an-underscore-data-parameter-takes-no-data.md +0 -20
- package/knowledge/decisions/a-provider-login-virtual-user-is-never-runnable.md +0 -27
- package/knowledge/decisions/a-remote-addon-bound-credential-or-secret-must-be-wired.md +0 -27
- package/knowledge/decisions/a-route-auth-overrides-its-groups-auth.md +0 -22
- package/knowledge/decisions/a-route-requires-a-session-when-any-auth-signal-says-so.md +0 -29
- package/knowledge/decisions/a-webhook-source-with-verify-gets-a-singleton-secret-credential.md +0 -21
- package/knowledge/decisions/a-wire-with-auth-disabled-cannot-point-at-a-pikkufunc.md +0 -21
- package/knowledge/decisions/addon-contracts-cannot-wire-or-declare-handlers.md +0 -25
- package/knowledge/decisions/addon-declarations-name-the-secrets-and-credentials-the-app-lends-it.md +0 -19
- package/knowledge/decisions/an-inline-wiring-function-is-sessionless-unless-built-with-pikkufunc.md +0 -23
- package/knowledge/decisions/better-auth-admin-plugin-is-refused.md +0 -24
- package/knowledge/decisions/define-screens-scopes-must-be-string-literals.md +0 -18
- package/knowledge/decisions/index.md +0 -38
- package/knowledge/decisions/inspector-records-a-non-literal-global-grant-as-source-text.md +0 -20
- package/knowledge/decisions/only-one-pikku-better-auth-export-per-codebase.md +0 -19
- package/knowledge/decisions/scope-references-and-secret-aliases-are-critical-diagnostics.md +0 -23
- package/knowledge/decisions/scope-segments-may-not-be-wildcards-or-contain-colons.md +0 -23
- package/knowledge/decisions/shorthand-permission-entries-are-extracted.md +0 -22
- package/knowledge/decisions/system-roles-declare-literal-unqualified-scopes.md +0 -28
- package/knowledge/decisions/the-inspector-records-agent-permissions-only-to-aggregate-services.md +0 -14
- package/knowledge/decisions/vault-secrets-reach-no-sink-and-pii-stays-out-of-logs-and-webhooks.md +0 -30
- package/knowledge/decisions/webhook-gateway-wrappers-are-sessionless-and-gateway-auth-runs-inside-the-handler.md +0 -26
- package/knowledge/index.md +0 -14
- package/knowledge/questions/does-a-non-literal-remote-addon-auth-get-checked.md +0 -26
- package/knowledge/questions/index.md +0 -16
- package/knowledge/questions/is-an-incomplete-oauth2-block-dropped-without-a-log.md +0 -23
- package/knowledge/questions/should-app-functions-named-like-scenario-instrumentation-be-refused.md +0 -28
- package/knowledge/questions/should-the-openapi-document-default-every-operation-to-a-bearer-or-api-key.md +0 -22
- package/run-tests.sh +0 -68
- package/src/utils/test-data/inspector-state.json +0 -1687
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import {
|
|
2
|
+
import { relative } from 'node:path';
|
|
3
|
+
import { extractFunctionName, isNamedExport, makeContextBasedId, } from '../utils/extract-function-name.js';
|
|
3
4
|
import { extractServicesFromFunction, extractUsedWires, } from '../utils/extract-services.js';
|
|
4
5
|
import { getPropertyValue } from '../utils/get-property-value.js';
|
|
5
6
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
|
|
6
|
-
|
|
7
|
+
import { PERMISSION_HELPERS, resolveCalleeName, } from '../utils/resolve-callee-name.js';
|
|
8
|
+
function isInsidePermissionContainer(node, checker) {
|
|
7
9
|
let current = node.parent;
|
|
8
10
|
while (current) {
|
|
9
11
|
if (ts.isCallExpression(current) &&
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
resolveCalleeName(current, checker, PERMISSION_HELPERS) ===
|
|
13
|
+
'pikkuPermissionFactory') {
|
|
12
14
|
return true;
|
|
13
15
|
}
|
|
14
16
|
current = current.parent;
|
|
@@ -16,20 +18,144 @@ function isInsidePermissionContainer(node) {
|
|
|
16
18
|
return false;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
|
-
*
|
|
21
|
+
* The id of a permission written inline in an `addGlobalPermission([...])`
|
|
22
|
+
* array, which has no variable to be named after. Derived from where it sits,
|
|
23
|
+
* so the registration and the definition agree on it without either having to
|
|
24
|
+
* be visited first.
|
|
20
25
|
*/
|
|
26
|
+
function inlineGlobalPermissionId(node, checker, rootDir) {
|
|
27
|
+
const array = node.parent;
|
|
28
|
+
if (!array || !ts.isArrayLiteralExpression(array))
|
|
29
|
+
return undefined;
|
|
30
|
+
const call = array.parent;
|
|
31
|
+
if (!call ||
|
|
32
|
+
!ts.isCallExpression(call) ||
|
|
33
|
+
call.arguments[0] !== array ||
|
|
34
|
+
resolveCalleeName(call, checker, PERMISSION_HELPERS) !==
|
|
35
|
+
'addGlobalPermission') {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const sourceFile = node.getSourceFile();
|
|
39
|
+
return makeContextBasedId('global', 'permission', relative(rootDir, sourceFile.fileName), String(node.getStart()));
|
|
40
|
+
}
|
|
41
|
+
const unwrapExpression = (expression) => {
|
|
42
|
+
let current = expression;
|
|
43
|
+
while (ts.isParenthesizedExpression(current) ||
|
|
44
|
+
ts.isAsExpression(current) ||
|
|
45
|
+
ts.isSatisfiesExpression(current) ||
|
|
46
|
+
ts.isNonNullExpression(current) ||
|
|
47
|
+
ts.isTypeAssertionExpression(current)) {
|
|
48
|
+
current = current.expression;
|
|
49
|
+
}
|
|
50
|
+
return current;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Follows `addGlobalPermission`'s argument to the array or object literal it was
|
|
54
|
+
* written as: a literal itself, or a `const` (declared here or imported) that is
|
|
55
|
+
* initialised with one. `undefined` when it is built any other way.
|
|
56
|
+
*/
|
|
57
|
+
const resolvePermissionLiteral = (argument, checker) => {
|
|
58
|
+
let current = unwrapExpression(argument);
|
|
59
|
+
for (let hops = 0; hops < 8; hops++) {
|
|
60
|
+
if (ts.isArrayLiteralExpression(current) ||
|
|
61
|
+
ts.isObjectLiteralExpression(current)) {
|
|
62
|
+
return current;
|
|
63
|
+
}
|
|
64
|
+
if (!ts.isIdentifier(current))
|
|
65
|
+
return undefined;
|
|
66
|
+
let symbol = checker.getSymbolAtLocation(current);
|
|
67
|
+
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
|
|
68
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
69
|
+
}
|
|
70
|
+
const declaration = symbol?.valueDeclaration;
|
|
71
|
+
if (!declaration ||
|
|
72
|
+
!ts.isVariableDeclaration(declaration) ||
|
|
73
|
+
!declaration.initializer) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
current = unwrapExpression(declaration.initializer);
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
};
|
|
80
|
+
const isNamedExportedFunction = (declaration, checker) => {
|
|
81
|
+
if (!declaration.name)
|
|
82
|
+
return false;
|
|
83
|
+
const flags = ts.getCombinedModifierFlags(declaration);
|
|
84
|
+
if (flags & ts.ModifierFlags.Default)
|
|
85
|
+
return false;
|
|
86
|
+
if (flags & ts.ModifierFlags.Export)
|
|
87
|
+
return true;
|
|
88
|
+
const moduleSymbol = checker.getSymbolAtLocation(declaration.getSourceFile());
|
|
89
|
+
const symbol = checker.getSymbolAtLocation(declaration.name);
|
|
90
|
+
return (!!moduleSymbol &&
|
|
91
|
+
!!symbol &&
|
|
92
|
+
checker.getExportsOfModule(moduleSymbol).some((exported) => {
|
|
93
|
+
const resolved = exported.flags & ts.SymbolFlags.Alias
|
|
94
|
+
? checker.getAliasedSymbol(exported)
|
|
95
|
+
: exported;
|
|
96
|
+
return resolved === symbol;
|
|
97
|
+
}));
|
|
98
|
+
};
|
|
99
|
+
const isImmediatelyInvoked = (fn) => {
|
|
100
|
+
let current = fn;
|
|
101
|
+
while (current.parent && ts.isParenthesizedExpression(current.parent)) {
|
|
102
|
+
current = current.parent;
|
|
103
|
+
}
|
|
104
|
+
return (!!current.parent &&
|
|
105
|
+
ts.isCallExpression(current.parent) &&
|
|
106
|
+
current.parent.expression === current);
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* How the generated permissions file makes this `addGlobalPermission` call run.
|
|
110
|
+
* Importing a file only evaluates its top level, so a call at the top level (or
|
|
111
|
+
* in a function invoked right where it is written) runs on import. A call inside
|
|
112
|
+
* an exported zero-argument function runs when the generated file calls that
|
|
113
|
+
* function. Anywhere else nothing ever calls it, so the gate would not exist.
|
|
114
|
+
*/
|
|
115
|
+
const globalPermissionActivation = (call, checker) => {
|
|
116
|
+
let current = call.parent;
|
|
117
|
+
while (current && !ts.isSourceFile(current)) {
|
|
118
|
+
if (ts.isArrowFunction(current) ||
|
|
119
|
+
ts.isFunctionExpression(current) ||
|
|
120
|
+
ts.isFunctionDeclaration(current) ||
|
|
121
|
+
ts.isMethodDeclaration(current) ||
|
|
122
|
+
ts.isConstructorDeclaration(current) ||
|
|
123
|
+
ts.isGetAccessorDeclaration(current) ||
|
|
124
|
+
ts.isSetAccessorDeclaration(current)) {
|
|
125
|
+
if (isImmediatelyInvoked(current)) {
|
|
126
|
+
current = current.parent;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (current.parameters.length > 0)
|
|
130
|
+
return { kind: 'unreachable' };
|
|
131
|
+
if ((ts.isArrowFunction(current) || ts.isFunctionExpression(current)) &&
|
|
132
|
+
ts.isVariableDeclaration(current.parent) &&
|
|
133
|
+
ts.isIdentifier(current.parent.name) &&
|
|
134
|
+
isNamedExport(current.parent, checker)) {
|
|
135
|
+
return { kind: 'factory', exportName: current.parent.name.text };
|
|
136
|
+
}
|
|
137
|
+
if (ts.isFunctionDeclaration(current) &&
|
|
138
|
+
current.name &&
|
|
139
|
+
isNamedExportedFunction(current, checker)) {
|
|
140
|
+
return { kind: 'factory', exportName: current.name.text };
|
|
141
|
+
}
|
|
142
|
+
return { kind: 'unreachable' };
|
|
143
|
+
}
|
|
144
|
+
current = current.parent;
|
|
145
|
+
}
|
|
146
|
+
return { kind: 'import' };
|
|
147
|
+
};
|
|
21
148
|
export const addPermission = (logger, node, checker, state) => {
|
|
22
149
|
if (!ts.isCallExpression(node))
|
|
23
150
|
return;
|
|
24
|
-
const {
|
|
25
|
-
|
|
26
|
-
if (!
|
|
151
|
+
const { arguments: args } = node;
|
|
152
|
+
const callee = resolveCalleeName(node, checker, PERMISSION_HELPERS);
|
|
153
|
+
if (!callee)
|
|
27
154
|
return;
|
|
28
|
-
}
|
|
29
155
|
// Handle pikkuPermission(...) - individual permission function definition
|
|
30
|
-
if (
|
|
156
|
+
if (callee === 'pikkuPermission') {
|
|
31
157
|
// Skip if nested inside pikkuPermissionFactory — the factory handler extracts services itself
|
|
32
|
-
if (isInsidePermissionContainer(node))
|
|
158
|
+
if (isInsidePermissionContainer(node, checker))
|
|
33
159
|
return;
|
|
34
160
|
const arg = args[0];
|
|
35
161
|
if (!arg)
|
|
@@ -70,9 +196,13 @@ export const addPermission = (logger, node, checker, state) => {
|
|
|
70
196
|
pikkuFuncId = node.parent.name.text;
|
|
71
197
|
}
|
|
72
198
|
else {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
199
|
+
const globalId = inlineGlobalPermissionId(node, checker, state.rootDir);
|
|
200
|
+
if (!globalId) {
|
|
201
|
+
logger.error(`• pikkuPermission() must be assigned to a variable or object property. ` +
|
|
202
|
+
`Extract it to a const: const myPermission = pikkuPermission(...)`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
pikkuFuncId = globalId;
|
|
76
206
|
}
|
|
77
207
|
}
|
|
78
208
|
const dataParam = actualHandler.parameters[1];
|
|
@@ -91,8 +221,8 @@ export const addPermission = (logger, node, checker, state) => {
|
|
|
91
221
|
logger.debug(`• Found permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}${!requiresData ? ' (auth-only)' : ''}`);
|
|
92
222
|
return;
|
|
93
223
|
}
|
|
94
|
-
if (
|
|
95
|
-
if (isInsidePermissionContainer(node))
|
|
224
|
+
if (callee === 'pikkuAuth') {
|
|
225
|
+
if (isInsidePermissionContainer(node, checker))
|
|
96
226
|
return;
|
|
97
227
|
const arg = args[0];
|
|
98
228
|
if (!arg)
|
|
@@ -137,9 +267,13 @@ export const addPermission = (logger, node, checker, state) => {
|
|
|
137
267
|
pikkuFuncId = node.parent.name.text;
|
|
138
268
|
}
|
|
139
269
|
else {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
270
|
+
const globalId = inlineGlobalPermissionId(node, checker, state.rootDir);
|
|
271
|
+
if (!globalId) {
|
|
272
|
+
logger.error(`• pikkuAuth() must be assigned to a variable or object property. ` +
|
|
273
|
+
`Extract it to a const: const myAuth = pikkuAuth(...)`);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
pikkuFuncId = globalId;
|
|
143
277
|
}
|
|
144
278
|
}
|
|
145
279
|
state.permissions.definitions[pikkuFuncId] = {
|
|
@@ -156,7 +290,7 @@ export const addPermission = (logger, node, checker, state) => {
|
|
|
156
290
|
return;
|
|
157
291
|
}
|
|
158
292
|
// Handle pikkuPermissionFactory(...) - permission factory function
|
|
159
|
-
if (
|
|
293
|
+
if (callee === 'pikkuPermissionFactory') {
|
|
160
294
|
const factoryNode = args[0];
|
|
161
295
|
if (!factoryNode)
|
|
162
296
|
return;
|
|
@@ -175,8 +309,8 @@ export const addPermission = (logger, node, checker, state) => {
|
|
|
175
309
|
};
|
|
176
310
|
const findPikkuPermissionCall = (node) => {
|
|
177
311
|
if (ts.isCallExpression(node)) {
|
|
178
|
-
|
|
179
|
-
|
|
312
|
+
if (resolveCalleeName(node, checker, PERMISSION_HELPERS) ===
|
|
313
|
+
'pikkuPermission') {
|
|
180
314
|
return node;
|
|
181
315
|
}
|
|
182
316
|
}
|
|
@@ -229,4 +363,61 @@ export const addPermission = (logger, node, checker, state) => {
|
|
|
229
363
|
logger.debug(`• Found permission factory with services: ${services.services.join(', ')}`);
|
|
230
364
|
return;
|
|
231
365
|
}
|
|
366
|
+
if (callee === 'addGlobalPermission') {
|
|
367
|
+
const permissionsArg = args[0];
|
|
368
|
+
const sourceFile = node.getSourceFile().fileName;
|
|
369
|
+
let entries;
|
|
370
|
+
const literal = permissionsArg && resolvePermissionLiteral(permissionsArg, checker);
|
|
371
|
+
if (literal && ts.isArrayLiteralExpression(literal)) {
|
|
372
|
+
entries = literal.elements;
|
|
373
|
+
}
|
|
374
|
+
else if (literal) {
|
|
375
|
+
entries = literal.properties.flatMap((p) => ts.isPropertyAssignment(p)
|
|
376
|
+
? [p.initializer]
|
|
377
|
+
: ts.isShorthandPropertyAssignment(p)
|
|
378
|
+
? [p.name]
|
|
379
|
+
: []);
|
|
380
|
+
}
|
|
381
|
+
if (!entries) {
|
|
382
|
+
logger.warn(`• addGlobalPermission(...) in ${sourceFile} is not given an array or object it can follow to a literal, so the services its permissions use cannot be seen.`);
|
|
383
|
+
}
|
|
384
|
+
const definitionIds = (entries ?? []).flatMap((entry) => {
|
|
385
|
+
if (ts.isIdentifier(entry)) {
|
|
386
|
+
const { pikkuFuncId } = extractFunctionName(entry, checker, state.rootDir);
|
|
387
|
+
return [pikkuFuncId.startsWith('__temp_') ? entry.text : pikkuFuncId];
|
|
388
|
+
}
|
|
389
|
+
if (ts.isCallExpression(entry)) {
|
|
390
|
+
const inline = inlineGlobalPermissionId(entry, checker, state.rootDir);
|
|
391
|
+
if (inline)
|
|
392
|
+
return [inline];
|
|
393
|
+
if (ts.isPropertyAssignment(entry.parent) &&
|
|
394
|
+
ts.isIdentifier(entry.parent.name) &&
|
|
395
|
+
resolveCalleeName(entry, checker, PERMISSION_HELPERS)) {
|
|
396
|
+
return [entry.parent.name.text];
|
|
397
|
+
}
|
|
398
|
+
const { pikkuFuncId } = extractFunctionName(entry.expression, checker, state.rootDir);
|
|
399
|
+
return pikkuFuncId.startsWith('__temp_') &&
|
|
400
|
+
ts.isIdentifier(entry.expression)
|
|
401
|
+
? [entry.expression.text]
|
|
402
|
+
: [pikkuFuncId];
|
|
403
|
+
}
|
|
404
|
+
return [];
|
|
405
|
+
});
|
|
406
|
+
const activation = globalPermissionActivation(node, checker);
|
|
407
|
+
if (activation.kind === 'unreachable') {
|
|
408
|
+
logger.warn(`• addGlobalPermission(...) in ${sourceFile} sits inside a function that nothing calls: only a call at the top level, or inside an exported function with no parameters, is run by the generated permissions file. This global permission is NOT registered. Export it as \`export const name = () => { addGlobalPermission(...) }\` or move it to the top level.`);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const exportName = activation.kind === 'factory' ? activation.exportName : null;
|
|
412
|
+
const position = node.getStart();
|
|
413
|
+
if (!state.permissions.global.some((g) => g.sourceFile === sourceFile && g.position === position)) {
|
|
414
|
+
state.permissions.global.push({
|
|
415
|
+
sourceFile,
|
|
416
|
+
position,
|
|
417
|
+
exportName,
|
|
418
|
+
definitionIds,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
logger.debug(`• Found global permissions in ${sourceFile}: [${definitionIds.join(', ')}]`);
|
|
422
|
+
}
|
|
232
423
|
};
|
package/dist/add/add-scope.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
import type { AddWiring } from '../types.js';
|
|
2
|
-
|
|
3
|
-
* Folds `declareScopes` ids into `state.scopes.definitions`. An id the
|
|
4
|
-
* definitions already hold (an addon's tree, a `defineScope`) is left alone;
|
|
5
|
-
* a missing one is added as a bare node under its root, creating the root when
|
|
6
|
-
* nothing declared it.
|
|
7
|
-
*/
|
|
8
|
-
export declare const applyDeclaredScopes: (state: Parameters<AddWiring>[3]) => void;
|
|
2
|
+
export { applyDeclaredScopes } from '../utils/apply-declared-scopes.js';
|
|
9
3
|
export declare const addScope: AddWiring;
|
package/dist/add/add-scope.js
CHANGED
|
@@ -111,27 +111,7 @@ const addDeclareScopes = (logger, node, state) => {
|
|
|
111
111
|
}
|
|
112
112
|
state.scopes.files.add(sourceFile);
|
|
113
113
|
};
|
|
114
|
-
|
|
115
|
-
* Folds `declareScopes` ids into `state.scopes.definitions`. An id the
|
|
116
|
-
* definitions already hold (an addon's tree, a `defineScope`) is left alone;
|
|
117
|
-
* a missing one is added as a bare node under its root, creating the root when
|
|
118
|
-
* nothing declared it.
|
|
119
|
-
*/
|
|
120
|
-
export const applyDeclaredScopes = (state) => {
|
|
121
|
-
for (const [id, sourceFile] of state.scopes.declared ?? []) {
|
|
122
|
-
const [rootName, ...rest] = id.split(SEPARATOR);
|
|
123
|
-
let root = state.scopes.definitions.find((d) => d.name === rootName);
|
|
124
|
-
if (!root) {
|
|
125
|
-
root = { name: rootName, sourceFile };
|
|
126
|
-
state.scopes.definitions.push(root);
|
|
127
|
-
}
|
|
128
|
-
let level = root;
|
|
129
|
-
for (const segment of rest) {
|
|
130
|
-
level.scopes ??= {};
|
|
131
|
-
level = level.scopes[segment] ??= {};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
};
|
|
114
|
+
export { applyDeclaredScopes } from '../utils/apply-declared-scopes.js';
|
|
135
115
|
export const addScope = (logger, node, checker, state, _options) => {
|
|
136
116
|
if (!ts.isCallExpression(node)) {
|
|
137
117
|
return;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AddWiring } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A middleware or permission helper the inspector can only see by its direct
|
|
4
|
+
* calls. Passed around as a value — into an array, through a ternary, as an
|
|
5
|
+
* argument — whatever it registers is invisible to codegen, and a gate that
|
|
6
|
+
* codegen never imports is a gate that never runs. Say so rather than
|
|
7
|
+
* generate an app that is silently open.
|
|
8
|
+
*/
|
|
9
|
+
export declare const checkIndirectRegistrations: AddWiring;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { ErrorCode } from '../error-codes.js';
|
|
3
|
+
import { MIDDLEWARE_HELPERS, PERMISSION_HELPERS, calleeNames, isNamespaceReceiver, } from '../utils/resolve-callee-name.js';
|
|
4
|
+
const isGateHelper = (name) => MIDDLEWARE_HELPERS.has(name) || PERMISSION_HELPERS.has(name);
|
|
5
|
+
const localNamesCache = new WeakMap();
|
|
6
|
+
/** The names a file can reach a gate helper by: its own, and its import aliases. */
|
|
7
|
+
const localGateNames = (sourceFile) => {
|
|
8
|
+
const cached = localNamesCache.get(sourceFile);
|
|
9
|
+
if (cached)
|
|
10
|
+
return cached;
|
|
11
|
+
const names = new Set([...MIDDLEWARE_HELPERS, ...PERMISSION_HELPERS]);
|
|
12
|
+
for (const statement of sourceFile.statements) {
|
|
13
|
+
const bindings = ts.isImportDeclaration(statement)
|
|
14
|
+
? statement.importClause?.namedBindings
|
|
15
|
+
: undefined;
|
|
16
|
+
if (!bindings || !ts.isNamedImports(bindings))
|
|
17
|
+
continue;
|
|
18
|
+
for (const element of bindings.elements) {
|
|
19
|
+
if (isGateHelper((element.propertyName ?? element.name).text)) {
|
|
20
|
+
names.add(element.name.text);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
localNamesCache.set(sourceFile, names);
|
|
25
|
+
return names;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Whether a reference to a gate helper sits where the inspector already
|
|
29
|
+
* accounts for it: called directly, imported or re-exported, bound to another
|
|
30
|
+
* `const` name, named as a declaration, or used as a type.
|
|
31
|
+
*/
|
|
32
|
+
const isAccountedFor = (reference) => {
|
|
33
|
+
const parent = reference.parent;
|
|
34
|
+
if (ts.isCallExpression(parent) && parent.expression === reference) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (ts.isImportSpecifier(parent) ||
|
|
38
|
+
ts.isExportSpecifier(parent) ||
|
|
39
|
+
ts.isImportClause(parent) ||
|
|
40
|
+
ts.isNamespaceImport(parent) ||
|
|
41
|
+
ts.isExportAssignment(parent) ||
|
|
42
|
+
ts.isQualifiedName(parent) ||
|
|
43
|
+
ts.isTypeNode(parent) ||
|
|
44
|
+
ts.isExpressionWithTypeArguments(parent)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (ts.isVariableDeclaration(parent) &&
|
|
48
|
+
parent.initializer === reference &&
|
|
49
|
+
ts.getCombinedNodeFlags(parent) & ts.NodeFlags.Const) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if ('name' in parent && parent.name === reference) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
for (let current = parent; current; current = current.parent) {
|
|
56
|
+
if ((ts.isVariableDeclaration(current) ||
|
|
57
|
+
ts.isFunctionDeclaration(current)) &&
|
|
58
|
+
current.name &&
|
|
59
|
+
ts.isIdentifier(current.name) &&
|
|
60
|
+
isGateHelper(current.name.text)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* A middleware or permission helper the inspector can only see by its direct
|
|
68
|
+
* calls. Passed around as a value — into an array, through a ternary, as an
|
|
69
|
+
* argument — whatever it registers is invisible to codegen, and a gate that
|
|
70
|
+
* codegen never imports is a gate that never runs. Say so rather than
|
|
71
|
+
* generate an app that is silently open.
|
|
72
|
+
*/
|
|
73
|
+
export const checkIndirectRegistrations = (logger, node, checker) => {
|
|
74
|
+
if (!ts.isIdentifier(node))
|
|
75
|
+
return;
|
|
76
|
+
const sourceFile = node.getSourceFile();
|
|
77
|
+
if (sourceFile.isDeclarationFile)
|
|
78
|
+
return;
|
|
79
|
+
if (!localGateNames(sourceFile).has(node.text))
|
|
80
|
+
return;
|
|
81
|
+
let reference = node;
|
|
82
|
+
if (ts.isPropertyAccessExpression(node.parent)) {
|
|
83
|
+
if (node.parent.name !== node)
|
|
84
|
+
return;
|
|
85
|
+
if (!isNamespaceReceiver(node.parent.expression, checker))
|
|
86
|
+
return;
|
|
87
|
+
reference = node.parent;
|
|
88
|
+
}
|
|
89
|
+
if (isAccountedFor(reference))
|
|
90
|
+
return;
|
|
91
|
+
const helper = [...calleeNames(reference, checker)]
|
|
92
|
+
.reverse()
|
|
93
|
+
.find(isGateHelper);
|
|
94
|
+
if (!helper)
|
|
95
|
+
return;
|
|
96
|
+
const symbol = checker.getSymbolAtLocation(node);
|
|
97
|
+
if (symbol?.valueDeclaration && ts.isParameter(symbol.valueDeclaration)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const type = checker.getTypeAtLocation(reference);
|
|
101
|
+
if (type.getCallSignatures().length === 0 &&
|
|
102
|
+
!(type.flags & ts.TypeFlags.Any)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
106
|
+
logger.diagnostic({
|
|
107
|
+
severity: 'error',
|
|
108
|
+
code: ErrorCode.REGISTRATION_NOT_A_DIRECT_CALL,
|
|
109
|
+
message: `'${reference.getText()}' (${helper}) is used as a value at ${sourceFile.fileName}:${line + 1}. ` +
|
|
110
|
+
`The inspector records middleware and permissions only where ${helper} is called directly, ` +
|
|
111
|
+
`so whatever this registers is never imported by the generated code and does not run. ` +
|
|
112
|
+
`Call ${helper}(...) directly.`,
|
|
113
|
+
});
|
|
114
|
+
};
|
package/dist/error-codes.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export declare enum ErrorCode {
|
|
|
61
61
|
EXPOSED_FUNCTION_HAS_NO_GATE = "PKU574",
|
|
62
62
|
TAG_RESOLVES_TO_NO_MIDDLEWARE = "PKU575",
|
|
63
63
|
PERMISSIONS_IN_BODY_NOT_ALLOWED = "PKU576",
|
|
64
|
+
REGISTRATION_NOT_A_DIRECT_CALL = "PKU577",
|
|
64
65
|
DUPLICATE_FUNCTION_VERSION = "PKU850",
|
|
65
66
|
DUPLICATE_FUNCTION_NAME = "PKU851",
|
|
66
67
|
MANIFEST_MISSING = "PKU860",
|
package/dist/error-codes.js
CHANGED
|
@@ -74,6 +74,7 @@ export var ErrorCode;
|
|
|
74
74
|
ErrorCode["EXPOSED_FUNCTION_HAS_NO_GATE"] = "PKU574";
|
|
75
75
|
ErrorCode["TAG_RESOLVES_TO_NO_MIDDLEWARE"] = "PKU575";
|
|
76
76
|
ErrorCode["PERMISSIONS_IN_BODY_NOT_ALLOWED"] = "PKU576";
|
|
77
|
+
ErrorCode["REGISTRATION_NOT_A_DIRECT_CALL"] = "PKU577";
|
|
77
78
|
// Versioning errors
|
|
78
79
|
ErrorCode["DUPLICATE_FUNCTION_VERSION"] = "PKU850";
|
|
79
80
|
ErrorCode["DUPLICATE_FUNCTION_NAME"] = "PKU851";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { InspectorState, InspectorLogger, InspectorOptions } from './types.js';
|
|
2
|
+
export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
|
|
3
|
+
export type { SerializableInspectorState } from './utils/serialize-inspector-state.js';
|
|
4
|
+
/**
|
|
5
|
+
* The checker-free half of an inspection: everything that derives from the
|
|
6
|
+
* state analysis produced. It never loads `typescript`, so it runs the same
|
|
7
|
+
* against a live analysis and against a state deserialized from another
|
|
8
|
+
* engine.
|
|
9
|
+
*/
|
|
10
|
+
export declare const finalizeInspectorState: <S extends Omit<InspectorState, "typesLookup">>(logger: InspectorLogger, analyzed: S, options?: InspectorOptions) => Promise<S>;
|
package/dist/finalize.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { performance } from 'perf_hooks';
|
|
2
|
+
import { applyDeclaredScopes } from './utils/apply-declared-scopes.js';
|
|
3
|
+
import { getFilesAndMethods } from './utils/get-files-and-methods.js';
|
|
4
|
+
import { aggregateRequiredServices, stampAuthHandlerServices, validateAgentModels, validateAgentToolReferences, validateAgentScorerReferences, validateSecretOverrides, validateVariableOverrides, validateCredentialOverrides, validateAddonUses, validateAddonContentGrants, validateRemoteAddonDependencies, validateRemoteAddonAuth, validateScopeReferences, validateSystemRoleScopes, validateVirtualUserRoles, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, validateNoSecretAliasServices, registerDerivedOAuth2AppSecrets, validateSecretUsage, computeDiagnostics, validateSchemaReferences, validateSchemaWiringSeparation, validateScenarioServices, validateScenarioSteps, validateScenarioFeatures, validateWorkflowGraphAddons, } from './utils/post-process.js';
|
|
5
|
+
import { validateExposedFunctionsGated } from './utils/validate-exposed-functions-gated.js';
|
|
6
|
+
import { validateTagsResolveToMiddleware } from './utils/validate-tags-resolve-to-middleware.js';
|
|
7
|
+
import { validateSingleStepWorkflows } from './utils/validate-single-step-workflows.js';
|
|
8
|
+
import { annotateHttpRouteAuth } from './utils/annotate-http-route-auth.js';
|
|
9
|
+
import { generateOpenAPISpec } from './utils/serialize-openapi-json.js';
|
|
10
|
+
import { pikkuState } from '@pikku/core/state';
|
|
11
|
+
import { finalizeWorkflows } from './utils/workflow/graph/finalize-workflows.js';
|
|
12
|
+
import { finalizeWorkflowHelperTypes, finalizeWorkflowWires, } from './utils/workflow/graph/finalize-workflow-wires.js';
|
|
13
|
+
import { loadAddonSchemas } from './utils/load-addon-functions-meta.js';
|
|
14
|
+
import { computeContractHashes, extractContractsFromMeta, updateManifest, createEmptyManifest, validateContracts, } from './utils/contract-hashes.js';
|
|
15
|
+
export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
|
|
16
|
+
/**
|
|
17
|
+
* The checker-free half of an inspection: everything that derives from the
|
|
18
|
+
* state analysis produced. It never loads `typescript`, so it runs the same
|
|
19
|
+
* against a live analysis and against a state deserialized from another
|
|
20
|
+
* engine.
|
|
21
|
+
*/
|
|
22
|
+
export const finalizeInspectorState = async (logger, analyzed, options = {}) => {
|
|
23
|
+
const state = analyzed;
|
|
24
|
+
if (!options.setupOnly) {
|
|
25
|
+
if (options.schemaConfig) {
|
|
26
|
+
computeContractHashes(state.schemas, state.functions.typesMap, state.functions.meta);
|
|
27
|
+
computeRequiredSchemas(state, options);
|
|
28
|
+
}
|
|
29
|
+
// Re-load addon schemas (generateAllSchemas replaces state.schemas)
|
|
30
|
+
await loadAddonSchemas(logger, state);
|
|
31
|
+
// After the addon merge, not before: an addon supplies its own schemas here,
|
|
32
|
+
// and checking earlier would report every one of them as unresolved.
|
|
33
|
+
if (options.schemaConfig) {
|
|
34
|
+
validateSchemaReferences(logger, state);
|
|
35
|
+
}
|
|
36
|
+
state.manifest.initial = options.manifest ?? null;
|
|
37
|
+
const contracts = extractContractsFromMeta(state.functions.meta);
|
|
38
|
+
const baseManifest = state.manifest.initial ?? createEmptyManifest();
|
|
39
|
+
state.manifest.current = updateManifest(baseManifest, contracts);
|
|
40
|
+
state.manifest.errors = validateContracts(baseManifest, contracts).errors;
|
|
41
|
+
finalizeWorkflows(state);
|
|
42
|
+
finalizeWorkflowHelperTypes(state);
|
|
43
|
+
finalizeWorkflowWires(state);
|
|
44
|
+
}
|
|
45
|
+
// Populate filesAndMethods
|
|
46
|
+
const startFilesAndMethods = performance.now();
|
|
47
|
+
const { result, errors } = getFilesAndMethods(state, options.types);
|
|
48
|
+
state.filesAndMethods = result;
|
|
49
|
+
state.filesAndMethodsErrors = errors;
|
|
50
|
+
logger.debug(`Get files and methods completed in ${(performance.now() - startFilesAndMethods).toFixed(2)}ms`);
|
|
51
|
+
if (!options.setupOnly) {
|
|
52
|
+
// Everything below re-derives from the state this pass just built, so it
|
|
53
|
+
// supersedes whatever an earlier pass concluded about a source graph the
|
|
54
|
+
// run has since finished generating into.
|
|
55
|
+
logger.beginValidationPass?.();
|
|
56
|
+
const startAggregate = performance.now();
|
|
57
|
+
// Apply the inspected auth handler service set before aggregation so it
|
|
58
|
+
// flows into requiredServices (the generated handler's own func is opaque).
|
|
59
|
+
stampAuthHandlerServices(state);
|
|
60
|
+
aggregateRequiredServices(state);
|
|
61
|
+
logger.debug(`Aggregate required services completed in ${(performance.now() - startAggregate).toFixed(2)}ms`);
|
|
62
|
+
computeResolvedIOTypes(state);
|
|
63
|
+
computeMiddlewareGroupsMeta(state);
|
|
64
|
+
computePermissionsGroupsMeta(state);
|
|
65
|
+
validateNoSecretAliasServices(logger, state);
|
|
66
|
+
registerDerivedOAuth2AppSecrets(state);
|
|
67
|
+
validateSecretUsage(logger, state);
|
|
68
|
+
computeDiagnostics(state);
|
|
69
|
+
validateSchemaWiringSeparation(logger, state);
|
|
70
|
+
validateWorkflowGraphAddons(logger, state);
|
|
71
|
+
validateSingleStepWorkflows(logger, state);
|
|
72
|
+
validateScenarioServices(logger, state);
|
|
73
|
+
validateScenarioSteps(logger, state);
|
|
74
|
+
validateScenarioFeatures(logger, state);
|
|
75
|
+
// Needs every route, every function and every wireAddon declaration
|
|
76
|
+
// together, so it can only run once the whole program is inspected.
|
|
77
|
+
annotateHttpRouteAuth(state);
|
|
78
|
+
// Last of the validators: they read function meta and every wireAddon
|
|
79
|
+
// declaration together, so they need the whole program inspected.
|
|
80
|
+
validateExposedFunctionsGated(logger, state);
|
|
81
|
+
validateTagsResolveToMiddleware(logger, state);
|
|
82
|
+
if (options.openAPI) {
|
|
83
|
+
state.openAPISpec = await generateOpenAPISpec(logger, state.functions.meta, state.http.meta, state.schemas, options.openAPI.additionalInfo, pikkuState(null, 'misc', 'errors'));
|
|
84
|
+
}
|
|
85
|
+
validateAgentModels(logger, state, options.modelAliases);
|
|
86
|
+
validateAgentToolReferences(logger, state, options);
|
|
87
|
+
validateAgentScorerReferences(logger, state);
|
|
88
|
+
validateSecretOverrides(logger, state);
|
|
89
|
+
validateVariableOverrides(logger, state);
|
|
90
|
+
validateCredentialOverrides(logger, state);
|
|
91
|
+
validateAddonUses(logger, state);
|
|
92
|
+
validateAddonContentGrants(logger, state);
|
|
93
|
+
validateRemoteAddonDependencies(logger, state);
|
|
94
|
+
validateRemoteAddonAuth(logger, state);
|
|
95
|
+
applyDeclaredScopes(state);
|
|
96
|
+
validateScopeReferences(logger, state);
|
|
97
|
+
validateSystemRoleScopes(logger, state);
|
|
98
|
+
validateVirtualUserRoles(logger, state);
|
|
99
|
+
logger.endValidationPass?.();
|
|
100
|
+
}
|
|
101
|
+
return analyzed;
|
|
102
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
export { inspect,
|
|
1
|
+
export { inspect, analyzeProgram } from './inspector.js';
|
|
2
|
+
export { getInitialInspectorState } from './initial-state.js';
|
|
3
|
+
export { finalizeInspectorState } from './finalize.js';
|
|
2
4
|
export type { TypesMap } from './types-map.js';
|
|
3
|
-
export type { AddonConfig, AuthDefinition, CredentialOverrideMeta, ScreensManifestMeta, AddonScreenMeta, InspectorDiagnostic, InspectorFilters, InspectorHTTPState, InspectorLogger, InspectorMiddlewareState, InspectorOptions, InspectorPermissionState, InspectorState, MetaInputTypes, MiddlewareGroupMeta, MiddlewareRegistration, SchemaRef, } from './types.js';
|
|
5
|
+
export type { AddonConfig, AuthDefinition, CredentialOverrideMeta, ScreensManifestMeta, AddonScreenMeta, InspectorDiagnostic, InspectorFilters, InspectorHTTPState, InspectorLogger, InspectorMiddlewareState, InspectorOptions, InspectorPermissionState, InspectorServiceTypes, InspectorState, MetaInputTypes, MiddlewareGroupMeta, MiddlewareRegistration, SchemaRef, } from './types.js';
|
|
4
6
|
export type { FilesAndMethodsErrors } from './utils/get-files-and-methods.js';
|
|
5
7
|
export { ErrorCode } from './error-codes.js';
|
|
6
8
|
export type { DiagnosticSeverity, CodedDiagnostic } from './error-codes.js';
|
|
7
|
-
export { AUTH_HANDLER_FUNC_ID } from './
|
|
9
|
+
export { AUTH_HANDLER_FUNC_ID } from './utils/inspector-constants.js';
|
|
8
10
|
export { SCENARIO_INSTRUMENTATION_FUNCTIONS, isScenarioInstrumentationFunction, } from './add/scenario-instrumentation.js';
|
|
9
11
|
export type { ScenarioInstrumentationFunction } from './add/scenario-instrumentation.js';
|
|
10
12
|
export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
|
|
11
13
|
export type { SerializableInspectorState } from './utils/serialize-inspector-state.js';
|
|
12
14
|
export { filterInspectorState } from './utils/filter-inspector-state.js';
|
|
13
15
|
export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
|
|
14
|
-
export { generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
16
|
+
export { generateCustomTypes, customTypeNames, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
17
|
+
export type { GenerateCustomTypesOptions } from './utils/custom-types-generator.js';
|
|
15
18
|
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
16
19
|
export type { SurfaceUsageCounts } from './utils/extract-surface-usage.js';
|
|
17
20
|
export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
@@ -19,11 +22,10 @@ export type { ContractEntry, VersionHashEntry, VersionValidateError, VersionMani
|
|
|
19
22
|
export type { InspectorFeature, InspectorFeatureEntry } from './types.js';
|
|
20
23
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
21
24
|
export type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js';
|
|
22
|
-
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/
|
|
25
|
+
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/deserialize-dsl-workflow.js';
|
|
23
26
|
export { getFilesAndMethods } from './utils/get-files-and-methods.js';
|
|
24
27
|
export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
|
|
25
28
|
export type { SerializedWorkflowGraph, SerializedWorkflowGraphs, } from './utils/workflow/graph/index.js';
|
|
26
|
-
export { resolveCoreType } from './utils/resolve-core-type.js';
|
|
27
29
|
export { readModuleSpecifiers } from './utils/read-module-specifiers.js';
|
|
28
30
|
export { readJsxLiteralText } from './utils/read-jsx-literal-text.js';
|
|
29
31
|
export { readTsconfigOutDir } from './utils/read-tsconfig-out-dir.js';
|