@astrale-os/sdk 0.6.0-beta.36 → 0.6.0-beta.38

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 (82) hide show
  1. package/dist/application/action/context.d.ts +14 -11
  2. package/dist/application/action/index.d.ts +1 -1
  3. package/dist/application/dependency/invoker.d.ts +10 -4
  4. package/dist/application/integration/define.d.ts +8 -1
  5. package/dist/application/integration/index.d.ts +2 -2
  6. package/dist/application/integration/integration.d.ts +13 -1
  7. package/dist/application/integration/operation.d.ts +14 -1
  8. package/dist/application/mutation/execution/execute.js +1 -1
  9. package/dist/application/mutation/failure.d.ts +6 -0
  10. package/dist/application/mutation/failure.js +24 -18
  11. package/dist/application/query/collection/union.js +11 -4
  12. package/dist/application/query/conformance.d.ts +15 -0
  13. package/dist/application/query/conformance.js +46 -0
  14. package/dist/application/query/define.js +4 -3
  15. package/dist/application/query/execution/execute.d.ts +0 -1
  16. package/dist/application/query/execution/execute.js +7 -1
  17. package/dist/application/query/failure.d.ts +8 -0
  18. package/dist/application/query/failure.js +8 -0
  19. package/dist/application/query/index.d.ts +1 -1
  20. package/dist/application/query/index.js +1 -1
  21. package/dist/application/workflow/authoring.d.ts +1 -1
  22. package/dist/application/workflow/context.d.ts +1 -1
  23. package/dist/application/workflow/durable.d.ts +55 -20
  24. package/dist/application/workflow/index.d.ts +1 -1
  25. package/dist/execution/identity/authentication.js +12 -6
  26. package/dist/execution/identity/callback.d.ts +7 -8
  27. package/dist/execution/identity/callback.js +17 -14
  28. package/dist/execution/invocation/capabilities/bind.d.ts +11 -0
  29. package/dist/execution/invocation/capabilities/bind.js +25 -0
  30. package/dist/execution/invocation/capabilities/index.d.ts +1 -0
  31. package/dist/execution/invocation/capabilities/index.js +1 -0
  32. package/dist/execution/invocation/dispatch/context.js +2 -21
  33. package/dist/execution/invocation/integration/authentication.d.ts +3 -3
  34. package/dist/execution/invocation/integration/authentication.js +7 -7
  35. package/dist/execution/invocation/integration/call.js +2 -2
  36. package/dist/execution/invocation/integration/index.d.ts +1 -0
  37. package/dist/execution/invocation/integration/index.js +1 -0
  38. package/dist/execution/invocation/invocation.js +2 -2
  39. package/dist/execution/invocation/lifecycle/active.d.ts +8 -2
  40. package/dist/execution/invocation/lifecycle/active.js +3 -3
  41. package/dist/execution/invocation/lifecycle/index.d.ts +1 -1
  42. package/dist/execution/service/create.js +1 -0
  43. package/dist/execution/workflows/engine.d.ts +12 -2
  44. package/dist/execution/workflows/index.d.ts +1 -1
  45. package/dist/execution/workflows/integrations.d.ts +13 -0
  46. package/dist/execution/workflows/integrations.js +25 -0
  47. package/dist/execution/workflows/options.js +3 -1
  48. package/dist/execution/workflows/receiver.d.ts +3 -0
  49. package/dist/execution/workflows/receiver.js +7 -2
  50. package/dist/execution/workflows/run.d.ts +3 -4
  51. package/dist/execution/workflows/run.js +32 -60
  52. package/dist/execution/workflows/steps.d.ts +42 -0
  53. package/dist/execution/workflows/steps.js +67 -0
  54. package/dist/testing/durable/engine.d.ts +111 -0
  55. package/dist/testing/durable/engine.js +540 -0
  56. package/dist/testing/durable/index.d.ts +3 -0
  57. package/dist/testing/durable/index.js +1 -0
  58. package/dist/testing/durable/steps.d.ts +30 -0
  59. package/dist/testing/durable/steps.js +40 -0
  60. package/dist/testing/index.d.ts +6 -1
  61. package/dist/testing/index.js +5 -1
  62. package/dist/tooling/cli/dotenv.d.ts +3 -3
  63. package/dist/tooling/cli/dotenv.js +3 -3
  64. package/dist/tooling/cli/log.d.ts +0 -2
  65. package/dist/tooling/cli/log.js +0 -2
  66. package/dist/tooling/linter/adapters/typescript/imports.d.ts +0 -1
  67. package/dist/tooling/linter/adapters/typescript/imports.js +1 -5
  68. package/dist/tooling/linter/adapters/typescript/index.d.ts +1 -1
  69. package/dist/tooling/linter/adapters/typescript/index.js +1 -1
  70. package/dist/tooling/linter/adapters/typescript/syntax.d.ts +3 -3
  71. package/dist/tooling/linter/adapters/typescript/syntax.js +13 -25
  72. package/dist/tooling/linter/implementations/source/callable-requirements.d.ts +0 -1
  73. package/dist/tooling/linter/implementations/source/callable-requirements.js +1 -6
  74. package/dist/tooling/linter/implementations/source/dependencies.js +1 -62
  75. package/dist/tooling/linter/implementations/source/mutations.js +1 -12
  76. package/dist/tooling/linter/implementations/source/queries.js +1 -17
  77. package/dist/tooling/linter/implementations/source/shared.d.ts +7 -4
  78. package/dist/tooling/linter/implementations/source/shared.js +93 -46
  79. package/dist/tooling/linter/implementations/source/workflows.js +406 -16
  80. package/dist/tooling/linter/policy/generated.js +2 -2
  81. package/dist/tooling/linter/requirements/registry.js +3 -1
  82. package/package.json +6 -6
