@gjermundgaraba/clankerbox-sdk 0.16.0 → 0.18.1

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/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as HostErrorTag, C as actionsByName, D as Conflict, E as ClankerboxError, F as Unavailable, I as hostErrors, M as Invalid, N as NotFound, O as ConflictKind, P as Precondition, S as Http, T as Capacity, _ as MachineSpec, b as SshEndpoint, c as ActionRecord, d as CheckpointActionName, f as CheckpointActionRecord, g as Machine, h as Limit, i as Interface, j as Internal, k as HostError, l as ActionStatus, m as Host, n as CreateOptions$1, o as client_d_exports, p as CreateRequest, s as ActionName, u as Checkpoint, v as Runtime, w as readTimeout, x as SshLogin, y as Setup } from "./client-DgDb-TR7.mjs";
1
+ import { A as ConflictKind, B as Refused, C as Declared, D as Capacity, E as readTimeout, F as NotFound, G as hostErrors, H as Unavailable, I as NotMade, K as retryable, L as NotOffered, M as Internal, N as Invalid, O as ClankerboxError, P as Missing, R as NotReady, S as stateLabel, T as actionsByName, U as Unsizable, V as ScriptFailed, W as WrongState, _ as Setup, b as Usage, c as ActionRecord, d as CreateRequest, f as Host, g as Runtime, h as Reading, i as Interface, j as HostError, k as Conflict, l as ActionStatus, m as MachineSpec, n as CreateOptions$1, o as client_d_exports, p as Machine, s as ActionName, u as Checkpoint, v as SshEndpoint, w as Http, x as isReady, y as SshLogin, z as PinMismatch } from "./client-8af586hb.mjs";
2
2
  import { Effect, FileSystem, Path, PlatformError, Schema, Scope } from "effect";
3
3
  //#region src/ids.d.ts
4
4
  export declare const HostId: Schema.String;
@@ -46,11 +46,14 @@ interface Built {
46
46
  }
47
47
  /**
48
48
  * Builds the spec's prebuild on the host a create of it is placed on, with a machine of its own
49
- * that it deletes however it ends. A prebuild that is there and done is the answer; one that
50
- * failed or is still being captured is refused. Of two builds of one spec at once, the second's
51
- * capture is `Conflict`, and neither touches the other's machine or prebuild.
49
+ * that it deletes however it ends. A prebuild that is there and done is the answer, one that
50
+ * failed is deleted and built again, and one still running is `Conflict{busy}`, as its capture
51
+ * holds it. Of two builds of one spec at once, the second's capture is `Conflict`, and neither
52
+ * touches the other's machine.
52
53
  */
53
- declare const build: (client: Interface, spec: MachineSpec, options?: CreateOptions$1) => Effect.Effect<Built, ClankerboxError>;
54
+ declare const build: (client: Interface, spec: MachineSpec & {
55
+ readonly setup: Setup;
56
+ }, options?: CreateOptions$1) => Effect.Effect<Built, ClankerboxError>;
54
57
  //#endregion
55
58
  //#region src/release.d.ts
56
59
  /** Also the version of the clankerbox binaries it ships with. */
@@ -120,21 +123,22 @@ export interface SshTarget {
120
123
  readonly login: SshLogin;
121
124
  readonly hostKey: string;
122
125
  }
123
- /** Only a running machine has an endpoint, and only a prepared one a host key. */
124
- export declare const sshTarget: (machine: Machine) => SshTarget | {
125
- readonly lacks: "SSH endpoint" | "SSH host key on record";
126
- };
127
126
  /**
128
- * ssh's arguments, pinned to the target's host key through a known-hosts file that lasts as long
129
- * as the scope, which must outlast the ssh. Ports change on fork, restore, and every boat start,
130
- * so read the target anew for each ssh.
127
+ * Only a running machine has an endpoint, and only a prepared one a host key; a machine without
128
+ * either is `WrongState`, naming what it lacks and why.
129
+ */
130
+ export declare const sshTarget: (machine: Machine) => Effect.Effect<SshTarget, WrongState>;
131
+ /** A known_hosts line pinning `name` to a host key on record, `<type> <base64>`. */
132
+ export declare const knownHostsLine: (name: string, hostKey: string) => string;
133
+ /**
134
+ * ssh's arguments to the target's address, pinned to its host key through a known-hosts file that
135
+ * lasts as long as the scope, which must outlast the ssh. The destination is the address, not the
136
+ * ID, so no ssh_config block for the ID applies. Ports change on fork, restore, and every boat
137
+ * start, so read the target anew for each ssh.
131
138
  */
