@norskvideo/ctl-sdk 0.1.51 → 0.1.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.
Files changed (3) hide show
  1. package/fleet.d.ts +76 -0
  2. package/fleet.js +70 -0
  3. package/package.json +5 -1
package/fleet.d.ts ADDED
@@ -0,0 +1,76 @@
1
+ export interface FleetEndpoint {
2
+ transport: string;
3
+ address: string;
4
+ params: Record<string, string>;
5
+ }
6
+ /** What norsk-discovery holds. */
7
+ export interface FleetResource {
8
+ name: string;
9
+ kind: string;
10
+ labels: Record<string, string>;
11
+ endpoints: FleetEndpoint[];
12
+ }
13
+ /** A runtime as the platform knows it: under norsk-mgr its job id,
14
+ * production, replica and job name; under norsk-ctl alone its instance id
15
+ * and product. */
16
+ export interface FleetRuntime {
17
+ id: string;
18
+ namespace: string;
19
+ [field: string]: unknown;
20
+ }
21
+ export interface FleetItem {
22
+ runtime: FleetRuntime;
23
+ /** The platform's own registration of the runtime (`norsk.runtime`), with
24
+ * its `norsk.state`. */
25
+ presence?: FleetResource;
26
+ entries: FleetResource[];
27
+ }
28
+ export interface FleetDirectory {
29
+ items: FleetItem[];
30
+ /** Whether discovery's replay has landed: before it, entries are partial. */
31
+ synced: boolean;
32
+ }
33
+ export interface FleetSelection {
34
+ /** The kinds to read, as literals. `norsk.runtime` lists runtimes by their
35
+ * own registration. */
36
+ kind: string | readonly string[];
37
+ /** Every one present on an entry, with the same value. */
38
+ labels?: Readonly<Record<string, string>>;
39
+ }
40
+ export interface FleetWatchHandlers {
41
+ /** The whole current set, after the snapshot and after every change. */
42
+ onChange?: (items: FleetItem[]) => void;
43
+ onSynced?: (synced: boolean) => void;
44
+ /** The stream failed; the browser retries it. */
45
+ onError?: () => void;
46
+ }
47
+ export interface FleetWatch {
48
+ current(): FleetItem[];
49
+ synced(): boolean;
50
+ close(): void;
51
+ }
52
+ interface EventSourceLike {
53
+ addEventListener(name: string, listener: (e: {
54
+ data: string;
55
+ }) => void): void;
56
+ close(): void;
57
+ }
58
+ export interface FleetOptions {
59
+ /** Where the directory is: `/_fleet` on a dashboard host (the default),
60
+ * `/api/v1/fleet` on the platform's own origin. */
61
+ base?: string;
62
+ fetch?: (url: string, init?: {
63
+ credentials?: "same-origin";
64
+ }) => Promise<Response>;
65
+ EventSource?: new (url: string, init?: {
66
+ withCredentials?: boolean;
67
+ }) => EventSourceLike;
68
+ }
69
+ export interface Fleet {
70
+ query(selection: FleetSelection): Promise<FleetDirectory>;
71
+ watch(selection: FleetSelection, handlers?: FleetWatchHandlers): FleetWatch;
72
+ }
73
+ export declare function fleetClient(options?: FleetOptions): Fleet;
74
+ /** The directory on the page's own origin, as a dashboard host serves it. */
75
+ export declare const fleet: Fleet;
76
+ export {};
package/fleet.js ADDED
@@ -0,0 +1,70 @@
1
+ // The directory, for a dashboard (RFC 0006 §4.5, task 122 K): the runtimes
2
+ // the signed-in user may see, each with what discovery holds under it, read
3
+ // from the page's own origin and filtered by the platform, never by the
4
+ // dashboard.
5
+ //
6
+ // On a dashboard host the directory is `/_fleet/`; in a product's own UI or a
7
+ // dashboard's dev loop it is the platform's API, `/api/v1/fleet/`. The calls
8
+ // are the same either way.
9
+ //
10
+ // Name the kinds a call reads with literals: ctl-dev-kit's dashboard build
11
+ // derives the registration's `uses` from them, and refuses one it cannot read.
12
+ //
13
+ // Browser-only, with no imports, so a dashboard's bundle carries nothing else.
14
+ function queryString(selection) {
15
+ const kinds = typeof selection.kind === "string" ? [selection.kind] : [...selection.kind];
16
+ const params = new URLSearchParams({ kinds: kinds.join(",") });
17
+ const labels = Object.entries(selection.labels ?? {}).map(([k, v]) => `${k}=${v}`);
18
+ if (labels.length > 0)
19
+ params.set("labels", labels.join(","));
20
+ return params.toString();
21
+ }
22
+ export function fleetClient(options = {}) {
23
+ const base = (options.base ?? "/_fleet").replace(/\/+$/, "");
24
+ return {
25
+ async query(selection) {
26
+ const doFetch = options.fetch ?? ((url, init) => globalThis.fetch(url, init));
27
+ const res = await doFetch(`${base}/directory?${queryString(selection)}`, { credentials: "same-origin" });
28
+ if (!res.ok)
29
+ throw new Error(`the directory answered ${res.status}`);
30
+ return (await res.json());
31
+ },
32
+ watch(selection, handlers = {}) {
33
+ const Source = options.EventSource ?? globalThis.EventSource;
34
+ if (Source === undefined)
35
+ throw new Error("this environment has no EventSource");
36
+ const items = new Map();
37
+ let isSynced = false;
38
+ const source = new Source(`${base}/directory/watch?${queryString(selection)}`, { withCredentials: true });
39
+ const changed = () => handlers.onChange?.([...items.values()]);
40
+ const on = (name, apply) => source.addEventListener(name, (e) => apply(JSON.parse(e.data)));
41
+ on("snapshot", (data) => {
42
+ items.clear();
43
+ for (const item of data.items)
44
+ items.set(item.runtime.id, item);
45
+ isSynced = data.synced;
46
+ changed();
47
+ });
48
+ on("upsert", (item) => {
49
+ items.set(item.runtime.id, item);
50
+ changed();
51
+ });
52
+ on("remove", (data) => {
53
+ items.delete(data.id);
54
+ changed();
55
+ });
56
+ on("synced", (data) => {
57
+ isSynced = data.synced;
58
+ handlers.onSynced?.(isSynced);
59
+ });
60
+ source.addEventListener("error", () => handlers.onError?.());
61
+ return {
62
+ current: () => [...items.values()],
63
+ synced: () => isSynced,
64
+ close: () => source.close(),
65
+ };
66
+ },
67
+ };
68
+ }
69
+ /** The directory on the page's own origin, as a dashboard host serves it. */
70
+ export const fleet = fleetClient();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-sdk",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -15,6 +15,10 @@
15
15
  "types": "./components/index.d.ts",
16
16
  "default": "./components/index.js"
17
17
  },
18
+ "./fleet": {
19
+ "types": "./fleet.d.ts",
20
+ "default": "./fleet.js"
21
+ },
18
22
  "./frontend-spec": {
19
23
  "types": "./frontend-spec.d.ts",
20
24
  "default": "./frontend-spec.js"