sdk beta.56

This commit is contained in:
Aiden McClelland
2026-03-05 15:12:50 -07:00
parent fd1ccc0c8c
commit 8fdeeab5bb
5 changed files with 104 additions and 5 deletions

View File

@@ -37,7 +37,7 @@
}, },
"../sdk/dist": { "../sdk/dist": {
"name": "@start9labs/start-sdk", "name": "@start9labs/start-sdk",
"version": "0.4.0-beta.55", "version": "0.4.0-beta.56",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@iarna/toml": "^3.0.0", "@iarna/toml": "^3.0.0",

View File

@@ -25,3 +25,4 @@ cd sdk && make baseDist dist # Rebuild SDK after ts-bindings
- When adding i18n keys, add all 5 locales in `core/locales/i18n.yaml` (see [i18n-patterns.md](i18n-patterns.md)) - When adding i18n keys, add all 5 locales in `core/locales/i18n.yaml` (see [i18n-patterns.md](i18n-patterns.md))
- When using DB watches, follow the `TypedDbWatch<T>` patterns in [patchdb.md](patchdb.md) - When using DB watches, follow the `TypedDbWatch<T>` patterns in [patchdb.md](patchdb.md)
- **Always use `.invoke(ErrorKind::...)` instead of `.status()` when running CLI commands** via `tokio::process::Command`. The `Invoke` trait (from `crate::util::Invoke`) captures stdout/stderr and checks exit codes properly. Using `.status()` leaks stderr directly to system logs, creating noise. For check-then-act patterns (e.g. `iptables -C`), use `.invoke(...).await.is_ok()` / `.is_err()` instead of `.status().await.map_or(false, |s| s.success())`. - **Always use `.invoke(ErrorKind::...)` instead of `.status()` when running CLI commands** via `tokio::process::Command`. The `Invoke` trait (from `crate::util::Invoke`) captures stdout/stderr and checks exit codes properly. Using `.status()` leaks stderr directly to system logs, creating noise. For check-then-act patterns (e.g. `iptables -C`), use `.invoke(...).await.is_ok()` / `.is_err()` instead of `.status().await.map_or(false, |s| s.success())`.
- Always use file utils in util::io instead of tokio::fs when available

View File

@@ -141,6 +141,7 @@ export class StartSdk<Manifest extends T.SDKManifest> {
| 'getSystemSmtp' | 'getSystemSmtp'
| 'getOutboundGateway' | 'getOutboundGateway'
| 'getContainerIp' | 'getContainerIp'
| 'getStatus'
| 'getDataVersion' | 'getDataVersion'
| 'setDataVersion' | 'setDataVersion'
| 'getServiceManifest' | 'getServiceManifest'
@@ -164,7 +165,6 @@ export class StartSdk<Manifest extends T.SDKManifest> {
getSslKey: (effects, ...args) => effects.getSslKey(...args), getSslKey: (effects, ...args) => effects.getSslKey(...args),
shutdown: (effects, ...args) => effects.shutdown(...args), shutdown: (effects, ...args) => effects.shutdown(...args),
getDependencies: (effects, ...args) => effects.getDependencies(...args), getDependencies: (effects, ...args) => effects.getDependencies(...args),
getStatus: (effects, ...args) => effects.getStatus(...args),
setHealth: (effects, ...args) => effects.setHealth(...args), setHealth: (effects, ...args) => effects.setHealth(...args),
} }
@@ -342,6 +342,104 @@ export class StartSdk<Manifest extends T.SDKManifest> {
} }
}, },
/**
* Get the service's current status with reactive subscription support.
*
* Returns an object with multiple read strategies: `const()` for a value
* that retries on change, `once()` for a single read, `watch()` for an async
* generator, `onChange()` for a callback, and `waitFor()` to block until a predicate is met.
*
* @param effects - The effects context
* @param options - Optional filtering options (e.g. `packageId`)
*/
getStatus: (
effects: T.Effects,
options: Omit<Parameters<T.Effects['getStatus']>[0], 'callback'> = {},
) => {
async function* watch(abort?: AbortSignal) {
const resolveCell = { resolve: () => {} }
effects.onLeaveContext(() => {
resolveCell.resolve()
})
abort?.addEventListener('abort', () => resolveCell.resolve())
while (effects.isInContext && !abort?.aborted) {
let callback: () => void = () => {}
const waitForNext = new Promise<void>((resolve) => {
callback = resolve
resolveCell.resolve = resolve
})
yield await effects.getStatus({ ...options, callback })
await waitForNext
}
}
return {
const: () =>
effects.getStatus({
...options,
callback:
effects.constRetry &&
(() => effects.constRetry && effects.constRetry()),
}),
once: () => effects.getStatus(options),
watch: (abort?: AbortSignal) => {
const ctrl = new AbortController()
abort?.addEventListener('abort', () => ctrl.abort())
return DropGenerator.of(watch(ctrl.signal), () => ctrl.abort())
},
onChange: (
callback: (
value: T.StatusInfo | null,
error?: Error,
) => { cancel: boolean } | Promise<{ cancel: boolean }>,
) => {
;(async () => {
const ctrl = new AbortController()
for await (const value of watch(ctrl.signal)) {
try {
const res = await callback(value)
if (res.cancel) {
ctrl.abort()
break
}
} catch (e) {
console.error(
'callback function threw an error @ getStatus.onChange',
e,
)
}
}
})()
.catch((e) => callback(null, e))
.catch((e) =>
console.error(
'callback function threw an error @ getStatus.onChange',
e,
),
)
},
waitFor: async (pred: (value: T.StatusInfo | null) => boolean) => {
const resolveCell = { resolve: () => {} }
effects.onLeaveContext(() => {
resolveCell.resolve()
})
while (effects.isInContext) {
let callback: () => void = () => {}
const waitForNext = new Promise<void>((resolve) => {
callback = resolve
resolveCell.resolve = resolve
})
const res = await effects.getStatus({ ...options, callback })
if (pred(res)) {
resolveCell.resolve()
return res
}
await waitForNext
}
return null
},
}
},
MultiHost: { MultiHost: {
/** /**
* Create a new MultiHost instance for binding ports and exporting interfaces. * Create a new MultiHost instance for binding ports and exporting interfaces.

View File

@@ -1,12 +1,12 @@
{ {
"name": "@start9labs/start-sdk", "name": "@start9labs/start-sdk",
"version": "0.4.0-beta.55", "version": "0.4.0-beta.56",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@start9labs/start-sdk", "name": "@start9labs/start-sdk",
"version": "0.4.0-beta.55", "version": "0.4.0-beta.56",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@iarna/toml": "^3.0.0", "@iarna/toml": "^3.0.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@start9labs/start-sdk", "name": "@start9labs/start-sdk",
"version": "0.4.0-beta.55", "version": "0.4.0-beta.56",
"description": "Software development kit to facilitate packaging services for StartOS", "description": "Software development kit to facilitate packaging services for StartOS",
"main": "./package/lib/index.js", "main": "./package/lib/index.js",
"types": "./package/lib/index.d.ts", "types": "./package/lib/index.d.ts",