@@ -1,7 +1,5 @@
1
1
  /** CLI output helpers and ANSI colours for the one project command owner. */
2
- export const GREEN = '\x1b[32m';
3
2
  export const DIM = '\x1b[2m';
4
- export const BOLD = '\x1b[1m';
5
3
  export const YELLOW = '\x1b[33m';
6
4
  export const RED = '\x1b[31m';
7
5
  export const RESET = '\x1b[0m';
@@ -3,4 +3,3 @@ import type { SourceProject, SourceDependencyIssue, SourceFile, SourceImport } f
3
3
  export declare function collectImports(source: ts.SourceFile): SourceImport[];
4
4
  export declare function collectDependencyIssues(source: ts.SourceFile): SourceDependencyIssue[];
5
5
  export declare function resolveProjectImport(project: SourceProject, importer: SourceFile, specifier: string): SourceFile | undefined;
6
- export declare function isWithin(directory: string, path: string): boolean;
@@ -1,4 +1,4 @@
1
- import { dirname, extname, isAbsolute, join, relative, resolve } from 'node:path';
1
+ import { dirname, extname, join, relative, resolve } from 'node:path';
2
2
  import ts from 'typescript';
3
3
  import { isLocallyDeclared } from './syntax.js';
4
4
  export function collectImports(source) {
@@ -166,7 +166,3 @@ function visit(node, callback) {
166
166
  callback(node);
167
167
  node.forEachChild((child) => visit(child, callback));
168
168
  }
169
- export function isWithin(directory, path) {
170
- const child = relative(directory, path);
171
- return child === '' || (!child.startsWith('..') && !isAbsolute(child));
172
- }
@@ -1,7 +1,7 @@
1
1
  export { discoverProject } from './discover.js';
2
2
  export { sourceEvidenceAdmission } from './evidence-admission.js';
3
3
  export { collectDependencyIssues, resolveProjectImport } from './imports.js';
4
- export { importedSymbol, isLocallyDeclared, resolveImportedSymbol, location, objectProperty, propertyChain, staticText, unwrap, visit, } from './syntax.js';
4
+ export { bindingContains, importedSymbol, isLocallyDeclared, resolveImportedSymbol, location, objectProperty, propertyChain, staticText, unwrap, visit, visitOwnBody, } from './syntax.js';
5
5
  export { assertSingleTypeUniverse } from './type-universe.js';
6
6
  export { observeQueryDefinitions } from './query-observation.js';
7
7
  export { queryCollectionKind } from './query-collection-type.js';
@@ -1,7 +1,7 @@
1
1
  export { discoverProject } from './discover.js';
2
2
  export { sourceEvidenceAdmission } from './evidence-admission.js';
3
3
  export { collectDependencyIssues, resolveProjectImport } from './imports.js';
4
- export { importedSymbol, isLocallyDeclared, resolveImportedSymbol, location, objectProperty, propertyChain, staticText, unwrap, visit, } from './syntax.js';
4
+ export { bindingContains, importedSymbol, isLocallyDeclared, resolveImportedSymbol, location, objectProperty, propertyChain, staticText, unwrap, visit, visitOwnBody, } from './syntax.js';
5
5
  export { assertSingleTypeUniverse } from './type-universe.js';
6
6
  export { observeQueryDefinitions } from './query-observation.js';
7
7
  export { queryCollectionKind } from './query-collection-type.js';
@@ -27,12 +27,12 @@ export type ImportedSymbolResolution = Readonly<{
27
27
  */
28
28
  export declare function resolveImportedSymbol(file: SourceFile, expression: ts.Expression): ImportedSymbolResolution;
29
29
  export declare function isLocallyDeclared(identifier: ts.Identifier): boolean;
30
+ export declare function bindingContains(binding: ts.BindingName, name: string): boolean;
31
+ /** Visit a function body, without descending into the functions nested in it. */
32
+ export declare function visitOwnBody(fn: ts.FunctionLikeDeclaration, callback: (node: ts.Node) => void): void;
30
33
  export declare function unwrap(expression: ts.Expression): ts.Expression;
31
34
  export declare function staticText(expression: ts.Expression | undefined): string | undefined;
32
35
  export declare function objectProperty(object: ts.ObjectLiteralExpression | undefined, name: string): ts.ObjectLiteralElementLike | undefined;
33
36
  export declare function propertyName(name: ts.PropertyName): string | undefined;
34
- export declare function callbackOf(property: ts.ObjectLiteralElementLike | undefined): ts.FunctionLikeDeclaration | undefined;
35
- export declare function objectArgument(call: ts.CallExpression): ts.ObjectLiteralExpression | undefined;
36
- export declare function isImportedCall(file: SourceFile, node: ts.Node, modules: ReadonlySet<string>, name: string): node is ts.CallExpression;
37
37
  export declare function propertyChain(expression: ts.Expression): readonly string[] | undefined;
38
38
  export declare function containingFunction(node: ts.Node): ts.FunctionLikeDeclaration | undefined;
@@ -103,11 +103,23 @@ function statementDeclares(statement, name) {
103
103
  }
104
104
  return false;
105
105
  }
106
- function bindingContains(binding, name) {
106
+ export function bindingContains(binding, name) {
107
107
  if (ts.isIdentifier(binding))
108
108
  return binding.text === name;
109
109
  return binding.elements.some((element) => !ts.isOmittedExpression(element) && bindingContains(element.name, name));
110
110
  }
111
+ /** Visit a function body, without descending into the functions nested in it. */
112
+ export function visitOwnBody(fn, callback) {
113
+ if (!fn.body)
114
+ return;
115
+ const walk = (node) => {
116
+ callback(node);
117
+ if (node !== fn.body && ts.isFunctionLike(node))
118
+ return;
119
+ node.forEachChild(walk);
120
+ };
121
+ walk(fn.body);
122
+ }
111
123
  export function unwrap(expression) {
112
124
  let current = expression;
113
125
  while (ts.isParenthesizedExpression(current) ||
@@ -138,30 +150,6 @@ export function propertyName(name) {
138
150
  return name.text;
139
151
  return ts.isComputedPropertyName(name) ? staticText(name.expression) : undefined;
140
152
  }
141
- export function callbackOf(property) {
142
- if (!property)
143
- return undefined;
144
- if (ts.isMethodDeclaration(property))
145
- return property;
146
- if (ts.isPropertyAssignment(property)) {
147
- const value = unwrap(property.initializer);
148
- if (ts.isArrowFunction(value) || ts.isFunctionExpression(value))
149
- return value;
150
- }
151
- return undefined;
152
- }
153
- export function objectArgument(call) {
154
- const value = call.arguments[0];
155
- return value && ts.isObjectLiteralExpression(unwrap(value))
156
- ? unwrap(value)
157
- : undefined;
158
- }
159
- export function isImportedCall(file, node, modules, name) {
160
- if (!ts.isCallExpression(node))
161
- return false;
162
- const symbol = importedSymbol(file, node.expression);
163
- return symbol?.name === name && modules.has(symbol.module);
164
- }
165
153
  export function propertyChain(expression) {
166
154
  const target = unwrap(expression);
167
155
  if (ts.isIdentifier(target))
@@ -25,4 +25,3 @@ export declare function callableReference(expression: ts.Expression): {
25
25
  };
26
26
  /** Resolve only one immutable lexical alias; the linter does not emulate program execution. */
27
27
  export declare function lexicalConstInitializer(identifier: ts.Identifier): ts.Expression | undefined;
28
- export declare function bindingContains(binding: ts.BindingName, name: string): boolean;
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { objectProperty, resolveImportedSymbol, resolveProjectImport, unwrap, visit, } from '../../adapters/typescript/index.js';
2
+ import { bindingContains, objectProperty, resolveImportedSymbol, resolveProjectImport, unwrap, visit, } from '../../adapters/typescript/index.js';
3
3
  import { definitionObjects, DSL_MODULES, production, propertyExpression } from './shared.js';
4
4
  export function domainFunctionRequirements(project) {
5
5
  const domainPath = project.policy.rootFiles.find(({ id }) => id === 'domain')?.sourcePath;
@@ -227,8 +227,3 @@ export function lexicalConstInitializer(identifier) {
227
227
  }
228
228
  return undefined;
229
229
  }
230
- export function bindingContains(binding, name) {
231
- if (ts.isIdentifier(binding))
232
- return binding.text === name;
233
- return binding.elements.some((element) => !ts.isOmittedExpression(element) && bindingContains(element.name, name));
234
- }
@@ -1,7 +1,7 @@
1
1
  import ts from 'typescript';
2
2
  import { propertyChain, resolveImportedSymbol, unwrap, visit, } from '../../adapters/typescript/index.js';
3
3
  import { domainFunctionRequirements, callableReference, callableSelector, declaredDomainDependencies, remoteCallableKey, } from './callable-requirements.js';
4
- import { ambiguity, calls, isForeignDomain, production, violation } from './shared.js';
4
+ import { ambiguity, calls, contextPath, isForeignDomain, production, violation } from './shared.js';
5
5
  export const dependencyRules = [
6
6
  {
7
7
  id: 'FNC-XDOM-DECLARED',
@@ -158,64 +158,3 @@ function selectedCallable(input) {
158
158
  const chain = expression && propertyChain(expression);
159
159
  return chain && chain[0] === parameter.text ? chain.slice(1) : undefined;
160
160
  }
161
- /** Resolve handler context bindings and one immutable local alias without following arbitrary helpers. */
162
- function contextPath(input, run, aliases = 1) {
163
- const expression = unwrap(input);
164
- if (ts.isPropertyAccessExpression(expression)) {
165
- const base = contextPath(expression.expression, run, aliases);
166
- return base && [...base, expression.name.text];
167
- }
168
- if (ts.isElementAccessExpression(expression)) {
169
- const base = contextPath(expression.expression, run, aliases);
170
- return base && [...base, '[computed]'];
171
- }
172
- if (!ts.isIdentifier(expression))
173
- return undefined;
174
- let current = expression.parent;
175
- while (current) {
176
- if (ts.isFunctionLike(current)) {
177
- for (const parameter of current.parameters) {
178
- const path = bindingPath(parameter.name, expression.text);
179
- if (path)
180
- return current === run && parameter === run.parameters[0] ? path : undefined;
181
- }
182
- }
183
- if (ts.isBlock(current) || ts.isSourceFile(current)) {
184
- for (const statement of current.statements) {
185
- if (ts.isFunctionDeclaration(statement) && statement.name?.text === expression.text)
186
- return undefined;
187
- if (!ts.isVariableStatement(statement))
188
- continue;
189
- for (const declaration of statement.declarationList.declarations) {
190
- const path = bindingPath(declaration.name, expression.text);
191
- if (!path)
192
- continue;
193
- if (aliases === 0 ||
194
- !declaration.initializer ||
195
- declaration.end > expression.pos ||
196
- !(statement.declarationList.flags & ts.NodeFlags.Const))
197
- return undefined;
198
- const base = contextPath(declaration.initializer, run, aliases - 1);
199
- return base && [...base, ...path];
200
- }
201
- }
202
- }
203
- current = current.parent;
204
- }
205
- return undefined;
206
- }
207
- function bindingPath(name, identifier) {
208
- if (ts.isIdentifier(name))
209
- return name.text === identifier ? [] : undefined;
210
- if (!ts.isObjectBindingPattern(name))
211
- return undefined;
212
- for (const element of name.elements) {
213
- if (element.dotDotDotToken)
214
- continue;
215
- const nested = bindingPath(element.name, identifier);
216
- const property = element.propertyName ?? (ts.isIdentifier(element.name) ? element.name : undefined);
217
- if (nested && property && (ts.isIdentifier(property) || ts.isStringLiteral(property)))
218
- return [property.text, ...nested];
219
- }
220
- return undefined;
221
- }
@@ -1,6 +1,6 @@
1
1
  import ts from 'typescript';
2
2
  import { isLocalAlias } from '../../../../platform/graph/alias.js';
3
- import { expressionType, importedSymbol, propertyChain, staticText, unwrap, visit, } from '../../adapters/typescript/index.js';
3
+ import { expressionType, importedSymbol, propertyChain, staticText, unwrap, visit, visitOwnBody, } from '../../adapters/typescript/index.js';
4
4
  import { ambiguity, callback, calls, DSL_MODULES, definitionOriginAmbiguity, definitionObjects, domainProjectorEvidence, forbiddenIoImport, hasForbiddenGlobalCall, production, violation, } from './shared.js';
5
5
  import { stateMachineIdentity, statePropertyIdentity } from './state-machine.js';
6
6
  const FORBIDDEN_LAYERS = new Set(['integrations', 'functions', 'providers', 'queries']);
@@ -1399,17 +1399,6 @@ function referencesIdentifier(arguments_, name) {
1399
1399
  }
1400
1400
  return found;
1401
1401
  }
1402
- function visitOwnBody(fn, callback) {
1403
- if (!fn.body)
1404
- return;
1405
- const walk = (node) => {
1406
- callback(node);
1407
- if (node !== fn.body && ts.isFunctionLike(node))
1408
- return;
1409
- node.forEachChild(walk);
1410
- };
1411
- walk(fn.body);
1412
- }
1413
1402
  function returnedValues(fn) {
1414
1403
  if (!fn.body)
1415
1404
  return 0;
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { importedSymbol, observeQueryDefinitions, propertyChain, queryCollectionKind, unwrap, visit, } from '../../adapters/typescript/index.js';
2
+ import { bindingContains, importedSymbol, observeQueryDefinitions, propertyChain, queryCollectionKind, unwrap, visit, visitOwnBody, } from '../../adapters/typescript/index.js';
3
3
  import { decideCanonicalQuery, decideSingleQuery } from '../../analysis/query/index.js';
4
4
  import { ambiguity, callback, definitionOriginAmbiguity, definitionObjects, domainProjectorEvidence, propertyExpression, production, returnedExpressions, staticId, violation, } from './shared.js';
5
5
  import { sourceEvidence } from './verifier.js';
@@ -1130,22 +1130,6 @@ function enclosingFunction(node) {
1130
1130
  current = current.parent;
1131
1131
  return current;
1132
1132
  }
1133
- function bindingContains(binding, name) {
1134
- if (ts.isIdentifier(binding))
1135
- return binding.text === name;
1136
- return binding.elements.some((element) => !ts.isOmittedExpression(element) && bindingContains(element.name, name));
1137
- }
1138
- function visitOwnBody(fn, callback) {
1139
- if (!fn.body)
1140
- return;
1141
- const walk = (node) => {
1142
- callback(node);
1143
- if (node !== fn.body && ts.isFunctionLike(node))
1144
- return;
1145
- node.forEachChild(walk);
1146
- };
1147
- walk(fn.body);
1148
- }
1149
1133
  function isCompositeLeaf(expression, file) {
1150
1134
  if (!expression || !ts.isIdentifier(unwrap(expression)))
1151
1135
  return false;
@@ -25,20 +25,23 @@ export declare function domainProjectorEvidence(file: SourceFile, importedName:
25
25
  export declare function violation(file: SourceFile, node: ts.Node, evidence: string): SourceRuleEvidence;
26
26
  export declare function ambiguity(file: SourceFile, node: ts.Node, evidence: string): SourceRuleEvidence;
27
27
  export declare function localTarget(project: SourceProject, file: SourceFile, sourceImport: SourceImport): SourceFile | undefined;
28
- export declare function importedLayer(project: SourceProject, file: SourceFile, sourceImport: SourceImport): string | undefined;
29
28
  export declare function isForeignDomain(specifier: string): boolean;
30
29
  export declare function packageRoot(specifier: string): string;
31
30
  export declare function isDeepPackageImport(specifier: string): boolean;
32
31
  export declare function propertyExpression(object: ts.ObjectLiteralExpression | undefined, name: string): ts.Expression | undefined;
33
32
  export declare function callback(object: ts.ObjectLiteralExpression | undefined, name: string): ts.FunctionLikeDeclaration | undefined;
34
33
  export declare function returnedExpressions(fn: ts.FunctionLikeDeclaration): ts.Expression[];
35
- export declare function callChain(expression: ts.Expression): readonly string[] | undefined;
36
34
  export declare function staticId(expression: ts.Expression | undefined): string | undefined;
37
35
  export declare function hasForbiddenGlobalCall(file: SourceFile): Array<{
38
36
  node: ts.Node;
39
37
  name: string;
40
38
  }>;
39
+ /** The global a call reaches, as `name(…)` with no import or local binding, or `globalThis.name(…)`. */
40
+ export declare function globalCallName(file: SourceFile, node: ts.CallExpression): string | undefined;
41
+ /**
42
+ * Resolve the path of `input` from the first parameter of `run`, or of a durable Step callback in
43
+ * it, through one immutable local alias, without following arbitrary helpers.
44
+ */
45
+ export declare function contextPath(input: ts.Expression, run: ts.FunctionLikeDeclaration, aliases?: number): readonly string[] | undefined;
41
46
  export declare function isLocallyBoundIdentifier(node: ts.Identifier): boolean;
42
47
  export declare function forbiddenIoImport(specifier: string): boolean;
43
- export declare function variableName(call: ts.CallExpression): string | undefined;
44
- export declare function allPropertyNames(object: ts.ObjectLiteralExpression): string[];
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { importedSymbol, location, objectProperty, propertyChain, resolveImportedSymbol, resolveProjectImport, staticText, unwrap, visit, } from '../../adapters/typescript/index.js';
2
+ import { bindingContains, importedSymbol, location, objectProperty, propertyChain, resolveImportedSymbol, resolveProjectImport, staticText, unwrap, visit, } from '../../adapters/typescript/index.js';
3
3
  export const SDK_MODULES = new Set([
4
4
  '@astrale-os/sdk',
5
5
  '@astrale-os/sdk/action',
@@ -159,9 +159,6 @@ export function ambiguity(file, node, evidence) {
159
159
  export function localTarget(project, file, sourceImport) {
160
160
  return resolveProjectImport(project, file, sourceImport.specifier);
161
161
  }
162
- export function importedLayer(project, file, sourceImport) {
163
- return localTarget(project, file, sourceImport)?.layer;
164
- }
165
162
  export function isForeignDomain(specifier) {
166
163
  if (specifier.startsWith('@astrale-domains/'))
167
164
  return true;
@@ -209,14 +206,6 @@ export function returnedExpressions(fn) {
209
206
  fn.body.forEachChild(walk);
210
207
  return expressions;
211
208
  }
212
- export function callChain(expression) {
213
- const target = unwrap(expression);
214
- if (ts.isCallExpression(target))
215
- return propertyChain(target.expression);
216
- if (ts.isPropertyAccessExpression(target))
217
- return propertyChain(target);
218
- return undefined;
219
- }
220
209
  export function staticId(expression) {
221
210
  return staticText(expression);
222
211
  }
@@ -226,22 +215,101 @@ export function hasForbiddenGlobalCall(file) {
226
215
  visit(file.source, (node) => {
227
216
  if (!ts.isCallExpression(node))
228
217
  return;
229
- const expression = unwrap(node.expression);
230
- if (ts.isIdentifier(expression)) {
231
- const name = expression.text;
232
- if (forbidden.has(name) &&
233
- !importedSymbol(file, expression) &&
234
- !isLocallyBound(expression, name)) {
235
- result.push({ node, name });
218
+ const name = globalCallName(file, node);
219
+ if (name !== undefined && forbidden.has(name))
220
+ result.push({ node, name });
221
+ });
222
+ return result;
223
+ }
224
+ /** The global a call reaches, as `name(…)` with no import or local binding, or `globalThis.name(…)`. */
225
+ export function globalCallName(file, node) {
226
+ const expression = unwrap(node.expression);
227
+ if (ts.isIdentifier(expression)) {
228
+ const name = expression.text;
229
+ return importedSymbol(file, expression) || isLocallyBound(expression, name) ? undefined : name;
230
+ }
231
+ const chain = propertyChain(expression);
232
+ if (chain?.length !== 2 || chain[0] !== 'globalThis')
233
+ return undefined;
234
+ let root = expression;
235
+ while (ts.isPropertyAccessExpression(root))
236
+ root = unwrap(root.expression);
237
+ return ts.isIdentifier(root) && isLocallyBound(root, root.text) ? undefined : chain[1];
238
+ }
239
+ /**
240
+ * Resolve the path of `input` from the first parameter of `run`, or of a durable Step callback in
241
+ * it, through one immutable local alias, without following arbitrary helpers.
242
+ */
243
+ export function contextPath(input, run, aliases = 1) {
244
+ const expression = unwrap(input);
245
+ if (ts.isPropertyAccessExpression(expression)) {
246
+ const base = contextPath(expression.expression, run, aliases);
247
+ return base && [...base, expression.name.text];
248
+ }
249
+ if (ts.isElementAccessExpression(expression)) {
250
+ const base = contextPath(expression.expression, run, aliases);
251
+ return base && [...base, '[computed]'];
252
+ }
253
+ if (!ts.isIdentifier(expression))
254
+ return undefined;
255
+ let current = expression.parent;
256
+ while (current) {
257
+ if (ts.isFunctionLike(current)) {
258
+ for (const parameter of current.parameters) {
259
+ const path = bindingPath(parameter.name, expression.text);
260
+ if (path)
261
+ return parameter === current.parameters[0] &&
262
+ (current === run || stepCallback(current, run))
263
+ ? path
264
+ : undefined;
236
265
  }
237
- return;
238
266
  }
239
- const chain = propertyChain(expression);
240
- if (chain?.length === 2 && chain[0] === 'globalThis' && forbidden.has(chain[1])) {
241
- result.push({ node, name: chain[1] });
267
+ if (ts.isBlock(current) || ts.isSourceFile(current)) {
268
+ for (const statement of current.statements) {
269
+ if (ts.isFunctionDeclaration(statement) && statement.name?.text === expression.text)
270
+ return undefined;
271
+ if (!ts.isVariableStatement(statement))
272
+ continue;
273
+ for (const declaration of statement.declarationList.declarations) {
274
+ const path = bindingPath(declaration.name, expression.text);
275
+ if (!path)
276
+ continue;
277
+ if (aliases === 0 ||
278
+ !declaration.initializer ||
279
+ declaration.end > expression.pos ||
280
+ !(statement.declarationList.flags & ts.NodeFlags.Const))
281
+ return undefined;
282
+ const base = contextPath(declaration.initializer, run, aliases - 1);
283
+ return base && [...base, ...path];
284
+ }
285
+ }
242
286
  }
243
- });
244
- return result;
287
+ current = current.parent;
288
+ }
289
+ return undefined;
290
+ }
291
+ /** A durable Step callback receives the handler's capabilities under the same names. */
292
+ function stepCallback(callback, run) {
293
+ const call = callback.parent;
294
+ if (!ts.isCallExpression(call) || call.arguments[1] !== callback)
295
+ return false;
296
+ const callee = contextPath(call.expression, run, 0);
297
+ return callee?.length === 2 && callee[0] === 'step' && callee[1] === 'run';
298
+ }
299
+ function bindingPath(name, identifier) {
300
+ if (ts.isIdentifier(name))
301
+ return name.text === identifier ? [] : undefined;
302
+ if (!ts.isObjectBindingPattern(name))
303
+ return undefined;
304
+ for (const element of name.elements) {
305
+ if (element.dotDotDotToken)
306
+ continue;
307
+ const nested = bindingPath(element.name, identifier);
308
+ const property = element.propertyName ?? (ts.isIdentifier(element.name) ? element.name : undefined);
309
+ if (nested && property && (ts.isIdentifier(property) || ts.isStringLiteral(property)))
310
+ return [property.text, ...nested];
311
+ }
312
+ return undefined;
245
313
  }
246
314
  export function isLocallyBoundIdentifier(node) {
247
315
  return isLocallyBound(node, node.text);
@@ -251,22 +319,6 @@ export function forbiddenIoImport(specifier) {
251
319
  !specifier.startsWith('node:assert') &&
252
320
  !specifier.startsWith('node:util/types'));
253
321
  }
254
- export function variableName(call) {
255
- const parent = call.parent;
256
- return ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)
257
- ? parent.name.text
258
- : undefined;
259
- }
260
- export function allPropertyNames(object) {
261
- return object.properties.flatMap((property) => {
262
- if (!('name' in property) || !property.name)
263
- return [];
264
- const name = ts.isIdentifier(property.name) || ts.isStringLiteral(property.name)
265
- ? property.name.text
266
- : undefined;
267
- return name ? [name] : [];
268
- });
269
- }
270
322
  function isLocallyBound(node, name) {
271
323
  let current = node.parent;
272
324
  while (current) {
@@ -312,8 +364,3 @@ function statementDeclares(statement, name) {
312
364
  }
313
365
  return false;
314
366
  }
315
- function bindingContains(binding, name) {
316
- if (ts.isIdentifier(binding))
317
- return binding.text === name;
318
- return binding.elements.some((element) => ts.isOmittedExpression(element) ? false : bindingContains(element.name, name));
319
- }