@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
@@ -0,0 +1,162 @@
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 const canonicalTypeString = (type) => {
15
+ const lead = type.length - type.replace(/^[ \t\n\r]+/, '').length;
16
+ const trail = type.length - type.replace(/[ \t\n\r]+$/, '').length;
17
+ if (lead === type.length)
18
+ return type;
19
+ const body = type.slice(lead, type.length - trail);
20
+ return (type.slice(0, lead) + canonicalBody(body) + type.slice(type.length - trail));
21
+ };
22
+ const OPENERS = '{[(<';
23
+ const CLOSE = { '{': '}', '[': ']', '(': ')', '<': '>' };
24
+ const CLOSERS = '}])>';
25
+ const QUOTES = `"'\``;
26
+ const UNSPLITTABLE = new Set(['extends', 'is', 'asserts', 'infer', 'as']);
27
+ const MAPPED_KEY = /^[A-Za-z_$][\w$]*[ \t\n\r]+in[ \t\n\r]/;
28
+ /** Where the comment starting at `i` ends (exclusive), or -1 if none starts there. */
29
+ const commentEnd = (s, i) => {
30
+ if (s[i] !== '/')
31
+ return -1;
32
+ if (s[i + 1] === '*') {
33
+ const end = s.indexOf('*/', i + 2);
34
+ return end === -1 ? s.length : end + 2;
35
+ }
36
+ if (s[i + 1] === '/') {
37
+ const end = s.indexOf('\n', i + 2);
38
+ return end === -1 ? s.length : end;
39
+ }
40
+ return -1;
41
+ };
42
+ /**
43
+ * `s` with everything inside brackets, string literals and comments blanked,
44
+ * and the outermost bracket pairs.
45
+ */
46
+ const topLevel = (s) => {
47
+ const mask = Array.from({ length: s.length }, () => ' ');
48
+ const groups = [];
49
+ let depth = 0;
50
+ let open = -1;
51
+ for (let i = 0; i < s.length; i++) {
52
+ const char = s[i];
53
+ if (QUOTES.includes(char)) {
54
+ let j = i + 1;
55
+ while (j < s.length && s[j] !== char)
56
+ j += s[j] === '\\' ? 2 : 1;
57
+ i = j;
58
+ continue;
59
+ }
60
+ const end = commentEnd(s, i);
61
+ if (end !== -1) {
62
+ i = end - 1;
63
+ continue;
64
+ }
65
+ if (OPENERS.includes(char)) {
66
+ if (depth === 0)
67
+ open = i;
68
+ depth++;
69
+ }
70
+ else if (CLOSERS.includes(char) && !(char === '>' && s[i - 1] === '=')) {
71
+ if (depth > 0)
72
+ depth--;
73
+ if (depth === 0 && open !== -1 && CLOSE[s[open]] === char) {
74
+ groups.push([open, i]);
75
+ open = -1;
76
+ }
77
+ }
78
+ else if (depth === 0) {
79
+ mask[i] = char;
80
+ }
81
+ }
82
+ return { mask: mask.join(''), groups };
83
+ };
84
+ const isNullish = (member) => member === 'null' || member === 'undefined';
85
+ const sortMembers = (members) => members.sort((a, b) => isNullish(a) !== isNullish(b)
86
+ ? isNullish(a)
87
+ ? 1
88
+ : -1
89
+ : a < b
90
+ ? -1
91
+ : a > b
92
+ ? 1
93
+ : 0);
94
+ const unsplittable = (mask) => {
95
+ if (/[?:]/.test(mask))
96
+ return true;
97
+ for (let i = 0; i < mask.length; i++) {
98
+ if (mask[i] === '=' && mask[i + 1] !== '>')
99
+ return true;
100
+ }
101
+ return mask.split(/[^A-Za-z0-9_$]+/).some((word) => UNSPLITTABLE.has(word));
102
+ };
103
+ const canonicalBody = (body) => {
104
+ const { mask } = topLevel(body);
105
+ const arrow = mask.indexOf('=>');
106
+ if (arrow !== -1 && !mask.slice(0, arrow).includes('|')) {
107
+ return `${rewriteGroups(body.slice(0, arrow))}=>${canonicalTypeString(body.slice(arrow + 2))}`;
108
+ }
109
+ const mapped = MAPPED_KEY.exec(mask);
110
+ if (mapped) {
111
+ const key = mapped[0].length;
112
+ return body.slice(0, key) + canonicalTypeString(body.slice(key));
113
+ }
114
+ if (!mask.includes('|') || unsplittable(mask))
115
+ return rewriteGroups(body);
116
+ const members = [];
117
+ let start = 0;
118
+ for (let i = 0; i <= mask.length; i++) {
119
+ if (i === mask.length || mask[i] === '|') {
120
+ const member = canonicalTypeString(body.slice(start, i)).trim();
121
+ if (member)
122
+ members.push(member);
123
+ start = i + 1;
124
+ }
125
+ }
126
+ return members.length > 1
127
+ ? sortMembers(members).join(' | ')
128
+ : rewriteGroups(body);
129
+ };
130
+ /** `s` with the contents of each outermost bracket pair canonicalized. */
131
+ const rewriteGroups = (s) => {
132
+ let out = '';
133
+ let last = 0;
134
+ for (const [open, close] of topLevel(s).groups) {
135
+ out +=
136
+ s.slice(last, open + 1) + rewriteList(s.slice(open + 1, close), s[open]);
137
+ last = close;
138
+ }
139
+ return out + s.slice(last);
140
+ };
141
+ /** A bracket's contents: members of an object, parameters, or type arguments. */
142
+ const rewriteList = (inner, opener) => {
143
+ const separators = opener === '{' ? ';,' : ',';
144
+ const { mask } = topLevel(inner);
145
+ let out = '';
146
+ let start = 0;
147
+ for (let i = 0; i <= mask.length; i++) {
148
+ if (i === mask.length || separators.includes(mask[i])) {
149
+ out += rewriteItem(inner.slice(start, i), opener) + (inner[i] ?? '');
150
+ start = i + 1;
151
+ }
152
+ }
153
+ return out;
154
+ };
155
+ const rewriteItem = (item, opener) => {
156
+ if (opener === '<')
157
+ return canonicalTypeString(item);
158
+ const colon = topLevel(item).mask.indexOf(':');
159
+ if (colon === -1)
160
+ return canonicalTypeString(item);
161
+ return `${rewriteGroups(item.slice(0, colon))}:${canonicalTypeString(item.slice(colon + 1))}`;
162
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The id of a function wired inline rather than declared on its own: the
3
+ * wiring type followed by whatever identifies the wiring (`queue:emails`,
4
+ * `http:get:/todos`). Kept apart from `extract-function-name.ts`, which needs
5
+ * the TypeScript compiler, so pure state helpers can use it without loading it.
6
+ */
7
+ export declare function makeContextBasedId(wiringType: string, ...segments: string[]): string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The id of a function wired inline rather than declared on its own: the
3
+ * wiring type followed by whatever identifies the wiring (`queue:emails`,
4
+ * `http:get:/todos`). Kept apart from `extract-function-name.ts`, which needs
5
+ * the TypeScript compiler, so pure state helpers can use it without loading it.
6
+ */
7
+ export function makeContextBasedId(wiringType, ...segments) {
8
+ return [wiringType, ...segments].join(':');
9
+ }
@@ -6,4 +6,18 @@ import type { TypesMap } from '../types-map.js';
6
6
  * ts-json-schema-generator can discover inline/custom types from typesMap.
7
7
  */
8
8
  export declare function sanitizeTypeName(name: string): string;
9
- export declare function generateCustomTypes(typesMap: TypesMap, requiredTypes: Set<string>): string;
9
+ /**
10
+ * The names `generateCustomTypes` declares, in the order it declares them. A
11
+ * file that imports from the shared custom-types file uses this to know which
12
+ * names exist there.
13
+ */
14
+ export declare function customTypeNames(typesMap: TypesMap): string[];
15
+ export interface GenerateCustomTypesOptions {
16
+ /**
17
+ * Turns the absolute path of a type's source file into the specifier the
18
+ * import line uses. Left out, the absolute path is used as-is — fine for the
19
+ * in-memory source the schema generator compiles, wrong for a file on disk.
20
+ */
21
+ resolveImportPath?: (absolutePath: string) => string;
22
+ }
23
+ export declare function generateCustomTypes(typesMap: TypesMap, requiredTypes: Set<string>, { resolveImportPath }?: GenerateCustomTypesOptions): string;
@@ -55,14 +55,26 @@ function stripClassificationWrappers(type) {
55
55
  }
56
56
  return output;
57
57
  }