132
139
  export declare const pinnedSsh: (target: SshTarget, user: string, extra: ReadonlyArray<string>) => Effect.Effect<Array<string>, PlatformError.PlatformError, FileSystem.FileSystem | Path.Path | Scope.Scope>;
133
140
  //#endregion
134
- //#region src/text.d.ts
135
- export declare const mibPerGib = 1024;
136
- /** Exactly, as a size flag takes it: `32G`, `1.5G`, `512M`, `1500M`. */
137
- export declare const formatMib: (mib: number) => string;
141
+ //#region src/describe.d.ts
138
142
  /** On one line: `cpu: missing; ram: not a known field`. `names` renames top-level fields. */
139
143
  export declare const describeIssues: (error: Schema.SchemaError, names?: ReadonlyMap<PropertyKey, string>) => string;
140
144
  /**
@@ -143,4 +147,11 @@ export declare const describeIssues: (error: Schema.SchemaError, names?: Readonl
143
147
  */
144
148
  export declare const describeFileError: ({ reason }: PlatformError.PlatformError) => string;
145
149
  //#endregion
146
- export { ActionName, ActionRecord, ActionStatus, Capacity, Checkpoint, CheckpointActionName, CheckpointActionRecord, ClankerboxError, client_d_exports as Client, Conflict, ConflictKind, CreateRequest, Host, HostError, HostErrorTag, Http, Internal, Invalid, Limit, Machine, MachineSpec, NotFound, prebuild_d_exports as Prebuild, Precondition, Runtime, Setup, SshEndpoint, SshLogin, Unavailable, actionsByName, hostErrors, readTimeout };
150
+ //#region src/sizes.d.ts
151
+ export declare const mibPerGib = 1024;
152
+ /** Exactly, as a size flag takes it: `32G`, `1.5G`, `512M`, `1500M`. */
153
+ export declare const formatMib: (mib: number) => string;
154
+ /** As a size flag takes it: `60G`. */
155
+ export declare const formatGib: (gib: number) => string;
156
+ //#endregion
157
+ export { ActionName, ActionRecord, ActionStatus, Capacity, Checkpoint, ClankerboxError, client_d_exports as Client, Conflict, ConflictKind, CreateRequest, Declared, Host, HostError, Http, Internal, Invalid, Machine, MachineSpec, Missing, NotFound, NotMade, NotOffered, NotReady, PinMismatch, prebuild_d_exports as Prebuild, Reading, Refused, Runtime, ScriptFailed, Setup, SshEndpoint, SshLogin, Unavailable, Unsizable, Usage, WrongState, actionsByName, hostErrors, isReady, readTimeout, retryable, stateLabel };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { A as Name, B as Invalid, C as MachineSpec, D as SshLogin, E as SshEndpoint, F as ClankerboxError, H as Precondition, I as Conflict, L as ConflictKind, M as parseId, N as parseName, O as HostId, P as Capacity, R as HostErrorTag, S as Machine, T as Setup, U as Unavailable, V as NotFound, W as hostErrors, _ as CheckpointActionName, a as formatMib, b as Host, c as version, d as actionsByName, f as readTimeout, g as Checkpoint, h as ActionStatus, i as describeIssues, j as formatId, k as Id, l as versionHeader, m as ActionRecord, o as mibPerGib, p as ActionName, r as describeFileError, s as release, t as client_exports, u as Http, v as CheckpointActionRecord, w as Runtime, x as Limit, y as CreateRequest, z as Internal } from "./client-CLemHDCl.mjs";
2
+ import { A as parseId, B as NotFound, C as Usage, D as Id, E as HostId, F as ConflictKind, G as Refused, H as NotOffered, I as HostError, J as Unsizable, K as ScriptFailed, L as Internal, M as Capacity, N as ClankerboxError, O as Name, P as Conflict, R as Invalid, S as SshLogin, T as stateLabel, U as NotReady, V as NotMade, W as PinMismatch, X as hostErrors, Y as WrongState, Z as retryable, _ as Machine, a as release, b as Setup, c as Http, d as ActionName, f as ActionRecord, g as Host, h as CreateRequest, i as describeIssues, j as parseName, k as formatId, l as actionsByName, m as Checkpoint, o as version, p as ActionStatus, q as Unavailable, r as describeFileError, s as versionHeader, t as client_exports, u as readTimeout, v as MachineSpec, w as isReady, x as SshEndpoint, y as Runtime, z as Missing } from "./client-JikMOSHo.mjs";
3
3
  import { Effect, FileSystem, Option, Path, Predicate, Record, Schema } from "effect";
