@stacksjs/actions 0.75.279 → 0.75.283

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.
@@ -11,6 +11,7 @@ export declare function resolvePantryToolchain(options: PantryToolchainOptions):
11
11
  export declare interface PantryToolchainOptions {
12
12
  cwd: string
13
13
  env?: Record<string, string | undefined>
14
+ allowOverride?: boolean
14
15
  probe?: (bin: string) => { exitCode: number, stdout: string }
15
16
  }
16
17
  export type PantryToolchain = | { ok: true, bin: string, version: string, declared?: string, declaredIn?: string }
@@ -1 +1 @@
1
- import{existsSync,readFileSync}from"node:fs";import{join}from"node:path";export function parsePantryVersion(output){return/\bpantry\s+v?(\d+\.\d+\.\d+(?:-[\w.]+)?)/i.exec(output)?.[1]}export function declaredPantryRange(cwd){for(const file of["storage/framework/core/buddy/package.json","package.json"]){const path=join(cwd,file);if(!existsSync(path))continue;try{const manifest=JSON.parse(readFileSync(path,"utf8")),range=manifest.dependencies?.["ts-pantry"]??manifest.devDependencies?.["ts-pantry"];if(range)return{range,file}}catch{}}return}function probeVersion(bin){try{const result=Bun.spawnSync([bin,"--version"],{stdout:"pipe",stderr:"pipe"});return{exitCode:result.exitCode??1,stdout:`${result.stdout.toString()}${result.stderr.toString()}`}}catch{return{exitCode:127,stdout:""}}}export function resolvePantryToolchain(options){const bin=(options.env??process.env).PANTRY_BIN?.trim()||"pantry",result=(options.probe??probeVersion)(bin),version=result.exitCode===0?parsePantryVersion(result.stdout):void 0;if(!version)return{ok:!1,bin,message:`Release aborted: could not run \`${bin} --version\`, so the Pantry that would regenerate pantry.lock is unknown. Install the declared Pantry, or set PANTRY_BIN to it, then re-run.`};const declared=declaredPantryRange(options.cwd);if(declared&&!Bun.semver.satisfies(version,declared.range))return{ok:!1,bin,message:`Release aborted before any file changed: \`${bin}\` is Pantry ${version}, but ${declared.file} declares ts-pantry ${declared.range}. A different Pantry writes a different pantry.lock than the one CI validates. Set PANTRY_BIN to a Pantry that satisfies the declared range (or put one first on PATH), then re-run.`};return{ok:!0,bin,version,declared:declared?.range,declaredIn:declared?.file}}
1
+ import{existsSync,readFileSync}from"node:fs";import{join}from"node:path";export function parsePantryVersion(output){return/\bpantry\s+v?(\d+\.\d+\.\d+(?:-[\w.]+)?)/i.exec(output)?.[1]}export function declaredPantryRange(cwd){for(const file of["storage/framework/core/buddy/package.json","package.json"]){const path=join(cwd,file);if(!existsSync(path))continue;try{const manifest=JSON.parse(readFileSync(path,"utf8")),range=manifest.dependencies?.["ts-pantry"]??manifest.devDependencies?.["ts-pantry"];if(range)return{range,file}}catch{}}return}function probeVersion(bin){try{const result=Bun.spawnSync([bin,"--version"],{stdout:"pipe",stderr:"pipe"});return{exitCode:result.exitCode??1,stdout:`${result.stdout.toString()}${result.stderr.toString()}`}}catch{return{exitCode:127,stdout:""}}}export function resolvePantryToolchain(options){const env=options.env??process.env,allowOverride=options.allowOverride!==!1,bin=allowOverride&&env.PANTRY_BIN?.trim()||"pantry",result=(options.probe??probeVersion)(bin),version=result.exitCode===0?parsePantryVersion(result.stdout):void 0;if(!version)return{ok:!1,bin,message:`Release aborted: could not run \`${bin} --version\`, so the Pantry that would regenerate pantry.lock is unknown. `+(allowOverride?"Install the declared Pantry, or set PANTRY_BIN to it, then re-run.":"Install the declared Pantry and put it first on PATH, then re-run.")};const declared=declaredPantryRange(options.cwd);if(declared&&!Bun.semver.satisfies(version,declared.range))return{ok:!1,bin,message:`Release aborted before any file changed: \`${bin}\` is Pantry ${version}, but ${declared.file} declares ts-pantry ${declared.range}. A different Pantry writes a different pantry.lock than the one CI validates. `+(allowOverride?"Set PANTRY_BIN to a Pantry that satisfies the declared range (or put one first on PATH), then re-run.":"Install a Pantry that satisfies the declared range and put it first on PATH, then re-run.")};return{ok:!0,bin,version,declared:declared?.range,declaredIn:declared?.file}}
@@ -1,5 +1,3 @@
1
- /** Internal Bun owner source; exported for real IPC completion-race tests. */
2
- export declare function windowsCheckHost(command: string[], windowsTaskkill: string): string;
3
1
  /** Execute declared project checks with the installed Bun and owned cleanup. */