58
- export function generateCustomTypes(typesMap, requiredTypes) {
59
- const typeDeclarations = Array.from(typesMap.customTypes.entries())
58
+ function emittableCustomTypes(typesMap) {
59
+ return Array.from(typesMap.customTypes.entries())
60
60
  .sort(([a], [b]) => a.localeCompare(b))
61
61
  .filter(([_name, { type }]) => {
62
62
  const hasUndefinedGeneric = /\b(Name|In|Out|Key)\b/.test(type) && /\[.*\]/.test(type);
63
63
  return !hasUndefinedGeneric;
64
- })
65
- .map(([originalName, { type, references }]) => {
64
+ });
65
+ }
66
+ /**
67
+ * The names `generateCustomTypes` declares, in the order it declares them. A
68
+ * file that imports from the shared custom-types file uses this to know which
69
+ * names exist there.
70
+ */
71
+ export function customTypeNames(typesMap) {
72
+ return emittableCustomTypes(typesMap)
73
+ .filter(([originalName, { type }]) => sanitizeTypeName(originalName) !== stripClassificationWrappers(type))
74
+ .map(([originalName]) => sanitizeTypeName(originalName));
75
+ }
76
+ export function generateCustomTypes(typesMap, requiredTypes, { resolveImportPath = (path) => path } = {}) {
77
+ const typeDeclarations = emittableCustomTypes(typesMap).map(([originalName, { type, references }]) => {
66
78
  const name = sanitizeTypeName(originalName);
67
79
  references.forEach((refName) => {
68
80
  if (refName !== '__object' &&
@@ -118,7 +130,7 @@ export function generateCustomTypes(typesMap, requiredTypes) {
118
130
  }
119
131
  }
120
132
  const importLines = Array.from(importsByPath.entries())
121
- .map(([path, types]) => `import type { ${Array.from(types).join(', ')} } from '${path}'`)
133
+ .map(([path, types]) => `import type { ${Array.from(types).join(', ')} } from '${resolveImportPath(path)}'`)
122
134
  .join('\n');
123
135
  return `${importLines}\n\n${typeDeclarations.filter(Boolean).join('\n')}`;
124
136
  }
@@ -7,18 +7,4 @@ import type { InspectorState } from '../types.js';
7
7
  * extracts tags/middleware/permissions from the pikkuFunc() config.
8
8
  */
9
9
  export declare function ensureFunctionMetadata(state: InspectorState, pikkuFuncId: string, fallbackName?: string, funcInitializer?: ts.Node, checker?: ts.TypeChecker, isHelper?: boolean): void;
10
- /**
11
- * Register metadata for a `func:` inlined into a wiring (queue/scheduler/
12
- * trigger/gateway/…).
13
- *
14
- * An inline function has no exported name, so `addFunctions` skips it (it bails
15
- * on `__temp_` ids) and only the wiring's context-based id exists. Without this
16
- * the wiring points at a function that was never registered and the worker
17
- * resolves to nothing at runtime ("Missing generated metadata for ...").
18
- *
19
- * The stub also carries no session info, so the runtime would treat it as
20
- * session-required and every invocation would fail with "Authentication
21
- * required". Derive `sessionless` from the helper the function was built with —
22
- * the same rule `addFunctions` applies to named ones (`!== 'pikkuFunc'`).
23
- */
24
10
  export declare function ensureInlineWiringFunction(state: InspectorState, pikkuFuncId: string, fallbackName: string | undefined, funcInitializer: ts.Node, checker: ts.TypeChecker, isHelper?: boolean): void;
@@ -3,6 +3,7 @@ import { funcIdToTypeName } from './extract-function-name.js';
3
3
  import { getCommonWireMetaData } from './get-property-value.js';
4
4
  import { resolveMiddleware } from './middleware.js';
5
5
  import { resolvePermissions } from './permissions.js';
6
+ import { resolveCalleeName } from './resolve-callee-name.js';
6
7
  function isVoidLike(type) {
7
8
  return !!(type.flags &
8
9
  (ts.TypeFlags.Void | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike));
@@ -224,6 +225,7 @@ export function ensureFunctionMetadata(state, pikkuFuncId, fallbackName, funcIni
224
225
  * required". Derive `sessionless` from the helper the function was built with —
225
226
  * the same rule `addFunctions` applies to named ones (`!== 'pikkuFunc'`).
226
227
  */
228
+ const isPikkuFunc = (name) => name === 'pikkuFunc';
227
229
  export function ensureInlineWiringFunction(state, pikkuFuncId, fallbackName, funcInitializer, checker, isHelper) {
228
230
  ensureFunctionMetadata(state, pikkuFuncId, fallbackName, funcInitializer, checker, isHelper);
229
231
  const meta = state.functions.meta[pikkuFuncId];
@@ -231,7 +233,8 @@ export function ensureInlineWiringFunction(state, pikkuFuncId, fallbackName, fun
231
233
  meta.sessionless === undefined &&
232
234
  ts.isCallExpression(funcInitializer) &&
233
235
  ts.isIdentifier(funcInitializer.expression)) {
234
- meta.sessionless = funcInitializer.expression.text !== 'pikkuFunc';
236
+ meta.sessionless =
237
+ resolveCalleeName(funcInitializer, checker, isPikkuFunc) !== 'pikkuFunc';
235
238
  }
236
239
  }
237
240
  function populateTypesLookup(state, pikkuFuncId, funcInitializer, checker) {
@@ -8,7 +8,7 @@ export type ExtractedFunctionName = {
8
8
  isHelper: boolean;
9
9
  version: number | null;
10
10
  };
11
- export declare function makeContextBasedId(wiringType: string, ...segments: string[]): string;
11
+ export { makeContextBasedId } from './context-based-id.js';
12
12
  export declare function funcIdToTypeName(id: string): string;
13
13
  export declare function extractFunctionName(callExpr: ts.Node, checker: ts.TypeChecker, rootDir: string): ExtractedFunctionName;
14
14
  /**
@@ -18,9 +18,7 @@ function tempFuncId(callExpr, rootDir) {
18
18
  .slice(0, 16);
19
19
  return `__temp_${hash}`;
20
20
  }
21
- export function makeContextBasedId(wiringType, ...segments) {
22
- return [wiringType, ...segments].join(':');
23
- }
21
+ export { makeContextBasedId } from './context-based-id.js';
24
22
  export function funcIdToTypeName(id) {
25
23
  return id
26
24
  .split(/[^a-zA-Z0-9]/)
@@ -30,7 +28,7 @@ export function funcIdToTypeName(id) {
30
28
  }
31
29
  /**
32
30
  * The step wrappers, whichever kind of subject acts in them. A scenario calls a
33
- * platform or addon step by name exactly as it calls a persona one.
31
+ * platform or addon step by name exactly as it calls a virtual user one.
34
32
  */
35
33
  const SCENARIO_STEP_WRAPPERS = new Set([
36
34
  'pikkuScenarioStep',
@@ -1,5 +1,5 @@
1
1
  import { parseVersionedId } from '@pikku/core/utils';
2
- import { makeContextBasedId } from './extract-function-name.js';
2
+ import { makeContextBasedId } from './context-based-id.js';
3
3
  import { aggregateRequiredServices } from './post-process.js';
4
4
  import { IncompatibleDeployTargetError, resolveDeployTarget, } from './resolve-deploy-target.js';
5
5
  /**
@@ -359,6 +359,11 @@ export function filterInspectorState(state, filters, logger) {
359
359
  meta: JSON.parse(JSON.stringify(state.channels.meta)),
360
360
  files: new Set(), // Will be repopulated with filtered files
361
361
  },
362
+ gateways: {
363
+ ...state.gateways,
364
+ meta: JSON.parse(JSON.stringify(state.gateways?.meta ?? {})),
365
+ files: new Set(),
366
+ },
362
367
  triggers: {
363
368
  ...state.triggers,
364
369
  meta: JSON.parse(JSON.stringify(state.triggers?.meta ?? {})),
@@ -511,6 +516,31 @@ export function filterInspectorState(state, filters, logger) {
511
516
  }
512
517
  }
513
518
  filteredState.channels.files = repopulateFileSetFromMeta(filteredState.channels.meta, state.channels.files, Object.keys(filteredState.channels.meta).length > 0);
519
+ // Filter gateways. A gateway is addressed by its own name or by the function
520
+ // it runs, so a deploy unit naming either keeps the wiring that serves it.
521
+ for (const name of Object.keys(filteredState.gateways.meta)) {
522
+ const gatewayMeta = filteredState.gateways.meta[name];
523
+ const matches = matchesFilters(filters, {
524
+ type: 'gateway',
525
+ name: gatewayMeta.pikkuFuncId,
526
+ altNames: [name],
527
+ tags: gatewayMeta.tags,
528
+ httpRoute: gatewayMeta.route,
529
+ }, logger, undefined, keptByDeploy);
530
+ if (!matches) {
531
+ delete filteredState.gateways.meta[name];
532
+ }
533
+ else {
534
+ filteredState.serviceAggregation.usedFunctions.add(gatewayMeta.pikkuFuncId);
535
+ extractWireNames(gatewayMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
536
+ }
537
+ }
538
+ // The meta records no source file per gateway, so a surviving gateway keeps
539
+ // every gateway file and none surviving drops them all.
540
+ filteredState.gateways.files =
541
+ Object.keys(filteredState.gateways.meta).length > 0
542
+ ? new Set(state.gateways?.files ?? [])
543
+ : new Set();
514
544
  // Filter triggers
515
545
  for (const name of Object.keys(filteredState.triggers.meta)) {
516
546
  const triggerMeta = filteredState.triggers.meta[name];
@@ -1,6 +1,11 @@
1
1
  import * as ts from 'typescript';
2
2
  import { ErrorCode } from '../error-codes.js';
3
3
  export declare const getArrayPropertyValue: (obj: ts.ObjectLiteralExpression, propertyName: string) => string[] | null;
4
+ /**
5
+ * Returns the object literal assigned to a property, or null when the property
6
+ * is absent or is not an inline object.
7
+ */
8
+ export declare const getObjectPropertyLiteral: (obj: ts.ObjectLiteralExpression, propertyName: string) => ts.ObjectLiteralExpression | null;
4
9
  /**
5
10
  * Extracts a string->string record from an object property.
6
11
  *
@@ -27,6 +27,20 @@ export const getArrayPropertyValue = (obj, propertyName) => {
27
27
  }
28
28
  return null;
29
29
  };
30
+ /**
31
+ * Returns the object literal assigned to a property, or null when the property
32
+ * is absent or is not an inline object.
33
+ */
34
+ export const getObjectPropertyLiteral = (obj, propertyName) => {
35
+ const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
36
+ ts.isIdentifier(p.name) &&
37
+ p.name.text === propertyName);
38
+ if (!property || !ts.isPropertyAssignment(property)) {
39
+ return null;
40
+ }
41
+ const initializer = unwrapAs(property.initializer);
42
+ return ts.isObjectLiteralExpression(initializer) ? initializer : null;
43
+ };
30
44
  /**
31
45
  * Extracts a string->string record from an object property.
32
46
  *
@@ -102,7 +116,7 @@ export const assertStringLiteralProperty = (obj, propertyName, wiringType, logge
102
116
  * that anything written as an expression comes through raw: a `description`
103
117
  * split across lines as `'Fetch the thing ' + 'from upstream'` reaches
104
118
  * `infra.json` with the quotes and the `+` still in it, and is rendered that
105
- * way in the fabric console.
119
+ * way in the cloud console.
106
120
  *
107
121
  * Deliberately checker-free, unlike `extractStringLiteral`. Every value this
108
122
  * resolves is a literal in the same expression, so no symbol resolution is
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The pikku function id of the single shared auth handler the CLI generates
3
+ * (`export const authHandler = pikkuSessionlessFunc(...)` in auth.gen.ts). An
4
+ * exported top-level const collapses the catch-all `/api/auth/**` route onto one
5
+ * worker, and the export name becomes the function id. Shared with the CLI
6
+ * codegen and the post-process service stamp so all three agree on the same id
7
+ * without the inspector having to import `@pikku/better-auth`.
8
+ */
9
+ export declare const AUTH_HANDLER_FUNC_ID = "authHandler";
10
+ /**
11
+ * Placeholder recorded when a scenario step's target isn't a static string
12
+ * literal. The step is kept rather than dropped so post-processing can raise
13
+ * PKU678 against it — a dropped step is the silent-failure mode this repo has
14
+ * hit repeatedly on workflow graph round-trips.
15
+ */
16
+ export declare const DYNAMIC_SCENARIO_STEP_TARGET = "<dynamic>";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The pikku function id of the single shared auth handler the CLI generates
3
+ * (`export const authHandler = pikkuSessionlessFunc(...)` in auth.gen.ts). An
4
+ * exported top-level const collapses the catch-all `/api/auth/**` route onto one
5
+ * worker, and the export name becomes the function id. Shared with the CLI
6
+ * codegen and the post-process service stamp so all three agree on the same id
7
+ * without the inspector having to import `@pikku/better-auth`.
8
+ */
9
+ export const AUTH_HANDLER_FUNC_ID = 'authHandler';
10
+ /**
11
+ * Placeholder recorded when a scenario step's target isn't a static string
12
+ * literal. The step is kept rather than dropped so post-processing can raise
13
+ * PKU678 against it — a dropped step is the silent-failure mode this repo has
14
+ * hit repeatedly on workflow graph round-trips.
15
+ */
16
+ export const DYNAMIC_SCENARIO_STEP_TARGET = '<dynamic>';
@@ -45,6 +45,7 @@ const applyPackageToCLICommands = (commands, packageName, namespace) => {
45
45
  }
46
46
  if (namespace &&
47
47
  typeof command.pikkuFuncId === 'string' &&
48
+ command.pikkuFuncId !== '' &&
48
49
  !command.pikkuFuncId.includes(':')) {
49
50
  command.pikkuFuncId = `${namespace}:${command.pikkuFuncId}`;
50
51
  }