4
- import { Base64 } from "effect/encoding";
4
+ import { Base64, Hex } from "effect/encoding";
5
5
  //#region src/prebuild.ts
6
6
  var prebuild_exports = /* @__PURE__ */ __exportAll({
7
7
  build: () => build,
@@ -10,12 +10,7 @@ var prebuild_exports = /* @__PURE__ */ __exportAll({
10
10
  });
11
11
  const prefix = "prebuilt-";
12
12
  const digestDigits = 12;
13
- /**
14
- * 64 bits, so no two builds draw one name. With `prebuilding-` it is 28 characters, inside the 29
15
- * a 32-character host ID and its `_` leave of an ID's 62.
16
- */
17
13
  const builderBytes = 8;
18
- const hex = (bytes) => Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, "0")).join("");
19
14
  /**
20
15
  * A digest of the spec's label, base, sizes, setup script as sent, and the host's runtime
21
16
  * version: a `ram` checkpoint restores only under the release that saved it, so after a runtime
@@ -30,8 +25,8 @@ const nameOf = (spec, host) => Effect.promise(() => globalThis.crypto.subtle.dig
30
25
  spec.diskGib,
31
26
  spec.setup?.script ?? null,
32
27
  host.runtimeVersion
33
- ])))).pipe(Effect.map((digest) => `${prefix}${hex(digest).slice(0, digestDigits)}`));
34
- const lookup = (client, id) => client.checkpoint(id).pipe(Effect.map(Option.some), Effect.catchTag("NotFound", () => Effect.succeedNone));
28
+ ])))).pipe(Effect.map((digest) => `${prefix}${Hex.encode(new Uint8Array(digest)).slice(0, digestDigits)}`));
29
+ const lookup = (client, id) => client.getCheckpoint({ id }).pipe(Effect.map(Option.some), Effect.catchTag("NotFound", () => Effect.succeedNone));
35
30
  /**
36
31
  * `client.create`, restoring the spec's prebuild when the host it is placed on has one whose
37
32
  * capture is done, unless `fresh`. A failed restore is the reply.
@@ -41,39 +36,53 @@ const create = (client, name, spec, options) => Effect.gen(function* () {
41
36
  const host = yield* client.place(spec.base, options);
42
37
  const id = yield* formatId(host.id, yield* nameOf(spec, host));
43
38
  const found = yield* lookup(client, id);
44
- if (Option.isSome(found) && found.value.action.status === "done") return {
45
- machine: yield* client.restore(id, name),
39
+ if (Option.isSome(found) && isReady(found.value)) return {
40
+ machine: yield* client.restoreMachine({
41
+ checkpoint: id,
42
+ name
43
+ }),
46
44
  prebuild: found.value
47
45
  };
48
46
  return { machine: yield* client.create(name, spec, { host: host.id }) };
49
47
  });
50
48
  /**
51
49
  * Builds the spec's prebuild on the host a create of it is placed on, with a machine of its own
52
- * that it deletes however it ends. A prebuild that is there and done is the answer; one that
53
- * failed or is still being captured is refused. Of two builds of one spec at once, the second's
54
- * capture is `Conflict`, and neither touches the other's machine or prebuild.
50
+ * that it deletes however it ends. A prebuild that is there and done is the answer, one that
51
+ * failed is deleted and built again, and one still running is `Conflict{busy}`, as its capture
52
+ * holds it. Of two builds of one spec at once, the second's capture is `Conflict`, and neither
53
+ * touches the other's machine.
55
54
  */
