@gjermundgaraba/clankerbox-sdk 0.15.0 → 0.15.2

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.
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import * as Action from "@gjermundgaraba/effect-actions/Action";
3
3
  import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
4
- import { Context, Duration, Effect, Fiber, Match, Predicate, Result, Schema } from "effect";
4
+ import { Context, Duration, Effect, Fiber, Match, Predicate, Result, Schema, SchemaIssue } from "effect";
5
5
  import { HttpClient, HttpClientRequest } from "effect/http";
6
6
  //#region src/errors.ts
7
7
  /**
@@ -129,7 +129,7 @@ const formatId = (host, name) => {
129
129
  };
130
130
  //#endregion
131
131
  //#region package.json
132
- var version$1 = "0.15.0";
132
+ var version$1 = "0.15.2";
133
133
  //#endregion
134
134
  //#region src/resources.ts
135
135
  /** The resources a host reports: machines, checkpoints and the host itself. */
@@ -426,6 +426,41 @@ const Http = ActionHttp.make([
426
426
  })
427
427
  ], { prefix: "/api" });
428
428
  //#endregion
429
+ //#region src/text.ts
430
+ /** How sizes, decode failures and file failures read in a message or a table cell. */
431
+ const mibPerGib = 1024;
432
+ /**
433
+ * A RAM size, exactly, as a size flag takes one: GiB when it is whole or half GiB, `32G` or
434
+ * `1.5G`, and MiB when it isn't, `512M` or `1500M`.
435
+ */
436
+ const formatMib = (mib) => mib >= mibPerGib && mib % (mibPerGib / 2) === 0 ? `${mib / mibPerGib}G` : `${mib}M`;
437
+ /**
438
+ * Where a decode failed, as a JSON path: `hosts[0].id`. `names` gives a top-level field the
439
+ * name its caller knows it by.
440
+ */
441
+ const jsonPath = (path, names) => path.map((key, index) => {
442
+ if (Number.isInteger(key)) return `[${String(key)}]`;
443
+ return index === 0 ? names?.get(key) ?? String(key) : `.${String(key)}`;
444
+ }).join("");
445
+ const flatten = SchemaIssue.makeFormatterStandardSchemaV1({ leafHook: (issue) => Match.value(issue).pipe(Match.tag("MissingKey", () => "missing"), Match.tag("UnexpectedKey", () => "not a known field"), Match.orElse(SchemaIssue.defaultLeafHook)) });
446
+ /**
447
+ * Everything a decode refused, on one line, each under its JSON path: `cpu: missing; ram: not a
448
+ * known field`. `names` renames top-level fields, as when a flag set each: `--cpu: missing`.
449
+ */
450
+ const describeIssues = (error, names) => flatten(error.issue).issues.map(({ path = [], message }) => {
451
+ const keys = path.map((segment) => Predicate.isPropertyKey(segment) ? segment : segment.key);
452
+ return keys.length === 0 ? message : `${jsonPath(keys, names)}: ${message}`;
453
+ }).join("; ");
454
+ /**
455
+ * Why a file couldn't be read, without the platform call that met it. Node describes a failure
456
+ * only in its cause, so what has no wording here reads as the system's own.
457
+ */
458
+ const describeFileError = ({ reason }) => {
459
+ if (Predicate.isTagged(reason, "NotFound")) return "no such file or directory";
460
+ if (Predicate.isTagged(reason, "PermissionDenied")) return "permission denied";
461
+ return reason.description ?? (reason.cause instanceof Error ? reason.cause.message : reason._tag);
462
+ };
463
+ //#endregion
429
464
  //#region src/client.ts
430
465
  /**
431
466
  * The client library: a host list in placement order, routing by ID, list fan-out and
@@ -497,7 +532,7 @@ const decodeHosts = Schema.decodeUnknownEffect(Schema.Array(HostEntry));
497
532
  */
