@stream44.studio/encapsulate 0.4.0-rc.14 → 0.4.0-rc.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream44.studio/encapsulate",
3
- "version": "0.4.0-rc.14",
3
+ "version": "0.4.0-rc.16",
4
4
  "license": "BSD-2-Clause-Patent",
5
5
  "repository": {
6
6
  "type": "git",
@@ -204,6 +204,23 @@ class MembraneContractCapsuleInstanceFactory extends ContractCapsuleInstanceFact
204
204
  }
205
205
  }
206
206
 
207
+ // Separate nested capsule-name-targeted options from own options
208
+ // Keys starting with '#' are own options for the mapped capsule
209
+ // Non-'#' keys are matched against capsule names in the mapping tree
210
+ let ownMappingOptions: Record<string, any> | undefined = undefined
211
+ let nestedCapsuleOptions: Record<string, any> | undefined = undefined
212
+ if (mappingOptions) {
213
+ for (const [key, value] of Object.entries(mappingOptions)) {
214
+ if (key.startsWith('#')) {
215
+ if (!ownMappingOptions) ownMappingOptions = {}
216
+ ownMappingOptions[key] = value
217
+ } else {
218
+ if (!nestedCapsuleOptions) nestedCapsuleOptions = {}
219
+ nestedCapsuleOptions[key] = value
220
+ }
221
+ }
222
+ }
223
+
207
224
  // Transform overrides if this mapping has a propertyContractDelegate
208
225
  let mappedOverrides = overrides
209
226
  if (property.definition.propertyContractDelegate) {
@@ -229,9 +246,21 @@ class MembraneContractCapsuleInstanceFactory extends ContractCapsuleInstanceFact
229
246
  }
230
247
  }
231
248
 
249
+ // Merge nested capsule-name-targeted options into overrides
250
+ // These will be picked up when child capsules with matching names are instantiated
251
+ if (nestedCapsuleOptions) {
252
+ mappedOverrides = { ...mappedOverrides }
253
+ for (const [capsuleNameKey, capsuleOptions] of Object.entries(nestedCapsuleOptions)) {
254
+ mappedOverrides[capsuleNameKey] = {
255
+ ...(mappedOverrides[capsuleNameKey] || {}),
256
+ ...capsuleOptions
257
+ }
258
+ }
259
+ }
260
+
232
261
  const mappedCapsuleInstance = await mappedCapsule.makeInstance({
233
262
  overrides: mappedOverrides,
234
- options: mappingOptions,
263
+ options: ownMappingOptions,
235
264
  runtimeSpineContracts: this.runtimeSpineContracts,
236
265
  rootCapsule: this.capsuleInstance?.rootCapsule
237
266
  })
@@ -214,6 +214,23 @@ export class ContractCapsuleInstanceFactory {
214
214
  }
215
215
  }
216
216
 
217
+ // Separate nested capsule-name-targeted options from own options
218
+ // Keys starting with '#' are own options for the mapped capsule
219
+ // Non-'#' keys are matched against capsule names in the mapping tree
220
+ let ownMappingOptions: Record<string, any> | undefined = undefined
221
+ let nestedCapsuleOptions: Record<string, any> | undefined = undefined
222
+ if (mappingOptions) {
223
+ for (const [key, value] of Object.entries(mappingOptions)) {
224
+ if (key.startsWith('#')) {
225
+ if (!ownMappingOptions) ownMappingOptions = {}
226
+ ownMappingOptions[key] = value
227
+ } else {
228
+ if (!nestedCapsuleOptions) nestedCapsuleOptions = {}
229
+ nestedCapsuleOptions[key] = value
230
+ }
231
+ }
232
+ }
233
+
217
234
  // Transform overrides if this mapping has a propertyContractDelegate
218
235
  let mappedOverrides = overrides
219
236
  if (property.definition.propertyContractDelegate) {
@@ -238,10 +255,22 @@ export class ContractCapsuleInstanceFactory {
238
255
  }
239
256
  }
240
257
 
