@stream44.studio/encapsulate 0.4.0-rc.34 → 0.4.0-rc.39

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <table>
2
2
  <tr>
3
- <td><a href="https://Stream44.Studio"><img src=".o/stream44.studio/assets/Icon-v1.svg" width="42" height="42"></a></td>
4
- <td><strong><a href="https://Stream44.Studio">Stream44 Studio</a></strong><br/>Open Development Project</td>
3
+ <td><a href="https://Stream44.Systems"><img src=".o/stream44.studio/assets/Icon-v1.svg" width="42" height="42"></a></td>
4
+ <td><strong><a href="https://Stream44.Systems">Stream44 Systems</a></strong><br/>Open Development Project</td>
5
5
  <td>Preview release for community feedback.<br/>Get in touch on <a href="https://discord.gg/9eBcQXEJAN">discord</a>.</td>
6
6
  <td>Designed by Hand<br/><b>AI assisted Code</a></td>
7
7
  </tr>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream44.studio/encapsulate",
3
- "version": "0.4.0-rc.34",
3
+ "version": "0.4.0-rc.39",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,11 +10,12 @@
10
10
  "type": "module",
11
11
  "exports": {
12
12
  "./encapsulate": "./src/encapsulate.ts",
13
- "./spine-contracts/CapsuleSpineContract.v0/Static.v0": "./src/spine-contracts/CapsuleSpineContract.v0/Static.v0.ts",
14
- "./spine-contracts/CapsuleSpineContract.v0/Membrane.v0": "./src/spine-contracts/CapsuleSpineContract.v0/Membrane.v0.ts",
15
- "./spine-factories/CapsuleSpineFactory.v0": "./src/spine-factories/CapsuleSpineFactory.v0.ts",
13
+ "./spine-contracts/CapsuleSpineContract.v0/Static": "./src/spine-contracts/CapsuleSpineContract.v0/Static.ts",
14
+ "./spine-contracts/CapsuleSpineContract.v0/Membrane": "./src/spine-contracts/CapsuleSpineContract.v0/Membrane.ts",
15
+ "./spine-factories/CapsuleSpineFactory": "./src/spine-factories/CapsuleSpineFactory.ts",
16
16
  "./spine-factories/TimingObserver": "./src/spine-factories/TimingObserver.ts",
17
- "./structs/Capsule": "./structs/Capsule.ts"
17
+ "./structs/Capsule": "./structs/Capsule.ts",
18
+ "./structs/CapsuleProjectionContext": "./structs/CapsuleProjectionContext.ts"
18
19
  },
