@pikku/inspector 0.12.103 → 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.
Files changed (74) hide show
  1. package/dist/add/add-auth.d.ts +1 -9
  2. package/dist/add/add-auth.js +64 -17
  3. package/dist/add/add-cli.js +25 -11
  4. package/dist/add/add-functions.js +35 -39
  5. package/dist/add/add-keyed-wiring.js +22 -2
  6. package/dist/add/add-middleware.js +17 -16
  7. package/dist/add/add-permission.d.ts +0 -3
  8. package/dist/add/add-permission.js +213 -22
  9. package/dist/add/add-scope.d.ts +1 -7
  10. package/dist/add/add-scope.js +1 -21
  11. package/dist/add/add-virtual-users.d.ts +13 -0
  12. package/dist/add/{add-personas.js → add-virtual-users.js} +33 -33
  13. package/dist/add/add-workflow.js +14 -10
  14. package/dist/add/check-indirect-registrations.d.ts +9 -0
  15. package/dist/add/check-indirect-registrations.js +114 -0
  16. package/dist/error-codes.d.ts +5 -3
  17. package/dist/error-codes.js +5 -3
  18. package/dist/finalize.d.ts +10 -0
  19. package/dist/finalize.js +102 -0
  20. package/dist/index.d.ts +8 -6
  21. package/dist/index.js +6 -5
  22. package/dist/initial-state.d.ts +7 -0
  23. package/dist/initial-state.js +216 -0
  24. package/dist/inspector.d.ts +6 -4
  25. package/dist/inspector.js +28 -300
  26. package/dist/lite.d.ts +26 -0
  27. package/dist/lite.js +25 -0
  28. package/dist/source-file-cache.d.ts +11 -0
  29. package/dist/source-file-cache.js +34 -10
  30. package/dist/types-map.js +2 -1
  31. package/dist/types.d.ts +35 -3
  32. package/dist/utils/apply-declared-scopes.d.ts +8 -0
  33. package/dist/utils/apply-declared-scopes.js +21 -0
  34. package/dist/utils/canonical-type-string.d.ts +14 -0
  35. package/dist/utils/canonical-type-string.js +162 -0
  36. package/dist/utils/context-based-id.d.ts +7 -0
  37. package/dist/utils/context-based-id.js +9 -0
  38. package/dist/utils/custom-types-generator.d.ts +15 -1
  39. package/dist/utils/custom-types-generator.js +17 -5
  40. package/dist/utils/ensure-function-metadata.d.ts +0 -14
  41. package/dist/utils/ensure-function-metadata.js +4 -1
  42. package/dist/utils/extract-function-name.d.ts +1 -1
  43. package/dist/utils/extract-function-name.js +2 -4
  44. package/dist/utils/filter-inspector-state.js +31 -1
  45. package/dist/utils/get-property-value.d.ts +5 -0
  46. package/dist/utils/get-property-value.js +15 -1
  47. package/dist/utils/inspector-constants.d.ts +16 -0
  48. package/dist/utils/inspector-constants.js +16 -0
  49. package/dist/utils/load-addon-functions-meta.js +1 -0
  50. package/dist/utils/post-process.d.ts +35 -36
  51. package/dist/utils/post-process.js +218 -193
  52. package/dist/utils/resolve-callee-name.d.ts +27 -0
  53. package/dist/utils/resolve-callee-name.js +147 -0
  54. package/dist/utils/resolve-service-types.d.ts +8 -0
  55. package/dist/utils/resolve-service-types.js +21 -0
  56. package/dist/utils/schema-generator.js +29 -2
  57. package/dist/utils/serialize-inspector-state.d.ts +9 -3
  58. package/dist/utils/serialize-inspector-state.js +22 -7
  59. package/dist/utils/single-declaration.d.ts +3 -3
  60. package/dist/utils/single-declaration.js +3 -3
  61. package/dist/utils/validate-single-step-workflows.d.ts +3 -0
  62. package/dist/utils/validate-single-step-workflows.js +32 -0
  63. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +3 -3
  64. package/dist/utils/workflow/dsl/patterns.d.ts +4 -11
  65. package/dist/utils/workflow/dsl/patterns.js +5 -11
  66. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +6 -6
  67. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +5 -5
  68. package/dist/visit.js +4 -2
  69. package/package.json +25 -9
  70. package/CHANGELOG.md +0 -4318
  71. package/OPTIMIZATION-PLAN.md +0 -201
  72. package/dist/add/add-personas.d.ts +0 -13
  73. package/run-tests.sh +0 -68
  74. package/src/utils/test-data/inspector-state.json +0 -1687