498
533
  const make = (hosts, options) => Effect.gen(function* () {
499
534
  if (hosts.length === 0) return yield* new Invalid({ message: "the host list is empty: a client needs a host" });
500
- yield* decodeHosts(hosts).pipe(Effect.mapError((error) => new Invalid({ message: `host list: ${error.message}` })));
535
+ yield* decodeHosts(hosts, { errors: "all" }).pipe(Effect.mapError((error) => new Invalid({ message: `host list: ${describeIssues(error)}` })));
501
536
  const twice = hosts.find(({ id }, index) => hosts.findIndex((other) => other.id === id) < index);
502
537
  if (twice !== void 0) return yield* new Invalid({ message: `host ${twice.id} appears twice in the host list` });
503
538
  const routed = yield* Effect.forEach(hosts, (entry) => Effect.map(makeHostApi(entry), (api) => ({
@@ -575,7 +610,7 @@ const make = (hosts, options) => Effect.gen(function* () {
575
610
  const result = yield* Fiber.join(fiber);
576
611
  if (Result.isFailure(result)) {
577
612
  noReply ||= Predicate.isTagged(result.failure, "Unavailable");
578
- misses.push(`${host} failed: ${result.failure._tag}: ${result.failure.message}`);
613
+ misses.push(`${host} failed: ${result.failure.message}`);
579
614
  } else if (result.success.bases.includes(base)) return host;
580
615
  else {
581
616
  const { bases } = result.success;
@@ -622,4 +657,4 @@ const make = (hosts, options) => Effect.gen(function* () {
622
657
  };
623
658
  });
624
659
  //#endregion
625
- export { ConflictKind as A, Id as C, parseName as D, parseId as E, NotFound as F, Precondition as I, Unavailable as L, HostErrorTag as M, Internal as N, Capacity as O, Invalid as P, hostErrors as R, HostId as S, formatId as T, Setup as _, readTimeout as a, release as b, ActionRecord as c, Host as d, Limit as f, Runtime as g, Profile as h, Http as i, ErrorTag as j, Conflict as k, Checkpoint as l, MachineSpec as m, client_exports as n, versionHeader as o, Machine as p, make as r, ActionName as s, Client as t, CreateRequest as u, SshEndpoint as v, Name as w, version as x, SshLogin as y };
660
+ export { parseName as A, Unavailable as B, release as C, Name as D, Id as E, HostErrorTag as F, Internal as I, Invalid as L, Conflict as M, ConflictKind as N, formatId as O, ErrorTag as P, NotFound as R, SshLogin as S, HostId as T, hostErrors as V, MachineSpec as _, describeIssues as a, Setup as b, readTimeout as c, ActionRecord as d, Checkpoint as f, Machine as g, Limit as h, describeFileError as i, Capacity as j, parseId as k, versionHeader as l, Host as m, client_exports as n, formatMib as o, CreateRequest as p, make as r, Http as s, Client as t, ActionName as u, Profile as v, version as w, SshEndpoint as x, Runtime as y, Precondition as z };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { A as Precondition, C as ConflictKind, D as Internal, E as HostErrorTag, M as hostErrors, O as Invalid, S as Conflict, T as HostError, _ as SshLogin, a as ActionName, b as Capacity, c as CreateRequest, d as Machine, f as MachineSpec, g as SshEndpoint, h as Setup, i as client_d_exports, j as Unavailable, k as NotFound, l as Host, m as Runtime, o as ActionRecord, p as Profile, s as Checkpoint, u as Limit, v as release, w as ErrorTag, x as ClankerboxError, y as version } from "./client-SX6jTU1R.mjs";
2
2
  import * as Action from "@gjermundgaraba/effect-actions/Action";
3
3
  import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
4
- import { Duration, Effect, FileSystem, Path, Schema } from "effect";
4
+ import { Duration, Effect, FileSystem, Path, PlatformError, Schema } from "effect";
5
5
  import { ChildProcessSpawner } from "effect/process";
6
6
  //#region src/api.d.ts
7
7
  /**
@@ -330,4 +330,21 @@ export declare const packRecipe: (directory: string) => Effect.Effect<string, In
330
330
  /** Reads a setup: a script file's text, or a recipe directory packed into one script. */
331
331
  export declare const readSetup: (path: string) => Effect.Effect<string, Invalid, FileSystem.FileSystem | ChildProcessSpawner.ChildProcessSpawner>;
332
332
  //#endregion
333
+ //#region src/text.d.ts
334
+ /**
335
+ * A RAM size, exactly, as a size flag takes one: GiB when it is whole or half GiB, `32G` or
336
+ * `1.5G`, and MiB when it isn't, `512M` or `1500M`.
337
+ */
338
+ export declare const formatMib: (mib: number) => string;
339
+ /**
340
+ * Everything a decode refused, on one line, each under its JSON path: `cpu: missing; ram: not a
341
+ * known field`. `names` renames top-level fields, as when a flag set each: `--cpu: missing`.
342
+ */
343
+ export declare const describeIssues: (error: Schema.SchemaError, names?: ReadonlyMap<PropertyKey, string>) => string;
344
+ /**
345
+ * Why a file couldn't be read, without the platform call that met it. Node describes a failure
346
+ * only in its cause, so what has no wording here reads as the system's own.
347
+ */
348
+ export declare const describeFileError: ({ reason }: PlatformError.PlatformError) => string;
349
+ //#endregion
333
350
  export { ActionName, ActionRecord, Capacity, Checkpoint, ClankerboxError, client_d_exports as Client, Conflict, ConflictKind, CreateRequest, ErrorTag, Host, HostError, HostErrorTag, Internal, Invalid, Limit, Machine, MachineSpec, NotFound, Precondition, Profile, Runtime, Setup, SshEndpoint, SshLogin, Unavailable, hostErrors, release, version };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as ConflictKind, C as Id, D as parseName, E as parseId, F as NotFound, I as Precondition, L as Unavailable, M as HostErrorTag, N as Internal, O as Capacity, P as Invalid, R as hostErrors, S as HostId, T as formatId, _ as Setup, a as readTimeout, b as release, c as ActionRecord, d as Host, f as Limit, g as Runtime, h as Profile, i as Http, j as ErrorTag, k as Conflict, l as Checkpoint, m as MachineSpec, n as client_exports, o as versionHeader, p as Machine, s as ActionName, u as CreateRequest, v as SshEndpoint, w as Name, x as version, y as SshLogin } from "./client-VNMPU7ER.mjs";
1
+ import { A as parseName, B as Unavailable, C as release, D as Name, E as Id, F as HostErrorTag, I as Internal, L as Invalid, M as Conflict, N as ConflictKind, O as formatId, P as ErrorTag, R as NotFound, S as SshLogin, T as HostId, V as hostErrors, _ as MachineSpec, a as describeIssues, b as Setup, c as readTimeout, d as ActionRecord, f as Checkpoint, g as Machine, h as Limit, i as describeFileError, j as Capacity, k as parseId, l as versionHeader, m as Host, n as client_exports, o as formatMib, p as CreateRequest, s as Http, u as ActionName, v as Profile, w as version, x as SshEndpoint, y as Runtime, z as Precondition } from "./client-cFk3r2_1.mjs";
2
2
  import { Effect, FileSystem, Path, Predicate, Schema, Stream } from "effect";
3
3
  import { ChildProcess, ChildProcessSpawner } from "effect/process";
4
4
  //#region src/profile.ts
@@ -13,8 +13,11 @@ const decodeProfile = Schema.decodeUnknownEffect(Schema.fromJsonString(Profile))
13
13
  const loadProfile = (file) => Effect.gen(function* () {
14
14
  const fs = yield* FileSystem.FileSystem;
15
15
  const path = yield* Path.Path;
16
- const text = yield* fs.readFileString(file).pipe(Effect.mapError((error) => new Invalid({ message: `couldn't read profile ${file}: ${error.message}` })));
17
- const profile = yield* decodeProfile(text, { onExcessProperty: "error" }).pipe(Effect.mapError((error) => new Invalid({ message: `profile ${file}: ${error.message}` })));
16
+ const text = yield* fs.readFileString(file).pipe(Effect.mapError((error) => new Invalid({ message: `couldn't read profile ${file}: ${describeFileError(error)}` })));
17
+ const profile = yield* decodeProfile(text, {
18
+ onExcessProperty: "error",
19
+ errors: "all"
20
+ }).pipe(Effect.mapError((error) => new Invalid({ message: `profile ${file}: ${describeIssues(error)}` })));
18
21
  const label = path.basename(file, ".json");
19
22
  return profile.setup === void 0 ? {
20
23
  ...profile,
@@ -95,12 +98,12 @@ const packRecipe = (directory) => Effect.gen(function* () {
95
98
  ], { concurrency: "unbounded" });
96
99
  if (code !== 0) return yield* new Invalid({ message: `tar couldn't pack recipe ${directory} (exit ${code}): ${errors.trim()}` });
97
100
  return selfExtracting(Buffer.from(archive.buffer, archive.byteOffset, archive.byteLength).toString("base64"));
98
- }).pipe(Effect.scoped, Effect.catchTag("PlatformError", (error) => Effect.fail(new Invalid({ message: `couldn't pack recipe ${directory}: ${error.message}` }))));
101
+ }).pipe(Effect.scoped, Effect.catchTag("PlatformError", (error) => Effect.fail(new Invalid({ message: `couldn't pack recipe ${directory}: ${describeFileError(error)}` }))));
99
102
  /** Reads a setup: a script file's text, or a recipe directory packed into one script. */
100
103
  const readSetup = (path) => Effect.gen(function* () {
101
104
  const fs = yield* FileSystem.FileSystem;
102
105
  if ((yield* fs.stat(path)).type === "Directory") return yield* packRecipe(path);
103
106
  return yield* fs.readFileString(path);
104
- }).pipe(Effect.catchTag("PlatformError", (error) => Effect.fail(new Invalid({ message: `couldn't read setup ${path}: ${error.message}` }))));
107
+ }).pipe(Effect.catchTag("PlatformError", (error) => Effect.fail(new Invalid({ message: `couldn't read setup ${path}: ${describeFileError(error)}` }))));
105
108
  //#endregion
106
- export { ActionName, ActionRecord, Capacity, Checkpoint, client_exports as Client, Conflict, ConflictKind, CreateRequest, ErrorTag, Host, HostErrorTag, HostId, Http, Id, Internal, Invalid, Limit, Machine, MachineSpec, Name, NotFound, Precondition, Profile, Runtime, Setup, SshEndpoint, SshLogin, Unavailable, formatId, hostErrors, loadProfile, packRecipe, parseId, parseName, readSetup, readTimeout, release, version, versionHeader };
109
+ export { ActionName, ActionRecord, Capacity, Checkpoint, client_exports as Client, Conflict, ConflictKind, CreateRequest, ErrorTag, Host, HostErrorTag, HostId, Http, Id, Internal, Invalid, Limit, Machine, MachineSpec, Name, NotFound, Precondition, Profile, Runtime, Setup, SshEndpoint, SshLogin, Unavailable, describeFileError, describeIssues, formatId, formatMib, hostErrors, loadProfile, packRecipe, parseId, parseName, readSetup, readTimeout, release, version, versionHeader };
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { r as make, t as Client } from "./client-VNMPU7ER.mjs";
1
+ import { r as make, t as Client } from "./client-cFk3r2_1.mjs";
2
2
  import { Layer } from "effect";
3
3
  import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient";
4
4
  //#region src/node.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjermundgaraba/clankerbox-sdk",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "clankerbox's API contract and client library, for Effect",
5
5
  "license": "MIT",
6
6
  "repository": {