19
20
  "scripts": {
20
21
  "test": "bun test",
@@ -50,7 +50,7 @@ async function constructCacheFilePath(moduleFilepath: string, importStackLine: n
50
50
  * Finds the nearest package.json and constructs an npm URI for cache files.
51
51
  * First checks if the path contains /node_modules/ and if so, extracts the portion
52
52
  * after the last /node_modules/ occurrence for consistent paths in dev and installed mode.
53
- * This matches the logic from static-analyzer.v0.ts
53
+ * This matches the logic from static-analyzer.ts
54
54
  */
55
55
  async function constructNpmUriForCache(absoluteFilepath: string, spineRoot: string): Promise<string | null> {
56
56
  // Check for /node_modules/ in the path — use the last occurrence to handle nested node_modules
@@ -199,6 +199,9 @@ export function CapsuleModuleProjector({
199
199
  for (const nestedPropName in propContract.properties) {
200
200
  if (nestedPropName.startsWith('/')) {
201
201
  const nestedProp = propContract.properties[nestedPropName]
202
+ // Skip if this mapping has a propertyContractDelegate — the delegate
203
+ // handles its own projection via OnFreeze
204
+ if (nestedProp.propertyContractDelegate) continue
202
205
  // Check if this is a Mapping type property
203
206
  if (nestedProp.type === 'CapsulePropertyTypes.Mapping') {
204
207
  // First try to find the mapped capsule in ambient references
@@ -287,23 +290,6 @@ export function CapsuleModuleProjector({
287
290
  return uris
288
291
  }
289
292
 
290
- // Helper: Check if capsule has solidjs.com/standalone property
291
- function hasSolidJsProperty(capsule: any, spineContractUri: string): boolean {
292
- const spineContract = capsule.cst.spineContracts[spineContractUri]
293
- // Check both top-level and nested under '#' property contract
294
- const topLevelProps = spineContract?.propertyContracts || {}
295
- const nestedProps = spineContract?.propertyContracts?.['#']?.properties || {}
296
-
297
- // Check for solidjs.com/standalone specifically
298
- for (const key of Object.keys(topLevelProps)) {
299
- if (key === 'solidjs.com/standalone') return true
300
- }
301
- for (const key of Object.keys(nestedProps)) {
302
- if (key === 'solidjs.com/standalone') return true
303
- }
304
- return false
305
- }
306
-
307
293
  // Helper: Check if capsule has encapsulate.dev/standalone property (with optional suffix)
308
294
  function hasStandaloneProperty(capsule: any, spineContractUri: string): boolean {
309
295
  const spineContract = capsule.cst.spineContracts[spineContractUri]
@@ -321,66 +307,6 @@ export function CapsuleModuleProjector({
321
307
  return false
322
308
  }
323
309
 
324
- // Helper: Extract SolidJS component function from capsule definition
325
- function extractSolidJsComponent(capsule: any, spineContractUri: string): string | null {
326
- const spineContract = capsule.cst.spineContracts[spineContractUri]
327
-
328
- // Check nested under '#' property contract first, looking for solidjs.com/standalone
329
- const nestedProps = spineContract?.propertyContracts?.['#']?.properties || {}
330
- const topLevelProps = spineContract?.propertyContracts || {}
331
-
332
- let solidjsProp = null
333
- for (const key of Object.keys(nestedProps)) {
334
- if (key === 'solidjs.com/standalone') {
335
- solidjsProp = nestedProps[key]
336
- break
337
- }
338
- }
339
- if (!solidjsProp) {
340
- for (const key of Object.keys(topLevelProps)) {
341
- if (key === 'solidjs.com/standalone') {
342
- solidjsProp = topLevelProps[key]
343
- break
344
- }
345
- }
346
- }
347
-
348
- if (!solidjsProp || solidjsProp.type !== 'CapsulePropertyTypes.Function') {
349
- return null
350
- }
351
-
352
- // Extract the value expression which contains the component function
353
- const valueExpression = solidjsProp.valueExpression
354
- if (!valueExpression) return null
355
-
356
- // The value expression is: "function (this: any): Function {\n return function ComponentName() { ... }\n}"
357
- // We need to extract the inner function after "return "
358
- // Use a more flexible regex that handles multiline and varying whitespace
359
- const match = valueExpression.match(/return\s+(function\s+\w*\s*\([^)]*\)\s*\{[\s\S]*)\s*\}\s*$/m)
360
- if (match) {
361
- // Clean up the extracted function - remove extra indentation
362
- let extracted = match[1].trim()
363
- // Add back the closing brace if it was removed
364
- if (!extracted.endsWith('}')) {
365
- extracted += '\n}'
366
- }
367
- // Remove leading indentation from each line
368
- const lines = extracted.split('\n')
369
- const minIndent = lines
370
- .filter(line => line.trim().length > 0)
371
- .map(line => line.match(/^(\s*)/)?.[1].length || 0)
372
- .reduce((min, indent) => Math.min(min, indent), Infinity)
373
-
374
- if (minIndent > 0 && minIndent !== Infinity) {
375
- extracted = lines.map(line => line.substring(minIndent)).join('\n')
376
- }
377
-
378
- return extracted
379
- }
380
-
381
- return null
382
- }
383
-
384
310
  // Helper: Extract standalone function from capsule definition
385
311
  function extractStandaloneFunction(capsule: any, spineContractUri: string): string | null {
386
312
  const spineContract = capsule.cst.spineContracts[spineContractUri]
@@ -812,196 +738,15 @@ export function CapsuleModuleProjector({
812
738
 
813
739
  const allStatements = [importStatements, literalReferences, moduleLocalFunctions].filter(Boolean).join('\n')
814
740
 
815
- // Check if this capsule has a solidjs.com or standalone property
816
- const hasSolidJs = hasSolidJsProperty(capsule, spineContractUri)
741
+ // Check if this capsule has an encapsulate.dev/standalone property
817
742
  const hasStandalone = hasStandaloneProperty(capsule, spineContractUri)
818
- const needsRuntime = hasSolidJs || hasStandalone
819
-
820
- // Determine which solid-js imports are needed (avoid duplicates with ambient references)
821
- const existingSolidJsImports = new Set<string>()
822
- for (const [name, ref] of Object.entries(ambientReferences)) {
823
- const refTyped = ref as any
824
- if (refTyped.type === 'import' && refTyped.moduleUri === 'solid-js') {
825
- // Parse existing imports from solid-js
826
- const match = refTyped.importSpecifier?.match(/\{([^}]+)\}/)
827
- if (match) {
828
- match[1].split(',').forEach((imp: string) => existingSolidJsImports.add(imp.trim()))
829
- }
830
- }
831
- }
832
-
833
- const neededSolidJsImports = ['createSignal', 'onMount', 'Show'].filter(imp => !existingSolidJsImports.has(imp))
834
- const solidJsImport = hasSolidJs && neededSolidJsImports.length > 0 ? `import { ${neededSolidJsImports.join(', ')} } from 'solid-js'\n` : ''
835
743
 
836
- // Add runtime imports for SolidJS and standalone functions
837
- const runtimeImport = needsRuntime ? `"use client"\nimport { Spine, SpineRuntime } from '@stream44.studio/encapsulate/encapsulate'\nimport { CapsuleSpineContract } from '@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0/Membrane.v0'\n${solidJsImport}` : ''
744
+ // Add runtime imports for standalone functions
745
+ const runtimeImport = hasStandalone ? `"use client"\nimport { Spine, SpineRuntime } from '@stream44.studio/encapsulate/encapsulate'\nimport { CapsuleSpineContract } from '@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0/Membrane'\n` : ''
838
746
 
839
747
  // Generate default export based on capsule type
840
748
  let defaultExport = ''
841
- if (hasSolidJs) {
842
- // Generate a wrapper that sets up runtime and exports the SolidJS component
843
- const capsuleSourceLineRef = capsule.cst.capsuleSourceLineRef
844
- const solidjsComponent = extractSolidJsComponent(capsule, spineContractUri)
845
- if (solidjsComponent) {
846
- // Collect all capsule URIs from CST (mappings and property contracts)
847
- const allCapsuleUris = collectAllCapsuleUris(capsule, spineContractUri)
848
-
849
- // Also collect from ambient references and build import paths from snapshots
850
- const capsuleDeps: Array<{ uri: string, importName: string, importPath: string }> = []
851
- for (const [name, ref] of Object.entries(ambientReferences)) {
852
- const refTyped = ref as any
853
- if (refTyped.type === 'capsule') {
854
- const snapshot = await buildCapsuleSnapshotForReference(refTyped, capsules, spineContractUri)
855
-
856
- // Use dynamic spineContractUri instead of hardcoded URI
857
- const contractData = snapshot.spineContracts?.[spineContractUri]
858
- const structKey = Object.keys(contractData || {}).find(k => k.includes('/structs/Capsule'))
859
- const capsuleName = structKey ? contractData[structKey]?.capsuleName : undefined
860
- const projectedFilepath = structKey ? contractData[structKey]?.projectedCapsuleFilepath : undefined
861
-
862
- if (capsuleName && projectedFilepath) {
863
- allCapsuleUris.add(capsuleName)
864
-
865
- // Build import path from projected filepath
866
- const importName = `_capsule_${capsuleName.replace(/[^a-zA-Z0-9]/g, '_')}`
867
- // Remove .~o/encapsulate.dev/caps/ prefix and strip extension
868
- const importPath = projectedFilepath.replace(/^\.~o\/encapsulate\.dev\/caps\//, '').replace(/\.(ts|tsx)$/, '')
869
-
870
- capsuleDeps.push({ uri: capsuleName, importName, importPath })
871
- }
872
- }
873
- }
874
-
875
- // Generate static imports for all capsule dependencies
876
- // Compute relative path from projected file to caps directory
877
- let importPrefix: string
878
- if (capsuleModuleProjectionPackage) {
879
- importPrefix = capsuleModuleProjectionPackage
880
- } else {
881
- const projectedFileDir = dirname(filepath)
882
- const capsDir = '.~o/encapsulate.dev/caps'
883
- const relativePathToCaps = relative(projectedFileDir, capsDir)
884
- importPrefix = relativePathToCaps.startsWith('.') ? relativePathToCaps : './' + relativePathToCaps
885
- }
886
- const capsuleImports = capsuleDeps.map(dep =>
887
- `import * as ${dep.importName} from '${importPrefix}/${dep.importPath}'`
888
- ).join('\n')
889
-
890
- // Generate capsules map
891
- const capsulesMapEntries = capsuleDeps.map(dep =>
892
- ` '${dep.uri}': ${dep.importName}`
893
- ).join(',\n')
894
-
895
- defaultExport = `
896
- ${capsuleImports}
897
-
898
- // Set up runtime for browser execution
899
- const sourceSpine: { encapsulate?: any } = {}
900
-
901
- // Map of statically imported capsules
902
- const capsulesMap: Record<string, any> = {
903
- ${capsulesMapEntries}
904
- }
905
-
906
- // Helper to import and instantiate a capsule from the capsules map
907
- const importCapsule = async (uri: string) => {
908
- const capsuleModule = capsulesMap[uri]
909
- if (!capsuleModule) {
910
- throw new Error(\`Capsule not found in static imports: \${uri}\`)
911
- }
912
- const capsule = await capsuleModule.capsule({
913
- encapsulate: sourceSpine.encapsulate,
914
- loadCapsule
915
- })
916
- return capsule
917
- }
918
-
919
- const loadCapsule = async ({ capsuleSourceLineRef, capsuleName }: any) => {
920
- // Return the capsule function from this projected file
921
- if (capsuleSourceLineRef === '${capsuleSourceLineRef}') {
922
- return capsule
923
- }
924
-
925
- // Use capsuleName directly if provided
926
- if (capsuleName) {
927
- return await importCapsule(capsuleName)
928
- }
929
-
930
- throw new Error(\`Cannot load capsule: \${capsuleSourceLineRef}\`)
931
- }
932
-
933
- const spineContractOpts = {
934
- spineFilesystemRoot: '.',
935
- resolve: async (uri: string) => uri,
936
- importCapsule
937
- }
938
-
939
- const runtimeSpineContracts = {
940
- ['#' + CapsuleSpineContract['#']]: CapsuleSpineContract(spineContractOpts)
941
- }
942
-
943
- const snapshot = {
944
- capsules: {
945
- ['${capsuleSourceLineRef}']: {
946
- spineContracts: {}
947
- }
948
- }
949
- }
950
-
951
- // Export wrapper function that initializes runtime and returns component
952
- export default function({ onMembraneEvent }: { onMembraneEvent?: (event: any) => void } = {}) {
953
- const [component, setComponent] = createSignal(null)
954
-
955
- onMount(async () => {
956
- // Add onMembraneEvent to spine contract opts - use provided or default logger
957
- const defaultMembraneLogger = (event: any) => {
958
- console.log('[Membrane Event]', event)
959
- }
960
- const opts = {
961
- ...spineContractOpts,
962
- onMembraneEvent: onMembraneEvent || defaultMembraneLogger
963
- }
964
- const contracts = {
965
- ['#' + CapsuleSpineContract['#']]: CapsuleSpineContract(opts)
966
- }
967
-
968
- const { encapsulate, capsules } = await Spine({
969
- spineFilesystemRoot: '.',
970
- spineContracts: contracts
971
- })
972
-
973
- sourceSpine.encapsulate = encapsulate
974
-
975
- const capsuleInstance = await capsule({ encapsulate, loadCapsule })
976
-
977
- const { run } = await SpineRuntime({
978
- spineFilesystemRoot: '.',
979
- spineContracts: contracts,
980
- snapshot,
981
- loadCapsule
982
- })
983
-
984
- const Component = await run({}, async ({ apis }) => {
985
- const capsuleApi = apis['${capsuleSourceLineRef}']
986
- const solidjsKey = Object.keys(capsuleApi).find(k => k === 'solidjs.com/standalone')
987
- if (!solidjsKey) throw new Error('solidjs.com/standalone property not found')
988
- return capsuleApi[solidjsKey]()
989
- })
990
-
991
- setComponent(() => Component)
992
- })
993
-
994
- // Return the wrapper function itself, not call it
995
- const WrapperComponent = () => {
996
- const Component = component()
997
- return Show({ when: Component, children: (Component) => Component() })
998
- }
999
-
1000
- return WrapperComponent
1001
- }
1002
- `
1003
- }
1004
- } else if (hasStandalone) {
749
+ if (hasStandalone) {
1005
750
  // Generate a wrapper function that directly invokes the standalone function
1006
751
  const capsuleSourceLineRef = capsule.cst.capsuleSourceLineRef
1007
752
 
@@ -1095,6 +840,7 @@ ${capsulesMapEntries}
1095
840
  }
1096
841
  const capsule = await capsuleModule.capsule({
1097
842
  encapsulate: sourceSpine.encapsulate,
843
+ CapsulePropertyTypes,
1098
844
  loadCapsule
1099
845
  })
1100
846
  return capsule
@@ -1255,8 +1001,12 @@ ${defaultExport}
1255
1001
  mappedCapsule.cst.source.moduleFilepath
1256
1002
  )
1257
1003
 
1258
- // Get ambient references for the mapped capsule
1259
- const mappedAmbientRefs = mappedCapsule.cst.source?.ambientReferences || {}
1004
+ // Get ambient references for the mapped capsule, excluding makeImportStack
1005
+ let mappedAmbientRefs = mappedCapsule.cst.source?.ambientReferences || {}
1006
+ if (mappedAmbientRefs['makeImportStack']) {
1007
+ mappedAmbientRefs = { ...mappedAmbientRefs }
1008
+ delete mappedAmbientRefs['makeImportStack']
1009
+ }
1260
1010
 
1261
1011
  // Generate import statements with projection CSS paths
1262
1012
  const mappedImportStatements = Object.entries(mappedAmbientRefs)
@@ -1315,191 +1065,17 @@ ${defaultExport}
1315
1065
 
1316
1066
  const mappedAllStatements = [mappedImportStatements, mappedLiteralReferences, mappedModuleLocalFunctions].filter(Boolean).join('\n')
1317
1067
 
1318
- // Check if mapped capsule has solidjs.com or encapsulate.dev/standalone property
1319
- const mappedHasSolidJs = hasSolidJsProperty(mappedCapsule, spineContractUri)
1068
+ // Check if mapped capsule has encapsulate.dev/standalone property
1320
1069
  const mappedHasStandalone = hasStandaloneProperty(mappedCapsule, spineContractUri)
1321
- const mappedNeedsRuntime = mappedHasSolidJs || mappedHasStandalone
1322
-
1323
- // Determine which solid-js imports are needed for mapped capsule
1324
- const mappedExistingSolidJsImports = new Set<string>()
1325
- for (const [name, ref] of Object.entries(mappedAmbientRefs)) {
1326
- const refTyped = ref as any
1327
- if (refTyped.type === 'import' && refTyped.moduleUri === 'solid-js') {
1328
- const match = refTyped.importSpecifier?.match(/\{([^}]+)\}/)
1329
- if (match) {
1330
- match[1].split(',').forEach((imp: string) => mappedExistingSolidJsImports.add(imp.trim()))
1331
- }
1332
- }
1333
- }
1334
-
1335
- const mappedNeededSolidJsImports = ['createSignal', 'onMount', 'Show'].filter(imp => !mappedExistingSolidJsImports.has(imp))
1336
- const mappedSolidJsImport = mappedHasSolidJs && mappedNeededSolidJsImports.length > 0 ? `import { ${mappedNeededSolidJsImports.join(', ')} } from 'solid-js'\n` : ''
1337
1070
 
1338
1071
  // Rewrite the mapped capsule expression to include CST (reuse the same function)
1339
1072
  const mappedCapsuleExpression = rewriteCapsuleExpressionWithCST(mappedCapsule)
1340
1073
 
1341
- // Add runtime imports for SolidJS and standalone functions
1342
- const mappedRuntimeImport = mappedNeedsRuntime ? `"use client"\nimport { Spine, SpineRuntime } from '@stream44.studio/encapsulate/encapsulate'\nimport { CapsuleSpineContract } from '@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0/Membrane.v0'\n${mappedSolidJsImport}` : ''
1074
+ // Add runtime imports for standalone functions
1075
+ const mappedRuntimeImport = mappedHasStandalone ? `"use client"\nimport { Spine, SpineRuntime } from '@stream44.studio/encapsulate/encapsulate'\nimport { CapsuleSpineContract } from '@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0/Membrane'\n` : ''
1343
1076
 
1344
1077
  let mappedDefaultExport = ''
1345
- if (mappedHasSolidJs) {
1346
- // Generate a wrapper that sets up runtime and exports the SolidJS component
1347
- const mappedCapsuleSourceLineRef = mappedCapsule.cst.capsuleSourceLineRef
1348
- const solidjsComponent = extractSolidJsComponent(mappedCapsule, spineContractUri)
1349
- if (solidjsComponent) {
1350
- // Collect all capsule URIs from CST (mappings and property contracts)
1351
- const allMappedCapsuleUris = collectAllCapsuleUris(mappedCapsule, spineContractUri)
1352
-
1353
- // Also collect from ambient references
1354
- for (const [name, ref] of Object.entries(mappedAmbientRefs)) {
1355
- const refTyped = ref as any
1356
- if (refTyped.type === 'capsule') {
1357
- const snapshot = await buildCapsuleSnapshotForReference(refTyped, capsules, spineContractUri)
1358
- const contractData = snapshot.spineContracts?.[spineContractUri]
1359
- const structKey = Object.keys(contractData || {}).find(k => k.includes('/structs/Capsule'))
1360
- const capsuleName = structKey ? contractData[structKey]?.capsuleName : undefined
1361
- if (capsuleName) {
1362
- allMappedCapsuleUris.add(capsuleName)
1363
- }
1364
- }
1365
- }
1366
-
1367
- // Build capsule dependencies array
1368
- const mappedCapsuleDeps: Array<{ uri: string, importName: string, importPath: string }> = []
1369
- for (const uri of allMappedCapsuleUris) {
1370
- const importName = `_capsule_${uri.replace(/[^a-zA-Z0-9]/g, '_')}`
1371
- // Strip leading @ to match caps filesystem paths
1372
- const importPath = uri.startsWith('@') ? uri.substring(1) : uri
1373
- mappedCapsuleDeps.push({ uri, importName, importPath })
1374
- }
1375
-
1376
- // Generate static imports for all capsule dependencies
1377
- // Compute relative path from projected file to caps directory
1378
- let importPrefix: string
1379
- if (capsuleModuleProjectionPackage) {
1380
- importPrefix = capsuleModuleProjectionPackage
1381
- } else {
1382
- const projectedFileDir = dirname(mapped.projectionPath)
1383
- const capsDir = '.~o/encapsulate.dev/caps'
1384
- const relativePathToCaps = relative(projectedFileDir, capsDir)
1385
- importPrefix = relativePathToCaps.startsWith('.') ? relativePathToCaps : './' + relativePathToCaps
1386
- }
1387
- const mappedCapsuleImports = mappedCapsuleDeps.map(dep =>
1388
- `import * as ${dep.importName} from '${importPrefix}/${dep.importPath}'`
1389
- ).join('\n')
1390
-
1391
- // Generate capsules map
1392
- const mappedCapsulesMapEntries = mappedCapsuleDeps.map(dep =>
1393
- ` '${dep.uri}': ${dep.importName}`
1394
- ).join(',\n')
1395
-
1396
- mappedDefaultExport = `
1397
- ${mappedCapsuleImports}
1398
-
1399
- // Set up runtime for browser execution
1400
- const sourceSpine: { encapsulate?: any } = {}
1401
-
1402
- // Map of statically imported capsules
1403
- const capsulesMap: Record<string, any> = {
1404
- ${mappedCapsulesMapEntries}
1405
- }
1406
-
1407
- // Helper to import and instantiate a capsule from the capsules map
1408
- const importCapsule = async (uri: string) => {
1409
- const capsuleModule = capsulesMap[uri]
1410
- if (!capsuleModule) {
1411
- throw new Error(\`Capsule not found in static imports: \${uri}\`)
1412
- }
1413
- const capsule = await capsuleModule.capsule({
1414
- encapsulate: sourceSpine.encapsulate,
1415
- loadCapsule
1416
- })
1417
- return capsule
1418
- }
1419
-
1420
- const loadCapsule = async ({ capsuleSourceLineRef, capsuleName }: any) => {
1421
- // Return the capsule function from this projected file
1422
- if (capsuleSourceLineRef === '${mappedCapsuleSourceLineRef}') {
1423
- return capsule
1424
- }
1425
-
1426
- // Use capsuleName directly if provided
1427
- if (capsuleName) {
1428
- return await importCapsule(capsuleName)
1429
- }
1430
-
1431
- throw new Error(\`Cannot load capsule: \${capsuleSourceLineRef}\`)
1432
- }
1433
-
1434
- const spineContractOpts = {
1435
- spineFilesystemRoot: '.',
1436
- resolve: async (uri: string) => uri,
1437
- importCapsule
1438
- }
1439
-
1440
- const runtimeSpineContracts = {
1441
- ['#' + CapsuleSpineContract['#']]: CapsuleSpineContract(spineContractOpts)
1442
- }
1443
-
1444
- const snapshot = {
1445
- capsules: {
1446
- ['${mappedCapsuleSourceLineRef}']: {
1447
- spineContracts: {}
1448
- }
1449
- }
1450
- }
1451
-
1452
- // Export wrapper function that initializes runtime and returns component
1453
- export default function({ onMembraneEvent }: { onMembraneEvent?: (event: any) => void } = {}) {
1454
- const [component, setComponent] = createSignal(null)
1455
-
1456
- onMount(async () => {
1457
- // Add onMembraneEvent to spine contract opts - use provided or default logger
1458
- const defaultMembraneLogger = (event: any) => {
1459
- console.log('[Membrane Event]', event.type, event)
1460
- }
1461
- const opts = {
1462
- ...spineContractOpts,
1463
- onMembraneEvent: onMembraneEvent || defaultMembraneLogger
1464
- }
1465
- const contracts = {
1466
- ['#' + CapsuleSpineContract['#']]: CapsuleSpineContract(opts)
1467
- }
1468
-
1469
- const { encapsulate, capsules } = await Spine({
1470
- spineFilesystemRoot: '.',
1471
- spineContracts: contracts
1472
- })
1473
-
1474
- sourceSpine.encapsulate = encapsulate
1475
-
1476
- const capsuleInstance = await capsule({ encapsulate, loadCapsule })
1477
-
1478
- const { run } = await SpineRuntime({
1479
- spineFilesystemRoot: '.',
1480
- spineContracts: contracts,
1481
- snapshot,
1482
- loadCapsule
1483
- })
1484
-
1485
- const Component = await run({}, async ({ apis }) => {
1486
- const capsuleApi = apis['${mappedCapsuleSourceLineRef}']
1487
- const solidjsKey = Object.keys(capsuleApi).find(k => k === 'solidjs.com/standalone')
1488
- if (!solidjsKey) throw new Error('solidjs.com/standalone property not found')
1489
- return capsuleApi[solidjsKey]()
1490
- })
1491
-
1492
- setComponent(() => Component)
1493
- })
1494
-
1495
- return () => {
1496
- const Component = component()
1497
- return Show({ when: Component, children: (Component) => Component() })
1498
- }
1499
- }
1500
- `
1501
- }
1502
- } else if (mappedHasStandalone) {
1078
+ if (mappedHasStandalone) {
1503
1079
  // Generate a wrapper function that directly invokes the standalone function
1504
1080
  const mappedCapsuleSourceLineRef = mappedCapsule.cst.capsuleSourceLineRef
1505
1081
 
@@ -1570,6 +1146,7 @@ ${mappedCapsulesMapEntries}
1570
1146
  }
1571
1147
  const capsule = await capsuleModule.capsule({
1572
1148
  encapsulate: sourceSpine.encapsulate,
1149
+ CapsulePropertyTypes,
1573
1150
  loadCapsule
1574
1151
  })
1575
1152
  return capsule
@@ -1726,10 +1303,38 @@ ${mappedDefaultExport}
1726
1303
  .filter(Boolean)
1727
1304
  .join('\n ')
1728
1305
 
1729
- // Add necessary imports
1730
- const imports = `import { CapsulePropertyTypes } from '@stream44.studio/encapsulate/encapsulate'
1731
- import { makeImportStack } from '@stream44.studio/encapsulate/encapsulate'
1732
- `
1306
+ // Filter out makeImportStack from ambient references (same as main capsule path)
1307
+ let filteredCapsuleAmbientRefs = capsuleAmbientRefs
1308
+ if (filteredCapsuleAmbientRefs['makeImportStack']) {
1309
+ filteredCapsuleAmbientRefs = { ...filteredCapsuleAmbientRefs }
1310
+ delete filteredCapsuleAmbientRefs['makeImportStack']
1311
+ }
1312
+
1313
+ // Generate imports from filtered ambient references
1314
+ const capsuleImportStatements = Object.entries(filteredCapsuleAmbientRefs)
1315
+ .map(([name, ref]: [string, any]) => {
1316
+ if (ref.type === 'import') {
1317
+ if (ref.moduleUri.endsWith('.css')) {
1318
+ return `import '${ref.moduleUri}'`
1319
+ }
1320
+ return `import ${ref.importSpecifier} from '${ref.moduleUri}'`
1321
+ }
1322
+ if (ref.type === 'assigned') {
1323
+ if (ref.moduleUri.includes('/spine-factories/')) {
1324
+ return `import { ${name} } from '@stream44.studio/encapsulate/encapsulate'`
1325
+ }
1326
+ }
1327
+ if (ref.type === 'invocation-argument') {
1328
+ if (ref.isEncapsulateExport) {
1329
+ return `import { ${name} } from '@stream44.studio/encapsulate/encapsulate'`
1330
+ }
1331
+ }
1332
+ return ''
1333
+ })
1334
+ .filter(Boolean)
1335
+ .join('\n')
1336
+
1337
+ const imports = capsuleImportStatements ? capsuleImportStatements + '\n' : ''
1733
1338
 
1734
1339
  // Get the capsule name for the assignment
1735
1340
  const capsuleName = registryCapsule.cst.source.capsuleName || ''