Skip to content

Commit ed34fc3

Browse files
committed
Published using @Stream44 Studio
Signed-off-by: Christoph <christoph@christoph.diy>
1 parent 7085efb commit ed34fc3

67 files changed

Lines changed: 2831 additions & 7679 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.o/stream44.studio/assets/Icon-v1.svg

Lines changed: 1170 additions & 0 deletions
Loading

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
⚠️ **WARNING:** This repository may get squashed and force-pushed if the [GordianOpenIntegrity](https://github.com/Stream44/t44-blockchaincommons.com) implementation must change in incompatible ways. Keep your diffs until the **GordianOpenIntegrity** system is stable.
2-
3-
🔷 **Open Development Project:** The implementation is a preview release for community feedback.
1+
<table>
2+
<tr>
3+
<td><a href="https://Stream44.Studio"><img src=".o/stream44.studio/assets/Icon-v1.svg" width="42" height="42"></a></td>
4+
<td><strong><a href="https://Stream44.Studio">Stream44 Studio</a></strong><br/>Open Development Project</td>
5+
<td>Preview release for community feedback.<br/>Get in touch on <a href="https://discord.gg/9eBcQXEJAN">discord</a>.</td>
6+
</tr>
7+
</table>
48

5-
⚠️ **Disclaimer:** Under active development. Code has not been audited, APIs and interfaces are subject to change.
9+
⚠️ **Disclaimer:** Under active development. Code has not been audited. APIs and interfaces are subject to change!
610

711
Terminal44 Workspace Foundation [![Tests](https://github.com/Stream44/t44/actions/workflows/test.yaml/badge.svg)](https://github.com/Stream44/t44/actions/workflows/test.yaml?query=branch%3Amain)
812
===
@@ -182,4 +186,4 @@ Repository DID: `did:repo:adba68602ba0b2eb0ea86e7b24427cf8fa9ab286`
182186
</tr>
183187
</table>
184188

185-
(c) 2026 [Christoph.diy](https://christoph.diy) • Code: `LGPL` • Text: [GNU Free Documentation License](https://www.gnu.org/licenses/fdl-1.3.txt) • Created with [Stream44.Studio](https://Stream44.Studio)
189+
(c) 2026 [Christoph.diy](https://christoph.diy) • Code: [LGPL](./LICENSE.txt) • Text: [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) • Created with [Stream44.Studio](https://Stream44.Studio)

caps/ProjectDeployment.ts

Lines changed: 243 additions & 251 deletions
Large diffs are not rendered by default.

caps/ProjectPublishing.ts

Lines changed: 341 additions & 277 deletions
Large diffs are not rendered by default.

caps/ProjectRepository.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ export async function capsule({
156156
},
157157
isAheadOfRemote: {
158158
type: CapsulePropertyTypes.Function,
159-
value: async function (this: any, { rootDir }: { rootDir: string }): Promise<boolean> {
159+
value: async function (this: any, { rootDir, branch }: { rootDir: string, branch?: string }): Promise<boolean> {
160+
const branchName = branch || 'main'
160161
const lsRemoteResult = await $`git ls-remote origin`.cwd(rootDir).quiet().nothrow()
161162
const lsRemoteOutput = lsRemoteResult.text().trim()
162163

@@ -165,22 +166,24 @@ export async function capsule({
165166
}
166167

167168
const localHead = (await $`git rev-parse HEAD`.cwd(rootDir).quiet()).text().trim()
168-
const remoteHeadLine = lsRemoteOutput.split('\n').find((l: string) => l.includes('refs/heads/main'))
169+
const remoteHeadLine = lsRemoteOutput.split('\n').find((l: string) => l.includes(`refs/heads/${branchName}`))
169170
const remoteHead = remoteHeadLine ? remoteHeadLine.split('\t')[0] : null
170171

171172
return !remoteHead || remoteHead !== localHead
172173
}
173174
},
174175
push: {
175176
type: CapsulePropertyTypes.Function,
176-
value: async function (this: any, { rootDir }: { rootDir: string }): Promise<void> {
177-
await $`git push -u origin main --tags`.cwd(rootDir)
177+
value: async function (this: any, { rootDir, branch }: { rootDir: string, branch?: string }): Promise<void> {
178+
const branchName = branch || 'main'
179+
await $`git push -u origin ${branchName} --tags`.cwd(rootDir)
178180
}
179181
},
180182
forcePush: {
181183
type: CapsulePropertyTypes.Function,
182-
value: async function (this: any, { rootDir }: { rootDir: string }): Promise<void> {
183-
await $`git push --force --tags`.cwd(rootDir)
184+
value: async function (this: any, { rootDir, branch }: { rootDir: string, branch?: string }): Promise<void> {
185+
const branchName = branch || 'main'
186+
await $`git push --force origin ${branchName} --tags`.cwd(rootDir)
184187
}
185188
},
186189
squashAllCommits: {
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export async function capsule({
1919
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
2020
'#@stream44.studio/encapsulate/structs/Capsule': {},
2121
'#': {
22+
lib: {
23+
type: CapsulePropertyTypes.Mapping,
24+
value: 't44/caps/ProjectTestLib',
25+
},
2226
bunTest: {
2327
type: CapsulePropertyTypes.Literal,
2428
value: undefined as any as typeof BunTest,
@@ -244,4 +248,4 @@ export async function capsule({
244248
capsuleName: capsule['#']
245249
})
246250
}
247-
capsule['#'] = 't44/caps/WorkspaceTest'
251+
capsule['#'] = 't44/caps/ProjectTest'

caps/ProjectTestLib.ts

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import * as path from 'path'
2+
import * as fsPromises from 'fs/promises'
3+
import { constants as fsConstants } from 'fs'
4+
import { spawn } from 'bun'
5+
6+
export async function capsule({
7+
encapsulate,
8+
CapsulePropertyTypes,
9+
makeImportStack
10+
}: {
11+
encapsulate: any
12+
CapsulePropertyTypes: any
13+
makeImportStack: any
14+
}) {
15+
return encapsulate({
16+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
17+
'#@stream44.studio/encapsulate/structs/Capsule': {},
18+
'#': {
19+
path: {
20+
type: CapsulePropertyTypes.Constant,
21+
value: path,
22+
},
23+
24+
fs: {
25+
type: CapsulePropertyTypes.Constant,
26+
value: {
27+
...fsPromises,
28+
...fsConstants
29+
},
30+
},
31+
32+
spawnProcess: {
33+
type: CapsulePropertyTypes.Function,
34+
value: async function (this: any, options: any): Promise<any> {
35+
const {
36+
cmd,
37+
cwd = process.cwd(),
38+
waitForReady = false,
39+
readySignal = 'READY',
40+
waitForExit = false,
41+
showOutput = false,
42+
env = {},
43+
verbose = false,
44+
detached = false
45+
} = options;
46+
47+
const outputData = { stdout: '', stderr: '' };
48+
const mergedEnv = { ...process.env, ...env };
49+
50+
if (verbose && env.NODE_ENV) {
51+
console.log('[spawnProcess] Setting NODE_ENV to:', env.NODE_ENV);
52+
console.log('[spawnProcess] Merged env NODE_ENV:', mergedEnv.NODE_ENV);
53+
}
54+
55+
const proc = spawn({
56+
cmd,
57+
cwd,
58+
stdout: 'pipe',
59+
stderr: 'pipe',
60+
env: mergedEnv
61+
});
62+
63+
if (detached && proc.unref) {
64+
proc.unref();
65+
}
66+
67+
let readySignalFound: (() => void) | null = null;
68+
const readyPromise = waitForReady ? new Promise<void>((resolve) => {
69+
readySignalFound = resolve;
70+
}) : null;
71+
72+
if (proc.stdout && !detached) {
73+
const reader = (proc.stdout as any).getReader();
74+
const decoder = new TextDecoder();
75+
(async () => {
76+
try {
77+
while (true) {
78+
const { done, value } = await reader.read();
79+
if (done) break;
80+
const chunk = decoder.decode(value);
81+
outputData.stdout += chunk;
82+
if (waitForReady && readySignalFound && chunk.indexOf(readySignal) !== -1) {
83+
readySignalFound();
84+
readySignalFound = null;
85+
}
86+
if (showOutput || verbose) {
87+
process.stdout.write(chunk);
88+
}
89+
}
90+
} catch (e) {
91+
// Stream closed
92+
}
93+
})();
94+
}
95+
96+
if (proc.stderr && !detached) {
97+
const reader = (proc.stderr as any).getReader();
98+
const decoder = new TextDecoder();
99+
(async () => {
100+
try {
101+
while (true) {
102+
const { done, value } = await reader.read();
103+
if (done) break;
104+
const chunk = decoder.decode(value);
105+
outputData.stderr += chunk;
106+
if (waitForReady && readySignalFound && chunk.indexOf(readySignal) !== -1) {
107+
readySignalFound();
108+
readySignalFound = null;
109+
}
110+
if (showOutput || verbose) {
111+
process.stderr.write(chunk);
112+
}
113+
}
114+
} catch (e) {
115+
// Stream closed
116+
}
117+
})();
118+
}
119+
120+
if (waitForReady && readyPromise) {
121+
await Promise.race([
122+
readyPromise,
123+
proc.exited.then(() => {
124+
throw new Error(`Process exited with code ${proc.exitCode} before emitting ${readySignal} signal. stderr: ${outputData.stderr}`);
125+
})
126+
]);
127+
} else if (waitForExit) {
128+
await proc.exited;
129+
}
130+
131+
return {
132+
process: proc,
133+
stdout: outputData.stdout,
134+
stderr: outputData.stderr,
135+
exitCode: proc.exitCode ?? 0,
136+
getStdout: () => outputData.stdout,
137+
getStderr: () => outputData.stderr
138+
};
139+
}
140+
},
141+
142+
runPackageScript: {
143+
type: CapsulePropertyTypes.Function,
144+
value: async function (this: any, options: any): Promise<any> {
145+
const {
146+
runtime = 'bun',
147+
script,
148+
args = [],
149+
cwd,
150+
env,
151+
verbose = false
152+
} = options;
153+
154+
const cmdArgs = [...args];
155+
if (cmdArgs.length) {
156+
cmdArgs.unshift('--')
157+
}
158+
159+
const spawned = await this.spawnProcess({
160+
cmd: [runtime, 'run', script, ...cmdArgs],
161+
cwd,
162+
waitForReady: false,
163+
waitForExit: true,
164+
env,
165+
verbose
166+
});
167+
168+
return {
169+
exitCode: spawned.exitCode,
170+
stdout: spawned.stdout,
171+
stderr: spawned.stderr
172+
};
173+
}
174+
},
175+
176+
waitForFetch: {
177+
type: CapsulePropertyTypes.Function,
178+
value: async function (this: any, options: any): Promise<boolean | Response> {
179+
const {
180+
url,
181+
method = 'GET',
182+
headers,
183+
body,
184+
status,
185+
retryDelayMs = 1000,
186+
requestTimeoutMs = 2000,
187+
timeoutMs = 30000,
188+
verbose = false,
189+
returnResponse = false
190+
} = options;
191+
192+
const startTime = Date.now();
193+
let attemptCount = 0;
194+
195+
while (Date.now() - startTime < timeoutMs) {
196+
attemptCount++;
197+
const elapsed = Date.now() - startTime;
198+
199+
try {
200+
const response = await fetch(url, {
201+
method,
202+
headers,
203+
body,
204+
signal: AbortSignal.timeout(requestTimeoutMs)
205+
});
206+
207+
if (status === true) {
208+
if (verbose) {
209+
console.log(`[waitForFetch] URL ${url} responded (status: ${response.status}) after ${attemptCount} attempts (${elapsed}ms)`);
210+
}
211+
return returnResponse ? response : true;
212+
} else if (typeof status === 'number') {
213+
if (response.status === status) {
214+
if (verbose) {
215+
console.log(`[waitForFetch] URL ${url} responded with status ${status} after ${attemptCount} attempts (${elapsed}ms)`);
216+
}
217+
return returnResponse ? response : true;
218+
} else {
219+
if (verbose) {
220+
console.log(`[waitForFetch] Attempt ${attemptCount}: Got status ${response.status}, expected ${status} (${elapsed}ms)`);
221+
}
222+
}
223+
}
224+
} catch (error) {
225+
if (status === false) {
226+
if (verbose) {
227+
console.log(`[waitForFetch] URL ${url} is not responding (as expected) after ${attemptCount} attempts (${elapsed}ms)`);
228+
}
229+
return true;
230+
} else {
231+
if (verbose) {
232+
console.log(`[waitForFetch] Attempt ${attemptCount}: Request failed (${elapsed}ms)`);
233+
}
234+
}
235+
}
236+
237+
const remainingTime = timeoutMs - (Date.now() - startTime);
238+
if (remainingTime > 0) {
239+
await new Promise(resolve => setTimeout(resolve, Math.min(retryDelayMs, remainingTime)));
240+
}
241+
}
242+
243+
if (verbose) {
244+
console.log(`[waitForFetch] Timeout reached after ${attemptCount} attempts (${Date.now() - startTime}ms)`);
245+
}
246+
return false;
247+
}
248+
},
249+
}
250+
}
251+
}, {
252+
importMeta: import.meta,
253+
importStack: makeImportStack(),
254+
capsuleName: capsule['#']
255+
})
256+
}
257+
capsule['#'] = 't44/caps/ProjectTestLib'

0 commit comments

Comments
 (0)