From 97ed21c2509b087524e5afba8055a8a60498fcbc Mon Sep 17 00:00:00 2001 From: mesh-tools Date: Tue, 20 Oct 2026 21:01:26 +0000 Subject: [PATCH] Add the Node build module's toolchain lock, main, recipe, guard and key keeper (issues 1488, 1484), part 16 of 19 --- .../cmd/mesh-build-node/keys.go | 42 ++ .../cmd/mesh-build-node/main.go | 61 ++ .../cmd/mesh-build-node/module.go | 178 ++++++ .../cmd/mesh-build-node/probe.go | 31 + .../cmd/mesh-build-node/recipe.go | 58 ++ .../files/toolchain/package-lock.json | 572 ++++++++++++++++++ 6 files changed, 942 insertions(+) create mode 100644 modules/mesh-build-node/cmd/mesh-build-node/keys.go create mode 100644 modules/mesh-build-node/cmd/mesh-build-node/main.go create mode 100644 modules/mesh-build-node/cmd/mesh-build-node/module.go create mode 100644 modules/mesh-build-node/cmd/mesh-build-node/probe.go create mode 100644 modules/mesh-build-node/cmd/mesh-build-node/recipe.go create mode 100644 modules/mesh-build-node/files/toolchain/package-lock.json diff --git a/modules/mesh-build-node/cmd/mesh-build-node/keys.go b/modules/mesh-build-node/cmd/mesh-build-node/keys.go new file mode 100644 index 00000000..9c92386d --- /dev/null +++ b/modules/mesh-build-node/cmd/mesh-build-node/keys.go @@ -0,0 +1,42 @@ +package main + +// The module's own forge key (novox/hq ADR 0466 §2, issue 1484): mesh-sdk's key keeper, kind builder, for the +// module's account in its home. It makes ~/.ssh/mesh_ed25519 when it is missing and writes one entry to this +// module's `keys` state at ., which gitea's provider reads to register the key on the forge user +// that reads and pushes nothing (ADR 0466 §3). + +import ( + "context" + "fmt" + "os" + "os/user" + "strings" + "time" + + stdio "git.novox.be/novox/mesh-sdk/go" + "git.novox.be/novox/mesh-sdk/go/forgekey" +) + +// keeperFor is this module's keeper: go/forgekey's, of kind builder, for account in home on node, whose entry lies +// at . of the keys state. +func keeperFor(home, node, account string, run forgekey.Runner, state forgekey.KeyPutter) *forgekey.KeyKeeper { + return &forgekey.KeyKeeper{Home: home, Node: node, Account: account, Kind: forgekey.KindBuilder, Run: run, State: state, Now: time.Now} +} + +// startKeeper runs the keeper when the runtime launched this process for a node: outside a launch — a build, a +// check — there is no state to put the entry in. +func startKeeper(ctx context.Context) { + node := strings.TrimSpace(os.Getenv("MESH_NODE")) + u, err := user.Current() + if node == "" || err != nil || u.HomeDir == "" { + return + } + k := keeperFor(u.HomeDir, node, u.Username, forgekey.ExecRunner, keysState{}) + k.Keep(ctx, func(f string, a ...any) { fmt.Fprintf(os.Stderr, "[mesh-build-node] "+f+"\n", a...) }) +} + +type keysState struct{} + +func (keysState) Put(key string, value any) (uint64, error) { + return stdio.State("keys").Put(key, value) +} diff --git a/modules/mesh-build-node/cmd/mesh-build-node/main.go b/modules/mesh-build-node/cmd/mesh-build-node/main.go new file mode 100644 index 00000000..7c54b9ed --- /dev/null +++ b/modules/mesh-build-node/cmd/mesh-build-node/main.go @@ -0,0 +1,61 @@ +// mesh-build-node: the Node and TypeScript build module (novox/hq ADR 0405, ADR 0466 §1, ADR 0469, ADR 0470; design +// 47 R4c-3, issue 1488). A Go tools bundle the node's runtime launches as the module's own account (runs-as). It names +// the one Node version it builds with, installs the TypeScript toolchain its own lock names, writes the Node version +// it finds to .typescript of its `toolchains` state at start and before every step, and refuses every step on +// a node that has another: its health is then unhealthy, so the controller raises the node's condition. What it +// says, it says on stderr; stdout is the MCP channel. +package main + +import ( + "context" + "fmt" + "os" + "os/user" + "path/filepath" + "strings" + + stdio "git.novox.be/novox/mesh-sdk/go" + "git.novox.be/novox/mesh-sdk/go/delivery" + "git.novox.be/novox/mesh-sdk/go/forgekey" +) + +// CacheDir is the cache directory, outside the account's home (ADR 0466 §1; module.json resource `cache`). +const CacheDir = "/var/lib/mesh-build-node-cache" + +// WorkDir is the work directory, outside the home (module.json resource `work`). +const WorkDir = "/var/lib/mesh-build-node-work" + +func logf(format string, a ...any) { fmt.Fprintf(os.Stderr, "[mesh-build-node] "+format+"\n", a...) } + +func main() { + ctx := context.Background() + home := "" + if u, err := user.Current(); err == nil { + home = u.HomeDir + } + paths := Paths{Home: home, Cache: CacheDir} + registry := func() (Registry, error) { + return ReadRegistry(filepath.Join(home, "package-registry.json"), filepath.Join(home, "package-registry.secret")) + } + m := &Module{ + Node: strings.TrimSpace(os.Getenv("MESH_NODE")), + Reload: func() (Recipe, error) { return ReadRecipe(paths.LockPath()) }, + Probe: ProbeNode, + Toolchain: &Toolchain{Paths: paths, Run: forgekey.ExecRunnerClean(0), Registry: registry}, + Toolchains: stdio.State(delivery.ToolchainsState).Put, + Steps: stdio.State(delivery.StepsState).Put, + Work: WorkDir, + Run: forgekey.ExecRunnerClean(0), + Ctx: ctx, + Say: logf, + } + // Outside a launch by the runtime — a build, a check — there is no node and no state to write. + if m.Node != "" { + go m.Start(ctx) + } + go startKeeper(ctx) + if err := stdio.Serve("", m.Tools(m.StepTools())); err != nil { + logf("%v", err) + os.Exit(1) + } +} diff --git a/modules/mesh-build-node/cmd/mesh-build-node/module.go b/modules/mesh-build-node/cmd/mesh-build-node/module.go new file mode 100644 index 00000000..9b091cfe --- /dev/null +++ b/modules/mesh-build-node/cmd/mesh-build-node/module.go @@ -0,0 +1,178 @@ +package main + +import ( + "context" + "fmt" + "os" + "sync" + "time" + + "git.novox.be/novox/mesh-sdk/go/buildstep" + "git.novox.be/novox/mesh-sdk/go/delivery" + "git.novox.be/novox/mesh-sdk/go/forgekey" + "git.novox.be/novox/mesh-sdk/go/health" +) + +// CheckRound is how often the module reads the installed toolchain again, beside the check before every step and +// the one every health look makes. +const CheckRound = 10 * time.Minute + +// Module is the Node build module's one running state: the node it runs on, its recipe, the toolchain guard, the +// toolchain directory and the states it writes (toolchains, steps). +type Module struct { + Node string + // Recipe is the recipe in force. Reload, when set, reads it again at every check (the lock file the mesh + // writes can change under a running module); a failure to read it is a refusal. + Recipe Recipe + Reload func() (Recipe, error) + // Probe reads the installed Node version (ProbeNode). + Probe func(ctx context.Context) (string, error) + // Toolchain installs the toolchain directory; nil in a module that only guards. + Toolchain *Toolchain + // Work is the work directory, outside the home: a workspace is //tree. Run runs the commands of a + // step (a fake in a test). Clock is the time the steps are timed by. + Work string + Run forgekey.Runner + Clock func() time.Time + // Toolchains and Steps are the Put of the states of those names. + Toolchains, Steps buildstep.Put + // Ctx is the process's context, which a step runs under (the tool call's is cut at 30 s, the step's is not). + Ctx context.Context + Say func(format string, a ...any) + + mu sync.Mutex // guards Recipe + install sync.Mutex // one install at a time +} + +func (m *Module) recipe() Recipe { + m.mu.Lock() + defer m.mu.Unlock() + return m.Recipe +} + +func (m *Module) guard(r Recipe) buildstep.Guard { + return buildstep.Guard{Language: "typescript", Want: r.Node, Recipe: r.Version(), Probe: m.Probe, Node: m.Node, Put: m.Toolchains} +} + +// Check reads the installed toolchain, writes .typescript of the toolchains state and answers nil, or the +// refusal (buildstep.Guard.Check). It runs at start, before every step, at every health look and every CheckRound. +func (m *Module) Check(ctx context.Context) error { + if m.Reload != nil { + r, err := m.Reload() + if err != nil { + return &buildstep.Refusal{Text: err.Error(), Want: RecipeNode} + } + m.mu.Lock() + m.Recipe = r + m.mu.Unlock() + } + return m.guard(m.recipe()).Check(ctx) +} + +// Prepare is Check and then the toolchain directory of the recipe in force: what every step runs behind. +func (m *Module) Prepare(ctx context.Context) error { + if err := m.Check(ctx); err != nil { + return err + } + if m.Toolchain == nil { + return nil + } + m.install.Lock() + defer m.install.Unlock() + return m.Toolchain.Ensure(ctx, m.recipe().Version()) +} + +// Start prepares once, as the module starts, and then every CheckRound until ctx ends. +func (m *Module) Start(ctx context.Context) { + for { + if err := m.Prepare(ctx); err != nil && m.Say != nil { + m.Say("the toolchain: %v", err) + } + select { + case <-ctx.Done(): + return + case <-time.After(CheckRound): + } + } +} + +// Health is the module's health (the runtime-health tool, ADR 0366): unhealthy while the toolchain installed is not +// the recipe's, saying both versions, so the controller raises the node's condition. It looks again each time it is +// asked. +func (m *Module) Health(ctx context.Context) health.Answer { + if err := m.Check(ctx); err != nil { + return health.Unhealthy(err.Error()) + } + r := m.recipe() + return health.Healthy(fmt.Sprintf("node %s installed, as the recipe %s builds with", r.Node, r.Version())) +} + +// Long is a tool that runs a long step (ADR 0469 §2): it answers at once with the step's id, and the step ends on +// its key in the steps state. The guard runs inside the step, before run, so a node that refuses ends every step +// refused with both versions named. +func (m *Module) Long(tool string, run func(ctx context.Context, args map[string]any) (any, error)) func(map[string]any) (any, error) { + return func(args map[string]any) (any, error) { + id, err := buildstep.Start(m.Ctx, m.Steps, m.Node, tool, func(ctx context.Context) (any, error) { + if err := m.Prepare(ctx); err != nil { + return nil, err + } + return run(ctx, args) + }) + if err != nil { + return nil, err + } + key, _ := delivery.StepKey(m.Node, id) + return map[string]any{"step": id, "key": key, "state": delivery.StepRunning}, nil + } +} + +// Short is a tool that answers in its own call; the guard runs before it, and a refusal is the call's error. +func (m *Module) Short(run func(ctx context.Context, args map[string]any) (any, error)) func(map[string]any) (any, error) { + return func(args map[string]any) (any, error) { + if err := m.Prepare(m.Ctx); err != nil { + return nil, err + } + return run(m.Ctx, args) + } +} + +// Status is what the module reports of itself: the node, the recipe, the toolchain found and why it refuses, if it +// does. +func (m *Module) Status(ctx context.Context) map[string]any { + err := m.Check(ctx) + installed, _ := m.Probe(ctx) + r := m.recipe() + out := map[string]any{"node": m.Node, "recipe": r.Version(), "node-version": r.Node, "installed": installed, "refusing": err != nil} + if m.Toolchain != nil { + out["toolchain-dir"] = m.Toolchain.Paths.Dir(r.Version()) + out["toolchain-installed"] = m.Toolchain.Present(r.Version()) + } + if err != nil { + out["why"] = err.Error() + } + return out +} + +func (m *Module) now() time.Time { + if m.Clock != nil { + return m.Clock() + } + return time.Now() +} + +// defaultPath is where a step looks for node, npm, git and ssh when this process was given no PATH. +const defaultPath = "/usr/local/bin:/usr/bin:/bin" + +// baseEnv is the environment of a step's command beyond what the step adds: PATH, the account's own HOME (its ssh +// files and git configuration) and a fixed locale. A command is given these and its own words, never this process's +// environment, which holds the module's bus credentials (ADR 0466 §4). +func (m *Module) baseEnv() []string { + return []string{"PATH=" + pathOrDefault(), "HOME=" + m.Toolchain.homeOrEmpty(), "LC_ALL=C"} +} + +func pathOrDefault() string { + if path := os.Getenv("PATH"); path != "" { + return path + } + return defaultPath +} diff --git a/modules/mesh-build-node/cmd/mesh-build-node/probe.go b/modules/mesh-build-node/cmd/mesh-build-node/probe.go new file mode 100644 index 00000000..e2f99dff --- /dev/null +++ b/modules/mesh-build-node/cmd/mesh-build-node/probe.go @@ -0,0 +1,31 @@ +package main + +import ( + "context" + "fmt" + "os/exec" + "strings" + "time" +) + +// ProbeTimeout bounds `node --version`. +const ProbeTimeout = 15 * time.Second + +// ProbeNode reads the installed Node version: what `node --version` prints ("v26.10.0"), as RecipeNode is written. +func ProbeNode(ctx context.Context) (string, error) { + ctx, cancel := context.WithTimeout(ctx, ProbeTimeout) + defer cancel() + out, err := exec.CommandContext(ctx, "node", "--version").Output() + if err != nil { + return "", err + } + return parseNodeVersion(string(out)) +} + +func parseNodeVersion(out string) (string, error) { + v := strings.TrimSpace(out) + if len(v) < 2 || v[0] != 'v' || v[1] < '0' || v[1] > '9' || strings.ContainsAny(v, " \n\t") { + return "", fmt.Errorf("`node --version` answered %q", v) + } + return v, nil +} diff --git a/modules/mesh-build-node/cmd/mesh-build-node/recipe.go b/modules/mesh-build-node/cmd/mesh-build-node/recipe.go new file mode 100644 index 00000000..b024df00 --- /dev/null +++ b/modules/mesh-build-node/cmd/mesh-build-node/recipe.go @@ -0,0 +1,58 @@ +package main + +// The recipe of the Node build module (novox/hq ADR 0405 §1 and §7, ADR 0469 §1 and §4; issue 1488): the one Node +// version it builds with, and the recipe's own version, a hash of the recipe's version, that Node version and the +// bytes of the lock file that names the TypeScript toolchain. A node whose installed Node differs from the recipe's +// refuses every step (module.go), so all builders run this version or none builds. + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "os" +) + +// RecipeNode is the Node version the recipe builds with: the text `node --version` prints for it, which is what the +// guard compares and what the indexer reads from .typescript of the toolchains state. +const RecipeNode = "v26.10.0" + +// RecipeSteps is the recipe's own version: changed whenever the steps of fetch, test or build change in a way that +// can change what they produce (ADR 0405 §7). It is one of the three values the recipe version hashes. +const RecipeSteps = "1" + +// Recipe is every value that makes up the recipe version. +type Recipe struct { + // Steps is RecipeSteps. + Steps string + // Node is the toolchain's version as `node --version` prints it. + Node string + // Lock is the hex sha256 of the bytes of toolchain/package-lock.json (LockHash). + Lock string +} + +// LockHash is the text a lock file's bytes stand for in the recipe version. +func LockHash(lock []byte) string { + sum := sha256.Sum256(lock) + return hex.EncodeToString(sum[:]) +} + +// ReadRecipe is the recipe of this build of the module with the lock file read from path. A missing lock is an +// error: with no lock there is no toolchain to name. +func ReadRecipe(lockPath string) (Recipe, error) { + raw, err := os.ReadFile(lockPath) + if err != nil { + return Recipe{}, fmt.Errorf("the toolchain's lock file cannot be read: %w", err) + } + return Recipe{Steps: RecipeSteps, Node: RecipeNode, Lock: LockHash(raw)}, nil +} + +// Version is the recipe version: a hash of the recipe's own version, the Node version and the lock file's hash, so a +// change of any of the three moves it and with it every fingerprint that holds it (ADR 0405 §7, ADR 0465 §2). The +// fields are named in the hashed text, so no two recipes share a text. +func (r Recipe) Version() string { + h := sha256.New() + for _, f := range [][2]string{{"steps", r.Steps}, {"node", r.Node}, {"lock", r.Lock}} { + h.Write([]byte(f[0] + "=" + f[1] + "\n")) + } + return hex.EncodeToString(h.Sum(nil))[:16] +} diff --git a/modules/mesh-build-node/files/toolchain/package-lock.json b/modules/mesh-build-node/files/toolchain/package-lock.json new file mode 100644 index 00000000..e4867d9c --- /dev/null +++ b/modules/mesh-build-node/files/toolchain/package-lock.json @@ -0,0 +1,572 @@ +{ + "name": "mesh-build-node-toolchain", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mesh-build-node-toolchain", + "version": "1.0.0", + "dependencies": { + "@novox/mesh-sdk": "0.1.14", + "nats": "2.29.3" + }, + "devDependencies": { + "@types/node": "22.20.1", + "esbuild": "0.25.12", + "typescript": "5.9.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@novox/mesh-sdk": { + "version": "0.1.14", + "resolved": "https://git.novox.be/api/packages/novox/npm/%40novox%2Fmesh-sdk/-/0.1.14/mesh-sdk-0.1.14.tgz", + "integrity": "sha512-ICdIfVicQQXSw6fkLiwzoRx3weiWh0jsIEYG/m98CKgFIlziZzb7V+ZmT/TvPO9bTDFDtfqDLoFu+OXY5+ryQw==" + }, + "node_modules/@types/node": { + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/nats": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.3.tgz", + "integrity": "sha512-tOQCRCwC74DgBTk4pWZ9V45sk4d7peoE2njVprMRCBXrhJ5q5cYM7i6W+Uvw2qUrcfOSnuisrX7bEx3b3Wx4QA==", + "deprecated": "Package moved. Use @nats-io/transport-node from https://github.com/nats-io/nats.js", + "license": "Apache-2.0", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "license": "Apache-2.0", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +}