@stacksjs/actions 0.75.274 → 0.75.276

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.
@@ -57,6 +57,8 @@ public static class StacksOwnedCheck {
57
57
  [DllImport("kernel32.dll", SetLastError = true)]
58
58
  static extern bool PeekNamedPipe(IntPtr pipe, IntPtr buffer, uint size, IntPtr read,
59
59
  out uint available, IntPtr remaining);
60
+ [DllImport("kernel32.dll", SetLastError = true)]
61
+ static extern bool ReadFile(IntPtr pipe, byte[] buffer, uint count, out uint read, IntPtr overlapped);
60
62
  [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
61
63
  static extern bool CreateProcessW(string application, StringBuilder command, IntPtr processSecurity,
62
64
  IntPtr threadSecurity, bool inherit, uint flags, IntPtr environment, string directory,
@@ -91,6 +93,27 @@ public static class StacksOwnedCheck {
91
93
  if (error == 109 || error == 232) return true; // Closed control pipe.
92
94
  throw new Win32Exception(error);
93
95
  }
96
+ static bool AwaitStart() {
97
+ // No consumer can exist until the live Bun host grants permission. It may
98
+ // safely kill a compiling or waiting helper when cancellation wins first.
99
+ Console.Out.WriteLine("STACKS_OWNED_CHECK_READY");
100
+ Console.Out.Flush();
101
+ while (true) {
102
+ uint available;
103
+ if (!PeekNamedPipe(GetStdHandle(-10), IntPtr.Zero, 0, IntPtr.Zero, out available, IntPtr.Zero)) {
104
+ int error = Marshal.GetLastWin32Error();
105
+ if (error == 109 || error == 232) return false;
106
+ throw new Win32Exception(error);
107
+ }
108
+ if (available != 0) {
109
+ byte[] command = new byte[1];
110
+ uint read;
111
+ Require(ReadFile(GetStdHandle(-10), command, 1, out read, IntPtr.Zero));
112
+ return read == 1 && command[0] == 1;
113
+ }
114
+ Thread.Sleep(10);
115
+ }
116
+ }
94
117
  static IntPtr Job() {
95
118
  IntPtr job = CreateJobObjectW(IntPtr.Zero, null);
96
119
  Require(job != IntPtr.Zero);
@@ -101,8 +124,8 @@ public static class StacksOwnedCheck {
101
124
  return job;
102
125
  }
103
126
  public static int Run(string bun, string script) {
104
- // A queued stop must prevent startup even when Add-Type took most of the
105
- // check's budget. The control pipe is never inherited as consumer stdin.
127
+ // The control pipe is never inherited as consumer stdin. Prepare both
128
+ // ownership jobs before reporting readiness or accepting startup permission.
106
129
  if (StopRequested()) return 1;
107
130
  // The outer job owns this host before it creates anything. If cancellation
108
131
  // races suspended-child assignment below, host death still kills that child.
@@ -113,6 +136,7 @@ public static class StacksOwnedCheck {
113
136
  IntPtr commandJob = Job();
114
137
  ProcessInfo child = new ProcessInfo();
115
138
  try {
139
+ if (!AwaitStart() || StopRequested()) return 1;
116
140
  Startup startup = new Startup();
117
141
  startup.Size = (uint)Marshal.SizeOf(typeof(Startup));
118
142
  startup.Flags = 0x100; // STARTF_USESTDHANDLES
@@ -175,6 +199,7 @@ exit [StacksOwnedCheck]::Run('${bun.replaceAll("'","''")}', '${script}')
175
199
  setInterval(() => {}, 60_000)
176
200
  let command
177
201
  let stopRequested = false
202
+ let consumerPermitted = false
178
203
  let start
179
204
  const permitted = new Promise(resolve => { start = resolve })
180
205
  process.on('message', async (message) => {
@@ -183,8 +208,16 @@ process.on('message', async (message) => {
183
208
  start()
184
209
  if (command) {
185
210
  try {
186
- command.stdin.write(new Uint8Array([1]))
187
- await command.stdin.flush()
211
+ if (!consumerPermitted) {
212
+ // Preparation may itself spawn the C# compiler. Before permission,
213
+ // the entire helper tree is disposable without interrupting user code.
214
+ const killed = Bun.spawnSync([${JSON.stringify(windowsTaskkill)}, '/PID', String(command.pid), '/T', '/F'], { stdout: 'ignore', stderr: 'ignore', timeout: 2000 })
215
+ if (killed.exitCode !== 0 && command.exitCode === null) throw new Error('Native preparation cleanup failed')
216
+ }
217
+ else {
218
+ command.stdin.write(new Uint8Array([2]))
219
+ await command.stdin.flush()
220
+ }
188
221
  } catch (error) { process.send({ error: String(error) }) }
189
222
  }
190
223
  } else if (message?.start) start()
@@ -194,10 +227,48 @@ await permitted
194
227
  if (stopRequested) process.send({ exitCode: 1 })
195
228
  else {
196
229
  try {
197
- command = Bun.spawn(${JSON.stringify(command)}, { stdin: 'pipe', stdout: 'inherit', stderr: 'inherit' })
230
+ command = Bun.spawn(${JSON.stringify(command)}, { stdin: 'pipe', stdout: 'pipe', stderr: 'inherit' })
198
231
  process.send({ helperPid: command.pid })
199
- process.send({ exitCode: await command.exited })
200
- } catch (error) { process.send({ error: String(error) }) }
232
+ const forward = async () => {
233
+ const reader = command.stdout.getReader()
234
+ let prefix = Buffer.alloc(0)
235
+ let ready = false
236
+ try {
237
+ while (true) {
238
+ const next = await reader.read()
239
+ if (next.done) break
240
+ if (ready) { await Bun.write(Bun.stdout, next.value); continue }
241
+ prefix = Buffer.concat([prefix, Buffer.from(next.value)])
242
+ const line = prefix.indexOf(10)
243
+ if (line < 0) {
244
+ if (prefix.length > 128) throw new Error('Native ownership readiness was malformed')
245
+ continue
246
+ }
247
+ if (prefix.subarray(0, line).toString().trimEnd() !== 'STACKS_OWNED_CHECK_READY')
248
+ throw new Error('Native ownership readiness was malformed')
249
+ ready = true
250
+ if (!stopRequested) {
251
+ // This assignment and write share one event-loop turn. A later
252
+ // stop therefore uses cooperative Job Object cleanup, never the
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
+ }
258
+ if (prefix.length > line + 1) await Bun.write(Bun.stdout, prefix.subarray(line + 1))
259
+ prefix = Buffer.alloc(0)
260
+ }
261
+ if (!ready && !stopRequested) throw new Error('Native ownership readiness was not received')
262
+ } finally { reader.releaseLock() }
263
+ }
264
+ const [code] = await Promise.all([command.exited, forward()])
265
+ process.send({ exitCode: code })
266
+ } catch (error) {
267
+ if (command && !consumerPermitted) {
268
+ try { command.kill('SIGKILL'); await command.exited } catch {}
269
+ }
270
+ process.send({ error: String(error) })
271
+ }
201
272
  }
202
273
  `:`
203
274
  setInterval(() => {}, 60_000)
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.274",
5
+ "version": "0.75.276",
6
6
  "description": "The Stacks actions.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -66,55 +66,55 @@
66
66
  "prepublishOnly": "bun run build"
67
67
  },
68
68
  "dependencies": {
69
- "@stacksjs/action-runner": "0.75.274",
70
- "@stacksjs/ai": "0.75.274",
71
- "@stacksjs/analytics": "0.75.274",
72
- "@stacksjs/api": "0.75.274",
73
- "@stacksjs/auth": "0.75.274",
74
- "@stacksjs/build": "0.75.274",
69
+ "@stacksjs/action-runner": "0.75.276",
70
+ "@stacksjs/ai": "0.75.276",
71
+ "@stacksjs/analytics": "0.75.276",
72
+ "@stacksjs/api": "0.75.276",
73
+ "@stacksjs/auth": "0.75.276",
74
+ "@stacksjs/build": "0.75.276",
75
75
  "@stacksjs/bumpx": "^0.2.26",
76
76
  "@stacksjs/bunpress": "^0.2.15",
77
- "@stacksjs/cli": "0.75.274",
78
- "@stacksjs/cloud": "0.75.274",
79
- "@stacksjs/cms": "0.75.274",
80
- "@stacksjs/components": "0.2.669",
81
- "@stacksjs/config": "0.75.274",
82
- "@stacksjs/database": "0.75.274",
83
- "@stacksjs/desktop-build": "0.75.274",
84
- "@stacksjs/dns": "0.75.274",
85
- "@stacksjs/enums": "0.75.274",
86
- "@stacksjs/env": "0.75.274",
87
- "@stacksjs/error-handling": "0.75.274",
88
- "@stacksjs/github": "0.75.274",
77
+ "@stacksjs/cli": "0.75.276",
78
+ "@stacksjs/cloud": "0.75.276",
79
+ "@stacksjs/cms": "0.75.276",
80
+ "@stacksjs/components": "0.2.670",
81
+ "@stacksjs/config": "0.75.276",
82
+ "@stacksjs/database": "0.75.276",
83
+ "@stacksjs/desktop-build": "0.75.276",
84
+ "@stacksjs/dns": "0.75.276",
85
+ "@stacksjs/enums": "0.75.276",
86
+ "@stacksjs/env": "0.75.276",
87
+ "@stacksjs/error-handling": "0.75.276",
88
+ "@stacksjs/github": "0.75.276",
89
89
  "@stacksjs/gitit": "^0.2.5",
90
- "@stacksjs/i18n": "0.75.274",
91
- "@stacksjs/image": "0.75.274",
92
- "@stacksjs/logging": "0.75.274",
90
+ "@stacksjs/i18n": "0.75.276",
91
+ "@stacksjs/image": "0.75.276",
92
+ "@stacksjs/logging": "0.75.276",
93
93
  "@stacksjs/logsmith": "^0.2.11",
94
- "@stacksjs/mobile": "0.75.274",
95
- "@stacksjs/orm": "0.75.274",
96
- "@stacksjs/path": "0.75.274",
97
- "@stacksjs/payments": "0.75.274",
98
- "@stacksjs/queue": "0.75.274",
99
- "@stacksjs/registry": "0.75.274",
100
- "@stacksjs/router": "0.75.274",
94
+ "@stacksjs/mobile": "0.75.276",
95
+ "@stacksjs/orm": "0.75.276",
96
+ "@stacksjs/path": "0.75.276",
97
+ "@stacksjs/payments": "0.75.276",
98
+ "@stacksjs/queue": "0.75.276",
99
+ "@stacksjs/registry": "0.75.276",
100
+ "@stacksjs/router": "0.75.276",
101
101
  "@stacksjs/rpx": "^0.11.54",
102
- "@stacksjs/scheduler": "0.75.274",
103
- "@stacksjs/search-engine": "0.75.274",
104
- "@stacksjs/security": "0.75.274",
105
- "@stacksjs/server": "0.75.274",
106
- "@stacksjs/sites": "0.75.274",
107
- "@stacksjs/storage": "0.75.274",
108
- "@stacksjs/strings": "0.75.274",
109
- "@stacksjs/stx": "0.2.669",
102
+ "@stacksjs/scheduler": "0.75.276",
103
+ "@stacksjs/search-engine": "0.75.276",
104
+ "@stacksjs/security": "0.75.276",
105
+ "@stacksjs/server": "0.75.276",
106
+ "@stacksjs/sites": "0.75.276",
107
+ "@stacksjs/storage": "0.75.276",
108
+ "@stacksjs/strings": "0.75.276",
109
+ "@stacksjs/stx": "0.2.670",
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.274",
114
- "@stacksjs/types": "0.75.274",
115
- "@stacksjs/utils": "0.75.274",
116
- "@stacksjs/whois": "0.75.274",
117
- "bun-plugin-stx": "0.2.669"
113
+ "@stacksjs/tunnel": "0.75.276",
114
+ "@stacksjs/types": "0.75.276",
115
+ "@stacksjs/utils": "0.75.276",
116
+ "@stacksjs/whois": "0.75.276",
117
+ "bun-plugin-stx": "0.2.670"
118
118
  },
119
119
  "peerDependencies": {
120
120
  "craft-native": ">=0.0.296",
@@ -129,7 +129,7 @@
129
129
  }
130
130
  },
131
131
  "devDependencies": {
132
- "@stacksjs/validation": "0.75.274",
132
+ "@stacksjs/validation": "0.75.276",
133
133
  "better-dx": "^0.2.24"
134
134
  }
135
135
  }