56
55
  const build = (client, spec, options) => Effect.gen(function* () {
57
- if (spec.setup === void 0) return yield* new Invalid({ message: "a prebuild needs a setup" });
58
56
  const host = yield* client.place(spec.base, options);
59
57
  const name = yield* nameOf(spec, host);
60
58
  const id = yield* formatId(host.id, name);
61
59
  const found = yield* lookup(client, id);
62
60
  if (Option.isSome(found)) {
63
61
  const { action } = found.value;
64
- return action.status === "done" ? {
62
+ if (isReady(found.value)) return {
65
63
  checkpoint: found.value,
66
64
  built: false
67
- } : yield* new Precondition({ message: `prebuild ${id}'s ${action.name} is ${action.status}; delete it to prebuild again` });
65
+ };
66
+ if (action.status === "running") return yield* new Conflict({
67
+ message: `prebuild ${id} is busy: its ${action.name} is running`,
68
+ kind: "busy",
69
+ host: host.id,
70
+ action: "captureCheckpoint",
71
+ id
72
+ });
73
+ yield* client.deleteCheckpoint({ id });
68
74
  }
69
- const builder = `prebuilding-${hex(globalThis.crypto.getRandomValues(new Uint8Array(builderBytes)))}`;
75
+ const builder = `prebuilding-${Hex.encode(globalThis.crypto.getRandomValues(new Uint8Array(builderBytes)))}`;
70
76
  const builderId = yield* formatId(host.id, builder);
71
- const removal = Effect.catchTag(client.delete(builderId), "NotFound", () => Effect.void);
77
+ const removal = client.deleteMachine({ id: builderId });
72
78
  return {
73
79
  checkpoint: yield* Effect.gen(function* () {
74
80
  yield* client.create(builder, spec, { host: host.id });
75
- if (host.captureFrom === "stopped") yield* client.stop(builderId);
76
- return yield* client.capture(builderId, name);
81
+ if (host.captureFrom === "stopped") yield* client.stopMachine({ id: builderId });
82
+ return yield* client.captureCheckpoint({
83
+ machine: builderId,
84
+ name
85
+ });
77
86
  }).pipe(Effect.onError(() => Effect.ignore(removal))),
78
87
  built: true,
79
88
  leftover: yield* removal.pipe(Effect.as(void 0), Effect.catch(() => Effect.succeed(builderId)))
@@ -81,9 +90,7 @@ const build = (client, spec, options) => Effect.gen(function* () {
81
90
  });
82
91
  //#endregion
83
92
  //#region src/setup.ts
84
- /** Nothing here logs a script or a packed recipe: a recipe can carry secrets. */
85
93
  const lineLength = 76;
86
- /** base64 has no `_`, so no line of a file's payload can end its here-document early. */
87
94
  const terminator = "CLANKERBOX_RECIPE";
88
95
  const wrap = (base64) => {
89
96
  const lines = [];
@@ -91,14 +98,8 @@ const wrap = (base64) => {
91
98
  return lines.join("\n");
92
99
  };
93
100
  const quote = (text) => `'${text.replaceAll("'", `'\\''`)}'`;
94
- /** Under `./`, so a name that starts with `-` is never read as an option. */
95
101
  const argument = (entry) => quote(`./${entry}`);
96
- /** The one bit of a mode a recipe carries, as git does, so a checkout's umask changes nothing. */
97
102
  const executable = 73;
98
- /**
99
- * Under `/var/tmp`, which nothing mounts `noexec`. Setup runs as a child rather than through
100
- * `exec`, so the cleanup trap still runs after it.
101
- */
102
103
  const selfExtracting = (steps) => [
103
104
  "#!/bin/sh",
104
105
  "set -eu",
@@ -242,36 +243,35 @@ const specRequest = (what, profile, overrides, names) => Effect.gen(function* ()
242
243
  });
243
244
  //#endregion
244
245
  //#region src/ssh.ts
245
- /** Only a running machine has an endpoint, and only a prepared one a host key. */
246
+ /**
247
+ * Only a running machine has an endpoint, and only a prepared one a host key; a machine without
248
+ * either is `WrongState`, naming what it lacks and why.
249
+ */
246
250
  const sshTarget = (machine) => {
247
- const { id, ssh: login, hostKey } = machine;
248
- if (login === void 0) return { lacks: "SSH endpoint" };
249
- return hostKey === void 0 ? { lacks: "SSH host key on record" } : {
251
+ const { id, ssh: login, hostKey, action } = machine;
252
+ if (login !== void 0 && hostKey !== void 0) return Effect.succeed({
250
253
  id,
251
254
  login,
252
255
  hostKey
253
- };
256
+ });
257
+ const lacks = login === void 0 ? "SSH endpoint" : "SSH host key on record";
258
+ const state = stateLabel(machine);
259
+ const message = `${id} has no ${lacks} (${state}, ${action.name} ${action.status}): see clankerbox get ${id}`;
260
+ return Effect.fail(new WrongState({
261
+ message,
262
+ machine: id,
263
+ state
264
+ }));
254
265
  };
255
- /**
256
- * ssh looks the key up by `HostKeyAlias`, so the line doesn't depend on the endpoint, which
257
- * changes.
258
- */
259
- const knownHostsLine = (id, hostKey) => {
266
+ /** A known_hosts line pinning `name` to a host key on record, `<type> <base64>`. */
267
+ const knownHostsLine = (name, hostKey) => {
260
268
  const [type = "", key = ""] = hostKey.trim().split(/\s+/u);
261
- return `${id} ${type} ${key}\n`;
269
+ return `${name} ${type} ${key}\n`;
262
270
  };
263
- /**
264
- * OpenSSH keeps the first user it is given, so the leading `-l` decides it. The destination is
265
- * the machine's ID, so a `Host <pattern>` block in the user's ssh config can match it, while the
266
- * endpoint and the pinned key come from options no config overrides. The caller's arguments go
267
- * after the destination, where OpenSSH still reads options (`-L …`) before a remote command.
268
- */
269
271
  const sshArguments = (target, user, knownHosts, extra) => [
270
272
  "-l",
271
273
  user,
272
274
  "-o",
273
- `HostName=${target.login.host}`,
274
- "-o",
275
275
  `Port=${target.login.port}`,
276
276
  "-o",
277
277
  `HostKeyAlias=${target.id}`,
@@ -281,13 +281,14 @@ const sshArguments = (target, user, knownHosts, extra) => [
281
281
  "GlobalKnownHostsFile=/dev/null",
282
282
  "-o",
283
283
  "StrictHostKeyChecking=yes",
284
- target.id,
284
+ target.login.host,
285
285
  ...extra
286
286
  ];
287
287
  /**
288
- * ssh's arguments, pinned to the target's host key through a known-hosts file that lasts as long
289
- * as the scope, which must outlast the ssh. Ports change on fork, restore, and every boat start,
290
- * so read the target anew for each ssh.
288
+ * ssh's arguments to the target's address, pinned to its host key through a known-hosts file that
289
+ * lasts as long as the scope, which must outlast the ssh. The destination is the address, not the
290
+ * ID, so no ssh_config block for the ID applies. Ports change on fork, restore, and every boat
291
+ * start, so read the target anew for each ssh.
291
292
  */
292
293
  const pinnedSsh = (target, user, extra) => Effect.gen(function* () {
293
294
  const fs = yield* FileSystem.FileSystem;
@@ -298,4 +299,11 @@ const pinnedSsh = (target, user, extra) => Effect.gen(function* () {
298
299
  return sshArguments(target, user, knownHosts, extra);
299
300
  });
300
301
  //#endregion
301
- export { ActionName, ActionRecord, ActionStatus, Capacity, Checkpoint, CheckpointActionName, CheckpointActionRecord, ClankerboxError, client_exports as Client, Conflict, ConflictKind, CreateRequest, Host, HostErrorTag, HostId, Http, Id, Internal, Invalid, Limit, Machine, MachineSpec, Name, NotFound, prebuild_exports as Prebuild, Precondition, Profile, Runtime, Setup, SshEndpoint, SshLogin, Unavailable, actionsByName, describeFileError, describeIssues, formatId, formatMib, hostErrors, loadProfile, mibPerGib, packRecipe, parseId, parseName, pinnedSsh, readSetup, readTimeout, release, specRequest, sshTarget, version, versionHeader };
302
+ //#region src/sizes.ts
303
+ const mibPerGib = 1024;
304
+ /** Exactly, as a size flag takes it: `32G`, `1.5G`, `512M`, `1500M`. */
305
+ const formatMib = (mib) => mib >= 1024 && mib % 512 === 0 ? `${mib / mibPerGib}G` : `${mib}M`;
306
+ /** As a size flag takes it: `60G`. */
307
+ const formatGib = (gib) => `${gib}G`;
308
+ //#endregion
309
+ export { ActionName, ActionRecord, ActionStatus, Capacity, Checkpoint, ClankerboxError, client_exports as Client, Conflict, ConflictKind, CreateRequest, Host, HostError, HostId, Http, Id, Internal, Invalid, Machine, MachineSpec, Missing, Name, NotFound, NotMade, NotOffered, NotReady, PinMismatch, prebuild_exports as Prebuild, Profile, Refused, Runtime, ScriptFailed, Setup, SshEndpoint, SshLogin, Unavailable, Unsizable, Usage, WrongState, actionsByName, describeFileError, describeIssues, formatGib, formatId, formatMib, hostErrors, isReady, knownHostsLine, loadProfile, mibPerGib, packRecipe, parseId, parseName, pinnedSsh, readSetup, readTimeout, release, retryable, specRequest, sshTarget, stateLabel, version, versionHeader };
package/dist/node.d.mts CHANGED
@@ -1,6 +1,10 @@
1
- import { M as Invalid, a as Options, o as client_d_exports, r as HostEntry, t as Client } from "./client-DgDb-TR7.mjs";
1
+ import { N as Invalid, a as Options, r as HostEntry, t as Client } from "./client-8af586hb.mjs";
2
2
  import { Layer } from "effect";
3
3
  //#region src/node.d.ts
4
- /** Over Node's `http`, which sets no timeout of its own: a mutation can run for hours. */
4
+ /**
5
+ * The client over Node's `http`, which sets no timeout of its own: a mutation can run for hours.
6
+ * It is in `./node`, apart from `.`, so an app that brings its own `HttpClient` never loads
7
+ * `@effect/platform-node`.
8
+ */
5
9
  export declare const layer: (hosts: ReadonlyArray<HostEntry>, options?: Options) => Layer.Layer<Client, Invalid>;
6
10
  //#endregion
package/dist/node.mjs CHANGED
@@ -1,12 +1,12 @@
1
- import { n as layer$1 } from "./client-CLemHDCl.mjs";
1
+ import { n as layer$1 } from "./client-JikMOSHo.mjs";
2
2
  import { Layer } from "effect";
3
3
  import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient";
4
4
  //#region src/node.ts
5
5
  /**
6
- * Apart from `.`, so an app that brings its own `HttpClient` never loads
6
+ * The client over Node's `http`, which sets no timeout of its own: a mutation can run for hours.
7
+ * It is in `./node`, apart from `.`, so an app that brings its own `HttpClient` never loads
7
8
  * `@effect/platform-node`.
8
9
  */
9
- /** Over Node's `http`, which sets no timeout of its own: a mutation can run for hours. */
10
10
  const layer = (hosts, options) => layer$1(hosts, options).pipe(Layer.provide(NodeHttpClient.layerNodeHttp));
11
11
  //#endregion
12
12
  export { layer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjermundgaraba/clankerbox-sdk",
3
- "version": "0.16.0",
3
+ "version": "0.18.1",
4
4
  "description": "clankerbox's API contract and client library, for Effect",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,7 +28,7 @@
28
28
  "access": "public"
29
29
  },
30
30
  "dependencies": {
31
- "@gjermundgaraba/effect-actions": "0.14.0"
31
+ "@gjermundgaraba/effect-actions": "0.17.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@effect/platform-node": "4.0.4",
@@ -36,8 +36,9 @@
36
36
  "@types/node": "26.6.4",
37
37
  "effect": "4.0.4",
38
38
  "typescript": "7.0.2",
39
- "vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
40
- "vite-plus": "1.1.0"
39
+ "vite": "npm:@voidzero-dev/vite-plus-core@1.2.0",
40
+ "vite-plus": "1.2.0",
41
+ "vitest": "5.0.4"
41
42
  },
42
43
  "peerDependencies": {
43
44
  "@effect/platform-node": "~4.0.4",