package/dist/inspector.js CHANGED
@@ -1,243 +1,21 @@
1
1
  import * as ts from 'typescript';
2
- import { applyDeclaredScopes } from './add/add-scope.js';
3
2
  import { performance } from 'perf_hooks';
4
3
  import { dirname, relative, resolve } from 'path';
5
4
  import { visitSetup, visitFunctions, visitRoutes } from './visit.js';
6
- import { TypesMap } from './types-map.js';
7
- import { getFilesAndMethods } from './utils/get-files-and-methods.js';
8
5
  import { findCommonAncestor } from './utils/find-root-dir.js';
9
6
  import { createSourceFileCacheHost } from './source-file-cache.js';
10
7
  import { createNestedProjectFilter } from './utils/nested-project-filter.js';
11
8
  import { toProgramPath } from './utils/program-path.js';
12
- import { aggregateRequiredServices, stampAuthHandlerServices, validateAgentModels, validateAgentToolReferences, validateAgentScorerReferences, validateSecretOverrides, validateVariableOverrides, validateCredentialOverrides, validateAddonUses, validateAddonContentGrants, validateRemoteAddonDependencies, validateRemoteAddonAuth, validateScopeReferences, validateSystemRoleScopes, validatePersonaRoles, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, validateNoSecretAliasServices, registerDerivedOAuth2AppSecrets, validateSecretUsage, computeDiagnostics, validateSchemaReferences, validateSchemaWiringSeparation, validateScenarioServices, validateScenarioSteps, validateScenarioFeatures, validateWorkflowGraphAddons, } from './utils/post-process.js';
13
- import { validateExposedFunctionsGated } from './utils/validate-exposed-functions-gated.js';
14
- import { validateTagsResolveToMiddleware } from './utils/validate-tags-resolve-to-middleware.js';
15
- import { annotateHttpRouteAuth } from './utils/annotate-http-route-auth.js';
16
- import { generateOpenAPISpec } from './utils/serialize-openapi-json.js';
17
- import { pikkuState } from '@pikku/core/state';
18
9
  import { resolveLatestVersions } from './utils/resolve-versions.js';
19
10
  import { validateWebhookSourceTriggers } from './add/add-trigger.js';
20
- import { finalizeWorkflows } from './utils/workflow/graph/finalize-workflows.js';
21
- import { finalizeWorkflowHelperTypes, finalizeWorkflowWires, } from './utils/workflow/graph/finalize-workflow-wires.js';
22
11
  import { generateAllSchemas } from './utils/schema-generator.js';
23
12
  import { extractSecretUsage } from './utils/extract-secret-usage.js';
24
13
  import { accumulateSurfaceUsage, surfaceUsageArea, } from './utils/extract-surface-usage.js';
