t44 0.2.0-rc.1

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.

Potentially problematic release.


This version of t44 might be problematic. Click here for more details.

Files changed (86) hide show
  1. package/LICENSE.md +203 -0
  2. package/README.md +154 -0
  3. package/bin/activate +36 -0
  4. package/bin/activate.ts +30 -0
  5. package/bin/postinstall.sh +19 -0
  6. package/bin/shell +27 -0
  7. package/bin/t44 +27 -0
  8. package/caps/HomeRegistry.v0.ts +298 -0
  9. package/caps/OpenApiSchema.v0.ts +192 -0
  10. package/caps/ProjectDeployment.v0.ts +363 -0
  11. package/caps/ProjectDevelopment.v0.ts +246 -0
  12. package/caps/ProjectPublishing.v0.ts +307 -0
  13. package/caps/ProjectRack.v0.ts +128 -0
  14. package/caps/WorkspaceCli.v0.ts +391 -0
  15. package/caps/WorkspaceConfig.v0.ts +626 -0
  16. package/caps/WorkspaceConfig.yaml +53 -0
  17. package/caps/WorkspaceConnection.v0.ts +240 -0
  18. package/caps/WorkspaceEntityConfig.v0.ts +64 -0
  19. package/caps/WorkspaceEntityFact.v0.ts +193 -0
  20. package/caps/WorkspaceInfo.v0.ts +554 -0
  21. package/caps/WorkspaceInit.v0.ts +30 -0
  22. package/caps/WorkspaceKey.v0.ts +186 -0
  23. package/caps/WorkspaceProjects.v0.ts +455 -0
  24. package/caps/WorkspacePrompt.v0.ts +396 -0
  25. package/caps/WorkspaceShell.sh +39 -0
  26. package/caps/WorkspaceShell.v0.ts +104 -0
  27. package/caps/WorkspaceShell.yaml +65 -0
  28. package/caps/WorkspaceShellCli.v0.ts +109 -0
  29. package/caps/WorkspaceTest.v0.ts +167 -0
  30. package/caps/providers/LICENSE.md +8 -0
  31. package/caps/providers/README.md +2 -0
  32. package/caps/providers/bunny.net/ProjectDeployment.v0.ts +328 -0
  33. package/caps/providers/bunny.net/api-pull.v0.test.ts +319 -0
  34. package/caps/providers/bunny.net/api-pull.v0.ts +161 -0
  35. package/caps/providers/bunny.net/api-storage.v0.test.ts +168 -0
  36. package/caps/providers/bunny.net/api-storage.v0.ts +245 -0
  37. package/caps/providers/bunny.net/api.v0.ts +95 -0
  38. package/caps/providers/dynadot.com/ProjectDeployment.v0.ts +207 -0
  39. package/caps/providers/dynadot.com/api-domains.v0.test.ts +147 -0
  40. package/caps/providers/dynadot.com/api-domains.v0.ts +137 -0
  41. package/caps/providers/dynadot.com/api.v0.ts +88 -0
  42. package/caps/providers/git-scm.com/ProjectPublishing.v0.ts +231 -0
  43. package/caps/providers/github.com/ProjectPublishing.v0.ts +75 -0
  44. package/caps/providers/github.com/api.v0.ts +90 -0
  45. package/caps/providers/npmjs.com/ProjectPublishing.v0.ts +741 -0
  46. package/caps/providers/vercel.com/ProjectDeployment.v0.ts +339 -0
  47. package/caps/providers/vercel.com/api.v0.test.ts +67 -0
  48. package/caps/providers/vercel.com/api.v0.ts +132 -0
  49. package/caps/providers/vercel.com/bun.lock +194 -0
  50. package/caps/providers/vercel.com/package.json +10 -0
  51. package/caps/providers/vercel.com/project.v0.test.ts +108 -0
  52. package/caps/providers/vercel.com/project.v0.ts +150 -0
  53. package/caps/providers/vercel.com/tsconfig.json +28 -0
  54. package/docs/Overview.drawio +189 -0
  55. package/docs/Overview.svg +4 -0
  56. package/lib/crypto.ts +53 -0
  57. package/lib/openapi.ts +132 -0
  58. package/lib/ucan.ts +137 -0
  59. package/package.json +41 -0
  60. package/structs/HomeRegistryConfig.v0.ts +27 -0
  61. package/structs/ProjectDeploymentConfig.v0.ts +27 -0
  62. package/structs/ProjectDeploymentFact.v0.ts +110 -0
  63. package/structs/ProjectPublishingFact.v0.ts +69 -0
  64. package/structs/ProjectRackConfig.v0.ts +27 -0
  65. package/structs/WorkspaceCliConfig.v0.ts +27 -0
  66. package/structs/WorkspaceConfig.v0.ts +27 -0
  67. package/structs/WorkspaceKeyConfig.v0.ts +27 -0
  68. package/structs/WorkspaceMappings.v0.ts +27 -0
  69. package/structs/WorkspaceProjectsConfig.v0.ts +27 -0
  70. package/structs/WorkspaceRepositories.v0.ts +27 -0
  71. package/structs/WorkspaceShellConfig.v0.ts +45 -0
  72. package/structs/providers/LICENSE.md +8 -0
  73. package/structs/providers/README.md +2 -0
  74. package/structs/providers/bunny.net/ProjectDeploymentFact.v0.ts +41 -0
  75. package/structs/providers/bunny.net/WorkspaceConnectionConfig.v0.ts +42 -0
  76. package/structs/providers/dynadot.com/DomainFact.v0.ts +146 -0
  77. package/structs/providers/dynadot.com/WorkspaceConnectionConfig.v0.ts +41 -0
  78. package/structs/providers/git-scm.com/ProjectPublishingFact.v0.ts +46 -0
  79. package/structs/providers/github.com/ProjectPublishingFact.v0.ts +52 -0
  80. package/structs/providers/github.com/WorkspaceConnectionConfig.v0.ts +42 -0
  81. package/structs/providers/npmjs.com/ProjectPublishingFact.v0.ts +48 -0
  82. package/structs/providers/vercel.com/ProjectDeploymentFact.v0.ts +38 -0
  83. package/structs/providers/vercel.com/WorkspaceConnectionConfig.v0.ts +48 -0
  84. package/tsconfig.json +28 -0
  85. package/workspace-rt.ts +134 -0
  86. package/workspace.yaml +5 -0
