@stream44.studio/t44 0.4.0-rc.24
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/.dco-signatures +9 -0
- package/.github/workflows/dco.yaml +12 -0
- package/.github/workflows/gordian-open-integrity.yaml +13 -0
- package/.github/workflows/test.yaml +31 -0
- package/.o/GordianOpenIntegrity-CurrentLifehash.svg +1026 -0
- package/.o/GordianOpenIntegrity-InceptionLifehash.svg +1026 -0
- package/.o/GordianOpenIntegrity.yaml +21 -0
- package/.o/assets/Hero-Terminal44-v0.jpeg +0 -0
- package/.o/stream44.studio/assets/Icon-v1.svg +1170 -0
- package/.repo-identifier +1 -0
- package/DCO.md +34 -0
- package/LICENSE.txt +186 -0
- package/README.md +189 -0
- package/bin/activate +36 -0
- package/bin/activate.ts +30 -0
- package/bin/postinstall.sh +19 -0
- package/bin/shell +27 -0
- package/bin/t44 +27 -0
- package/caps/ConfigSchemaStruct.ts +55 -0
- package/caps/Home.ts +57 -0
- package/caps/HomeRegistry.ts +319 -0
- package/caps/HomeRegistryFile.ts +144 -0
- package/caps/JsonSchemas.ts +220 -0
- package/caps/OpenApiSchema.ts +67 -0
- package/caps/PackageDescriptor.ts +88 -0
- package/caps/ProjectCatalogs.ts +153 -0
- package/caps/ProjectDeployment.ts +426 -0
- package/caps/ProjectDevelopment.ts +257 -0
- package/caps/ProjectPublishing.ts +654 -0
- package/caps/ProjectPulling.ts +234 -0
- package/caps/ProjectRack.ts +155 -0
- package/caps/ProjectRepository.ts +332 -0
- package/caps/ProjectTest.ts +251 -0
- package/caps/ProjectTestLib.ts +257 -0
- package/caps/RootKey.ts +219 -0
- package/caps/SigningKey.ts +243 -0
- package/caps/TaskWorkflow.ts +192 -0
- package/caps/WorkspaceCli.ts +448 -0
- package/caps/WorkspaceConfig.ts +268 -0
- package/caps/WorkspaceConfig.yaml +87 -0
- package/caps/WorkspaceConfigFile.ts +902 -0
- package/caps/WorkspaceConnection.ts +329 -0
- package/caps/WorkspaceEntityConfig.ts +78 -0
- package/caps/WorkspaceEntityConfig.v0.ts +77 -0
- package/caps/WorkspaceEntityFact.ts +218 -0
- package/caps/WorkspaceInfo.ts +619 -0
- package/caps/WorkspaceInit.ts +30 -0
- package/caps/WorkspaceKey.ts +338 -0
- package/caps/WorkspaceModel.ts +373 -0
- package/caps/WorkspaceProjects.ts +636 -0
- package/caps/WorkspacePrompt.ts +430 -0
- package/caps/WorkspaceShell.sh +39 -0
- package/caps/WorkspaceShell.ts +104 -0
- package/caps/WorkspaceShell.yaml +64 -0
- package/caps/WorkspaceShellCli.ts +109 -0
- package/caps/patterns/README.md +2 -0
- package/caps/patterns/git-scm.com/ProjectPublishing.ts +507 -0
- package/caps/patterns/semver.org/ProjectPublishing.ts +458 -0
- package/docs/Overview.drawio +248 -0
- package/docs/Overview.svg +4 -0
- package/examples/01-Lifecycle/main.test.ts +223 -0
- package/lib/crypto.ts +53 -0
- package/lib/key.ts +381 -0
- package/lib/schema-console-renderer.ts +181 -0
- package/lib/schema-resolver.ts +349 -0
- package/lib/ucan.ts +137 -0
- package/package.json +91 -0
- package/standalone-rt.test.ts +150 -0
- package/standalone-rt.ts +140 -0
- package/structs/HomeRegistry.ts +55 -0
- package/structs/HomeRegistryConfig.ts +60 -0
- package/structs/ProjectCatalogsConfig.ts +53 -0
- package/structs/ProjectDeploymentConfig.ts +56 -0
- package/structs/ProjectDeploymentFact.ts +106 -0
- package/structs/ProjectPublishingConfig.ts +78 -0
- package/structs/ProjectPublishingFact.ts +68 -0
- package/structs/ProjectPullingConfig.ts +52 -0
- package/structs/ProjectRack.ts +51 -0
- package/structs/ProjectRackConfig.ts +56 -0
- package/structs/RepositoryOriginDescriptor.ts +51 -0
- package/structs/RootKeyConfig.ts +64 -0
- package/structs/SigningKeyConfig.ts +64 -0
- package/structs/Workspace.ts +56 -0
- package/structs/WorkspaceCatalogs.ts +56 -0
- package/structs/WorkspaceCliConfig.ts +53 -0
- package/structs/WorkspaceConfig.ts +64 -0
- package/structs/WorkspaceConfigFile.ts +50 -0
- package/structs/WorkspaceConfigFileMeta.ts +70 -0
- package/structs/WorkspaceKey.ts +55 -0
- package/structs/WorkspaceKeyConfig.ts +56 -0
- package/structs/WorkspaceMappingsConfig.ts +56 -0
- package/structs/WorkspaceProject.ts +104 -0
- package/structs/WorkspaceProjectsConfig.ts +67 -0
- package/structs/WorkspaceShellConfig.ts +83 -0
- package/structs/patterns/README.md +2 -0
- package/structs/patterns/git-scm.com/ProjectPublishingFact.ts +46 -0
- package/tsconfig.json +33 -0
- package/workspace-rt.ts +152 -0
- package/workspace.yaml +3 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
|
|
2
|
+
import { join, dirname } from 'path'
|
|
3
|
+
import { readFile, writeFile, access } from 'fs/promises'
|
|
4
|
+
import glob from 'fast-glob'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
|
|
7
|
+
function detectIndent(content: string): number {
|
|
8
|
+
const match = content.match(/^\{\s*\n([ \t]+)/)
|
|
9
|
+
if (match) {
|
|
10
|
+
return match[1].length
|
|
11
|
+
}
|
|
12
|
+
return 2
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function capsule({
|
|
16
|
+
encapsulate,
|
|
17
|
+
CapsulePropertyTypes,
|
|
18
|
+
makeImportStack
|
|
19
|
+
}: {
|
|
20
|
+
encapsulate: any
|
|
21
|
+
CapsulePropertyTypes: any
|
|
22
|
+
makeImportStack: any
|
|
23
|
+
}) {
|
|
24
|
+
return encapsulate({
|
|
25
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
26
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
27
|
+
'#@stream44.studio/t44/structs/ProjectPublishingConfig': {
|
|
28
|
+
as: '$WorkspaceRepositories'
|
|
29
|
+
},
|
|
30
|
+
'#@stream44.studio/t44/structs/WorkspaceMappingsConfig': {
|
|
31
|
+
as: '$WorkspaceMappings'
|
|
32
|
+
},
|
|
33
|
+
'#': {
|
|
34
|
+
reverseRenamePatch: {
|
|
35
|
+
type: CapsulePropertyTypes.Function,
|
|
36
|
+
value: async function (this: any, { patchContent }: { patchContent: string }): Promise<{ content: string, modified: boolean }> {
|
|
37
|
+
const mappingsConfig = await this.$WorkspaceMappings.config
|
|
38
|
+
const publishingMappings = mappingsConfig?.mappings?.['@stream44.studio/t44/caps/patterns/ProjectPublishing']
|
|
39
|
+
if (!publishingMappings?.npm) return { content: patchContent, modified: false }
|
|
40
|
+
|
|
41
|
+
const npmRenames: Record<string, string> = publishingMappings.npm
|
|
42
|
+
|
|
43
|
+
// Build reverse map: publicName → workspaceName
|
|
44
|
+
// Only include mappings where the public name starts with '@' to avoid
|
|
45
|
+
// overly broad replacements (e.g. 't44' would match too many things)
|
|
46
|
+
const reverseRenames: Record<string, string> = {}
|
|
47
|
+
for (const [workspaceName, publicName] of Object.entries(npmRenames)) {
|
|
48
|
+
if (publicName.startsWith('@')) {
|
|
49
|
+
reverseRenames[publicName] = workspaceName
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Sort by length descending to replace longer names first (avoid partial matches)
|
|
54
|
+
const reverseEntries = Object.entries(reverseRenames)
|
|
55
|
+
.sort((a, b) => b[0].length - a[0].length)
|
|
56
|
+
|
|
57
|
+
if (reverseEntries.length === 0) return { content: patchContent, modified: false }
|
|
58
|
+
|
|
59
|
+
let content = patchContent
|
|
60
|
+
let modified = false
|
|
61
|
+
|
|
62
|
+
for (const [publicName, workspaceName] of reverseEntries) {
|
|
63
|
+
// Replace the literal public name with workspace name
|
|
64
|
+
const regex = new RegExp(publicName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
|
65
|
+
const replaced = content.replace(regex, workspaceName)
|
|
66
|
+
if (replaced !== content) {
|
|
67
|
+
content = replaced
|
|
68
|
+
modified = true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Also replace regex-escaped versions of the public name
|
|
72
|
+
// (e.g. @stream44\.studio\/dco in test patterns)
|
|
73
|
+
const pubEscaped = publicName.replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
|
|
74
|
+
if (pubEscaped !== publicName) {
|
|
75
|
+
const wsEscaped = workspaceName.replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
|
|
76
|
+
const escapedRegex = new RegExp(pubEscaped.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
|
77
|
+
const replacedEscaped = content.replace(escapedRegex, wsEscaped)
|
|
78
|
+
if (replacedEscaped !== content) {
|
|
79
|
+
content = replacedEscaped
|
|
80
|
+
modified = true
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { content, modified }
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
rename: {
|
|
89
|
+
type: CapsulePropertyTypes.Function,
|
|
90
|
+
value: async function (this: any, { dirs, repos }: { dirs: Iterable<string>, repos?: Record<string, any> }) {
|
|
91
|
+
const mappingsConfig = await this.$WorkspaceMappings.config
|
|
92
|
+
const publishingMappings = mappingsConfig?.mappings?.['@stream44.studio/t44/caps/patterns/ProjectPublishing']
|
|
93
|
+
if (publishingMappings?.npm) {
|
|
94
|
+
const npmRenames: Record<string, string> = publishingMappings.npm
|
|
95
|
+
const renameEntries = Object.entries(npmRenames)
|
|
96
|
+
.sort((a, b) => b[0].length - a[0].length)
|
|
97
|
+
|
|
98
|
+
if (renameEntries.length > 0) {
|
|
99
|
+
console.log('[t44] Applying package name renames ...\n')
|
|
100
|
+
|
|
101
|
+
for (const dir of dirs) {
|
|
102
|
+
const files = await glob('**/*', {
|
|
103
|
+
cwd: dir,
|
|
104
|
+
absolute: true,
|
|
105
|
+
onlyFiles: true,
|
|
106
|
+
dot: true
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
for (const file of files) {
|
|
110
|
+
try {
|
|
111
|
+
const buffer = await readFile(file)
|
|
112
|
+
|
|
113
|
+
// Detect binary files by checking for null bytes (the standard
|
|
114
|
+
// heuristic used by git, diff, file(1), etc.)
|
|
115
|
+
if (buffer.includes(0x00)) continue
|
|
116
|
+
|
|
117
|
+
let content = buffer.toString('utf-8')
|
|
118
|
+
let modified = false
|
|
119
|
+
|
|
120
|
+
for (const [workspaceName, publicName] of renameEntries) {
|
|
121
|
+
// Replace the literal workspace name
|
|
122
|
+
const regex = new RegExp(workspaceName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
|
123
|
+
const replaced = content.replace(regex, publicName)
|
|
124
|
+
if (replaced !== content) {
|
|
125
|
+
content = replaced
|
|
126
|
+
modified = true
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Also replace regex-escaped versions of the workspace name
|
|
130
|
+
// (e.g. @stream44\.studio\/encapsulate in test patterns)
|
|
131
|
+
const wsEscaped = workspaceName.replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
|
|
132
|
+
if (wsEscaped !== workspaceName) {
|
|
133
|
+
const pubEscaped = publicName.replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
|
|
134
|
+
const escapedRegex = new RegExp(wsEscaped.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
|
135
|
+
const replacedEscaped = content.replace(escapedRegex, pubEscaped)
|
|
136
|
+
if (replacedEscaped !== content) {
|
|
137
|
+
content = replacedEscaped
|
|
138
|
+
modified = true
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (modified) {
|
|
144
|
+
await writeFile(file, content, 'utf-8')
|
|
145
|
+
}
|
|
146
|
+
} catch (e) {
|
|
147
|
+
// Skip files that can't be read
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Resolve workspace:* dependencies
|
|
155
|
+
if (repos) {
|
|
156
|
+
const repositoriesConfig = await this.$WorkspaceRepositories.config
|
|
157
|
+
const mappingsConfig = await this.$WorkspaceMappings.config
|
|
158
|
+
const npmRenames: Record<string, string> = mappingsConfig?.mappings?.['@stream44.studio/t44/caps/patterns/ProjectPublishing']?.npm || {}
|
|
159
|
+
const { publicNpmPackageNames, workspaceNpmPackageNames, workspacePackageSourceDirs } = await buildWorkspacePackageMaps(repositoriesConfig, npmRenames)
|
|
160
|
+
|
|
161
|
+
console.log('[t44] Resolving workspace dependencies ...\n')
|
|
162
|
+
for (const [repoName, repoSourceDir] of Object.entries(repos)) {
|
|
163
|
+
const packageJsonPath = join(repoSourceDir as string, 'package.json')
|
|
164
|
+
|
|
165
|
+
const packageJsonContent = await readFile(packageJsonPath, 'utf-8')
|
|
166
|
+
const indent = detectIndent(packageJsonContent)
|
|
167
|
+
const packageJson = JSON.parse(packageJsonContent)
|
|
168
|
+
|
|
169
|
+
await updateWorkspaceDependencies(packageJson, workspaceNpmPackageNames, workspacePackageSourceDirs, publicNpmPackageNames)
|
|
170
|
+
|
|
171
|
+
const updatedContent = JSON.stringify(packageJson, null, indent) + '\n'
|
|
172
|
+
if (updatedContent !== packageJsonContent) {
|
|
173
|
+
await writeFile(packageJsonPath, updatedContent, 'utf-8')
|
|
174
|
+
console.log(chalk.green(` ✓ Updated workspace dependencies in ${packageJsonPath}\n`))
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Follow workspaces declarations to update sub-workspace package.json files
|
|
178
|
+
const workspaces: string[] = packageJson.workspaces || []
|
|
179
|
+
if (workspaces.length > 0) {
|
|
180
|
+
const workspacePatterns = workspaces.map(ws => join(ws, 'package.json'))
|
|
181
|
+
const subPackageJsonPaths = await glob(workspacePatterns, {
|
|
182
|
+
cwd: repoSourceDir as string,
|
|
183
|
+
absolute: true,
|
|
184
|
+
onlyFiles: true,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
for (const subPkgPath of subPackageJsonPaths) {
|
|
188
|
+
try {
|
|
189
|
+
const subContent = await readFile(subPkgPath, 'utf-8')
|
|
190
|
+
const subIndent = detectIndent(subContent)
|
|
191
|
+
const subPkg = JSON.parse(subContent)
|
|
192
|
+
|
|
193
|
+
await updateWorkspaceDependencies(subPkg, workspaceNpmPackageNames, workspacePackageSourceDirs, publicNpmPackageNames)
|
|
194
|
+
|
|
195
|
+
const subUpdated = JSON.stringify(subPkg, null, subIndent) + '\n'
|
|
196
|
+
if (subUpdated !== subContent) {
|
|
197
|
+
await writeFile(subPkgPath, subUpdated, 'utf-8')
|
|
198
|
+
console.log(chalk.green(` ✓ Updated workspace dependencies in ${subPkgPath}\n`))
|
|
199
|
+
}
|
|
200
|
+
} catch (e) {
|
|
201
|
+
// Skip sub-workspaces with unreadable package.json
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Clean up tsconfig.json extends paths that don't resolve in the published package
|
|
208
|
+
console.log('[t44] Cleaning up tsconfig.json extends paths ...\n')
|
|
209
|
+
for (const [repoName, repoSourceDir] of Object.entries(repos)) {
|
|
210
|
+
await cleanupTsconfigExtends(join(repoSourceDir as string, 'tsconfig.json'))
|
|
211
|
+
|
|
212
|
+
// Follow workspaces to find sub-workspace tsconfig.json files
|
|
213
|
+
const pkgPath = join(repoSourceDir as string, 'package.json')
|
|
214
|
+
try {
|
|
215
|
+
const pkgContent = await readFile(pkgPath, 'utf-8')
|
|
216
|
+
const pkg = JSON.parse(pkgContent)
|
|
217
|
+
const workspaces: string[] = pkg.workspaces || []
|
|
218
|
+
if (workspaces.length > 0) {
|
|
219
|
+
const tsconfigPatterns = workspaces.map(ws => join(ws, 'tsconfig.json'))
|
|
220
|
+
const tsconfigPaths = await glob(tsconfigPatterns, {
|
|
221
|
+
cwd: repoSourceDir as string,
|
|
222
|
+
absolute: true,
|
|
223
|
+
onlyFiles: true,
|
|
224
|
+
})
|
|
225
|
+
for (const tsconfigPath of tsconfigPaths) {
|
|
226
|
+
await cleanupTsconfigExtends(tsconfigPath)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} catch { }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
bump: {
|
|
235
|
+
type: CapsulePropertyTypes.Function,
|
|
236
|
+
value: async function (this: any, { config, ctx }: { config: any, ctx: any }) {
|
|
237
|
+
const { rc, release } = ctx.options || {}
|
|
238
|
+
|
|
239
|
+
const projectSourceDir = join(ctx.repoSourceDir)
|
|
240
|
+
const packageJsonPath = join(projectSourceDir, 'package.json')
|
|
241
|
+
|
|
242
|
+
const packageJsonContent = await readFile(packageJsonPath, 'utf-8')
|
|
243
|
+
const packageJson = JSON.parse(packageJsonContent)
|
|
244
|
+
const currentVersion = packageJson.version
|
|
245
|
+
|
|
246
|
+
let newVersion: string
|
|
247
|
+
|
|
248
|
+
if (release) {
|
|
249
|
+
const rcMatch = currentVersion.match(/^(.+)-rc\.\d+$/)
|
|
250
|
+
if (rcMatch) {
|
|
251
|
+
newVersion = rcMatch[1]
|
|
252
|
+
console.log(chalk.cyan(` Removing RC suffix: ${currentVersion} → ${newVersion}`))
|
|
253
|
+
} else {
|
|
254
|
+
console.log(chalk.yellow(` Version ${currentVersion} has no RC suffix, skipping bump`))
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
} else if (rc) {
|
|
258
|
+
const rcMatch = currentVersion.match(/^(.+)-rc\.(\d+)$/)
|
|
259
|
+
if (rcMatch) {
|
|
260
|
+
const baseVersion = rcMatch[1]
|
|
261
|
+
const rcNumber = parseInt(rcMatch[2], 10)
|
|
262
|
+
newVersion = `${baseVersion}-rc.${rcNumber + 1}`
|
|
263
|
+
console.log(chalk.cyan(` Incrementing RC version: ${currentVersion} → ${newVersion}`))
|
|
264
|
+
} else {
|
|
265
|
+
const versionParts = currentVersion.split('.')
|
|
266
|
+
if (versionParts.length !== 3) {
|
|
267
|
+
throw new Error(`Invalid version format: ${currentVersion}`)
|
|
268
|
+
}
|
|
269
|
+
const [major, minor, patch] = versionParts
|
|
270
|
+
const newMinor = parseInt(minor, 10) + 1
|
|
271
|
+
newVersion = `${major}.${newMinor}.0-rc.1`
|
|
272
|
+
console.log(chalk.cyan(` Bumping minor version and adding RC: ${currentVersion} → ${newVersion}`))
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
console.log(chalk.yellow(` No version bump requested`))
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
packageJson.version = newVersion
|
|
280
|
+
const indent = detectIndent(packageJsonContent)
|
|
281
|
+
const updatedContent = JSON.stringify(packageJson, null, indent) + '\n'
|
|
282
|
+
await writeFile(packageJsonPath, updatedContent, 'utf-8')
|
|
283
|
+
|
|
284
|
+
// Write bumped version back to the original source package.json
|
|
285
|
+
// so that if this run fails partway through, the next run starts
|
|
286
|
+
// from the already-bumped version instead of producing a duplicate tag.
|
|
287
|
+
const originalSourceDir = ctx.repoConfig?.sourceDir
|
|
288
|
+
if (originalSourceDir) {
|
|
289
|
+
const originalPackageJsonPath = join(originalSourceDir, 'package.json')
|
|
290
|
+
try {
|
|
291
|
+
const originalContent = await readFile(originalPackageJsonPath, 'utf-8')
|
|
292
|
+
const originalJson = JSON.parse(originalContent)
|
|
293
|
+
originalJson.version = newVersion
|
|
294
|
+
const originalIndent = detectIndent(originalContent)
|
|
295
|
+
await writeFile(originalPackageJsonPath, JSON.stringify(originalJson, null, originalIndent) + '\n', 'utf-8')
|
|
296
|
+
console.log(chalk.green(` ✓ Updated ${packageJsonPath} to version ${newVersion}`))
|
|
297
|
+
console.log(chalk.green(` ✓ Written back version ${newVersion} to source ${originalPackageJsonPath}\n`))
|
|
298
|
+
} catch {
|
|
299
|
+
console.log(chalk.green(` ✓ Updated ${packageJsonPath} to version ${newVersion}\n`))
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
console.log(chalk.green(` ✓ Updated ${packageJsonPath} to version ${newVersion}\n`))
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
ctx.metadata.bumped = true
|
|
306
|
+
ctx.metadata.newVersion = newVersion
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}, {
|
|
312
|
+
importMeta: import.meta,
|
|
313
|
+
importStack: makeImportStack(),
|
|
314
|
+
capsuleName: capsule['#'],
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
capsule['#'] = '@stream44.studio/t44/caps/patterns/semver.org/ProjectPublishing'
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
async function buildWorkspacePackageMaps(repositoriesConfig: any, npmRenames: Record<string, string>) {
|
|
321
|
+
const publicNpmPackageNames: Record<string, string> = {}
|
|
322
|
+
const workspaceNpmPackageNames: Record<string, string> = {}
|
|
323
|
+
const workspacePackageSourceDirs: Record<string, string> = {}
|
|
324
|
+
|
|
325
|
+
// Build reverse rename map: publicName → workspaceName
|
|
326
|
+
const reverseRenames: Record<string, string> = {}
|
|
327
|
+
for (const [workspaceName, publicName] of Object.entries(npmRenames)) {
|
|
328
|
+
reverseRenames[publicName] = workspaceName
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (repositoriesConfig?.repositories) {
|
|
332
|
+
for (const [repoKey, repoConfig] of Object.entries(repositoriesConfig.repositories as any)) {
|
|
333
|
+
const sourceDir = (repoConfig as any).sourceDir
|
|
334
|
+
if (!sourceDir) continue
|
|
335
|
+
|
|
336
|
+
const packageJsonPath = join(sourceDir, 'package.json')
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
const packageJsonContent = await readFile(packageJsonPath, 'utf-8')
|
|
340
|
+
const packageJson = JSON.parse(packageJsonContent)
|
|
341
|
+
const workspacePackageName = packageJson.name
|
|
342
|
+
|
|
343
|
+
// Index source dir by the original workspace package name
|
|
344
|
+
workspacePackageSourceDirs[workspacePackageName] = sourceDir
|
|
345
|
+
|
|
346
|
+
// Also index by the renamed (public) name if a rename mapping exists
|
|
347
|
+
const renamedName = npmRenames[workspacePackageName]
|
|
348
|
+
if (renamedName) {
|
|
349
|
+
workspacePackageSourceDirs[renamedName] = sourceDir
|
|
350
|
+
workspaceNpmPackageNames[renamedName] = workspacePackageName
|
|
351
|
+
}
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.log(chalk.gray(` [debug] Could not read ${packageJsonPath}: ${error}`))
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const providers = (repoConfig as any).providers || ((repoConfig as any).provider ? [(repoConfig as any).provider] : [])
|
|
357
|
+
|
|
358
|
+
for (const provider of providers) {
|
|
359
|
+
if (provider.capsule === '@stream44.studio/t44-npmjs.com/caps/ProjectPublishing') {
|
|
360
|
+
try {
|
|
361
|
+
const packageJsonContent = await readFile(packageJsonPath, 'utf-8')
|
|
362
|
+
const packageJson = JSON.parse(packageJsonContent)
|
|
363
|
+
const workspacePackageName = packageJson.name
|
|
364
|
+
const publicPackageName = provider.config.PackageSettings.name
|
|
365
|
+
|
|
366
|
+
publicNpmPackageNames[workspacePackageName] = publicPackageName
|
|
367
|
+
workspaceNpmPackageNames[publicPackageName] = workspacePackageName
|
|
368
|
+
// Also index source dir by the public package name
|
|
369
|
+
workspacePackageSourceDirs[publicPackageName] = sourceDir
|
|
370
|
+
} catch (error) {
|
|
371
|
+
throw new Error(`Could not read package.json from ${packageJsonPath}: ${error}`)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return { publicNpmPackageNames, workspaceNpmPackageNames, workspacePackageSourceDirs }
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async function updateWorkspaceDependencies(
|
|
382
|
+
packageJson: any,
|
|
383
|
+
workspaceNpmPackageNames: Record<string, string>,
|
|
384
|
+
workspacePackageSourceDirs: Record<string, string>,
|
|
385
|
+
publicNpmPackageNames: Record<string, string>
|
|
386
|
+
) {
|
|
387
|
+
const dependencyFields = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']
|
|
388
|
+
const currentPackageName = packageJson.name
|
|
389
|
+
|
|
390
|
+
for (const depField of dependencyFields) {
|
|
391
|
+
if (packageJson[depField]) {
|
|
392
|
+
const updatedDeps: Record<string, string> = {}
|
|
393
|
+
|
|
394
|
+
for (const [depName, depVersion] of Object.entries(packageJson[depField])) {
|
|
395
|
+
// Skip self-referencing dependencies
|
|
396
|
+
if (depName === currentPackageName) {
|
|
397
|
+
continue
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (typeof depVersion === 'string' && depVersion.startsWith('workspace:')) {
|
|
401
|
+
try {
|
|
402
|
+
const workspaceDepName = workspaceNpmPackageNames[depName] || depName
|
|
403
|
+
const depSourceDir = workspacePackageSourceDirs[workspaceDepName] || workspacePackageSourceDirs[depName]
|
|
404
|
+
|
|
405
|
+
if (!depSourceDir) {
|
|
406
|
+
throw new Error(
|
|
407
|
+
`Could not find source directory for workspace dependency ${depName} (resolved to: ${workspaceDepName})\n` +
|
|
408
|
+
` workspaceNpmPackageNames keys: ${Object.keys(workspaceNpmPackageNames).join(', ')}\n` +
|
|
409
|
+
` workspacePackageSourceDirs keys: ${Object.keys(workspacePackageSourceDirs).join(', ')}`
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const depPackageJsonPath = join(depSourceDir, 'package.json')
|
|
414
|
+
const depPackageJsonContent = await readFile(depPackageJsonPath, 'utf-8')
|
|
415
|
+
const depPackageJson = JSON.parse(depPackageJsonContent)
|
|
416
|
+
|
|
417
|
+
// Replace workspace package name with public package name
|
|
418
|
+
const publicDepName = publicNpmPackageNames[workspaceDepName] || depName
|
|
419
|
+
updatedDeps[publicDepName] = `^${depPackageJson.version}`
|
|
420
|
+
} catch (error) {
|
|
421
|
+
throw new Error(`Could not resolve workspace dependency ${depName}: ${error}`)
|
|
422
|
+
}
|
|
423
|
+
} else {
|
|
424
|
+
// Keep non-workspace dependencies as-is
|
|
425
|
+
updatedDeps[depName] = depVersion as string
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
packageJson[depField] = updatedDeps
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async function cleanupTsconfigExtends(tsconfigPath: string): Promise<void> {
|
|
435
|
+
try {
|
|
436
|
+
const content = await readFile(tsconfigPath, 'utf-8')
|
|
437
|
+
const tsconfig = JSON.parse(content)
|
|
438
|
+
|
|
439
|
+
if (!tsconfig.extends) return
|
|
440
|
+
|
|
441
|
+
// Resolve the extends path relative to the tsconfig.json directory
|
|
442
|
+
const tsconfigDir = dirname(tsconfigPath)
|
|
443
|
+
const extendsPath = join(tsconfigDir, tsconfig.extends)
|
|
444
|
+
|
|
445
|
+
try {
|
|
446
|
+
await access(extendsPath)
|
|
447
|
+
// Path exists — keep it
|
|
448
|
+
} catch {
|
|
449
|
+
// Path does not exist — remove the extends field
|
|
450
|
+
delete tsconfig.extends
|
|
451
|
+
const indent = detectIndent(content)
|
|
452
|
+
await writeFile(tsconfigPath, JSON.stringify(tsconfig, null, indent) + '\n', 'utf-8')
|
|
453
|
+
console.log(chalk.green(` ✓ Removed invalid extends from ${tsconfigPath}\n`))
|
|
454
|
+
}
|
|
455
|
+
} catch {
|
|
456
|
+
// tsconfig.json doesn't exist or isn't valid JSON — skip
|
|
457
|
+
}
|
|
458
|
+
}
|