25
- import { loadAddonFunctionsMeta, loadAddonSchemas, } from './utils/load-addon-functions-meta.js';
26
- import { computeContractHashes, extractContractsFromMeta, updateManifest, createEmptyManifest, validateContracts, } from './utils/contract-hashes.js';
27
- /**
28
- * Creates an initial/empty inspector state with all required properties initialized
29
- * @param rootDir - The root directory for the project
30
- * @returns A fresh InspectorState with empty collections
31
- */
32
- export function getInitialInspectorState(rootDir) {
33
- return {
34
- rootDir,
35
- singletonServicesTypeImportMap: new Map(),
36
- wireServicesTypeImportMap: new Map(),
37
- userSessionTypeImportMap: new Map(),
38
- configTypeImportMap: new Map(),
39
- singletonServicesFactories: new Map(),
40
- wireServicesFactories: new Map(),
41
- wireServicesMeta: new Map(),
42
- addonRequiredParentServices: [],
43
- addonCreatedServices: [],
44
- addonServicesFactorySeen: false,
45
- addonServerlessIncompatible: new Map(),
46
- configFactories: new Map(),
47
- serverLifecycleFactories: new Map(),
48
- filesAndMethods: {},
49
- filesAndMethodsErrors: new Map(),
50
- typesLookup: new Map(),
51
- schemaLookup: new Map(),
52
- schemas: {},
53
- functions: {
54
- typesMap: new TypesMap(),
55
- meta: {},
56
- files: new Map(),
57
- approvalDescriptions: {},
58
- dynamicImportIds: new Set(),
59
- },
60
- http: {
61
- metaInputTypes: new Map(),
62
- meta: {
63
- get: {},
64
- post: {},
65
- put: {},
66
- delete: {},
67
- head: {},
68
- patch: {},
69
- options: {},
70
- },
71
- files: new Set(),
72
- routeMiddleware: new Map(),
73
- },
74
- channels: {
75
- files: new Set(),
76
- meta: {},
77
- },
78
- gateways: {
79
- meta: {},
80
- files: new Set(),
81
- },
82
- triggers: {
83
- meta: {},
84
- sourceMeta: {},
85
- webhookSourceMeta: {},
86
- files: new Set(),
87
- },
88
- scheduledTasks: {
89
- meta: {},
90
- files: new Set(),
91
- },
92
- queueWorkers: {
93
- meta: {},
94
- files: new Set(),
95
- },
96
- workflows: {
97
- meta: {},
98
- files: new Map(),
99
- graphMeta: {},
100
- graphFiles: new Map(),
101
- featureFiles: new Map(),
102
- invokedWorkflows: new Set(),
103
- },
104
- rpc: {
105
- internalMeta: {},
106
- internalFiles: new Map(),
107
- exposedMeta: {},
108
- exposedFiles: new Map(),
109
- remoteMeta: {},
110
- remoteFiles: new Map(),
111
- invokedFunctions: new Set(),
112
- invokedFunctionsByFile: new Map(),
113
- usedAddons: new Set(),
114
- wireAddonDeclarations: new Map(),
115
- wireAddonFiles: new Set(),
116
- },
117
- mcpEndpoints: {
118
- resourcesMeta: {},
119
- toolsMeta: {},
120
- promptsMeta: {},
121
- files: new Set(),
122
- surfaces: {},
123
- },
124
- agents: {
125
- agentsMeta: {},
126
- files: new Map(),
127
- invokedAgentsByFile: new Map(),
128
- },
129
- scorers: {
130
- scorersMeta: {},
131
- files: new Map(),
132
- },
133
- cli: {
134
- meta: {
135
- programs: {},
136
- renderers: {},
137
- },
138
- files: new Set(),
139
- },
140
- nodes: {
141
- meta: {},
142
- files: new Set(),
143
- },
144
- auth: {
145
- providers: [],
146
- plugins: [],
147
- files: new Set(),
148
- definition: null,
149
- },
150
- secrets: {
151
- definitions: [],
152
- files: new Set(),
153
- usage: new Map(),
154
- },
155
- surfaceUsage: {},
156
- credentials: {
157
- definitions: [],
158
- files: new Set(),
159
- },
160
- scopes: {
161
- definitions: [],
162
- files: new Set(),
163
- },
164
- systemRoles: {
165
- definitions: [],
166
- files: new Set(),
167
- },
168
- featureFlags: {
169
- definitions: [],
170
- files: new Set(),
171
- },
172
- personas: {
173
- definitions: [],
174
- files: new Set(),
175
- },
176
- variables: {
177
- definitions: [],
178
- files: new Set(),
179
- },
180
- manifest: {
181
- initial: null,
182
- current: null,
183
- errors: [],
184
- },
185
- middleware: {
186
- definitions: {},
187
- instances: {},
188
- tagMiddleware: new Map(),
189
- globalFiles: new Set(),
190
- },
191
- channelMiddleware: {
192
- definitions: {},
193
- instances: {},
194
- tagMiddleware: new Map(),
195
- },
196
- agentMiddleware: {
197
- definitions: {},
198
- },
199
- permissions: {
200
- definitions: {},
201
- instances: {},
202
- },
203
- serviceAggregation: {
204
- requiredServices: new Set(),
205
- usedFunctions: new Set(),
206
- usedMiddleware: new Set(),
207
- usedPermissions: new Set(),
208
- allSingletonServices: [],
209
- allWireServices: [],
210
- },
211
- resolvedIOTypes: {},
212
- middlewareGroupsMeta: {
213
- definitions: {},
214
- instances: {},
215
- httpGroups: {},
216
- tagGroups: {},
217
- channelMiddleware: {
218
- definitions: {},
219
- instances: {},
220
- tagGroups: {},
221
- },
222
- },
223
- permissionsGroupsMeta: {
224
- definitions: {},
225
- },
226
- requiredSchemas: new Set(),
227
- openAPISpec: null,
228
- diagnostics: [],
229
- addonFunctions: {},
230
- exportedContracts: {
231
- http: {},
232
- cli: {},
233
- channel: {},
234
- addonHttp: {},
235
- addonCli: {},
236
- addonChannel: {},
237
- },
238
- program: null,
239
- };
240
- }
14
+ import { loadAddonFunctionsMeta } from './utils/load-addon-functions-meta.js';
15
+ import { resolveServiceTypes } from './utils/resolve-service-types.js';
16
+ import { finalizeInspectorState } from './finalize.js';
17
+ import { getInitialInspectorState } from './initial-state.js';
18
+ export { getInitialInspectorState };
241
19
  /**
242
20
  * The path mappings the inspector's program has to share with the project.
243
21
  *
@@ -271,7 +49,12 @@ const readProjectPathMappings = (logger, tsconfig) => {
271
49
  ...(pathsBasePath !== undefined ? { pathsBasePath } : {}),
272
50
  };
273
51
  };
274
- export const inspect = async (logger, routeFiles, options = {}) => {
52
+ /**
53
+ * The engine-specific half of an inspection: builds the program, walks it with
54
+ * the checker and generates the schemas. What it returns is the pre-finalize
55
+ * state that `finalizeInspectorState` completes.
56
+ */
57
+ export const analyzeProgram = async (logger, routeFiles, options = {}) => {
275
58
  const normalizedRouteFiles = routeFiles.map((file) => resolve(file));
276
59
  const compilerOptions = {
277
60
  ...readProjectPathMappings(logger, options.tsconfig),
@@ -363,80 +146,11 @@ export const inspect = async (logger, routeFiles, options = {}) => {
363
146
  const startSchemas = performance.now();
364
147
  state.schemas = await generateAllSchemas(logger, options.schemaConfig, state);
365
148
  logger.debug(`generateAllSchemas took ${(performance.now() - startSchemas).toFixed(0)}ms`);
366
- computeContractHashes(state.schemas, state.functions.typesMap, state.functions.meta);
367
- computeRequiredSchemas(state, options);
368
- }
369
- // Re-load addon schemas (generateAllSchemas replaces state.schemas)
370
- await loadAddonSchemas(logger, state);
371
- // After the addon merge, not before: an addon supplies its own schemas here,
372
- // and checking earlier would report every one of them as unresolved.
373
- if (options.schemaConfig) {
374
- validateSchemaReferences(logger, state);
375
149
  }
376
- state.manifest.initial = options.manifest ?? null;
377
- const contracts = extractContractsFromMeta(state.functions.meta);
378
- const baseManifest = state.manifest.initial ?? createEmptyManifest();
379
- state.manifest.current = updateManifest(baseManifest, contracts);
380
- state.manifest.errors = validateContracts(baseManifest, contracts).errors;
381
- finalizeWorkflows(state);
382
- finalizeWorkflowHelperTypes(state);
383
- finalizeWorkflowWires(state);
384
- }
385
- // Populate filesAndMethods
386
- const startFilesAndMethods = performance.now();
387
- const { result, errors } = getFilesAndMethods(state, options.types);
388
- state.filesAndMethods = result;
389
- state.filesAndMethodsErrors = errors;
390
- logger.debug(`Get files and methods completed in ${(performance.now() - startFilesAndMethods).toFixed(2)}ms`);
391
- if (!options.setupOnly) {
392
- // Everything below re-derives from the state this pass just built, so it
393
- // supersedes whatever an earlier pass concluded about a source graph the
394
- // run has since finished generating into.
395
- logger.beginValidationPass?.();
396
- const startAggregate = performance.now();
397
- // Apply the inspected auth handler service set before aggregation so it
398
- // flows into requiredServices (the generated handler's own func is opaque).
399
- stampAuthHandlerServices(state);
400
- aggregateRequiredServices(state);
401
- logger.debug(`Aggregate required services completed in ${(performance.now() - startAggregate).toFixed(2)}ms`);
402
- computeResolvedIOTypes(state);
403
- computeMiddlewareGroupsMeta(state);
404
- computePermissionsGroupsMeta(state);
405
- validateNoSecretAliasServices(logger, checker, state);
406
- registerDerivedOAuth2AppSecrets(state);
407
- validateSecretUsage(logger, state);
408
- computeDiagnostics(state);
409
- validateSchemaWiringSeparation(logger, state);
410
- validateWorkflowGraphAddons(logger, state);
411
- validateScenarioServices(logger, state);
412
- validateScenarioSteps(logger, state);
413
- validateScenarioFeatures(logger, state);
414
- // Needs every route, every function and every wireAddon declaration
415
- // together, so it can only run once the whole program is inspected.
416
- annotateHttpRouteAuth(state);
417
- // Last of the validators: they read function meta and every wireAddon
418
- // declaration together, so they need the whole program inspected.
419
- validateExposedFunctionsGated(logger, state);
420
- validateTagsResolveToMiddleware(logger, state);
421
- if (options.openAPI) {
422
- state.openAPISpec = await generateOpenAPISpec(logger, state.functions.meta, state.http.meta, state.schemas, options.openAPI.additionalInfo, pikkuState(null, 'misc', 'errors'));
423
- }
424
- validateAgentModels(logger, state, options.modelAliases);
425
- validateAgentToolReferences(logger, state, options);
426
- validateAgentScorerReferences(logger, state);
427
- validateSecretOverrides(logger, state);
428
- validateVariableOverrides(logger, state);
429
- validateCredentialOverrides(logger, state);
430
- validateAddonUses(logger, state);
431
- validateAddonContentGrants(logger, state);
432
- validateRemoteAddonDependencies(logger, state);
433
- validateRemoteAddonAuth(logger, state);
434
- applyDeclaredScopes(state);
435
- validateScopeReferences(logger, state);
436
- validateSystemRoleScopes(logger, state);
437
- validatePersonaRoles(logger, state);
438
- logger.endValidationPass?.();
439
150
  }
151
+ // On a setup-only pass too: the CLI reads the singleton service names off
152
+ // a setup-only state to decide whether workflows have a workflowService.
153
+ state.serviceTypes = resolveServiceTypes(state, checker);
440
154
  const cpu = process.cpuUsage(cpuStart);
441
155
  state.stats = {
442
156
  files: program.getSourceFiles().length,
@@ -452,3 +166,17 @@ export const inspect = async (logger, routeFiles, options = {}) => {
452
166
  state.program = program;
453
167
  return state;
454
168
  };
169
+ export const inspect = async (logger, routeFiles, options = {}) => {
170
+ const cpuStart = process.cpuUsage();
171
+ const start = performance.now();
172
+ const state = await analyzeProgram(logger, routeFiles, options);
173
+ await finalizeInspectorState(logger, state, options);
174
+ const cpu = process.cpuUsage(cpuStart);
175
+ state.stats = {
176
+ ...state.stats,
177
+ cpuMs: Math.round((cpu.user + cpu.system) / 1000),
178
+ wallMs: Math.round(performance.now() - start),
179
+ heapUsedMb: Math.round(process.memoryUsage().heapUsed / 1048576),
180
+ };
181
+ return state;
182
+ };
package/dist/lite.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The inspector's exports that never load the TypeScript compiler.
3
+ *
4
+ * `@pikku/inspector` itself pulls in `typescript` and `ts-json-schema-generator`
5
+ * at module load, which costs a CLI command that only wants an error code or a
6
+ * serializer most of its startup time, and costs the native engine the
7
+ * compiler's memory for nothing. Everything a consumer of an `InspectorState`
8
+ * needs is here and safe to import eagerly; `lite.test.ts` fails if anything
9
+ * reachable from this file imports `typescript`.
10
+ */
11
+ export { getInitialInspectorState } from './initial-state.js';
12
+ export { ErrorCode } from './error-codes.js';
13
+ export type { DiagnosticSeverity, CodedDiagnostic } from './error-codes.js';
14
+ export { AUTH_HANDLER_FUNC_ID } from './utils/inspector-constants.js';
15
+ export { SCENARIO_INSTRUMENTATION_FUNCTIONS, isScenarioInstrumentationFunction, } from './add/scenario-instrumentation.js';
16
+ export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
17
+ export { filterInspectorState } from './utils/filter-inspector-state.js';
18
+ export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
19
+ export { customTypeNames, generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
20
+ export { unresolvedSchemaReferences } from './utils/post-process.js';
21
+ export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
22
+ export { serializeMCPJson } from './utils/serialize-mcp-json.js';
23
+ export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/deserialize-dsl-workflow.js';
24
+ export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
25
+ export { resolveCoreType } from './utils/resolve-core-type.js';
26
+ export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
package/dist/lite.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The inspector's exports that never load the TypeScript compiler.
3
+ *
4
+ * `@pikku/inspector` itself pulls in `typescript` and `ts-json-schema-generator`
5
+ * at module load, which costs a CLI command that only wants an error code or a
6
+ * serializer most of its startup time, and costs the native engine the
7
+ * compiler's memory for nothing. Everything a consumer of an `InspectorState`
8
+ * needs is here and safe to import eagerly; `lite.test.ts` fails if anything
9
+ * reachable from this file imports `typescript`.
10
+ */
11
+ export { getInitialInspectorState } from './initial-state.js';
12
+ export { ErrorCode } from './error-codes.js';
13
+ export { AUTH_HANDLER_FUNC_ID } from './utils/inspector-constants.js';
14
+ export { SCENARIO_INSTRUMENTATION_FUNCTIONS, isScenarioInstrumentationFunction, } from './add/scenario-instrumentation.js';
15
+ export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
16
+ export { filterInspectorState } from './utils/filter-inspector-state.js';
17
+ export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
18
+ export { customTypeNames, generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
19
+ export { unresolvedSchemaReferences } from './utils/post-process.js';
20
+ export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
21
+ export { serializeMCPJson } from './utils/serialize-mcp-json.js';
22
+ export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/deserialize-dsl-workflow.js';
23
+ export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
24
+ export { resolveCoreType } from './utils/resolve-core-type.js';
25
+ export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
@@ -1,4 +1,15 @@
1
1
  import * as ts from 'typescript';
2
+ /**
3
+ * Parse through the shared cache. With `populate: false` the cache is only
4
+ * read: a short-lived program (the schema generator's) reuses what the
5
+ * inspector parsed without pinning its own files past its lifetime.
6
+ */
7
+ export declare const getCachedSourceFile: (fileName: string, text: string, languageVersionOrOptions: ts.ScriptTarget | ts.CreateSourceFileOptions, { populate }?: {
8
+ populate?: boolean;
9
+ }) => {
10
+ file: ts.SourceFile;
11
+ reused: boolean;
12
+ };
2
13
  export interface SourceFileCacheHost extends ts.CompilerHost {
3
14
  /** Files served from the cache by the program just built. */
4
15
  readonly reused: () => number;
@@ -17,6 +17,35 @@ import { createHash } from 'crypto';
17
17
  * worth having if it outlives the call.
18
18
  */
19
19
  const cache = new Map();
20
+ /**
21
+ * A SourceFile is only shareable between programs that would have parsed it
22
+ * the same way: the language version and the module format the program
23
+ * implies for it (ESM vs CJS) are baked into the AST. The inspector and the
24
+ * schema generator use different compiler options, so both go into the key.
25
+ */
26
+ const cacheKey = (fileName, languageVersionOrOptions) => {
27
+ if (typeof languageVersionOrOptions !== 'object') {
28
+ return `${fileName}\0${languageVersionOrOptions}`;
29
+ }
30
+ const { languageVersion, impliedNodeFormat, jsDocParsingMode } = languageVersionOrOptions;
31
+ return `${fileName}\0${languageVersion}\0${impliedNodeFormat ?? ''}\0${jsDocParsingMode ?? ''}`;
32
+ };
33
+ /**
34
+ * Parse through the shared cache. With `populate: false` the cache is only
35
+ * read: a short-lived program (the schema generator's) reuses what the
36
+ * inspector parsed without pinning its own files past its lifetime.
37
+ */
38
+ export const getCachedSourceFile = (fileName, text, languageVersionOrOptions, { populate = true } = {}) => {
39
+ const key = cacheKey(fileName, languageVersionOrOptions);
40
+ const hash = createHash('sha1').update(text).digest('hex');
41
+ const hit = cache.get(key);
42
+ if (hit && hit.hash === hash)
43
+ return { file: hit.file, reused: true };
44
+ const file = ts.createSourceFile(fileName, text, languageVersionOrOptions, true);
45
+ if (populate)
46
+ cache.set(key, { hash, file });
47
+ return { file, reused: false };
48
+ };
20
49
  export const createSourceFileCacheHost = (options) => {
21
50
  const host = ts.createCompilerHost(options, true);
22
51
  let reused = 0;
@@ -31,23 +60,18 @@ export const createSourceFileCacheHost = (options) => {
31
60
  }
32
61
  if (text === undefined)
33
62
  return undefined;
34
- const hash = createHash('sha1').update(text).digest('hex');
35
- const hit = cache.get(fileName);
36
- if (hit && hit.hash === hash) {
63
+ const { file, reused: hit } = getCachedSourceFile(fileName, text, languageVersionOrOptions);
64
+ if (hit)
37
65
  reused++;
38
- return hit.file;
39
- }
40
- const file = ts.createSourceFile(fileName, text, languageVersionOrOptions, true);
41
- cache.set(fileName, { hash, file });
42
66
  return file;
43
67
  };
44
68
  return Object.assign(host, {
45
69
  reused: () => reused,
46
70
  prune: (program) => {
47
71
  const live = new Set(program.getSourceFiles().map((sf) => sf.fileName));
48
- for (const fileName of cache.keys()) {
49
- if (!live.has(fileName))
50
- cache.delete(fileName);
72
+ for (const [key, entry] of cache) {
73
+ if (!live.has(entry.file.fileName))
74
+ cache.delete(key);
51
75
  }
52
76
  },
53
77
  });
package/dist/types-map.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { canonicalTypeString } from './utils/canonical-type-string.js';
2
3
  export class TypesMap {
3
4
  map = new Map();
4
5
  customTypes = new Map();
5
6
  addCustomType(name, type, references) {
6
- this.customTypes.set(name, { type, references });
7
+ this.customTypes.set(name, { type: canonicalTypeString(type), references });
7
8
  }
8
9
  addType(originalName, path) {
9
10
  this.map.set(originalName, { originalName, path });
package/dist/types.d.ts CHANGED
@@ -19,7 +19,7 @@ import type { CredentialDefinitions } from '@pikku/core/credential';
19
19
  import type { ScopeDefinitions } from '@pikku/core/scope';
20
20
  import type { SystemRoleDefinitions } from '@pikku/core/role';
21
21
  import type { FeatureFlagDefinitions } from '@pikku/core/flag';
22
- import type { PersonaDefinitions } from '@pikku/core/persona';
22
+ import type { VirtualUserDefinitions } from '@pikku/core/virtual-user';
23
23
  import type { VariableDefinitions } from '@pikku/core/variable';
24
24
  import type { TypesMap } from './types-map.js';
25
25
  import type { FunctionsMeta } from '@pikku/core/services';
@@ -206,9 +206,21 @@ export interface InspectorPermissionInstance {
206
206
  position: number;
207
207
  isFactoryCall: boolean;
208
208
  }
209
+ /**
210
+ * One `addGlobalPermission(...)` call. It registers at module evaluation, so
211
+ * codegen must import its file — or call `exportName`, when the call sits in an
212
+ * exported zero-argument factory — or the global gate does not exist.
213
+ */
214
+ export interface InspectorGlobalPermissionRegistration {
215
+ sourceFile: string;
216
+ position: number;
217
+ exportName: string | null;
218
+ definitionIds: string[];
219
+ }
209
220
  export interface InspectorPermissionState {
210
221
  definitions: Record<string, InspectorPermissionDefinition>;
211
222
  instances: Record<string, InspectorPermissionInstance>;
223
+ global: InspectorGlobalPermissionRegistration[];
212
224
  }
213
225
  export type InspectorFilters = {
214
226
  names?: string[];
@@ -428,6 +440,12 @@ export interface AuthDefinition {
428
440
  /** Whether `session.cookieCache` is enabled — drives the stateless session
429
441
  * middleware split in the auth codegen. Absent/false ⇒ stateful middleware. */
430
442
  cookieCache?: boolean;
443
+ /** `advanced.cookiePrefix`, when written as a string literal. */
444
+ cookiePrefix?: string;
445
+ /** `session.cookieCache.strategy`, when written as a string literal. */
446
+ cookieCacheStrategy?: string;
447
+ /** `session.cookieCache.version`, when written as a string literal. */
448
+ cookieCacheVersion?: string;
431
449
  /**
432
450
  * Singleton services the generated auth handler must have available at
433
451
  * runtime — the services the `pikkuBetterAuth` factory reaches for (either
@@ -518,6 +536,14 @@ export type ScreensManifestMeta = {
518
536
  */
519
537
  scopes?: string[];
520
538
  };
539
+ export interface InspectorServiceTypes {
540
+ /** Property names of the project's SingletonServices type, sorted. */
541
+ singletonServices: string[] | null;
542
+ /** Property names of the project's Services type, unfiltered. */
543
+ services: string[] | null;
544
+ /** Singleton services that are a `SecretService` under another name. */
545
+ secretAliases: string[];
546
+ }
521
547
  export interface InspectorState {
522
548
  /** Set when this package calls `defineScreens`: the screens it ships. */
523
549
  screensManifest?: ScreensManifestMeta;
@@ -784,8 +810,8 @@ export interface InspectorState {
784
810
  definitions: FeatureFlagDefinitions;
785
811
  files: Set<string>;
786
812
  };
787
- personas: {
788
- definitions: PersonaDefinitions;
813
+ virtualUsers: {
814
+ definitions: VirtualUserDefinitions;
789
815
  files: Set<string>;
790
816
  };
791
817
  variables: {
@@ -801,6 +827,12 @@ export interface InspectorState {
801
827
  channelMiddleware: InspectorChannelMiddlewareState;
802
828
  agentMiddleware: InspectorAIMiddlewareState;
803
829
  permissions: InspectorPermissionState;
830
+ /**
831
+ * What the checker concluded about the project's core service types, read
832
+ * during analysis so that finalize never needs `typesLookup`. A field is null
833
+ * when the type did not resolve.
834
+ */
835
+ serviceTypes?: InspectorServiceTypes;
804
836
  serviceAggregation: {
805
837
  requiredServices: Set<string>;
806
838
  usedFunctions: Set<string>;
@@ -0,0 +1,8 @@
1
+ import type { InspectorState } 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: Pick<InspectorState, "scopes">) => void;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Folds `declareScopes` ids into `state.scopes.definitions`. An id the
3
+ * definitions already hold (an addon's tree, a `defineScope`) is left alone;
4
+ * a missing one is added as a bare node under its root, creating the root when
5
+ * nothing declared it.
6
+ */
7
+ export const applyDeclaredScopes = (state) => {
8
+ for (const [id, sourceFile] of state.scopes.declared ?? []) {
9
+ const [rootName, ...rest] = id.split(':');
10
+ let root = state.scopes.definitions.find((d) => d.name === rootName);
11
+ if (!root) {
12
+ root = { name: rootName, sourceFile };
13
+ state.scopes.definitions.push(root);
14
+ }
15
+ let level = root;
16
+ for (const segment of rest) {
17
+ level.scopes ??= {};
18
+ level = level.scopes[segment] ??= {};
19
+ }
20
+ }
21
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * A printed type with the members of every union in it sorted: by text, with
3
+ * `null` and `undefined` last. The checker prints a union in the order its
4
+ * members were created, which follows whichever file it happened to check
5
+ * first, and so differs between runtimes and between the TypeScript and native
6
+ * inspectors. Everything other than a union's order is left byte for byte.
7
+ *
8
+ * A level the scanner cannot split safely (a conditional type, a type
9
+ * parameter's constraint, a type predicate) keeps its order and only has the
10
+ * brackets inside it canonicalized. The native inspector's
11
+ * `typesmap.CanonicalTypeString` is the same algorithm, and both run the cases
12
+ * in `canonical-type-string.cases.json`.
13
+ */
14
+ export declare const canonicalTypeString: (type: string) => string;