@norskvideo/ctl-sdk 0.1.21 → 0.1.23
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/docs-router.d.ts +21 -0
- package/docs-router.js +35 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/manifest-fetch.d.ts +12 -1
- package/manifest-fetch.js +14 -4
- package/package.json +2 -2
- package/product-service.js +1 -1
package/docs-router.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Express } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Where a product's docs bundle (dev-kit doc-guide `buildBundle`) is, given the
|
|
4
|
+
* backend's own directory — or null when the product shipped none.
|
|
5
|
+
*
|
|
6
|
+
* Two layouts, one relative path: the image bakes the bundle at `<app>/docs`
|
|
7
|
+
* beside `backend/` (Dockerfile.bundle), a repo checkout has it at
|
|
8
|
+
* `<repo>/docs/generated/bundle`; from `backend/dist` and `backend/src`
|
|
9
|
+
* respectively both are `../../docs`. The image's `docs/` exists even when
|
|
10
|
+
* empty, so presence is judged by `bundle.json`, not the directory.
|
|
11
|
+
*/
|
|
12
|
+
export declare function docsBundleDir(fromDir: string): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* Mount the docs bundle at `/docs`: `manual.html` as the index, `bundle.json`,
|
|
15
|
+
* `pages/*.md` and `captures/*` by path. Returns whether anything was mounted,
|
|
16
|
+
* so a product without a bundle boots exactly as before. `/docs` redirects to
|
|
17
|
+
* `/docs/` with a root-absolute Location, which the daemon's product proxy
|
|
18
|
+
* rewrites under `/products/<name>` — so the manual's hash routing works
|
|
19
|
+
* through the daemon too.
|
|
20
|
+
*/
|
|
21
|
+
export declare function serveDocs(app: Express, dir: string | null): boolean;
|
package/docs-router.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import express from "express";
|
|
4
|
+
/**
|
|
5
|
+
* Where a product's docs bundle (dev-kit doc-guide `buildBundle`) is, given the
|
|
6
|
+
* backend's own directory — or null when the product shipped none.
|
|
7
|
+
*
|
|
8
|
+
* Two layouts, one relative path: the image bakes the bundle at `<app>/docs`
|
|
9
|
+
* beside `backend/` (Dockerfile.bundle), a repo checkout has it at
|
|
10
|
+
* `<repo>/docs/generated/bundle`; from `backend/dist` and `backend/src`
|
|
11
|
+
* respectively both are `../../docs`. The image's `docs/` exists even when
|
|
12
|
+
* empty, so presence is judged by `bundle.json`, not the directory.
|
|
13
|
+
*/
|
|
14
|
+
export function docsBundleDir(fromDir) {
|
|
15
|
+
const docs = resolve(fromDir, "../../docs");
|
|
16
|
+
for (const candidate of [docs, resolve(docs, "generated/bundle")]) {
|
|
17
|
+
if (existsSync(resolve(candidate, "bundle.json")))
|
|
18
|
+
return candidate;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Mount the docs bundle at `/docs`: `manual.html` as the index, `bundle.json`,
|
|
24
|
+
* `pages/*.md` and `captures/*` by path. Returns whether anything was mounted,
|
|
25
|
+
* so a product without a bundle boots exactly as before. `/docs` redirects to
|
|
26
|
+
* `/docs/` with a root-absolute Location, which the daemon's product proxy
|
|
27
|
+
* rewrites under `/products/<name>` — so the manual's hash routing works
|
|
28
|
+
* through the daemon too.
|
|
29
|
+
*/
|
|
30
|
+
export function serveDocs(app, dir) {
|
|
31
|
+
if (!dir)
|
|
32
|
+
return false;
|
|
33
|
+
app.use("/docs", express.static(dir, { index: "manual.html" }));
|
|
34
|
+
return true;
|
|
35
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./capabilities-router.js";
|
|
|
3
3
|
export * from "./cjs-interop.js";
|
|
4
4
|
export * from "./dev-url.js";
|
|
5
5
|
export * from "./docker-runner.js";
|
|
6
|
+
export * from "./docs-router.js";
|
|
6
7
|
export * from "./http-proxy.js";
|
|
7
8
|
export * from "./license-registration.js";
|
|
8
9
|
export * from "./license-staged.js";
|
package/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from "./capabilities-router.js";
|
|
|
5
5
|
export * from "./cjs-interop.js";
|
|
6
6
|
export * from "./dev-url.js";
|
|
7
7
|
export * from "./docker-runner.js";
|
|
8
|
+
export * from "./docs-router.js";
|
|
8
9
|
export * from "./http-proxy.js";
|
|
9
10
|
export * from "./license-registration.js";
|
|
10
11
|
export * from "./license-staged.js";
|
package/manifest-fetch.d.ts
CHANGED
|
@@ -17,7 +17,18 @@ export declare function specBaseUrl(spec: ProductSpec, port?: number, reachHost?
|
|
|
17
17
|
* so a recorded reachHost is never forgotten. */
|
|
18
18
|
export declare function productBaseUrl(reg: Pick<ProductRegistration, "spec" | "port" | "reachHost">): string;
|
|
19
19
|
export declare function fetchManifest(baseUrl: string): Promise<Manifest>;
|
|
20
|
-
|
|
20
|
+
/** Poll a product's manifest until it answers.
|
|
21
|
+
*
|
|
22
|
+
* The timeout names `baseUrl`. That is the whole point of the message: the
|
|
23
|
+
* daemon dials a container product on loopback, and where that loopback is not
|
|
24
|
+
* the daemon's own (a docker-outside-of-docker runner, where it belongs to the
|
|
25
|
+
* docker host — see product-reach.ts) the product is perfectly healthy and
|
|
26
|
+
* simply unreachable. Without the address, that reads identically to a product
|
|
27
|
+
* that genuinely failed to start. */
|
|
28
|
+
export declare function waitForReady(baseUrl: string, opts?: {
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
intervalMs?: number;
|
|
31
|
+
}): Promise<void>;
|
|
21
32
|
/**
|
|
22
33
|
* Probe the product's configScreenUrl after registration. Two hard
|
|
23
34
|
* rejections:
|
package/manifest-fetch.js
CHANGED
|
@@ -56,8 +56,18 @@ export async function fetchManifest(baseUrl) {
|
|
|
56
56
|
}
|
|
57
57
|
return parsed.data;
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/** Poll a product's manifest until it answers.
|
|
60
|
+
*
|
|
61
|
+
* The timeout names `baseUrl`. That is the whole point of the message: the
|
|
62
|
+
* daemon dials a container product on loopback, and where that loopback is not
|
|
63
|
+
* the daemon's own (a docker-outside-of-docker runner, where it belongs to the
|
|
64
|
+
* docker host — see product-reach.ts) the product is perfectly healthy and
|
|
65
|
+
* simply unreachable. Without the address, that reads identically to a product
|
|
66
|
+
* that genuinely failed to start. */
|
|
67
|
+
export async function waitForReady(baseUrl, opts = {}) {
|
|
68
|
+
const timeoutMs = opts.timeoutMs ?? READINESS_TIMEOUT_MS;
|
|
69
|
+
const intervalMs = opts.intervalMs ?? READINESS_INTERVAL_MS;
|
|
70
|
+
const deadline = Date.now() + timeoutMs;
|
|
61
71
|
while (Date.now() < deadline) {
|
|
62
72
|
try {
|
|
63
73
|
const r = await fetch(`${baseUrl}/manifest.json`);
|
|
@@ -67,9 +77,9 @@ export async function waitForReady(baseUrl) {
|
|
|
67
77
|
catch {
|
|
68
78
|
// not ready yet
|
|
69
79
|
}
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
71
81
|
}
|
|
72
|
-
throw new ProductError("READINESS_TIMEOUT", `product did not become ready within ${
|
|
82
|
+
throw new ProductError("READINESS_TIMEOUT", `product did not become ready within ${timeoutMs}ms — nothing answered ${baseUrl}/manifest.json`);
|
|
73
83
|
}
|
|
74
84
|
/**
|
|
75
85
|
* Probe the product's configScreenUrl after registration. Two hard
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@norskvideo/ctl-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"types": "./index.d.ts",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@norskvideo/ctl-foundation": "^0.1.0",
|
|
40
|
-
"@norskvideo/ctl-product-template-schema": "^0.1.
|
|
40
|
+
"@norskvideo/ctl-product-template-schema": "^0.1.12",
|
|
41
41
|
"express": "5",
|
|
42
42
|
"lucide-react": "^0.483.0",
|
|
43
43
|
"react": "^19.0.0",
|
package/product-service.js
CHANGED
|
@@ -127,7 +127,7 @@ export class ProductService {
|
|
|
127
127
|
}
|
|
128
128
|
port = allocated;
|
|
129
129
|
await this.refreshImage(spec.image);
|
|
130
|
-
logger.info(`Starting product container: ${spec.image} on host port ${port}`);
|
|
130
|
+
logger.info(`Starting product container: ${spec.image} on host port ${port} (reach: ${this.reach})`);
|
|
131
131
|
containerId = await this.containerOps.run(spec.image, port);
|
|
132
132
|
reachHost = await this.reachHostFor(containerId);
|
|
133
133
|
baseUrl = specBaseUrl(spec, port, reachHost);
|