@norskvideo/ctl-dev-kit 0.2.50 → 0.2.52

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.
@@ -20,7 +20,7 @@
20
20
  // workspace-symlinked and installed from the published tarball.
21
21
 
22
22
  import { execFileSync } from "node:child_process";
23
- import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
23
+ import { existsSync, lstatSync, readdirSync, readFileSync, statSync } from "node:fs";
24
24
  import { join } from "node:path";
25
25
  import { parseManifestSeed } from "@norskvideo/ctl-sdk/manifest-seed";
26
26
  import { checkTheme } from "../docs-theme/sync.ts";
@@ -369,16 +369,21 @@ const CHECKS_LOGS = /\bproductTemplateLogsProblems\b/;
369
369
  const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", "lib", "coverage", ".wireit"]);
370
370
 
371
371
  function sourceFiles(dir: string, out: string[] = []): string[] {
372
- let names: string[];
373
- try {
374
- names = readdirSync(dir);
375
- } catch {
376
- return out;
377
- }
378
- for (const name of names) {
372
+ // `dir` is the repo root or an entry just stat'd as a directory: one the walk
373
+ // cannot list is a real failure, never an empty directory
374
+ for (const name of readdirSync(dir)) {
379
375
  if (SKIP_DIRS.has(name)) continue;
380
376
  const path = join(dir, name);
381
- const st = statSync(path);
377
+ let st: ReturnType<typeof statSync>;
378
+ try {
379
+ st = statSync(path);
380
+ } catch (e) {
381
+ // Only a symlink whose target is absent is skipped: a docs site's link to
382
+ // a doc-guide capture not generated yet (a fresh checkout). Anything else
383
+ // that stops the walk reading an entry is a real failure.
384
+ if ((e as NodeJS.ErrnoException).code === "ENOENT" && lstatSync(path).isSymbolicLink()) continue;
385
+ throw e;
386
+ }
382
387
  if (st.isDirectory()) sourceFiles(path, out);
383
388
  else if (/\.(ts|tsx)$/.test(name)) out.push(path);
384
389
  }
@@ -33,7 +33,9 @@ function packageJson(ctx: ShapeContext): string {
33
33
  type: "commonjs",
34
34
  main: "lib/index.js",
35
35
  scripts: {
36
- build: "bun run scripts/codegen.ts && bunx tsc && bun run scripts/copy-yamls.ts",
36
+ // clean first: tsc never deletes output, so a removed component would
37
+ // otherwise still ship from lib/.
38
+ build: "bun run clean && bun run scripts/codegen.ts && bunx tsc && bun run scripts/copy-yamls.ts",
37
39
  codegen: "bun run scripts/codegen.ts",
38
40
  clean: "rm -rf lib",
39
41
  test: "bun test src/",
@@ -155,6 +155,13 @@ export type ExampleState = { ticks: number };
155
155
 
156
156
  export const getStatus = (env: Env) => apiGet<ExampleState>(env, "/status");
157
157
 
158
+ /** Studio wraps the state in every message that carries one; events and alerts carry none. */
159
+ export function stateFromMessage(msg: unknown): unknown {
160
+ const m = msg as { type?: string; state?: unknown } | null;
161
+ if (m && typeof m === "object" && (m.type === "initial-state" || m.type === "state-update")) return m.state;
162
+ return undefined;
163
+ }
164
+
158
165
  /**
159
166
  * The component's state feed: Studio broadcasts every updates.update() here.
160
167
  *
@@ -165,7 +172,8 @@ export const getStatus = (env: Env) => apiGet<ExampleState>(env, "/status");
165
172
  export function openStateSocket(env: Env, onState: (state: ExampleState) => void): WebSocket {
166
173
  const socket = new WebSocket(\`\${env.wsBasePath}/\${COMPONENT_ID}/ws\`);
167
174
  socket.onmessage = (event) => {
168
- onState(JSON.parse(event.data as string) as ExampleState);
175
+ const state = stateFromMessage(JSON.parse(event.data as string));
176
+ if (state !== undefined) onState(state as ExampleState);
169
177
  };
170
178
  return socket;
171
179
  }
@@ -336,10 +336,16 @@ function rootPackageJson(ctx: ShapeContext): string {
336
336
  ...(hasFrontend ? { "build:frontend": "bun run --cwd frontend build" } : {}),
337
337
  "build:backend": "bun run --cwd backend build",
338
338
  "build:image": "bash deployment/build-image.sh",
339
- // The backend SERVES frontend/dist, so a dev loop that never builds it
340
- // hands the daemon's configure-screen probe a 404. `dev:frontend` is
341
- // vite's own server, for iterating on the screen with HMR.
342
- dev: hasFrontend ? "bun run build:frontend && bun run --cwd backend dev" : "bun run --cwd backend dev",
339
+ // The backend SERVES frontend/dist and PACKS components/lib and
340
+ // dashboards/, so a dev loop that skips a build hands the daemon a 404
341
+ // configure screen, a stale component or a dashboard that 404s.
342
+ // `dev:frontend` is vite's own server, for iterating on the screen with HMR.
343
+ dev: [
344
+ ...(hasComponents ? ["bun run build:components"] : []),
345
+ ...(hasDashboard ? ["bun run build:dashboard"] : []),
346
+ ...(hasFrontend ? ["bun run build:frontend"] : []),
347
+ "bun run --cwd backend dev",
348
+ ].join(" && "),
343
349
  ...(hasFrontend ? { "dev:frontend": "bun run --cwd frontend dev" } : {}),
344
350
  start: "bun run --cwd backend start",
345
351
  iterate: "bash deployment/iterate.sh",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.2.50",
3
+ "version": "0.2.52",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./create-product": "./create-product/create-product.ts",