258
+ // Merge nested capsule-name-targeted options into overrides
259
+ // These will be picked up when child capsules with matching names are instantiated
260
+ if (nestedCapsuleOptions) {
261
+ mappedOverrides = { ...mappedOverrides }
262
+ for (const [capsuleNameKey, capsuleOptions] of Object.entries(nestedCapsuleOptions)) {
263
+ mappedOverrides[capsuleNameKey] = {
264
+ ...(mappedOverrides[capsuleNameKey] || {}),
265
+ ...capsuleOptions
266
+ }
267
+ }
268
+ }
269
+
241
270
  const apiTarget = this.getApiTarget({ property })
242
271
  const mappedInstance = await mappedCapsule.makeInstance({
243
272
  overrides: mappedOverrides,
244
- options: mappingOptions,
273
+ options: ownMappingOptions,
245
274
  runtimeSpineContracts: this.runtimeSpineContracts,
246
275
  rootCapsule: this.capsuleInstance?.rootCapsule
247
276
  })
@@ -849,7 +849,7 @@ function extractModuleLocalCode(
849
849
  }
850
850
  }
851
851
 
852
- // Also collect functions from the local scope around the call node
852
+ // Also collect functions and variables from the enclosing scope around the call node
853
853
  if (callNode) {
854
854
  let currentNode: ts.Node | undefined = callNode
855
855
  while (currentNode) {
@@ -859,6 +859,13 @@ function extractModuleLocalCode(
859
859
  if (ts.isFunctionDeclaration(statement) && statement.name) {
860
860
  moduleLocalFunctions.set(statement.name.text, statement)
861
861
  }
862
+ if (ts.isVariableStatement(statement)) {
863
+ for (const decl of statement.declarationList.declarations) {
864
+ if (ts.isIdentifier(decl.name)) {
865
+ moduleLocalVariables.set(decl.name.text, decl)
866
+ }
867
+ }
868
+ }
862
869
  }
863
870
  }
864
871
  break
@@ -925,7 +932,6 @@ function extractModuleLocalCode(
925
932
  if (varDecl) {
926
933
  // Analyze the variable to see if it's self-contained
927
934
  const varDependencies = analyzeVariableDependencies(varDecl, sourceFile, importMap, assignmentMap, moduleLocalFunctions, moduleLocalVariables)
928
-
929
935
  if (varDependencies.isContained) {
930
936
  // Mark this as module-local in ambient references
931
937
  refTyped.type = 'module-local'
@@ -939,6 +945,14 @@ function extractModuleLocalCode(
939
945
  // Fallback to just the declaration
940
946
  moduleLocalCode[name] = varDecl.getText(sourceFile)
941
947
  }
948
+
949
+ // Recursively collect transitive variable dependencies
950
+ // (e.g., if appDir = join(baseDir, 'x'), also collect baseDir)
951
+ collectTransitiveVariableDependencies(
952
+ varDecl, sourceFile, importMap, assignmentMap,
953
+ moduleLocalFunctions, moduleLocalVariables,
954
+ ambientReferences, moduleLocalCode
955
+ )
942
956
  }
943
957
  }
944
958
  }
@@ -962,6 +976,122 @@ function extractModuleLocalCode(
962
976
  return moduleLocalCode
963
977
  }
964
978
 
979
+ // Recursively collect transitive variable dependencies from a variable's initializer.
980
+ // When a module-local variable references another module-local variable, collect that
981
+ // dependency's code and add it to ambientReferences and moduleLocalCode.
982
+ function collectTransitiveVariableDependencies(
983
+ varDecl: ts.VariableDeclaration,
984
+ sourceFile: ts.SourceFile,
985
+ importMap: Map<string, { importSpecifier: string, moduleUri: string }>,
986
+ assignmentMap: Map<string, { importSpecifier: string, moduleUri: string }>,
987
+ moduleLocalFunctions: Map<string, ts.FunctionDeclaration>,
988
+ moduleLocalVariables: Map<string, ts.VariableDeclaration>,
989
+ ambientReferences: Record<string, any>,
990
+ moduleLocalCode: Record<string, string>
991
+ ): void {
992
+ if (!varDecl.initializer) return
993
+
994
+ function visit(node: ts.Node) {
995
+ if (ts.isTypeNode(node)) return
996
+
997
+ if (ts.isIdentifier(node)) {
998
+ const name = node.text
999
+
1000
+ // Skip special keywords
1001
+ if (name === 'this' || name === 'undefined' || name === 'null' || name === 'arguments') return
1002
+
1003
+ // Skip property access names and property assignments
1004
+ const parent = node.parent
1005
+ if (parent && ts.isPropertyAccessExpression(parent) && parent.name === node) return
1006
+ if (parent && ts.isPropertyAssignment(parent) && parent.name === node) return
1007
+ if (parent && ts.isBindingElement(parent) && parent.propertyName === node) return
1008
+
1009
+ // Skip if already tracked
1010
+ if (ambientReferences[name] || moduleLocalCode[name]) return
1011
+
1012
+ // Skip builtins
1013
+ if (MODULE_GLOBAL_BUILTINS.has(name)) return
1014
+
1015
+ // Check if it's an import — add to ambient refs
1016
+ const importInfo = importMap.get(name)
1017
+ if (importInfo) {
1018
+ if (!ambientReferences[name]) {
1019
+ ambientReferences[name] = {
1020
+ type: 'import',
1021
+ importSpecifier: importInfo.importSpecifier,
1022
+ moduleUri: importInfo.moduleUri
1023
+ }
1024
+ }
1025
+ return
1026
+ }
1027
+
1028
+ // Check if it's an assignment from import
1029
+ const assignmentInfo = assignmentMap.get(name)
1030
+ if (assignmentInfo) {
1031
+ if (!ambientReferences[name]) {
1032
+ ambientReferences[name] = {
1033
+ type: 'assigned',
1034
+ importSpecifier: assignmentInfo.importSpecifier,
1035
+ moduleUri: assignmentInfo.moduleUri
1036
+ }
1037
+ }
1038
+ return
1039
+ }
1040
+
1041
+ // Check if it's another module-local variable — recursively collect
1042
+ const depVarDecl = moduleLocalVariables.get(name)
1043
+ if (depVarDecl) {
1044
+ const depDependencies = analyzeVariableDependencies(depVarDecl, sourceFile, importMap, assignmentMap, moduleLocalFunctions, moduleLocalVariables)
1045
+ if (depDependencies.isContained) {
1046
+ ambientReferences[name] = { type: 'module-local' }
1047
+
1048
+ const depVarStatement = depVarDecl.parent?.parent
1049
+ if (depVarStatement && ts.isVariableStatement(depVarStatement)) {
1050
+ moduleLocalCode[name] = depVarStatement.getText(sourceFile)
1051
+ } else {
1052
+ moduleLocalCode[name] = depVarDecl.getText(sourceFile)
1053
+ }
1054
+
1055
+ // Add import dependencies
1056
+ for (const [depName, depInfo] of depDependencies.importDependencies) {
1057
+ if (!ambientReferences[depName]) {
1058
+ ambientReferences[depName] = {
1059
+ type: 'import',
1060
+ importSpecifier: depInfo.importSpecifier,
1061
+ moduleUri: depInfo.moduleUri
1062
+ }
1063
+ }
1064
+ }
1065
+
1066
+ // Recurse into this dependency's initializer
1067
+ collectTransitiveVariableDependencies(
1068
+ depVarDecl, sourceFile, importMap, assignmentMap,
1069
+ moduleLocalFunctions, moduleLocalVariables,
1070
+ ambientReferences, moduleLocalCode
1071
+ )
1072
+ }
1073
+ return
1074
+ }
1075
+
1076
+ // Check if it's a module-local function
1077
+ if (moduleLocalFunctions.has(name)) {
1078
+ if (!ambientReferences[name]) {
1079
+ ambientReferences[name] = { type: 'module-local' }
1080
+ }
1081
+ if (!moduleLocalCode[name]) {
1082
+ const funcDecl = moduleLocalFunctions.get(name)!
1083
+ moduleLocalCode[name] = funcDecl.getText(sourceFile)
1084
+ }
1085
+ return
1086
+ }
1087
+ }
1088
+
1089
+ ts.forEachChild(node, visit)
1090
+ }
1091
+
1092
+ visit(varDecl.initializer)
1093
+ }
1094
+
965
1095
  // Analyze if a function is self-contained (only depends on other module-local functions or builtins)
966
1096
  function analyzeFunctionDependencies(
967
1097
  funcDecl: ts.FunctionDeclaration,
@@ -1250,7 +1380,7 @@ function extractCapsuleAmbientReferences(
1250
1380
  }
1251
1381
  }
1252
1382
 
1253
- // Find enclosing function and collect its parameters and local functions
1383
+ // Find enclosing function and collect its parameters, local variables, and local functions
1254
1384
  let currentNode: ts.Node | undefined = call
1255
1385
  let enclosingBlock: ts.Block | undefined
1256
1386
  while (currentNode) {
@@ -1259,7 +1389,7 @@ function extractCapsuleAmbientReferences(
1259
1389
  for (const param of currentNode.parameters) {
1260
1390
  extractParameterNames(param.name, invocationParameters)
1261
1391
  }
1262
- // Get the function body to collect local functions
1392
+ // Get the function body to collect local functions and variables
1263
1393
  if (currentNode.body && ts.isBlock(currentNode.body)) {
1264
1394
  enclosingBlock = currentNode.body
1265
1395
  }
@@ -1268,12 +1398,19 @@ function extractCapsuleAmbientReferences(
1268
1398
  currentNode = currentNode.parent
1269
1399
  }
1270
1400
 
1271
- // Collect function declarations from the enclosing block
1401
+ // Collect function declarations and variable declarations from the enclosing block
1272
1402
  if (enclosingBlock) {
1273
1403
  for (const statement of enclosingBlock.statements) {
1274
1404
  if (ts.isFunctionDeclaration(statement) && statement.name) {
1275
1405
  moduleLocalFunctions.set(statement.name.text, statement)
1276
1406
  }
1407
+ if (ts.isVariableStatement(statement)) {
1408
+ for (const decl of statement.declarationList.declarations) {
1409
+ if (ts.isIdentifier(decl.name)) {
1410
+ moduleLocalVariables.set(decl.name.text, decl)
1411
+ }
1412
+ }
1413
+ }
1277
1414
  }
1278
1415
  }
1279
1416
 
@@ -1541,7 +1678,7 @@ function extractAndValidateAmbientReferences(
1541
1678
  const localIdentifiers = new Set<string>()
1542
1679
  const invocationParameters = new Set<string>()
1543
1680
 
1544
- // Find enclosing function and collect its parameters as invocation arguments and local functions
1681
+ // Find enclosing function and collect its parameters as invocation arguments and local functions/variables
1545
1682
  let currentNode: ts.Node | undefined = fn
1546
1683
  let enclosingBlock: ts.Block | undefined
1547
1684
  while (currentNode) {
@@ -1551,7 +1688,7 @@ function extractAndValidateAmbientReferences(
1551
1688
  for (const param of currentNode.parameters) {
1552
1689
  extractParameterNamesForInvocation(param.name, invocationParameters)
1553
1690
  }
1554
- // Get the function body to collect local functions
1691
+ // Get the function body to collect local functions and variables
1555
1692
  if (currentNode.body && ts.isBlock(currentNode.body)) {
1556
1693
  enclosingBlock = currentNode.body
1557
1694
  }
@@ -1561,12 +1698,19 @@ function extractAndValidateAmbientReferences(
1561
1698
  currentNode = currentNode.parent
1562
1699
  }
1563
1700
 
1564
- // Collect function declarations from the enclosing block
1701
+ // Collect function declarations and variable declarations from the enclosing block
1565
1702
  if (enclosingBlock) {
1566
1703
  for (const statement of enclosingBlock.statements) {
1567
1704
  if (ts.isFunctionDeclaration(statement) && statement.name) {
1568
1705
  moduleLocalFunctions.set(statement.name.text, statement)
1569
1706
  }
1707
+ if (ts.isVariableStatement(statement)) {
1708
+ for (const decl of statement.declarationList.declarations) {
1709
+ if (ts.isIdentifier(decl.name)) {
1710
+ moduleLocalVariables.set(decl.name.text, decl)
1711
+ }
1712
+ }
1713
+ }
1570
1714
  }
1571
1715
  }
1572
1716