4
2
  export declare function runProjectScripts(options: ProjectScriptOptions): Promise<ProjectScriptReceipt>;
5
3
  export declare interface ProjectScriptOptions {
@@ -1,286 +1,147 @@
1
- import{createHash}from"node:crypto";import{readFileSync}from"node:fs";import{join}from"node:path";function fail(message){throw Error(message)}function windowsCheckCommand(powershell,bun,script){const source=`
2
- $ErrorActionPreference = 'Stop'
3
- $ProgressPreference = 'SilentlyContinue'
4
- Add-Type -TypeDefinition @'
5
- using System;
6
- using System.ComponentModel;
7
- using System.Runtime.InteropServices;
8
- using System.Text;
9
- using System.Threading;
10
- public static class StacksOwnedCheck {
11
- [StructLayout(LayoutKind.Sequential)] struct BasicLimits {
12
- public long ProcessTime, JobTime;
13
- public uint Flags;
14
- public UIntPtr MinimumWorkingSet, MaximumWorkingSet;
15
- public uint ActiveProcessLimit;
16
- public UIntPtr Affinity;
17
- public uint Priority, Scheduling;
18
- }
19
- [StructLayout(LayoutKind.Sequential)] struct ExtendedLimits {
20
- public BasicLimits Basic;
21
- public ulong ReadOperations, WriteOperations, OtherOperations, ReadBytes, WriteBytes, OtherBytes;
22
- public UIntPtr ProcessMemory, JobMemory, PeakProcessMemory, PeakJobMemory;
23
- }
24
- [StructLayout(LayoutKind.Sequential)] struct Accounting {
25
- public long UserTime, KernelTime, PeriodUserTime, PeriodKernelTime;
26
- public uint PageFaults, TotalProcesses, ActiveProcesses, TerminatedProcesses;
27
- }
28
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct Startup {
29
- public uint Size;
30
- public IntPtr Reserved, Desktop, Title;
31
- public uint X, Y, XSize, YSize, XChars, YChars, Fill, Flags;
32
- public ushort Show, ReservedSize;
33
- public IntPtr ReservedBytes, Input, Output, Error;
34
- }
35
- [StructLayout(LayoutKind.Sequential)] struct ProcessInfo {
36
- public IntPtr Process, Thread;
37
- public uint ProcessId, ThreadId;
38
- }
39
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
40
- static extern IntPtr CreateJobObjectW(IntPtr attributes, string name);
41
- [DllImport("kernel32.dll", SetLastError = true)]
42
- static extern bool SetInformationJobObject(IntPtr job, int kind, ref ExtendedLimits limits, uint size);
43
- [DllImport("kernel32.dll", SetLastError = true)]
44
- static extern bool QueryInformationJobObject(IntPtr job, int kind, out Accounting info, uint size, IntPtr returned);
45
- [DllImport("kernel32.dll", SetLastError = true)]
46
- static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
47
- [DllImport("kernel32.dll", SetLastError = true)]
48
- static extern bool TerminateJobObject(IntPtr job, uint code);
49
- [DllImport("kernel32.dll", SetLastError = true)]
50
- static extern bool DuplicateHandle(IntPtr sourceProcess, IntPtr source, IntPtr targetProcess,
51
- out IntPtr target, uint access, bool inherit, uint options);
52
- [DllImport("kernel32.dll")] static extern IntPtr GetCurrentProcess();
53
- [DllImport("kernel32.dll")] static extern IntPtr GetStdHandle(int kind);
54
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
55
- static extern IntPtr CreateFileW(string name, uint access, uint share, IntPtr security,
56
- uint disposition, uint flags, IntPtr template);
57
- [DllImport("kernel32.dll", SetLastError = true)]
58
- static extern bool PeekNamedPipe(IntPtr pipe, IntPtr buffer, uint size, IntPtr read,
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);
62
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
63
- static extern bool CreateProcessW(string application, StringBuilder command, IntPtr processSecurity,
64
- IntPtr threadSecurity, bool inherit, uint flags, IntPtr environment, string directory,
65
- ref Startup startup, out ProcessInfo process);
66
- [DllImport("kernel32.dll", SetLastError = true)] static extern uint ResumeThread(IntPtr thread);
67
- [DllImport("kernel32.dll", SetLastError = true)] static extern uint WaitForSingleObject(IntPtr handle, uint timeout);
68
- [DllImport("kernel32.dll", SetLastError = true)] static extern bool GetExitCodeProcess(IntPtr process, out uint code);
69
- [DllImport("kernel32.dll", SetLastError = true)] static extern bool TerminateProcess(IntPtr process, uint code);
70
- [DllImport("kernel32.dll", SetLastError = true)] static extern bool CloseHandle(IntPtr handle);
71
- static void Require(bool success) { if (!success) throw new Win32Exception(Marshal.GetLastWin32Error()); }
72
- static IntPtr InheritableStandardHandle(int kind) {
73
- IntPtr source = GetStdHandle(kind);
74
- if (source == IntPtr.Zero || source == new IntPtr(-1)) return IntPtr.Zero;
75
- IntPtr inherited;
76
- Require(DuplicateHandle(GetCurrentProcess(), source, GetCurrentProcess(), out inherited, 0, true, 2));
77
- return inherited;
1
+ import{createHash}from"node:crypto";import{mkdtempSync,readFileSync,rmSync,writeFileSync}from"node:fs";import{tmpdir}from"node:os";import{join}from"node:path";function fail(message){throw Error(message)}function windowsCheckHost(bun,script){return`
2
+ import { dlopen, FFIType as f, ptr } from 'bun:ffi'
3
+ setInterval(() => {}, 60_000)
4
+ let stopRequested = false
5
+ let permit
6
+ const permitted = new Promise(resolve => { permit = resolve })
7
+ process.on('message', message => {
8
+ if (message?.stop) { stopRequested = true; permit() }
9
+ else if (message?.start) permit()
10
+ })
11
+ let api
12
+ let lifetime = 0n
13
+ let job = 0n
14
+ let childProcess = 0n
15
+ let childThread = 0n
16
+ let failure = ''
17
+ let exitCode = 1
18
+ const wide = value => Uint16Array.from({ length: value.length + 1 }, (_, i) => i === value.length ? 0 : value.charCodeAt(i))
19
+ const must = (condition, operation) => {
20
+ if (!condition) throw new Error('Native Windows ' + operation + ' failed (Win32 ' + api.GetLastError() + ')')
21
+ }
22
+ const makeJob = () => {
23
+ const handle = api.CreateJobObjectW(null, null)
24
+ must(handle !== 0n, 'job creation')
25
+ const limits = new BigUint64Array(18) // JOBOBJECT_EXTENDED_LIMIT_INFORMATION, Win64.
26
+ new DataView(limits.buffer).setUint32(16, 0x2000, true) // KILL_ON_JOB_CLOSE.
27
+ if (!api.SetInformationJobObject(handle, 9, ptr(limits), limits.byteLength)) {
28
+ api.CloseHandle(handle)
29
+ must(false, 'job limits')
78
30
  }
79
- static IntPtr ConsumerInput() {
80
- IntPtr source = CreateFileW("NUL", 0x80000000, 3, IntPtr.Zero, 3, 0, IntPtr.Zero);
81
- Require(source != new IntPtr(-1));
31
+ return handle
32
+ }
33
+ const inherited = source => {
34
+ const result = new BigUint64Array(1)
35
+ const current = api.GetCurrentProcess()
36
+ must(api.DuplicateHandle(current, source, current, ptr(result), 0, 1, 2), 'standard handle inheritance')
37
+ return result[0]
38
+ }
39
+ try {
40
+ api = dlopen('kernel32.dll', {
41
+ GetLastError: { args: [], returns: f.u32 },
42
+ GetCurrentProcess: { args: [], returns: f.u64 },
43
+ GetStdHandle: { args: [f.u32], returns: f.u64 },
44
+ CreateJobObjectW: { args: [f.ptr, f.ptr], returns: f.u64 },
45
+ SetInformationJobObject: { args: [f.u64, f.i32, f.ptr, f.u32], returns: f.i32 },
46
+ QueryInformationJobObject: { args: [f.u64, f.i32, f.ptr, f.u32, f.ptr], returns: f.i32 },
47
+ AssignProcessToJobObject: { args: [f.u64, f.u64], returns: f.i32 },
48
+ TerminateJobObject: { args: [f.u64, f.u32], returns: f.i32 },
49
+ DuplicateHandle: { args: [f.u64, f.u64, f.u64, f.ptr, f.u32, f.i32, f.u32], returns: f.i32 },
50
+ CreateFileW: { args: [f.ptr, f.u32, f.u32, f.ptr, f.u32, f.u32, f.u64], returns: f.u64 },
51
+ CreateProcessW: { args: [f.ptr, f.ptr, f.ptr, f.ptr, f.i32, f.u32, f.ptr, f.ptr, f.ptr, f.ptr], returns: f.i32 },
52
+ ResumeThread: { args: [f.u64], returns: f.u32 },
53
+ WaitForSingleObject: { args: [f.u64, f.u32], returns: f.u32 },
54
+ GetExitCodeProcess: { args: [f.u64, f.ptr], returns: f.i32 },
55
+ TerminateProcess: { args: [f.u64, f.u32], returns: f.i32 },
56
+ CloseHandle: { args: [f.u64], returns: f.i32 },
57
+ }).symbols
58
+ // Own the host before it can create a consumer. Parent death closes this
59
+ // non-inheritable lifetime handle and terminates all remaining descendants.
60
+ lifetime = makeJob()
61
+ must(api.AssignProcessToJobObject(lifetime, api.GetCurrentProcess()), 'host ownership')
62
+ job = makeJob()
63
+ process.send({ ready: true })
64
+ await permitted
65
+ if (!stopRequested) {
66
+ const startup = new BigUint64Array(13) // STARTUPINFOW, Win64: 104 bytes.
67
+ const fields = new DataView(startup.buffer)
68
+ fields.setUint32(0, startup.byteLength, true)
69
+ fields.setUint32(60, 0x100, true) // STARTF_USESTDHANDLES.
70
+ const inputName = wide('NUL')
71
+ const sourceInput = api.CreateFileW(ptr(inputName), 0x80000000, 3, null, 3, 0, 0n)
72
+ must(sourceInput !== 0xffffffffffffffffn, 'consumer stdin')
73
+ const handles = []
82
74
  try {
83
- IntPtr inherited;
84
- Require(DuplicateHandle(GetCurrentProcess(), source, GetCurrentProcess(), out inherited, 0, true, 2));
85
- return inherited;
86
- } finally { CloseHandle(source); }
87
- }
88
- static bool StopRequested() {
89
- uint available;
90
- if (PeekNamedPipe(GetStdHandle(-10), IntPtr.Zero, 0, IntPtr.Zero, out available, IntPtr.Zero))
91
- return available != 0;
92
- int error = Marshal.GetLastWin32Error();
93
- if (error == 109 || error == 232) return true; // Closed control pipe.
94
- throw new Win32Exception(error);
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;
75
+ handles.push(inherited(sourceInput))
76
+ handles.push(inherited(api.GetStdHandle(0xfffffff5))) // STD_OUTPUT_HANDLE.
77
+ handles.push(inherited(api.GetStdHandle(0xfffffff4))) // STD_ERROR_HANDLE.
78
+ for (let i = 0; i < handles.length; i++) fields.setBigUint64(80 + i * 8, handles[i], true)
79
+ const processInfo = new BigUint64Array(3) // PROCESS_INFORMATION, Win64.
80
+ const executable = wide(${JSON.stringify(bun)})
81
+ const commandLine = wide(${JSON.stringify(`"${bun}" run ${script}`)})
82
+ const created = api.CreateProcessW(ptr(executable), ptr(commandLine), null, null, 1, 0x08000004, null, null, ptr(startup), ptr(processInfo))
83
+ childProcess = processInfo[0]
84
+ childThread = processInfo[1]
85
+ must(created, 'suspended consumer creation')
86
+ }
87
+ finally {
88
+ api.CloseHandle(sourceInput)
89
+ for (const handle of handles) api.CloseHandle(handle)
90
+ }
91
+ // No user instruction runs until both native ownership jobs contain it.
92
+ must(api.AssignProcessToJobObject(job, childProcess), 'consumer ownership')
93
+ must(api.ResumeThread(childThread) !== 0xffffffff, 'consumer startup')
94
+ while (!stopRequested) {
95
+ const state = api.WaitForSingleObject(childProcess, 0)
96
+ if (state === 0) {
97
+ const code = new Uint32Array(1)
98
+ must(api.GetExitCodeProcess(childProcess, ptr(code)), 'consumer exit status')
99
+ exitCode = code[0] | 0
100
+ break
113
101
  }
114
- Thread.Sleep(10);
102
+ must(state === 258, 'consumer wait') // WAIT_TIMEOUT.
103
+ await Bun.sleep(10)
115
104
  }
116
105
  }
117
- static IntPtr Job() {
118
- IntPtr job = CreateJobObjectW(IntPtr.Zero, null);
119
- Require(job != IntPtr.Zero);
120
- ExtendedLimits limits = new ExtendedLimits();
121
- limits.Basic.Flags = 0x2000; // JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
122
- try { Require(SetInformationJobObject(job, 9, ref limits, (uint)Marshal.SizeOf(typeof(ExtendedLimits)))); }
123
- catch { CloseHandle(job); throw; }
124
- return job;
125
- }
126
- public static int Run(string bun, string script) {
127
- // The control pipe is never inherited as consumer stdin. Prepare both
128
- // ownership jobs before reporting readiness or accepting startup permission.
129
- if (StopRequested()) return 1;
130
- // The outer job owns this host before it creates anything. If cancellation
131
- // races suspended-child assignment below, host death still kills that child.
132
- // Retain this non-inheritable handle until process exit; closing it earlier
133
- // would terminate this host before it can return the command's exit code.
134
- IntPtr lifetime = Job();
135
- Require(AssignProcessToJobObject(lifetime, GetCurrentProcess()));
136
- IntPtr commandJob = Job();
137
- ProcessInfo child = new ProcessInfo();
106
+ }
107
+ catch (error) { failure = String(error) }
108
+ try {
109
+ if (api && job !== 0n) {
138
110
  try {
139
- if (!AwaitStart() || StopRequested()) return 1;
140
- Startup startup = new Startup();
141
- startup.Size = (uint)Marshal.SizeOf(typeof(Startup));
142
- startup.Flags = 0x100; // STARTF_USESTDHANDLES
143
- // Bun can supply non-inheritable standard handles to PowerShell. A
144
- // native CreateProcess child needs explicitly inheritable duplicates.
145
- // Never execute user code before it belongs to the inner cleanup job.
146
- try {
147
- startup.Input = ConsumerInput();
148
- startup.Output = InheritableStandardHandle(-11);
149
- startup.Error = InheritableStandardHandle(-12);
150
- Require(CreateProcessW(bun, new StringBuilder("\\"" + bun + "\\" run " + script),
151
- IntPtr.Zero, IntPtr.Zero, true, 0x08000004, IntPtr.Zero, null, ref startup, out child));
152
- } finally {
153
- foreach (IntPtr handle in new[] { startup.Input, startup.Output, startup.Error })
154
- if (handle != IntPtr.Zero) CloseHandle(handle);
155
- }
156
- Require(AssignProcessToJobObject(commandJob, child.Process));
157
- bool cancelled = StopRequested();
158
- if (!cancelled) Require(ResumeThread(child.Thread) != 0xffffffff);
159
- while (!cancelled) {
160
- uint state = WaitForSingleObject(child.Process, 10);
161
- if (state == 0) break;
162
- Require(state == 258); // WAIT_TIMEOUT, otherwise retain the native error.
163
- cancelled = StopRequested();
111
+ if (childProcess !== 0n) {
112
+ const state = api.WaitForSingleObject(childProcess, 0)
113
+ must(state === 0 || state === 258, 'final consumer wait')
114
+ if (state === 258) must(api.TerminateProcess(childProcess, 1), 'suspended consumer disposal')
164
115
  }
165
- uint code = 1;
166
- if (!cancelled) Require(GetExitCodeProcess(child.Process, out code));
167
- return unchecked((int)code);
168
- } finally {
169
- try {
170
- // Also dispose a suspended child if assigning its inner job failed.
171
- if (child.Process != IntPtr.Zero) {
172
- uint state = WaitForSingleObject(child.Process, 0);
173
- Require(state == 0 || state == 258);
174
- if (state == 258) Require(TerminateProcess(child.Process, 1));
175
- }
176
- // Membership survives an exited intermediate process, unlike a PID
177
- // tree. Query it on every exit path before closing the owning handle.
178
- // https://learn.microsoft.com/windows/win32/procthread/job-objects
179
- Require(TerminateJobObject(commandJob, 1));
180
- var cleanup = System.Diagnostics.Stopwatch.StartNew();
181
- while (true) {
182
- Accounting info;
183
- Require(QueryInformationJobObject(commandJob, 1, out info, (uint)Marshal.SizeOf(typeof(Accounting)), IntPtr.Zero));
184
- if (info.ActiveProcesses == 0 && (child.Process == IntPtr.Zero || WaitForSingleObject(child.Process, 0) == 0)) break;
185
- if (cleanup.ElapsedMilliseconds >= 1000) throw new TimeoutException("Owned Windows processes did not finish cleanup");
186
- Thread.Sleep(10);
187
- }
188
- } finally {
189
- if (child.Thread != IntPtr.Zero) CloseHandle(child.Thread);
190
- if (child.Process != IntPtr.Zero) CloseHandle(child.Process);
191
- Require(CloseHandle(commandJob));
116
+ must(api.TerminateJobObject(job, 1), 'owned process termination')
117
+ const accounting = new BigUint64Array(6)
118
+ const fields = new DataView(accounting.buffer)
119
+ const deadline = performance.now() + 1000
120
+ while (true) {
121
+ must(api.QueryInformationJobObject(job, 1, ptr(accounting), accounting.byteLength, null), 'owned process accounting')
122
+ if (fields.getUint32(40, true) === 0 && (childProcess === 0n || api.WaitForSingleObject(childProcess, 0) === 0)) break
123
+ if (performance.now() >= deadline) throw new Error('Owned Windows processes did not finish cleanup')
124
+ await Bun.sleep(10)
192
125
  }
193
126
  }
194
- }
195
- }
196
- '@
197
- exit [StacksOwnedCheck]::Run('${bun.replaceAll("'","''")}', '${script}')
198
- `;return[powershell,"-NoLogo","-NoProfile","-NonInteractive","-OutputFormat","Text","-EncodedCommand",Buffer.from(source,"utf16le").toString("base64")]}export function windowsCheckHost(command,windowsTaskkill){return`
199
- setInterval(() => {}, 60_000)
200
- let command
201
- let stopRequested = false
202
- let consumerPermitted = false
203
- let start
204
- const permitted = new Promise(resolve => { start = resolve })
205
- process.on('message', async (message) => {
206
- if (message?.stop) {
207
- stopRequested = true
208
- start()
209
- if (command) {
210
- try {
211
- if (!consumerPermitted) {
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) })
221
- }
222
- else {
223
- command.stdin.write(new Uint8Array([2]))
224
- await command.stdin.flush()
225
- }
226
- } catch (error) { process.send({ error: String(error) }) }
127
+ finally {
128
+ if (childThread !== 0n) api.CloseHandle(childThread)
129
+ if (childProcess !== 0n) api.CloseHandle(childProcess)
130
+ must(api.CloseHandle(job), 'owned job handle disposal')
227
131
  }
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
132
  }
234
- })
235
- process.send({ ready: true })
236
- await permitted
237
- if (stopRequested) process.send({ exitCode: 1 })
238
- else {
133
+ }
134
+ catch (error) { failure += (failure ? '; ' : '') + String(error) }
135
+ // Keep the lifetime job open until the parent disposes this owned Bun process.
136
+ // Closing it here would kill this host before its completion report is sent.
137
+ if (failure) process.send({ error: failure })
138
+ else process.send({ exitCode })
139
+ `}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;const hostDirectory=mkdtempSync(join(tmpdir(),"stacks-project-host-"));try{const hostConfig=join(hostDirectory,"bunfig.toml");writeFileSync(hostConfig,`preload = []
140
+ `);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 windows=process.platform==="win32",command=[bun,"run",script];let reportExit=()=>{},reportFailure=()=>{};const reported=new Promise((resolve,reject)=>{reportExit=resolve;reportFailure=reject});let interrupted=!1;const host=windows?windowsCheckHost(bun,script):`setInterval(() => {}, 60_000)
239
141
  try {
240
- command = Bun.spawn(${JSON.stringify(command)}, { stdin: 'pipe', stdout: 'pipe', stderr: 'inherit' })
241
- process.send({ helperPid: command.pid })
242
- const forward = async () => {
243
- const reader = command.stdout.getReader()
244
- let prefix = Buffer.alloc(0)
245
- let ready = false
246
- try {
247
- while (true) {
248
- const next = await reader.read()
249
- if (next.done) break
250
- if (ready) { await Bun.write(Bun.stdout, next.value); continue }
251
- prefix = Buffer.concat([prefix, Buffer.from(next.value)])
252
- const line = prefix.indexOf(10)
253
- if (line < 0) {
254
- if (prefix.length > 128) throw new Error('Native ownership readiness was malformed')
255
- continue
256
- }
257
- if (prefix.subarray(0, line).toString().trimEnd() !== 'STACKS_OWNED_CHECK_READY')
258
- throw new Error('Native ownership readiness was malformed')
259
- ready = true
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 })
263
- if (prefix.length > line + 1) await Bun.write(Bun.stdout, prefix.subarray(line + 1))
264
- prefix = Buffer.alloc(0)
265
- }
266
- if (!ready && !stopRequested) throw new Error('Native ownership readiness was not received')
267
- } finally { reader.releaseLock() }
268
- }
269
- const [code] = await Promise.all([command.exited, forward()])
270
- process.send({ exitCode: code })
142
+ const command = Bun.spawn(${JSON.stringify(command)}, { stdin: 'ignore', stdout: 'inherit', stderr: 'inherit' })
143
+ process.send({ exitCode: await command.exited })
271
144
  } catch (error) {
272
- if (command && !consumerPermitted) {
273
- try { command.kill('SIGKILL'); await command.exited } catch {}
274
- }
275
145
  process.send({ error: String(error) })
276
146
  }
277
- }
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):`
279
- setInterval(() => {}, 60_000)
280
- try {
281
- const command = Bun.spawn(${JSON.stringify(command)}, { stdin: 'ignore', stdout: 'inherit', stderr: 'inherit' })
282
- process.send({ exitCode: await command.exited })
283
- } catch (error) {
284
- process.send({ error: String(error) })
285
- }
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}
147
+ `,child=Bun.spawn([bun,`--config=${hostConfig}`,"--eval",host],{cwd:options.cwd,env:{...process.env,...options.env},stdin:"ignore",stdout:"pipe",stderr:"pipe",detached:process.platform!=="win32",ipc(message){if(windows&&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(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(windows){if(!preserveOutput)try{child.send({stop:!0});await waitWithin(reported,1000)}catch(error){errors.push(`Native Windows cleanup completion was not confirmed: ${error instanceof Error?error.message:String(error)}`)}try{child.kill("SIGKILL")}catch{errors.push("Native Windows owner disposal 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}finally{rmSync(hostDirectory,{recursive:!0,force:!0})}}
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.279",
5
+ "version": "0.75.283",
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.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",
69
+ "@stacksjs/action-runner": "0.75.283",
70
+ "@stacksjs/ai": "0.75.283",
71
+ "@stacksjs/analytics": "0.75.283",
72
+ "@stacksjs/api": "0.75.283",
73
+ "@stacksjs/auth": "0.75.283",
74
+ "@stacksjs/build": "0.75.283",
75
75
  "@stacksjs/bumpx": "^0.2.26",
76
76
  "@stacksjs/bunpress": "^0.2.15",
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",
77
+ "@stacksjs/cli": "0.75.283",
78
+ "@stacksjs/cloud": "0.75.283",
79
+ "@stacksjs/cms": "0.75.283",
80
+ "@stacksjs/components": "0.2.676",
81
+ "@stacksjs/config": "0.75.283",
82
+ "@stacksjs/database": "0.75.283",
83
+ "@stacksjs/desktop-build": "0.75.283",
84
+ "@stacksjs/dns": "0.75.283",
85
+ "@stacksjs/enums": "0.75.283",
86
+ "@stacksjs/env": "0.75.283",
87
+ "@stacksjs/error-handling": "0.75.283",
88
+ "@stacksjs/github": "0.75.283",
89
89
  "@stacksjs/gitit": "^0.2.5",
90
- "@stacksjs/i18n": "0.75.279",
91
- "@stacksjs/image": "0.75.279",
92
- "@stacksjs/logging": "0.75.279",
90
+ "@stacksjs/i18n": "0.75.283",
91
+ "@stacksjs/image": "0.75.283",
92
+ "@stacksjs/logging": "0.75.283",
93
93
  "@stacksjs/logsmith": "^0.2.11",
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",
94
+ "@stacksjs/mobile": "0.75.283",
95
+ "@stacksjs/orm": "0.75.283",
96
+ "@stacksjs/path": "0.75.283",
97
+ "@stacksjs/payments": "0.75.283",
98
+ "@stacksjs/queue": "0.75.283",
99
+ "@stacksjs/registry": "0.75.283",
100
+ "@stacksjs/router": "0.75.283",
101
101
  "@stacksjs/rpx": "^0.11.54",
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",
102
+ "@stacksjs/scheduler": "0.75.283",
103
+ "@stacksjs/search-engine": "0.75.283",
104
+ "@stacksjs/security": "0.75.283",
105
+ "@stacksjs/server": "0.75.283",
106
+ "@stacksjs/sites": "0.75.283",
107
+ "@stacksjs/storage": "0.75.283",
108
+ "@stacksjs/strings": "0.75.283",
109
+ "@stacksjs/stx": "0.2.676",
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.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"
113
+ "@stacksjs/tunnel": "0.75.283",
114
+ "@stacksjs/types": "0.75.283",
115
+ "@stacksjs/utils": "0.75.283",
116
+ "@stacksjs/whois": "0.75.283",
117
+ "bun-plugin-stx": "0.2.676"
118
118
  },
119
119
  "peerDependencies": {
120
120
  "craft-native": ">=0.0.301",
@@ -129,7 +129,7 @@
129
129
  }
130
130
  },
131
131
  "devDependencies": {
132
- "@stacksjs/validation": "0.75.279",
132
+ "@stacksjs/validation": "0.75.283",
133
133
  "better-dx": "^0.2.24"
134
134
  }
135
135
  }