t44 0.4.0-rc.21 → 0.4.0-rc.23
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
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<td><a href="https://Stream44.Studio"><img src=".o/stream44.studio/assets/Icon-v1.svg" width="42" height="42"></a></td>
|
|
4
4
|
<td><strong><a href="https://Stream44.Studio">Stream44 Studio</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
|
+
<td>Hand Designed<br/><b>AI Coded Alpha</a></td>
|
|
6
7
|
</tr>
|
|
7
8
|
</table>
|
|
8
9
|
|
|
@@ -168,7 +169,6 @@ allows for rapid deployment of changes but more fundamentally allows for the
|
|
|
168
169
|
introduction of capability-based computing where code executes in restricted
|
|
169
170
|
sandboxes only able to leverage capabilities specifically given.
|
|
170
171
|
|
|
171
|
-
|
|
172
172
|
Provenance
|
|
173
173
|
===
|
|
174
174
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
import { join } from 'path'
|
|
2
|
+
import { join, dirname } from 'path'
|
|
3
|
+
import { readFile, access } from 'fs/promises'
|
|
4
|
+
import { constants } from 'fs'
|
|
3
5
|
import chalk from 'chalk'
|
|
4
6
|
|
|
5
7
|
// ── Provider Lifecycle ─────────────────────────────────────────────
|
|
@@ -159,6 +161,14 @@ export async function capsule({
|
|
|
159
161
|
const stepText = deprovision ? 'Deprovisioning' : 'Deploying'
|
|
160
162
|
console.log(`\n=> ${stepText} provider project alias '${alias}' for workspace project '${projectName}' ...\n`)
|
|
161
163
|
|
|
164
|
+
// ── Build step (deploy only) ──────────────────
|
|
165
|
+
if (!deprovision) {
|
|
166
|
+
const aliasConfig = projectConfig[alias]
|
|
167
|
+
if (aliasConfig.sourceDir) {
|
|
168
|
+
await runBuildIfAvailable(aliasConfig.sourceDir)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
162
172
|
await callProvidersForAlias(step, projectConfig[alias], { alias, projectName })
|
|
163
173
|
|
|
164
174
|
console.log(`\n<= ${stepText} of provider project alias '${alias}' for workspace project '${projectName}' done.\n`)
|
|
@@ -319,6 +329,67 @@ function formatDuration(status: any): string {
|
|
|
319
329
|
}
|
|
320
330
|
|
|
321
331
|
|
|
332
|
+
// ── Build step: find closest package.json with build script ───────
|
|
333
|
+
async function runBuildIfAvailable(sourceDir: string): Promise<void> {
|
|
334
|
+
// Walk up from sourceDir to find the closest package.json with a build script
|
|
335
|
+
let currentDir = sourceDir
|
|
336
|
+
|
|
337
|
+
// Normalize: if sourceDir ends with a known build output folder, start from parent
|
|
338
|
+
const buildOutputFolders = ['dist', 'build', 'out', '.next', '.output']
|
|
339
|
+
const lastSegment = sourceDir.split('/').pop()
|
|
340
|
+
if (lastSegment && buildOutputFolders.includes(lastSegment)) {
|
|
341
|
+
currentDir = dirname(sourceDir)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Walk up looking for package.json with build script
|
|
345
|
+
const maxDepth = 5
|
|
346
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
347
|
+
const pkgPath = join(currentDir, 'package.json')
|
|
348
|
+
try {
|
|
349
|
+
await access(pkgPath, constants.F_OK)
|
|
350
|
+
const pkgContent = await readFile(pkgPath, 'utf-8')
|
|
351
|
+
const pkg = JSON.parse(pkgContent)
|
|
352
|
+
|
|
353
|
+
if (pkg.scripts?.build) {
|
|
354
|
+
console.log(chalk.cyan(`Building ${pkg.name || currentDir} ...`))
|
|
355
|
+
console.log(chalk.gray(` Directory: ${currentDir}`))
|
|
356
|
+
console.log(chalk.gray(` Script: ${pkg.scripts.build}\n`))
|
|
357
|
+
|
|
358
|
+
const proc = Bun.spawn(['bun', 'run', 'build'], {
|
|
359
|
+
cwd: currentDir,
|
|
360
|
+
stdin: 'inherit',
|
|
361
|
+
stdout: 'inherit',
|
|
362
|
+
stderr: 'inherit'
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
const exitCode = await proc.exited
|
|
366
|
+
if (exitCode !== 0) {
|
|
367
|
+
throw new Error(`Build failed with exit code ${exitCode}`)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
console.log(chalk.green(`Build complete.\n`))
|
|
371
|
+
return
|
|
372
|
+
}
|
|
373
|
+
} catch (err: any) {
|
|
374
|
+
// If it's our own error (build failed), rethrow
|
|
375
|
+
if (err.message?.includes('Build failed')) {
|
|
376
|
+
throw err
|
|
377
|
+
}
|
|
378
|
+
// Otherwise, package.json doesn't exist or isn't valid, continue walking up
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const parentDir = dirname(currentDir)
|
|
382
|
+
if (parentDir === currentDir) {
|
|
383
|
+
// Reached filesystem root
|
|
384
|
+
break
|
|
385
|
+
}
|
|
386
|
+
currentDir = parentDir
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// No build script found - that's okay, just skip
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
|
|
322
393
|
function orderAliasesByDependencies(deploymentConfig: Record<string, any>): string[] {
|
|
323
394
|
const aliases = Object.keys(deploymentConfig)
|
|
324
395
|
const ordered: string[] = []
|
|
@@ -104,12 +104,15 @@ export async function capsule({
|
|
|
104
104
|
const isDryRun = !rc && !release && !bump && !publish
|
|
105
105
|
const shouldBumpVersions = rc || release || bump
|
|
106
106
|
|
|
107
|
-
// ── Provider filter (tag-based)
|
|
107
|
+
// ── Provider filter (tag-based + enabled flag) ───────
|
|
108
108
|
// When --publish <filter> is given, only providers whose
|
|
109
109
|
// capsule exposes a matching `tags` property will run.
|
|
110
110
|
// Tags are queried from the loaded capsule, not from config.
|
|
111
|
+
// Additionally, providers with `enabled: false` are always skipped.
|
|
111
112
|
const publishFilter = typeof publish === 'string' ? publish : null
|
|
112
113
|
const isProviderIncluded = async (providerConfig: any): Promise<boolean> => {
|
|
114
|
+
// Check enabled flag first - if explicitly false, skip this provider
|
|
115
|
+
if (providerConfig.enabled === false) return false
|
|
113
116
|
if (!publishFilter) return true
|
|
114
117
|
const provider = await getProvider(providerConfig.capsule)
|
|
115
118
|
const tags: string[] | undefined = provider.tags
|
|
@@ -147,10 +150,23 @@ export async function capsule({
|
|
|
147
150
|
for (const repoProvider of repoProviders) {
|
|
148
151
|
const capsuleName = repoProvider.capsule
|
|
149
152
|
const globalDefault = globalDefaults.get(capsuleName)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
if (globalDefault) {
|
|
154
|
+
// Merge: repo-level enabled overrides global, config is deep-merged
|
|
155
|
+
const mergedProvider = {
|
|
156
|
+
...globalDefault,
|
|
157
|
+
...repoProvider,
|
|
158
|
+
config: deepMerge(globalDefault.config, repoProvider.config),
|
|
159
|
+
}
|
|
160
|
+
// Explicit enabled at repo level takes precedence
|
|
161
|
+
if ('enabled' in repoProvider) {
|
|
162
|
+
mergedProvider.enabled = repoProvider.enabled
|
|
163
|
+
} else if ('enabled' in globalDefault) {
|
|
164
|
+
mergedProvider.enabled = globalDefault.enabled
|
|
165
|
+
}
|
|
166
|
+
merged.push(mergedProvider)
|
|
167
|
+
} else {
|
|
168
|
+
merged.push(repoProvider)
|
|
169
|
+
}
|
|
154
170
|
seen.add(capsuleName)
|
|
155
171
|
}
|
|
156
172
|
|
|
@@ -242,6 +258,7 @@ export async function capsule({
|
|
|
242
258
|
ctx: any,
|
|
243
259
|
) => {
|
|
244
260
|
const providers = resolveRepoProviders(repoConfig, globalProviders)
|
|
261
|
+
ctx.mergedProviders = providers
|
|
245
262
|
for (const providerConfig of providers) {
|
|
246
263
|
if (!await isProviderIncluded(providerConfig)) continue
|
|
247
264
|
|
|
@@ -53,7 +53,7 @@ export async function capsule({
|
|
|
53
53
|
const projectionDir = ctx.publishingApi.getProjectionDir(capsule['#'])
|
|
54
54
|
const stageDir = join(projectionDir, 'stage', originUri.replace(/[\/]/g, '~'))
|
|
55
55
|
|
|
56
|
-
// Clone if repository doesn't exist yet
|
|
56
|
+
// ── 1. Clone if repository doesn't exist yet ────────────
|
|
57
57
|
let isNewEmptyRepo = false
|
|
58
58
|
const repoExists = await this.ProjectRepository.exists({ rootDir: stageDir })
|
|
59
59
|
if (!repoExists) {
|
|
@@ -62,7 +62,14 @@ export async function capsule({
|
|
|
62
62
|
isNewEmptyRepo = result.isNewEmptyRepo
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
//
|
|
65
|
+
// ── 2. Ensure origin remote exists (heal if missing) ────
|
|
66
|
+
const hasOrigin = await this.ProjectRepository.hasRemote({ rootDir: stageDir, name: 'origin' })
|
|
67
|
+
if (!hasOrigin) {
|
|
68
|
+
console.log(`Re-adding missing 'origin' remote ...`)
|
|
69
|
+
await this.ProjectRepository.addRemote({ rootDir: stageDir, name: 'origin', url: originUri })
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── 3. Set local git author ─────────────────────────────
|
|
66
73
|
if (authorConfig?.name) {
|
|
67
74
|
await $`git config user.name ${authorConfig.name}`.cwd(stageDir).quiet()
|
|
68
75
|
}
|
|
@@ -70,7 +77,85 @@ export async function capsule({
|
|
|
70
77
|
await $`git config user.email ${authorConfig.email}`.cwd(stageDir).quiet()
|
|
71
78
|
}
|
|
72
79
|
|
|
73
|
-
//
|
|
80
|
+
// ── 4. Determine target branch ──────────────────────────
|
|
81
|
+
const targetBranch = ctx.options.branch
|
|
82
|
+
const effectiveBranch = targetBranch || 'main'
|
|
83
|
+
|
|
84
|
+
// ── 5. Detect empty repo ────────────────────────────────
|
|
85
|
+
const headCheck = await $`git rev-parse HEAD`.cwd(stageDir).quiet().nothrow()
|
|
86
|
+
const isEmptyRepo = isNewEmptyRepo || headCheck.exitCode !== 0
|
|
87
|
+
|
|
88
|
+
// ── 6. Fetch from remote ────────────────────────────────
|
|
89
|
+
// Always fetch so we know the true state of the remote
|
|
90
|
+
// before making any branch decisions. Skip for empty repos
|
|
91
|
+
// that were just created (nothing to fetch).
|
|
92
|
+
if (!isEmptyRepo) {
|
|
93
|
+
await $`git fetch origin`.cwd(stageDir).quiet().nothrow()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── 7. Clean working tree and sync branch to remote ─────
|
|
97
|
+
// This is the critical section: we must get the local branch
|
|
98
|
+
// to exactly match the remote before rsyncing source files.
|
|
99
|
+
let branchSwitched = false
|
|
100
|
+
if (isEmptyRepo) {
|
|
101
|
+
await $`git checkout -b ${effectiveBranch}`.cwd(stageDir).quiet().nothrow()
|
|
102
|
+
console.log(`Initialized branch '${effectiveBranch}' on empty repository`)
|
|
103
|
+
branchSwitched = true
|
|
104
|
+
} else {
|
|
105
|
+
// Discard any uncommitted changes from previous runs
|
|
106
|
+
await $`git checkout .`.cwd(stageDir).quiet().nothrow()
|
|
107
|
+
await $`git clean -fd`.cwd(stageDir).quiet().nothrow()
|
|
108
|
+
|
|
109
|
+
const currentBranch = await this.ProjectRepository.getBranch({ rootDir: stageDir })
|
|
110
|
+
|
|
111
|
+
if (currentBranch !== effectiveBranch) {
|
|
112
|
+
console.log(`Switching from branch '${currentBranch}' to '${effectiveBranch}' ...`)
|
|
113
|
+
|
|
114
|
+
// Check if branch exists locally
|
|
115
|
+
const localBranchCheck = await $`git branch --list ${effectiveBranch}`.cwd(stageDir).quiet().nothrow()
|
|
116
|
+
const localBranchExists = localBranchCheck.text().trim().length > 0
|
|
117
|
+
|
|
118
|
+
if (localBranchExists) {
|
|
119
|
+
await $`git checkout ${effectiveBranch}`.cwd(stageDir).quiet()
|
|
120
|
+
} else {
|
|
121
|
+
// Check if branch exists on remote
|
|
122
|
+
const remoteBranchCheck = await $`git ls-remote --heads origin ${effectiveBranch}`.cwd(stageDir).quiet().nothrow()
|
|
123
|
+
const remoteBranchExists = remoteBranchCheck.text().trim().length > 0
|
|
124
|
+
|
|
125
|
+
if (remoteBranchExists) {
|
|
126
|
+
await $`git checkout -b ${effectiveBranch} origin/${effectiveBranch}`.cwd(stageDir).quiet()
|
|
127
|
+
} else {
|
|
128
|
+
await $`git checkout -b ${effectiveBranch}`.cwd(stageDir).quiet()
|
|
129
|
+
console.log(`Created new branch '${effectiveBranch}'`)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
branchSwitched = true
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Hard-reset local branch to match remote (if remote branch exists).
|
|
136
|
+
// This ensures the local stage repo always starts from the true
|
|
137
|
+
// remote state, regardless of what happened in previous runs.
|
|
138
|
+
const remoteRef = `origin/${effectiveBranch}`
|
|
139
|
+
const remoteRefCheck = await $`git rev-parse --verify ${remoteRef}`.cwd(stageDir).quiet().nothrow()
|
|
140
|
+
if (remoteRefCheck.exitCode === 0) {
|
|
141
|
+
const localHead = (await $`git rev-parse HEAD`.cwd(stageDir).quiet()).text().trim()
|
|
142
|
+
const remoteHead = remoteRefCheck.text().trim()
|
|
143
|
+
if (localHead !== remoteHead) {
|
|
144
|
+
await $`git reset --hard ${remoteRef}`.cwd(stageDir).quiet()
|
|
145
|
+
console.log(`Synced local '${effectiveBranch}' to remote (${remoteHead.slice(0, 8)})`)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (branchSwitched) {
|
|
150
|
+
console.log(`On branch '${effectiveBranch}'`)
|
|
151
|
+
} else if (targetBranch) {
|
|
152
|
+
console.log(`Already on branch '${targetBranch}'`)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── 8. Rsync source files into stage repo ───────────────
|
|
157
|
+
// Now that the branch is in sync with remote, overlay
|
|
158
|
+
// the workspace source files on top.
|
|
74
159
|
const gitignorePath = join(ctx.repoSourceDir, '.gitignore')
|
|
75
160
|
await this.ProjectRepository.sync({
|
|
76
161
|
rootDir: stageDir,
|
|
@@ -79,7 +164,7 @@ export async function capsule({
|
|
|
79
164
|
excludePatterns: ctx.alwaysIgnore || []
|
|
80
165
|
})
|
|
81
166
|
|
|
82
|
-
//
|
|
167
|
+
// ── 9. Security check: .env* files ──────────────────────
|
|
83
168
|
const envFilesResult = await $`find . -name '.env*' -not -path './.git/*'`.cwd(stageDir).quiet().nothrow()
|
|
84
169
|
const envFiles = envFilesResult.text().trim().split('\n').filter(Boolean)
|
|
85
170
|
if (envFiles.length > 0) {
|
|
@@ -94,7 +179,7 @@ export async function capsule({
|
|
|
94
179
|
process.exit(1)
|
|
95
180
|
}
|
|
96
181
|
|
|
97
|
-
// Generate files from config
|
|
182
|
+
// ── 10. Generate files from config ──────────────────────
|
|
98
183
|
// This happens AFTER rsync so generated files are not overwritten
|
|
99
184
|
if (config.config) {
|
|
100
185
|
for (const [key, value] of Object.entries(config.config)) {
|
|
@@ -127,52 +212,7 @@ export async function capsule({
|
|
|
127
212
|
}
|
|
128
213
|
}
|
|
129
214
|
|
|
130
|
-
//
|
|
131
|
-
const targetBranch = ctx.options.branch
|
|
132
|
-
const effectiveBranch = targetBranch || 'main'
|
|
133
|
-
|
|
134
|
-
// Detect if the stage repo is on a different branch than the target
|
|
135
|
-
// Also detect empty repos that were cloned in a previous run but still have no commits
|
|
136
|
-
const headCheck = await $`git rev-parse HEAD`.cwd(stageDir).quiet().nothrow()
|
|
137
|
-
const isEmptyRepo = isNewEmptyRepo || headCheck.exitCode !== 0
|
|
138
|
-
let branchSwitched = false
|
|
139
|
-
if (isEmptyRepo) {
|
|
140
|
-
// Empty repo has no HEAD — create the target branch directly
|
|
141
|
-
// (git checkout -b works even without commits as an orphan branch)
|
|
142
|
-
await $`git checkout -b ${effectiveBranch}`.cwd(stageDir).quiet().nothrow()
|
|
143
|
-
console.log(`Initialized branch '${effectiveBranch}' on empty repository`)
|
|
144
|
-
branchSwitched = true
|
|
145
|
-
} else {
|
|
146
|
-
const currentBranch = await this.ProjectRepository.getBranch({ rootDir: stageDir })
|
|
147
|
-
if (currentBranch !== effectiveBranch) {
|
|
148
|
-
console.log(`Switching from branch '${currentBranch}' to '${effectiveBranch}' ...`)
|
|
149
|
-
// Check if branch exists locally
|
|
150
|
-
const localBranchCheck = await $`git branch --list ${effectiveBranch}`.cwd(stageDir).quiet().nothrow()
|
|
151
|
-
const localBranchExists = localBranchCheck.text().trim().length > 0
|
|
152
|
-
|
|
153
|
-
if (localBranchExists) {
|
|
154
|
-
await $`git checkout ${effectiveBranch}`.cwd(stageDir).quiet()
|
|
155
|
-
console.log(`Checked out existing local branch '${effectiveBranch}'`)
|
|
156
|
-
} else {
|
|
157
|
-
// Check if branch exists on remote
|
|
158
|
-
const remoteBranchCheck = await $`git ls-remote --heads origin ${effectiveBranch}`.cwd(stageDir).quiet().nothrow()
|
|
159
|
-
const remoteBranchExists = remoteBranchCheck.text().trim().length > 0
|
|
160
|
-
|
|
161
|
-
if (remoteBranchExists) {
|
|
162
|
-
await $`git checkout -b ${effectiveBranch} origin/${effectiveBranch}`.cwd(stageDir).quiet()
|
|
163
|
-
console.log(`Checked out remote branch '${effectiveBranch}'`)
|
|
164
|
-
} else {
|
|
165
|
-
await $`git checkout -b ${effectiveBranch}`.cwd(stageDir).quiet()
|
|
166
|
-
console.log(`Created new branch '${effectiveBranch}'`)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
branchSwitched = true
|
|
170
|
-
} else if (targetBranch) {
|
|
171
|
-
console.log(`Already on branch '${targetBranch}'`)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Handle --dangerously-squash-to-commit on the stage repo
|
|
215
|
+
// ── 11. Handle --dangerously-squash-to-commit ───────────
|
|
176
216
|
let squashedToCommit = false
|
|
177
217
|
const squashToCommit = ctx.options.dangerouslySquashToCommit
|
|
178
218
|
if (squashToCommit) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { join } from 'path'
|
|
3
|
-
import { readFile, writeFile } from 'fs/promises'
|
|
2
|
+
import { join, dirname } from 'path'
|
|
3
|
+
import { readFile, writeFile, access } from 'fs/promises'
|
|
4
4
|
import glob from 'fast-glob'
|
|
5
5
|
import chalk from 'chalk'
|
|
6
6
|
|
|
@@ -64,12 +64,26 @@ export async function capsule({
|
|
|
64
64
|
let modified = false
|
|
65
65
|
|
|
66
66
|
for (const [workspaceName, publicName] of renameEntries) {
|
|
67
|
+
// Replace the literal workspace name
|
|
67
68
|
const regex = new RegExp(workspaceName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
|
68
69
|
const replaced = content.replace(regex, publicName)
|
|
69
70
|
if (replaced !== content) {
|
|
70
71
|
content = replaced
|
|
71
72
|
modified = true
|
|
72
73
|
}
|
|
74
|
+
|
|
75
|
+
// Also replace regex-escaped versions of the workspace name
|
|
76
|
+
// (e.g. @stream44\.studio\/encapsulate in test patterns)
|
|
77
|
+
const wsEscaped = workspaceName.replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
|
|
78
|
+
if (wsEscaped !== workspaceName) {
|
|
79
|
+
const pubEscaped = publicName.replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
|
|
80
|
+
const escapedRegex = new RegExp(wsEscaped.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
|
|
81
|
+
const replacedEscaped = content.replace(escapedRegex, pubEscaped)
|
|
82
|
+
if (replacedEscaped !== content) {
|
|
83
|
+
content = replacedEscaped
|
|
84
|
+
modified = true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
if (modified) {
|
|
@@ -135,6 +149,31 @@ export async function capsule({
|
|
|
135
149
|
}
|
|
136
150
|
}
|
|
137
151
|
}
|
|
152
|
+
|
|
153
|
+
// Clean up tsconfig.json extends paths that don't resolve in the published package
|
|
154
|
+
console.log('[t44] Cleaning up tsconfig.json extends paths ...\n')
|
|
155
|
+
for (const [repoName, repoSourceDir] of Object.entries(repos)) {
|
|
156
|
+
await cleanupTsconfigExtends(join(repoSourceDir as string, 'tsconfig.json'))
|
|
157
|
+
|
|
158
|
+
// Follow workspaces to find sub-workspace tsconfig.json files
|
|
159
|
+
const pkgPath = join(repoSourceDir as string, 'package.json')
|
|
160
|
+
try {
|
|
161
|
+
const pkgContent = await readFile(pkgPath, 'utf-8')
|
|
162
|
+
const pkg = JSON.parse(pkgContent)
|
|
163
|
+
const workspaces: string[] = pkg.workspaces || []
|
|
164
|
+
if (workspaces.length > 0) {
|
|
165
|
+
const tsconfigPatterns = workspaces.map(ws => join(ws, 'tsconfig.json'))
|
|
166
|
+
const tsconfigPaths = await glob(tsconfigPatterns, {
|
|
167
|
+
cwd: repoSourceDir as string,
|
|
168
|
+
absolute: true,
|
|
169
|
+
onlyFiles: true,
|
|
170
|
+
})
|
|
171
|
+
for (const tsconfigPath of tsconfigPaths) {
|
|
172
|
+
await cleanupTsconfigExtends(tsconfigPath)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} catch { }
|
|
176
|
+
}
|
|
138
177
|
}
|
|
139
178
|
}
|
|
140
179
|
},
|
|
@@ -337,3 +376,29 @@ async function updateWorkspaceDependencies(
|
|
|
337
376
|
}
|
|
338
377
|
}
|
|
339
378
|
}
|
|
379
|
+
|
|
380
|
+
async function cleanupTsconfigExtends(tsconfigPath: string): Promise<void> {
|
|
381
|
+
try {
|
|
382
|
+
const content = await readFile(tsconfigPath, 'utf-8')
|
|
383
|
+
const tsconfig = JSON.parse(content)
|
|
384
|
+
|
|
385
|
+
if (!tsconfig.extends) return
|
|
386
|
+
|
|
387
|
+
// Resolve the extends path relative to the tsconfig.json directory
|
|
388
|
+
const tsconfigDir = dirname(tsconfigPath)
|
|
389
|
+
const extendsPath = join(tsconfigDir, tsconfig.extends)
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
await access(extendsPath)
|
|
393
|
+
// Path exists — keep it
|
|
394
|
+
} catch {
|
|
395
|
+
// Path does not exist — remove the extends field
|
|
396
|
+
delete tsconfig.extends
|
|
397
|
+
const indent = detectIndent(content)
|
|
398
|
+
await writeFile(tsconfigPath, JSON.stringify(tsconfig, null, indent) + '\n', 'utf-8')
|
|
399
|
+
console.log(chalk.green(` ✓ Removed invalid extends from ${tsconfigPath}\n`))
|
|
400
|
+
}
|
|
401
|
+
} catch {
|
|
402
|
+
// tsconfig.json doesn't exist or isn't valid JSON — skip
|
|
403
|
+
}
|
|
404
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "t44",
|
|
3
|
-
"version": "0.4.0-rc.
|
|
3
|
+
"version": "0.4.0-rc.23",
|
|
4
4
|
"license": "LGPL",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -67,18 +67,18 @@
|
|
|
67
67
|
"fast-glob": "^3.3.3",
|
|
68
68
|
"inquirer": "^12.4.0",
|
|
69
69
|
"axios": "^1.13.4",
|
|
70
|
-
"turbo": "^2.7.5",
|
|
71
70
|
"@ucanto/principal": "^9.0.3",
|
|
72
71
|
"@ucanto/server": "^11.0.3",
|
|
73
72
|
"json-schema-ref-resolver": "^3.0.0",
|
|
74
|
-
"@stream44.studio/encapsulate": "^0.4.0-rc.
|
|
73
|
+
"@stream44.studio/encapsulate": "^0.4.0-rc.23",
|
|
74
|
+
"turbo": "^2.7.5"
|
|
75
75
|
},
|
|
76
76
|
"optionalDependencies": {
|
|
77
|
-
"@stream44.studio/t44-bunny.net": "^0.1.0-rc.
|
|
78
|
-
"@stream44.studio/t44-vercel.com": "^0.1.0-rc.
|
|
79
|
-
"@stream44.studio/t44-github.com": "^0.1.0-rc.
|
|
80
|
-
"@stream44.studio/t44-dynadot.com": "^0.1.0-rc.
|
|
81
|
-
"@stream44.studio/t44-npmjs.com": "^0.1.0-rc.
|
|
77
|
+
"@stream44.studio/t44-bunny.net": "^0.1.0-rc.4",
|
|
78
|
+
"@stream44.studio/t44-vercel.com": "^0.1.0-rc.4",
|
|
79
|
+
"@stream44.studio/t44-github.com": "^0.1.0-rc.4",
|
|
80
|
+
"@stream44.studio/t44-dynadot.com": "^0.1.0-rc.4",
|
|
81
|
+
"@stream44.studio/t44-npmjs.com": "^0.1.0-rc.4"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@types/bun": "^1.3.4",
|