@@ -0,0 +1,307 @@
1
+
2
+ import { join } from 'path'
3
+ import { $ } from 'bun'
4
+ import { mkdir, access, readFile, writeFile } from 'fs/promises'
5
+ import { constants } from 'fs'
6
+ import chalk from 'chalk'
7
+
8
+ export async function capsule({
9
+ encapsulate,
10
+ CapsulePropertyTypes,
11
+ makeImportStack
12
+ }: {
13
+ encapsulate: any
14
+ CapsulePropertyTypes: any
15
+ makeImportStack: any
16
+ }) {
17
+ return encapsulate({
18
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
19
+ '#@stream44.studio/encapsulate/structs/Capsule.v0': {},
20
+ '#t44/structs/WorkspaceConfig.v0': {
21
+ as: '$WorkspaceConfig'
22
+ },
23
+ '#t44/structs/WorkspaceRepositories.v0': {
24
+ as: '$WorkspaceRepositories'
25
+ },
26
+ '#': {
27
+ WorkspaceConfig: {
28
+ type: CapsulePropertyTypes.Mapping,
29
+ value: 't44/caps/WorkspaceConfig.v0'
30
+ },
31
+ WorkspaceProjects: {
32
+ type: CapsulePropertyTypes.Mapping,
33
+ value: 't44/caps/WorkspaceProjects.v0'
34
+ },
35
+ GitRepository: {
36
+ type: CapsulePropertyTypes.Mapping,
37
+ value: 't44/caps/providers/git-scm.com/ProjectPublishing.v0'
38
+ },
39
+ NpmRegistry: {
40
+ type: CapsulePropertyTypes.Mapping,
41
+ value: 't44/caps/providers/npmjs.com/ProjectPublishing.v0'
42
+ },
43
+ GitHubRepository: {
44
+ type: CapsulePropertyTypes.Mapping,
45
+ value: 't44/caps/providers/github.com/ProjectPublishing.v0'
46
+ },
47
+ run: {
48
+ type: CapsulePropertyTypes.Function,
49
+ value: async function (this: any, { args }: any): Promise<void> {
50
+
51
+ const { projectSelector, rc, release, dangerouslyResetMain } = args
52
+
53
+ const repositoriesConfig = await this.$WorkspaceRepositories.config
54
+
55
+ if (!repositoriesConfig?.repositories) {
56
+ throw new Error('No repositories configuration found')
57
+ }
58
+
59
+ if (dangerouslyResetMain && !projectSelector) {
60
+ throw new Error('--dangerously-reset-main flag requires a projectSelector to be specified')
61
+ }
62
+
63
+ let matchingRepositories: Record<string, any>
64
+
65
+ if (!projectSelector) {
66
+ matchingRepositories = repositoriesConfig.repositories
67
+ } else {
68
+ matchingRepositories = await this.WorkspaceProjects.resolveMatchingRepositories({
69
+ workspaceProject: projectSelector,
70
+ repositories: repositoriesConfig.repositories
71
+ })
72
+ }
73
+
74
+ // Phase 0: Copy source directories to central location
75
+ console.log('[t44] Copying source directories to central location ...\n')
76
+ const centralSourceDirs: Map<string, string> = new Map()
77
+
78
+ for (const [repoName, repoConfig] of Object.entries(matchingRepositories)) {
79
+ const projectSourceDir = join((repoConfig as any).sourceDir)
80
+ const repoSourceDir = join(
81
+ this.WorkspaceConfig.workspaceRootDir,
82
+ '.~o/workspace.foundation/ProjectPublishing/repos',
83
+ repoName
84
+ )
85
+
86
+ console.log(`=> Syncing '${repoName}' to central location ...`)
87
+
88
+ await mkdir(repoSourceDir, { recursive: true })
89
+
90
+ const gitignorePath = join(projectSourceDir, '.gitignore')
91
+ let gitignoreExists = false
92
+ try {
93
+ await access(gitignorePath, constants.F_OK)
94
+ gitignoreExists = true
95
+ } catch { }
96
+
97
+ const rsyncArgs = ['rsync', '-a', '--delete', '--exclude', '.git']
98
+ if (gitignoreExists) rsyncArgs.push('--exclude-from=' + gitignorePath)
99
+ rsyncArgs.push(projectSourceDir + '/', repoSourceDir + '/')
100
+ await $`${rsyncArgs}`
101
+
102
+ centralSourceDirs.set(repoName, repoSourceDir)
103
+ console.log(` Synced to: ${repoSourceDir}\n`)
104
+ }
105
+
106
+ // Helper to iterate providers with custom callback
107
+ const forEachProvider = async (callback: (params: {
108
+ repoName: string,
109
+ repoConfig: any,
110
+ providerConfig: any,
111
+ capsuleName: string,
112
+ repoSourceDir: string
113
+ }) => Promise<void>) => {
114
+ for (const [repoName, repoConfig] of Object.entries(matchingRepositories)) {
115
+ const providers = Array.isArray((repoConfig as any).providers)
116
+ ? (repoConfig as any).providers
117
+ : (repoConfig as any).provider
118
+ ? [(repoConfig as any).provider]
119
+ : []
120
+
121
+ const repoSourceDir = centralSourceDirs.get(repoName)!
122
+ for (const providerConfig of providers) {
123
+ const capsuleName = providerConfig.capsule
124
+ await callback({ repoName, repoConfig, providerConfig, capsuleName, repoSourceDir })
125
+ }
126
+ }
127
+ }
128
+
129
+ // Phase 1: Prepare - analyze all packages and collect metadata
130
+ console.log('[t44] Analyzing packages ...\n')
131
+ const packageMetadata: Map<string, any> = new Map()
132
+
133
+ await forEachProvider(async ({ repoName, repoConfig, providerConfig, capsuleName, repoSourceDir }) => {
134
+ if (capsuleName === 't44/caps/providers/npmjs.com/ProjectPublishing.v0') {
135
+ const metadata = await this.NpmRegistry.prepare({
136
+ config: { ...repoConfig, provider: providerConfig, sourceDir: repoSourceDir },
137
+ projectionDir: join(
138
+ this.WorkspaceConfig.workspaceRootDir,
139
+ '.~o/workspace.foundation/o/npmjs.com'
140
+ ),
141
+ repoSourceDir
142
+ })
143
+ packageMetadata.set(repoName, metadata)
144
+ }
145
+ })
146
+
147
+ // Phase 2: Bump - only bump packages that have changes
148
+ if (rc || release) {
149
+ if (rc) console.log('[t44] Release candidate mode enabled\n')
150
+ if (release) console.log('[t44] Release mode enabled\n')
151
+
152
+ console.log('[t44] Bumping versions for changed packages ...\n')
153
+
154
+ const bumpedRepos = new Set<string>()
155
+ await forEachProvider(async ({ repoName, repoConfig, providerConfig, capsuleName, repoSourceDir }) => {
156
+ if (capsuleName === 't44/caps/providers/npmjs.com/ProjectPublishing.v0') {
157
+ const metadata = packageMetadata.get(repoName)
158
+
159
+ if (metadata && metadata.hasChanges) {
160
+ if (!bumpedRepos.has(repoName)) {
161
+ console.log(`\n=> Bumping version for '${repoName}' ...\n`)
162
+ bumpedRepos.add(repoName)
163
+ }
164
+
165
+ await this.NpmRegistry.bump({
166
+ config: { ...repoConfig, provider: providerConfig },
167
+ options: { rc, release },
168
+ repoSourceDir,
169
+ metadata
170
+ })
171
+ } else if (metadata && !bumpedRepos.has(repoName)) {
172
+ console.log(`\n=> Skipping '${repoName}' (no changes)\n`)
173
+ bumpedRepos.add(repoName)
174
+ }
175
+ }
176
+ })
177
+
178
+ console.log('[t44] Version bump complete!\n')
179
+
180
+ // Tag git-scm repos with the bumped version
181
+ const taggedRepos = new Set<string>()
182
+ await forEachProvider(async ({ repoName, repoConfig, providerConfig, capsuleName, repoSourceDir }) => {
183
+ if (capsuleName === 't44/caps/providers/git-scm.com/ProjectPublishing.v0' && !taggedRepos.has(repoName)) {
184
+ // Read the bumped version from the source package.json
185
+ const packageJsonPath = join(repoSourceDir, 'package.json')
186
+ try {
187
+ const packageJsonContent = await readFile(packageJsonPath, 'utf-8')
188
+ const packageJson = JSON.parse(packageJsonContent)
189
+ const version = packageJson.version
190
+ const tag = `v${version}`
191
+
192
+ const originUri = providerConfig.config.RepositorySettings.origin
193
+ const projectionDir = join(
194
+ this.WorkspaceConfig.workspaceRootDir,
195
+ '.~o/workspace.foundation/o/git-scm.com'
196
+ )
197
+ const projectProjectionDir = join(projectionDir, 'repos', originUri.replace(/[@:\/]/g, '~'))
198
+
199
+ // Check if the projection repo exists
200
+ try {
201
+ await access(projectProjectionDir, constants.F_OK)
202
+ } catch {
203
+ // Repo not cloned yet, tagging will happen after push
204
+ return
205
+ }
206
+
207
+ // Check if tag already exists locally or on remote
208
+ const localTagCheck = await $`git tag -l ${tag}`.cwd(projectProjectionDir).quiet().nothrow()
209
+ if (localTagCheck.text().trim() === tag) {
210
+ throw new Error(
211
+ `Git tag '${tag}' already exists in repository '${repoName}'.\n` +
212
+ ` Please bump to a different version before pushing.`
213
+ )
214
+ }
215
+
216
+ const remoteTagCheck = await $`git ls-remote --tags origin ${tag}`.cwd(projectProjectionDir).quiet().nothrow()
217
+ if (remoteTagCheck.text().trim().length > 0) {
218
+ throw new Error(
219
+ `Git tag '${tag}' already exists on remote for repository '${repoName}'.\n` +
220
+ ` Please bump to a different version before pushing.`
221
+ )
222
+ }
223
+
224
+ await $`git tag ${tag}`.cwd(projectProjectionDir)
225
+ console.log(chalk.green(` ✓ Tagged repository '${repoName}' with ${tag}\n`))
226
+ taggedRepos.add(repoName)
227
+ } catch (error: any) {
228
+ if (error.message?.includes('already exists')) {
229
+ throw error
230
+ }
231
+ // Skip if package.json can't be read
232
+ }
233
+ }
234
+ })
235
+
236
+ await forEachProvider(async ({ repoName, repoConfig, providerConfig, capsuleName, repoSourceDir }) => {
237
+ if (capsuleName === 't44/caps/providers/npmjs.com/ProjectPublishing.v0') {
238
+ const metadata = await this.NpmRegistry.prepare({
239
+ config: { ...repoConfig, provider: providerConfig, sourceDir: repoSourceDir },
240
+ projectionDir: join(
241
+ this.WorkspaceConfig.workspaceRootDir,
242
+ '.~o/workspace.foundation/o/npmjs.com'
243
+ ),
244
+ repoSourceDir
245
+ })
246
+ packageMetadata.set(repoName, metadata)
247
+ }
248
+ })
249
+ }
250
+
251
+ // Phase 3: Push - publish packages
252
+ console.log('[t44] Publishing packages ...\n')
253
+
254
+ const processedRepos = new Set<string>()
255
+ await forEachProvider(async ({ repoName, repoConfig, providerConfig, capsuleName, repoSourceDir }) => {
256
+ if (!processedRepos.has(repoName)) {
257
+ console.log(`\n=> Processing repository '${repoName}' ...\n`)
258
+ processedRepos.add(repoName)
259
+ }
260
+
261
+ console.log(` -> Running provider '${capsuleName}' ...\n`)
262
+
263
+ const metadata = packageMetadata.get(repoName)
264
+
265
+ if (capsuleName === 't44/caps/providers/github.com/ProjectPublishing.v0') {
266
+ await this.GitHubRepository.push({
267
+ config: { ...repoConfig, provider: providerConfig, sourceDir: repoSourceDir }
268
+ })
269
+ } else if (capsuleName === 't44/caps/providers/git-scm.com/ProjectPublishing.v0') {
270
+ await this.GitRepository.push({
271
+ config: { ...repoConfig, provider: providerConfig, sourceDir: repoSourceDir },
272
+ projectionDir: join(
273
+ this.WorkspaceConfig.workspaceRootDir,
274
+ '.~o/workspace.foundation/o/git-scm.com'
275
+ ),
276
+ dangerouslyResetMain
277
+ })
278
+ } else if (capsuleName === 't44/caps/providers/npmjs.com/ProjectPublishing.v0') {
279
+ await this.NpmRegistry.push({
280
+ config: { ...repoConfig, provider: providerConfig, sourceDir: repoSourceDir },
281
+ projectionDir: join(
282
+ this.WorkspaceConfig.workspaceRootDir,
283
+ '.~o/workspace.foundation/o/npmjs.com'
284
+ ),
285
+ metadata
286
+ })
287
+ }
288
+
289
+ console.log(` <- Provider '${capsuleName}' complete.\n`)
290
+ })
291
+
292
+ for (const repoName of processedRepos) {
293
+ console.log(`<= Repository '${repoName}' processing complete.\n`)
294
+ }
295
+
296
+ console.log('[t44] Project repositories pushed OK!')
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }, {
302
+ importMeta: import.meta,
303
+ importStack: makeImportStack(),
304
+ capsuleName: capsule['#'],
305
+ })
306
+ }
307
+ capsule['#'] = 't44/caps/ProjectPublishing.v0'
@@ -0,0 +1,128 @@
1
+ export async function capsule({
2
+ encapsulate,
3
+ CapsulePropertyTypes,
4
+ makeImportStack
5
+ }: {
6
+ encapsulate: any
7
+ CapsulePropertyTypes: any
8
+ makeImportStack: any
9
+ }) {
10
+ return encapsulate({
11
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
12
+ '#@stream44.studio/encapsulate/structs/Capsule.v0': {},
13
+ '#t44/structs/WorkspaceConfig.v0': {
14
+ as: '$WorkspaceConfig'
15
+ },
16
+ '#t44/structs/ProjectRackConfig.v0': {
17
+ as: '$ProjectRackConfig'
18
+ },
19
+ '#': {
20
+ WorkspacePrompt: {
21
+ type: CapsulePropertyTypes.Mapping,
22
+ value: 't44/caps/WorkspacePrompt.v0'
23
+ },
24
+ HomeRegistry: {
25
+ type: CapsulePropertyTypes.Mapping,
26
+ value: 't44/caps/HomeRegistry.v0'
27
+ },
28
+ ensureRack: {
29
+ type: CapsulePropertyTypes.Function,
30
+ value: async function (this: any): Promise<{ rackName: string }> {
31
+ const workspaceConfig = await this.$WorkspaceConfig.config
32
+ const rackConfig = await this.$ProjectRackConfig.config
33
+
34
+ // Check if projectRack is already set in config (object format: { name, identifier })
35
+ if (rackConfig?.name && rackConfig?.identifier) {
36
+ return { rackName: rackConfig.name }
37
+ }
38
+
39
+ let rackName: string
40
+
41
+ const rackConfigStructKey = '#t44/structs/ProjectRackConfig.v0'
42
+ if (!rackConfig?.name) {
43
+ rackName = await this.WorkspacePrompt.setupPrompt({
44
+ title: '📦 Project Rack Setup',
45
+ description: [
46
+ `Workspace: ${workspaceConfig?.name || 'unknown'}`,
47
+ `Root: ${workspaceConfig?.rootDir || 'unknown'}`,
48
+ '',
49
+ 'The project rack holds an integrated set of projects which can be',
50
+ 'pulled into one or more workspaces.',
51
+ 'A workspace attached to a rack has access to all projects in the rack',
52
+ 'and is able to add more projects to the rack.',
53
+ 'All workspaces attached to a rack automatically sync their projects',
54
+ 'to the rack.',
55
+ '',
56
+ ],
57
+ message: 'Enter a name for the project rack:',
58
+ defaultValue: 'genesis',
59
+ validate: (input: string) => {
60
+ if (!input || input.trim().length === 0) {
61
+ return 'Project rack name cannot be empty'
62
+ }
63
+ if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
64
+ return 'Project rack name can only contain letters, numbers, underscores, and hyphens'
65
+ }
66
+ return true
67
+ },
68
+ configPath: [rackConfigStructKey],
69
+ onSuccess: async () => {
70
+ // Don't write to config yet — we write the full object after rack registration
71
+ }
72
+ })
73
+ } else {
74
+ rackName = rackConfig.name
75
+ }
76
+
77
+ // Check if rack already exists in registry
78
+ let rackData = await this.HomeRegistry.getRack(rackName)
79
+
80
+ if (!rackData) {
81
+ const chalk = (await import('chalk')).default
82
+ console.log(chalk.cyan(`\n Registering project rack '${rackName}'...\n`))
83
+
84
+ const { generateKeypair } = await import('../lib/ucan.js')
85
+ const { did, privateKey } = await generateKeypair()
86
+
87
+ rackData = {
88
+ did,
89
+ privateKey,
90
+ createdAt: new Date().toISOString()
91
+ }
92
+
93
+ const rackPath = await this.HomeRegistry.setRack(rackName, rackData)
94
+
95
+ console.log(chalk.green(` ✓ Project rack registered at:`))
96
+ console.log(chalk.green(` ${rackPath}`))
97
+ console.log(chalk.green(` ✓ DID: ${rackData.did}\n`))
98
+ } else {
99
+ const chalk = (await import('chalk')).default
100
+ const rackPath = await this.HomeRegistry.getRackPath(rackName)
101
+ console.log(chalk.green(`\n ✓ Using existing project rack at:`))
102
+ console.log(chalk.green(` ${rackPath}`))
103
+ console.log(chalk.green(` ✓ DID: ${rackData.did}\n`))
104
+ }
105
+
106
+ // Store rack as object { name, identifier } in rack config struct
107
+ await this.$ProjectRackConfig.setConfigValue(['name'], rackName)
108
+ await this.$ProjectRackConfig.setConfigValue(['identifier'], rackData.did)
109
+
110
+ return { rackName }
111
+ }
112
+ },
113
+ getRackName: {
114
+ type: CapsulePropertyTypes.Function,
115
+ value: async function (this: any): Promise<string | null> {
116
+ const rackConfig = await this.$ProjectRackConfig.config
117
+ return rackConfig?.name || null
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }, {
123
+ importMeta: import.meta,
124
+ importStack: makeImportStack(),
125
+ capsuleName: capsule['#'],
126
+ })
127
+ }
128
+ capsule['#'] = 't44/caps/ProjectRack.v0'