@stacksjs/actions 0.75.276 → 0.75.279
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/dist/project-scripts.d.ts +2 -0
- package/dist/project-scripts.js +21 -16
- package/package.json +44 -44
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Internal Bun owner source; exported for real IPC completion-race tests. */
|
|
2
|
+
export declare function windowsCheckHost(command: string[], windowsTaskkill: string): string;
|
|
1
3
|
/** Execute declared project checks with the installed Bun and owned cleanup. */
|
|
2
4
|
export declare function runProjectScripts(options: ProjectScriptOptions): Promise<ProjectScriptReceipt>;
|
|
3
5
|
export declare interface ProjectScriptOptions {
|
package/dist/project-scripts.js
CHANGED
|
@@ -195,7 +195,7 @@ public static class StacksOwnedCheck {
|
|
|
195
195
|
}
|
|
196
196
|
'@
|
|
197
197
|
exit [StacksOwnedCheck]::Run('${bun.replaceAll("'","''")}', '${script}')
|
|
198
|
-
`;return[powershell,"-NoLogo","-NoProfile","-NonInteractive","-OutputFormat","Text","-EncodedCommand",Buffer.from(source,"utf16le").toString("base64")]}export
|
|
198
|
+
`;return[powershell,"-NoLogo","-NoProfile","-NonInteractive","-OutputFormat","Text","-EncodedCommand",Buffer.from(source,"utf16le").toString("base64")]}export function windowsCheckHost(command,windowsTaskkill){return`
|
|
199
199
|
setInterval(() => {}, 60_000)
|
|
200
200
|
let command
|
|
201
201
|
let stopRequested = false
|
|
@@ -209,10 +209,15 @@ process.on('message', async (message) => {
|
|
|
209
209
|
if (command) {
|
|
210
210
|
try {
|
|
211
211
|
if (!consumerPermitted) {
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
const killed = Bun.spawnSync([${JSON.stringify(windowsTaskkill)}, '/PID', String(command.pid), '/T', '/F'], { stdout: '
|
|
215
|
-
|
|
212
|
+
// Retain the Bun host until the preparation helper's native process
|
|
213
|
+
// handle and output have completed, even when taskkill returns first.
|
|
214
|
+
const killed = Bun.spawnSync([${JSON.stringify(windowsTaskkill)}, '/PID', String(command.pid), '/T', '/F'], { stdout: 'pipe', stderr: 'pipe', timeout: 2000 })
|
|
215
|
+
// spawnSync prevents Bun from observing a concurrent helper exit.
|
|
216
|
+
// A stale exitCode is not evidence of a live process. The completion
|
|
217
|
+
// below still requires BOTH the native exit and the output drain;
|
|
218
|
+
// retain taskkill diagnostics only if that bounded cleanup fails.
|
|
219
|
+
if (killed.exitCode !== 0)
|
|
220
|
+
process.send({ preparationCleanupError: 'Native preparation cleanup failed (taskkill exit ' + killed.exitCode + '): ' + killed.stdout.toString().trim().slice(0, 1000) + ' ' + killed.stderr.toString().trim().slice(0, 1000) })
|
|
216
221
|
}
|
|
217
222
|
else {
|
|
218
223
|
command.stdin.write(new Uint8Array([2]))
|
|
@@ -220,7 +225,12 @@ process.on('message', async (message) => {
|
|
|
220
225
|
}
|
|
221
226
|
} catch (error) { process.send({ error: String(error) }) }
|
|
222
227
|
}
|
|
223
|
-
} else if (message?.
|
|
228
|
+
} else if (message?.prepare) start()
|
|
229
|
+
else if (message?.start && command && !stopRequested) {
|
|
230
|
+
consumerPermitted = true
|
|
231
|
+
command.stdin.write(new Uint8Array([1]))
|
|
232
|
+
await command.stdin.flush()
|
|
233
|
+
}
|
|
224
234
|
})
|
|
225
235
|
process.send({ ready: true })
|
|
226
236
|
await permitted
|
|
@@ -247,14 +257,9 @@ else {
|
|
|
247
257
|
if (prefix.subarray(0, line).toString().trimEnd() !== 'STACKS_OWNED_CHECK_READY')
|
|
248
258
|
throw new Error('Native ownership readiness was malformed')
|
|
249
259
|
ready = true
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
// preparation-only kill once user code could be running.
|
|
254
|
-
consumerPermitted = true
|
|
255
|
-
command.stdin.write(new Uint8Array([1]))
|
|
256
|
-
await command.stdin.flush()
|
|
257
|
-
}
|
|
260
|
+
// The outer owner must observe native readiness before granting
|
|
261
|
+
// startup. Until then it can dispose only preparation with taskkill.
|
|
262
|
+
process.send({ nativeReady: true })
|
|
258
263
|
if (prefix.length > line + 1) await Bun.write(Bun.stdout, prefix.subarray(line + 1))
|
|
259
264
|
prefix = Buffer.alloc(0)
|
|
260
265
|
}
|
|
@@ -270,7 +275,7 @@ else {
|
|
|
270
275
|
process.send({ error: String(error) })
|
|
271
276
|
}
|
|
272
277
|
}
|
|
273
|
-
|
|
278
|
+
`}export async function runProjectScripts(options){const timeoutMs=options.timeoutMs??300000;if(!Number.isSafeInteger(timeoutMs)||timeoutMs<=0||!options.scripts.length||options.scripts.some((name)=>!/^[a-z0-9][\w:.-]*$/i.test(name))||new Set(options.scripts).size!==options.scripts.length)fail("Project checks need unique script names and a positive execution budget");const receipt={bunVersion:"",checks:[]},manifestPath=join(options.cwd,"package.json"),originalManifest=readFileSync(manifestPath,"utf8"),manifest=JSON.parse(originalManifest),bun=Bun.which("bun",{cwd:options.cwd});if(!bun)fail("Consumer Bun executable is unavailable");const probe=Bun.spawnSync([bun,"--version"],{cwd:options.cwd,stdout:"pipe",stderr:"pipe",timeout:1e4}),bunVersion=probe.stdout.toString().trim();if(probe.exitCode!==0||!/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(bunVersion)||!Bun.semver.satisfies(bunVersion,manifest.engines?.bun??">=1.3.0"))fail("Consumer Bun executable does not satisfy the declared toolchain");receipt.bunVersion=bunVersion;for(const script of options.scripts){if(!Object.hasOwn(manifest.scripts??{},script)||typeof manifest.scripts?.[script]!=="string"||!manifest.scripts[script].trim())fail(`Consumer script ${script} is not declared`);options.signal?.throwIfAborted();const windowsTaskkill=process.platform==="win32"?Bun.which("taskkill.exe"):void 0,powershell=process.platform==="win32"?Bun.which("powershell.exe")??Bun.which("pwsh.exe"):void 0;if(process.platform==="win32"&&(!windowsTaskkill||!powershell))fail("Windows process ownership requires taskkill.exe and PowerShell with .NET Add-Type");const command=powershell?windowsCheckCommand(powershell,bun,script):[bun,"run",script];let reportExit=()=>{},reportFailure=()=>{};const reported=new Promise((resolve,reject)=>{reportExit=resolve;reportFailure=reject});let interrupted=!1,windowsHelperPid,windowsNativeReady=!1,preparationCleanupError="";const host=powershell?windowsCheckHost(command,windowsTaskkill):`
|
|
274
279
|
setInterval(() => {}, 60_000)
|
|
275
280
|
try {
|
|
276
281
|
const command = Bun.spawn(${JSON.stringify(command)}, { stdin: 'ignore', stdout: 'inherit', stderr: 'inherit' })
|
|
@@ -278,4 +283,4 @@ try {
|
|
|
278
283
|
} catch (error) {
|
|
279
284
|
process.send({ error: String(error) })
|
|
280
285
|
}
|
|
281
|
-
`,child=Bun.spawn([bun,"--eval",host],{cwd:options.cwd,env:{...process.env,...options.env},stdin:"ignore",stdout:"pipe",stderr:"pipe",detached:process.platform!=="win32",ipc(message){if(powershell&&message?.ready===!0)try{child.send(interrupted?{stop:!0}:{start:!0})}catch(error){reportFailure(Error(`Consumer script ${script} control channel failed: ${String(error)}`))}else if(powershell&&Number.isSafeInteger(message?.helperPid)&&message.helperPid>0)windowsHelperPid=message.helperPid;else if(Number.isInteger(message?.exitCode))reportExit(message.exitCode);else reportFailure(Error(`Consumer script ${script} host failed: ${message?.error??"invalid completion report"}`))}}),pipes=[child.stdout,child.stderr].map((stream,index)=>({reader:stream.getReader(),done:!1,source:index===0?"stdout":"stderr"}));let teardown,announceStop=()=>{};const stopped=new Promise((resolve)=>{announceStop=resolve}),waitWithin=async(promise,milliseconds)=>{let timer;try{return await Promise.race([promise,new Promise((_,reject)=>{timer=setTimeout(()=>reject(Error("Process or output cleanup did not finish within its grace period")),milliseconds)})])}finally{if(timer)clearTimeout(timer)}};let draining;const dispose=(preserveOutput)=>{if(teardown)return teardown;teardown=(async()=>{const errors=[];if(windowsTaskkill){if(!preserveOutput)try{child.send({stop:!0});await waitWithin(reported,1000)}catch{errors.push(
|
|
286
|
+
`,child=Bun.spawn([bun,"--eval",host],{cwd:options.cwd,env:{...process.env,...options.env},stdin:"ignore",stdout:"pipe",stderr:"pipe",detached:process.platform!=="win32",ipc(message){if(powershell&&message?.ready===!0)try{child.send(interrupted?{stop:!0}:{prepare:!0})}catch(error){reportFailure(Error(`Consumer script ${script} control channel failed: ${String(error)}`))}else if(powershell&&message?.nativeReady===!0){windowsNativeReady=!0;try{child.send(interrupted?{stop:!0}:{start:!0})}catch(error){reportFailure(Error(`Consumer script ${script} native control channel failed: ${String(error)}`))}}else if(powershell&&Number.isSafeInteger(message?.helperPid)&&message.helperPid>0)windowsHelperPid=message.helperPid;else if(powershell&&typeof message?.preparationCleanupError==="string")preparationCleanupError=message.preparationCleanupError;else if(Number.isInteger(message?.exitCode))reportExit(message.exitCode);else reportFailure(Error(`Consumer script ${script} host failed: ${message?.error??"invalid completion report"}`))}}),pipes=[child.stdout,child.stderr].map((stream,index)=>({reader:stream.getReader(),done:!1,source:index===0?"stdout":"stderr"}));let teardown,announceStop=()=>{};const stopped=new Promise((resolve)=>{announceStop=resolve}),waitWithin=async(promise,milliseconds)=>{let timer;try{return await Promise.race([promise,new Promise((_,reject)=>{timer=setTimeout(()=>reject(Error("Process or output cleanup did not finish within its grace period")),milliseconds)})])}finally{if(timer)clearTimeout(timer)}};let draining;const dispose=(preserveOutput)=>{if(teardown)return teardown;teardown=(async()=>{const errors=[];if(windowsTaskkill){if(!preserveOutput)try{child.send({stop:!0});await waitWithin(reported,windowsNativeReady?1000:3000)}catch(error){errors.push(`Native Windows cleanup completion was not confirmed: ${error instanceof Error?error.message:String(error)}${preparationCleanupError?`; ${preparationCleanupError}`:""}`)}try{const result=Bun.spawnSync([windowsTaskkill,"/PID",String(child.pid),"/T","/F"],{stdout:"ignore",stderr:"ignore",timeout:2000});if(result.exitCode!==0)errors.push(`taskkill failed with exit code ${result.exitCode}`)}catch{errors.push("Windows process-tree cleanup could not execute taskkill")}if(windowsHelperPid){const until=performance.now()+1000;let alive=!0;while(alive){try{process.kill(windowsHelperPid,0)}catch(error){if(error.code==="ESRCH")alive=!1;else break}if(!alive||performance.now()>=until)break;await new Promise((resolve)=>setTimeout(resolve,10))}if(alive)errors.push("Native Windows helper disposal was not confirmed")}if(errors.length)try{child.kill("SIGKILL")}catch{errors.push("Direct child cleanup also failed")}}else{const signalGroup=(signal)=>{try{process.kill(-child.pid,signal)}catch(error){if(error.code!=="ESRCH"){const cause=error;errors.push(`Process-group ${signal} failed (code=${cause.code}, errno=${cause.errno}, message=${cause.message})`)}}};signalGroup("SIGTERM");await new Promise((resolve)=>setTimeout(resolve,500));signalGroup("SIGKILL");const until=performance.now()+1000;let pendingError="";while(!0){try{process.kill(-child.pid,0)}catch(error){const cause=error;if(cause.code==="ESRCH")break;pendingError=`signal=0, code=${cause.code}, errno=${cause.errno}, message=${cause.message}`;if(cause.code!=="EPERM"){errors.push(`Process-group disposal could not be confirmed (${pendingError})`);break}}if(performance.now()>=until){errors.push(`Owned process group remained after cleanup${pendingError?` (${pendingError})`:""}`);break}await new Promise((resolve)=>setTimeout(resolve,10))}}if(preserveOutput)try{await waitWithin(draining,1000)}catch{errors.push("Output draining exceeded its cleanup grace period")}const close=Promise.all([...pipes.filter((pipe)=>!pipe.done).map(async(pipe)=>{try{await pipe.reader.cancel()}catch{errors.push("Output reader cancellation failed")}}),child.exited]);try{await waitWithin(close,1000)}catch{errors.push("Process or output cleanup exceeded its grace period")}if(errors.length)fail(`Consumer script ${script} cleanup failed: ${errors.join("; ")}`)})();teardown.catch(()=>{});return teardown},stop=()=>{if(interrupted)return;interrupted=!0;dispose(!1);announceStop()};child.exited.then((code)=>{if(!teardown)reportFailure(Error(`Consumer script ${script} host exited before reporting completion (exit code ${code})`))});const deadline=setTimeout(stop,timeoutMs);options.signal?.addEventListener("abort",stop,{once:!0});if(options.signal?.aborted)stop();const output=createHash("sha256");async function drain(pipe){try{while(!0){const next=await pipe.reader.read();if(next.done)break;output.update(next.value);options.onOutput?.(next.value.slice(),pipe.source)}}finally{pipe.done=!0;pipe.reader.releaseLock()}}let exitCode;try{draining=Promise.all(pipes.map(drain)).then(()=>{});draining.catch(reportFailure);const completion=reported.then((exitCode)=>({kind:"completed",exitCode})),result=await Promise.race([completion,stopped.then(()=>({kind:"interrupted"}))]);if(result.kind==="interrupted"||interrupted){await teardown;fail(`Consumer script ${script} was interrupted or exceeded its execution budget`)}exitCode=result.exitCode;await dispose(!0)}catch(error){const wasInterrupted=interrupted;stop();try{await teardown}catch(cleanupError){if(wasInterrupted)fail(`Consumer script ${script} was interrupted or exceeded its execution budget; ${cleanupError instanceof Error?cleanupError.message:String(cleanupError)}`);throw cleanupError}throw error}finally{clearTimeout(deadline);options.signal?.removeEventListener("abort",stop)}if(interrupted||options.signal?.aborted)fail(`Consumer script ${script} was interrupted or exceeded its execution budget`);if(exitCode!==0)fail(`Consumer script ${script} failed with exit code ${exitCode}`);if(readFileSync(manifestPath,"utf8")!==originalManifest)fail(`Consumer script ${script} changed the project manifest`);receipt.checks.push({script,exitCode:0,outputSha256:output.digest("hex")})}return receipt}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/actions",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.279",
|
|
6
6
|
"description": "The Stacks actions.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -66,58 +66,58 @@
|
|
|
66
66
|
"prepublishOnly": "bun run build"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@stacksjs/action-runner": "0.75.
|
|
70
|
-
"@stacksjs/ai": "0.75.
|
|
71
|
-
"@stacksjs/analytics": "0.75.
|
|
72
|
-
"@stacksjs/api": "0.75.
|
|
73
|
-
"@stacksjs/auth": "0.75.
|
|
74
|
-
"@stacksjs/build": "0.75.
|
|
69
|
+
"@stacksjs/action-runner": "0.75.279",
|
|
70
|
+
"@stacksjs/ai": "0.75.279",
|
|
71
|
+
"@stacksjs/analytics": "0.75.279",
|
|
72
|
+
"@stacksjs/api": "0.75.279",
|
|
73
|
+
"@stacksjs/auth": "0.75.279",
|
|
74
|
+
"@stacksjs/build": "0.75.279",
|
|
75
75
|
"@stacksjs/bumpx": "^0.2.26",
|
|
76
76
|
"@stacksjs/bunpress": "^0.2.15",
|
|
77
|
-
"@stacksjs/cli": "0.75.
|
|
78
|
-
"@stacksjs/cloud": "0.75.
|
|
79
|
-
"@stacksjs/cms": "0.75.
|
|
80
|
-
"@stacksjs/components": "0.2.
|
|
81
|
-
"@stacksjs/config": "0.75.
|
|
82
|
-
"@stacksjs/database": "0.75.
|
|
83
|
-
"@stacksjs/desktop-build": "0.75.
|
|
84
|
-
"@stacksjs/dns": "0.75.
|
|
85
|
-
"@stacksjs/enums": "0.75.
|
|
86
|
-
"@stacksjs/env": "0.75.
|
|
87
|
-
"@stacksjs/error-handling": "0.75.
|
|
88
|
-
"@stacksjs/github": "0.75.
|
|
77
|
+
"@stacksjs/cli": "0.75.279",
|
|
78
|
+
"@stacksjs/cloud": "0.75.279",
|
|
79
|
+
"@stacksjs/cms": "0.75.279",
|
|
80
|
+
"@stacksjs/components": "0.2.671",
|
|
81
|
+
"@stacksjs/config": "0.75.279",
|
|
82
|
+
"@stacksjs/database": "0.75.279",
|
|
83
|
+
"@stacksjs/desktop-build": "0.75.279",
|
|
84
|
+
"@stacksjs/dns": "0.75.279",
|
|
85
|
+
"@stacksjs/enums": "0.75.279",
|
|
86
|
+
"@stacksjs/env": "0.75.279",
|
|
87
|
+
"@stacksjs/error-handling": "0.75.279",
|
|
88
|
+
"@stacksjs/github": "0.75.279",
|
|
89
89
|
"@stacksjs/gitit": "^0.2.5",
|
|
90
|
-
"@stacksjs/i18n": "0.75.
|
|
91
|
-
"@stacksjs/image": "0.75.
|
|
92
|
-
"@stacksjs/logging": "0.75.
|
|
90
|
+
"@stacksjs/i18n": "0.75.279",
|
|
91
|
+
"@stacksjs/image": "0.75.279",
|
|
92
|
+
"@stacksjs/logging": "0.75.279",
|
|
93
93
|
"@stacksjs/logsmith": "^0.2.11",
|
|
94
|
-
"@stacksjs/mobile": "0.75.
|
|
95
|
-
"@stacksjs/orm": "0.75.
|
|
96
|
-
"@stacksjs/path": "0.75.
|
|
97
|
-
"@stacksjs/payments": "0.75.
|
|
98
|
-
"@stacksjs/queue": "0.75.
|
|
99
|
-
"@stacksjs/registry": "0.75.
|
|
100
|
-
"@stacksjs/router": "0.75.
|
|
94
|
+
"@stacksjs/mobile": "0.75.279",
|
|
95
|
+
"@stacksjs/orm": "0.75.279",
|
|
96
|
+
"@stacksjs/path": "0.75.279",
|
|
97
|
+
"@stacksjs/payments": "0.75.279",
|
|
98
|
+
"@stacksjs/queue": "0.75.279",
|
|
99
|
+
"@stacksjs/registry": "0.75.279",
|
|
100
|
+
"@stacksjs/router": "0.75.279",
|
|
101
101
|
"@stacksjs/rpx": "^0.11.54",
|
|
102
|
-
"@stacksjs/scheduler": "0.75.
|
|
103
|
-
"@stacksjs/search-engine": "0.75.
|
|
104
|
-
"@stacksjs/security": "0.75.
|
|
105
|
-
"@stacksjs/server": "0.75.
|
|
106
|
-
"@stacksjs/sites": "0.75.
|
|
107
|
-
"@stacksjs/storage": "0.75.
|
|
108
|
-
"@stacksjs/strings": "0.75.
|
|
109
|
-
"@stacksjs/stx": "0.2.
|
|
102
|
+
"@stacksjs/scheduler": "0.75.279",
|
|
103
|
+
"@stacksjs/search-engine": "0.75.279",
|
|
104
|
+
"@stacksjs/security": "0.75.279",
|
|
105
|
+
"@stacksjs/server": "0.75.279",
|
|
106
|
+
"@stacksjs/sites": "0.75.279",
|
|
107
|
+
"@stacksjs/storage": "0.75.279",
|
|
108
|
+
"@stacksjs/strings": "0.75.279",
|
|
109
|
+
"@stacksjs/stx": "0.2.671",
|
|
110
110
|
"@stacksjs/tlsx": "^0.13.19",
|
|
111
111
|
"@stacksjs/ts-cloud": "^0.16.58",
|
|
112
112
|
"@stacksjs/ts-md": "^0.1.1",
|
|
113
|
-
"@stacksjs/tunnel": "0.75.
|
|
114
|
-
"@stacksjs/types": "0.75.
|
|
115
|
-
"@stacksjs/utils": "0.75.
|
|
116
|
-
"@stacksjs/whois": "0.75.
|
|
117
|
-
"bun-plugin-stx": "0.2.
|
|
113
|
+
"@stacksjs/tunnel": "0.75.279",
|
|
114
|
+
"@stacksjs/types": "0.75.279",
|
|
115
|
+
"@stacksjs/utils": "0.75.279",
|
|
116
|
+
"@stacksjs/whois": "0.75.279",
|
|
117
|
+
"bun-plugin-stx": "0.2.671"
|
|
118
118
|
},
|
|
119
119
|
"peerDependencies": {
|
|
120
|
-
"craft-native": ">=0.0.
|
|
120
|
+
"craft-native": ">=0.0.301",
|
|
121
121
|
"pickier": "^0.1.75"
|
|
122
122
|
},
|
|
123
123
|
"peerDependenciesMeta": {
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
131
|
"devDependencies": {
|
|
132
|
-
"@stacksjs/validation": "0.75.
|
|
132
|
+
"@stacksjs/validation": "0.75.279",
|
|
133
133
|
"better-dx": "^0.2.24"
|
|
134
134
|
}
|
|
135
135
|
}
|