@elaraai/e3-rack 0.0.0-bootstrap.0 → 1.0.94
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/README.md +407 -2
- package/dist/src/client/client.spec.d.ts +6 -0
- package/dist/src/client/client.spec.d.ts.map +1 -0
- package/dist/src/client/client.spec.js +150 -0
- package/dist/src/client/client.spec.js.map +1 -0
- package/dist/src/client/connect.d.ts +52 -0
- package/dist/src/client/connect.d.ts.map +1 -0
- package/dist/src/client/connect.js +112 -0
- package/dist/src/client/connect.js.map +1 -0
- package/dist/src/client/hub-client.d.ts +57 -0
- package/dist/src/client/hub-client.d.ts.map +1 -0
- package/dist/src/client/hub-client.js +87 -0
- package/dist/src/client/hub-client.js.map +1 -0
- package/dist/src/client/local-http.d.ts +78 -0
- package/dist/src/client/local-http.d.ts.map +1 -0
- package/dist/src/client/local-http.js +149 -0
- package/dist/src/client/local-http.js.map +1 -0
- package/dist/src/client/session-attempts.d.ts +20 -0
- package/dist/src/client/session-attempts.d.ts.map +1 -0
- package/dist/src/client/session-attempts.js +88 -0
- package/dist/src/client/session-attempts.js.map +1 -0
- package/dist/src/client/session-data-server.d.ts +37 -0
- package/dist/src/client/session-data-server.d.ts.map +1 -0
- package/dist/src/client/session-data-server.js +280 -0
- package/dist/src/client/session-data-server.js.map +1 -0
- package/dist/src/client/session-data-server.spec.d.ts +6 -0
- package/dist/src/client/session-data-server.spec.d.ts.map +1 -0
- package/dist/src/client/session-data-server.spec.js +192 -0
- package/dist/src/client/session-data-server.spec.js.map +1 -0
- package/dist/src/client/session.d.ts +119 -0
- package/dist/src/client/session.d.ts.map +1 -0
- package/dist/src/client/session.js +290 -0
- package/dist/src/client/session.js.map +1 -0
- package/dist/src/hub/config.d.ts +74 -0
- package/dist/src/hub/config.d.ts.map +1 -0
- package/dist/src/hub/config.js +159 -0
- package/dist/src/hub/config.js.map +1 -0
- package/dist/src/hub/fair-lease-store.d.ts +36 -0
- package/dist/src/hub/fair-lease-store.d.ts.map +1 -0
- package/dist/src/hub/fair-lease-store.js +73 -0
- package/dist/src/hub/fair-lease-store.js.map +1 -0
- package/dist/src/hub/hub.d.ts +91 -0
- package/dist/src/hub/hub.d.ts.map +1 -0
- package/dist/src/hub/hub.js +641 -0
- package/dist/src/hub/hub.js.map +1 -0
- package/dist/src/hub/hub.spec.d.ts +6 -0
- package/dist/src/hub/hub.spec.d.ts.map +1 -0
- package/dist/src/hub/hub.spec.js +363 -0
- package/dist/src/hub/hub.spec.js.map +1 -0
- package/dist/src/hub/infrastructure.spec.d.ts +6 -0
- package/dist/src/hub/infrastructure.spec.d.ts.map +1 -0
- package/dist/src/hub/infrastructure.spec.js +211 -0
- package/dist/src/hub/infrastructure.spec.js.map +1 -0
- package/dist/src/hub/lock.d.ts +62 -0
- package/dist/src/hub/lock.d.ts.map +1 -0
- package/dist/src/hub/lock.js +165 -0
- package/dist/src/hub/lock.js.map +1 -0
- package/dist/src/hub/main.d.ts +16 -0
- package/dist/src/hub/main.d.ts.map +1 -0
- package/dist/src/hub/main.js +56 -0
- package/dist/src/hub/main.js.map +1 -0
- package/dist/src/hub/session-bridge.d.ts +116 -0
- package/dist/src/hub/session-bridge.d.ts.map +1 -0
- package/dist/src/hub/session-bridge.js +336 -0
- package/dist/src/hub/session-bridge.js.map +1 -0
- package/dist/src/identity/file-identity-store.d.ts +18 -0
- package/dist/src/identity/file-identity-store.d.ts.map +1 -0
- package/dist/src/identity/file-identity-store.js +34 -0
- package/dist/src/identity/file-identity-store.js.map +1 -0
- package/dist/src/identity/file-identity-store.spec.d.ts +6 -0
- package/dist/src/identity/file-identity-store.spec.d.ts.map +1 -0
- package/dist/src/identity/file-identity-store.spec.js +59 -0
- package/dist/src/identity/file-identity-store.spec.js.map +1 -0
- package/dist/src/identity/in-memory-identity-store.d.ts +48 -0
- package/dist/src/identity/in-memory-identity-store.d.ts.map +1 -0
- package/dist/src/identity/in-memory-identity-store.js +102 -0
- package/dist/src/identity/in-memory-identity-store.js.map +1 -0
- package/dist/src/identity/machine-identity.d.ts +134 -0
- package/dist/src/identity/machine-identity.d.ts.map +1 -0
- package/dist/src/identity/machine-identity.js +65 -0
- package/dist/src/identity/machine-identity.js.map +1 -0
- package/dist/src/index.d.ts +34 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +34 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lease/closure.d.ts +96 -0
- package/dist/src/lease/closure.d.ts.map +1 -0
- package/dist/src/lease/closure.js +192 -0
- package/dist/src/lease/closure.js.map +1 -0
- package/dist/src/lease/closure.spec.d.ts +6 -0
- package/dist/src/lease/closure.spec.d.ts.map +1 -0
- package/dist/src/lease/closure.spec.js +35 -0
- package/dist/src/lease/closure.spec.js.map +1 -0
- package/dist/src/lease/coordinator.d.ts +34 -0
- package/dist/src/lease/coordinator.d.ts.map +1 -0
- package/dist/src/lease/coordinator.js +42 -0
- package/dist/src/lease/coordinator.js.map +1 -0
- package/dist/src/lease/in-memory-lease-store.d.ts +58 -0
- package/dist/src/lease/in-memory-lease-store.d.ts.map +1 -0
- package/dist/src/lease/in-memory-lease-store.js +132 -0
- package/dist/src/lease/in-memory-lease-store.js.map +1 -0
- package/dist/src/lease/lease-store.d.ts +169 -0
- package/dist/src/lease/lease-store.d.ts.map +1 -0
- package/dist/src/lease/lease-store.js +94 -0
- package/dist/src/lease/lease-store.js.map +1 -0
- package/dist/src/lease/lease-store.spec.d.ts +6 -0
- package/dist/src/lease/lease-store.spec.d.ts.map +1 -0
- package/dist/src/lease/lease-store.spec.js +8 -0
- package/dist/src/lease/lease-store.spec.js.map +1 -0
- package/dist/src/lease/rack-dispatch.d.ts +105 -0
- package/dist/src/lease/rack-dispatch.d.ts.map +1 -0
- package/dist/src/lease/rack-dispatch.js +6 -0
- package/dist/src/lease/rack-dispatch.js.map +1 -0
- package/dist/src/mutation-queue.d.ts +13 -0
- package/dist/src/mutation-queue.d.ts.map +1 -0
- package/dist/src/mutation-queue.js +17 -0
- package/dist/src/mutation-queue.js.map +1 -0
- package/dist/src/paths.d.ts +56 -0
- package/dist/src/paths.d.ts.map +1 -0
- package/dist/src/paths.js +82 -0
- package/dist/src/paths.js.map +1 -0
- package/dist/src/protocol/control.d.ts +295 -0
- package/dist/src/protocol/control.d.ts.map +1 -0
- package/dist/src/protocol/control.js +74 -0
- package/dist/src/protocol/control.js.map +1 -0
- package/dist/src/protocol/index.d.ts +9 -0
- package/dist/src/protocol/index.d.ts.map +1 -0
- package/dist/src/protocol/index.js +9 -0
- package/dist/src/protocol/index.js.map +1 -0
- package/dist/src/protocol/rack-wire.d.ts +357 -0
- package/dist/src/protocol/rack-wire.d.ts.map +1 -0
- package/dist/src/protocol/rack-wire.js +262 -0
- package/dist/src/protocol/rack-wire.js.map +1 -0
- package/dist/src/protocol/rack-wire.spec.d.ts +6 -0
- package/dist/src/protocol/rack-wire.spec.d.ts.map +1 -0
- package/dist/src/protocol/rack-wire.spec.js +14 -0
- package/dist/src/protocol/rack-wire.spec.js.map +1 -0
- package/dist/src/protocol/registration.d.ts +70 -0
- package/dist/src/protocol/registration.d.ts.map +1 -0
- package/dist/src/protocol/registration.js +67 -0
- package/dist/src/protocol/registration.js.map +1 -0
- package/dist/src/protocol/session-data.d.ts +69 -0
- package/dist/src/protocol/session-data.d.ts.map +1 -0
- package/dist/src/protocol/session-data.js +26 -0
- package/dist/src/protocol/session-data.js.map +1 -0
- package/dist/src/protocol/task-envelope.d.ts +198 -0
- package/dist/src/protocol/task-envelope.d.ts.map +1 -0
- package/dist/src/protocol/task-envelope.js +93 -0
- package/dist/src/protocol/task-envelope.js.map +1 -0
- package/dist/src/protocol/wire-golden.d.ts +8 -0
- package/dist/src/protocol/wire-golden.d.ts.map +1 -0
- package/dist/src/protocol/wire-golden.js +34 -0
- package/dist/src/protocol/wire-golden.js.map +1 -0
- package/dist/src/protocol/wire-samples.d.ts +11 -0
- package/dist/src/protocol/wire-samples.d.ts.map +1 -0
- package/dist/src/protocol/wire-samples.js +52 -0
- package/dist/src/protocol/wire-samples.js.map +1 -0
- package/dist/src/registry/file-rack-registry.d.ts +68 -0
- package/dist/src/registry/file-rack-registry.d.ts.map +1 -0
- package/dist/src/registry/file-rack-registry.js +138 -0
- package/dist/src/registry/file-rack-registry.js.map +1 -0
- package/dist/src/registry/file-rack-registry.spec.d.ts +6 -0
- package/dist/src/registry/file-rack-registry.spec.d.ts.map +1 -0
- package/dist/src/registry/file-rack-registry.spec.js +50 -0
- package/dist/src/registry/file-rack-registry.spec.js.map +1 -0
- package/dist/src/registry/rack-registry.d.ts +64 -0
- package/dist/src/registry/rack-registry.d.ts.map +1 -0
- package/dist/src/registry/rack-registry.js +65 -0
- package/dist/src/registry/rack-registry.js.map +1 -0
- package/dist/src/registry/rack-registry.spec.d.ts +6 -0
- package/dist/src/registry/rack-registry.spec.d.ts.map +1 -0
- package/dist/src/registry/rack-registry.spec.js +8 -0
- package/dist/src/registry/rack-registry.spec.js.map +1 -0
- package/dist/src/routing/eligibility.d.ts +38 -0
- package/dist/src/routing/eligibility.d.ts.map +1 -0
- package/dist/src/routing/eligibility.js +78 -0
- package/dist/src/routing/eligibility.js.map +1 -0
- package/dist/src/routing/host-coupling.d.ts +26 -0
- package/dist/src/routing/host-coupling.d.ts.map +1 -0
- package/dist/src/routing/host-coupling.js +62 -0
- package/dist/src/routing/host-coupling.js.map +1 -0
- package/dist/src/routing/policy.d.ts +188 -0
- package/dist/src/routing/policy.d.ts.map +1 -0
- package/dist/src/routing/policy.js +212 -0
- package/dist/src/routing/policy.js.map +1 -0
- package/dist/src/routing/routing.spec.d.ts +6 -0
- package/dist/src/routing/routing.spec.d.ts.map +1 -0
- package/dist/src/routing/routing.spec.js +100 -0
- package/dist/src/routing/routing.spec.js.map +1 -0
- package/dist/src/routing/tiers.d.ts +22 -0
- package/dist/src/routing/tiers.d.ts.map +1 -0
- package/dist/src/routing/tiers.js +26 -0
- package/dist/src/routing/tiers.js.map +1 -0
- package/dist/src/runner/api-server.d.ts +45 -0
- package/dist/src/runner/api-server.d.ts.map +1 -0
- package/dist/src/runner/api-server.js +21 -0
- package/dist/src/runner/api-server.js.map +1 -0
- package/dist/src/runner/capacity.d.ts +13 -0
- package/dist/src/runner/capacity.d.ts.map +1 -0
- package/dist/src/runner/capacity.js +18 -0
- package/dist/src/runner/capacity.js.map +1 -0
- package/dist/src/runner/rack-body.d.ts +43 -0
- package/dist/src/runner/rack-body.d.ts.map +1 -0
- package/dist/src/runner/rack-body.js +208 -0
- package/dist/src/runner/rack-body.js.map +1 -0
- package/dist/src/runner/rack-runner.d.ts +62 -0
- package/dist/src/runner/rack-runner.d.ts.map +1 -0
- package/dist/src/runner/rack-runner.js +75 -0
- package/dist/src/runner/rack-runner.js.map +1 -0
- package/dist/src/runner/rack-runner.spec.d.ts +6 -0
- package/dist/src/runner/rack-runner.spec.d.ts.map +1 -0
- package/dist/src/runner/rack-runner.spec.js +302 -0
- package/dist/src/runner/rack-runner.spec.js.map +1 -0
- package/dist/src/runner/split.spec.d.ts +6 -0
- package/dist/src/runner/split.spec.d.ts.map +1 -0
- package/dist/src/runner/split.spec.js +188 -0
- package/dist/src/runner/split.spec.js.map +1 -0
- package/dist/src/runner-event.d.ts +261 -0
- package/dist/src/runner-event.d.ts.map +1 -0
- package/dist/src/runner-event.js +332 -0
- package/dist/src/runner-event.js.map +1 -0
- package/dist/src/server/beast2.d.ts +19 -0
- package/dist/src/server/beast2.d.ts.map +1 -0
- package/dist/src/server/beast2.js +18 -0
- package/dist/src/server/beast2.js.map +1 -0
- package/dist/src/server/body-limit.d.ts +16 -0
- package/dist/src/server/body-limit.d.ts.map +1 -0
- package/dist/src/server/body-limit.js +53 -0
- package/dist/src/server/body-limit.js.map +1 -0
- package/dist/src/server/body-limit.spec.d.ts +2 -0
- package/dist/src/server/body-limit.spec.d.ts.map +1 -0
- package/dist/src/server/body-limit.spec.js +32 -0
- package/dist/src/server/body-limit.spec.js.map +1 -0
- package/dist/src/server/bounded.d.ts +16 -0
- package/dist/src/server/bounded.d.ts.map +1 -0
- package/dist/src/server/bounded.js +39 -0
- package/dist/src/server/bounded.js.map +1 -0
- package/dist/src/server/dispatch.d.ts +137 -0
- package/dist/src/server/dispatch.d.ts.map +1 -0
- package/dist/src/server/dispatch.js +267 -0
- package/dist/src/server/dispatch.js.map +1 -0
- package/dist/src/server/env-publication.d.ts +45 -0
- package/dist/src/server/env-publication.d.ts.map +1 -0
- package/dist/src/server/env-publication.js +10 -0
- package/dist/src/server/env-publication.js.map +1 -0
- package/dist/src/server/index.d.ts +16 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +17 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/server/protocol.spec.d.ts +6 -0
- package/dist/src/server/protocol.spec.d.ts.map +1 -0
- package/dist/src/server/protocol.spec.js +78 -0
- package/dist/src/server/protocol.spec.js.map +1 -0
- package/dist/src/server/rack-routes.d.ts +175 -0
- package/dist/src/server/rack-routes.d.ts.map +1 -0
- package/dist/src/server/rack-routes.js +755 -0
- package/dist/src/server/rack-routes.js.map +1 -0
- package/dist/src/server/rack-routes.spec.d.ts +6 -0
- package/dist/src/server/rack-routes.spec.d.ts.map +1 -0
- package/dist/src/server/rack-routes.spec.js +477 -0
- package/dist/src/server/rack-routes.spec.js.map +1 -0
- package/dist/src/server/storage-bridge.d.ts +140 -0
- package/dist/src/server/storage-bridge.d.ts.map +1 -0
- package/dist/src/server/storage-bridge.js +6 -0
- package/dist/src/server/storage-bridge.js.map +1 -0
- package/dist/src/state-file.d.ts +10 -0
- package/dist/src/state-file.d.ts.map +1 -0
- package/dist/src/state-file.js +38 -0
- package/dist/src/state-file.js.map +1 -0
- package/dist/src/testing/in-memory.d.ts +116 -0
- package/dist/src/testing/in-memory.d.ts.map +1 -0
- package/dist/src/testing/in-memory.js +193 -0
- package/dist/src/testing/in-memory.js.map +1 -0
- package/dist/src/testing/index.d.ts +14 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +14 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/lease-store-contract.d.ts +14 -0
- package/dist/src/testing/lease-store-contract.d.ts.map +1 -0
- package/dist/src/testing/lease-store-contract.js +78 -0
- package/dist/src/testing/lease-store-contract.js.map +1 -0
- package/dist/src/testing/protocol-suite.d.ts +38 -0
- package/dist/src/testing/protocol-suite.d.ts.map +1 -0
- package/dist/src/testing/protocol-suite.js +207 -0
- package/dist/src/testing/protocol-suite.js.map +1 -0
- package/dist/src/testing/rack-registry-contract.d.ts +15 -0
- package/dist/src/testing/rack-registry-contract.d.ts.map +1 -0
- package/dist/src/testing/rack-registry-contract.js +106 -0
- package/dist/src/testing/rack-registry-contract.js.map +1 -0
- package/dist/src/testing/test-rack-agent.d.ts +121 -0
- package/dist/src/testing/test-rack-agent.d.ts.map +1 -0
- package/dist/src/testing/test-rack-agent.js +316 -0
- package/dist/src/testing/test-rack-agent.js.map +1 -0
- package/dist/src/tls/tls.d.ts +67 -0
- package/dist/src/tls/tls.d.ts.map +1 -0
- package/dist/src/tls/tls.js +200 -0
- package/dist/src/tls/tls.js.map +1 -0
- package/dist/src/tls/tls.spec.d.ts +6 -0
- package/dist/src/tls/tls.spec.d.ts.map +1 -0
- package/dist/src/tls/tls.spec.js +101 -0
- package/dist/src/tls/tls.spec.js.map +1 -0
- package/dist/src/version.d.ts +9 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/src/version.js +10 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +67 -7
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
import { X509Certificate, generateKeyPairSync, randomBytes, sign } from 'node:crypto';
|
|
6
|
+
import { Agent, request } from 'node:https';
|
|
7
|
+
import { isIP } from 'node:net';
|
|
8
|
+
import { Readable } from 'node:stream';
|
|
9
|
+
import { connect } from 'node:tls';
|
|
10
|
+
function length(n) {
|
|
11
|
+
if (n < 0x80)
|
|
12
|
+
return Buffer.from([n]);
|
|
13
|
+
const bytes = [];
|
|
14
|
+
for (let rest = n; rest > 0; rest = Math.floor(rest / 256))
|
|
15
|
+
bytes.unshift(rest & 0xff);
|
|
16
|
+
return Buffer.from([0x80 | bytes.length, ...bytes]);
|
|
17
|
+
}
|
|
18
|
+
const der = (tag, ...contents) => {
|
|
19
|
+
const body = Buffer.concat(contents);
|
|
20
|
+
return Buffer.concat([Buffer.from([tag]), length(body.length), body]);
|
|
21
|
+
};
|
|
22
|
+
const sequence = (...contents) => der(0x30, ...contents);
|
|
23
|
+
const oid = (dotted) => {
|
|
24
|
+
const [first, second, ...rest] = dotted.split('.').map(Number);
|
|
25
|
+
const out = [40 * first + second];
|
|
26
|
+
for (const arc of rest) {
|
|
27
|
+
const groups = [arc & 0x7f];
|
|
28
|
+
for (let high = Math.floor(arc / 128); high > 0; high = Math.floor(high / 128))
|
|
29
|
+
groups.unshift(0x80 | (high & 0x7f));
|
|
30
|
+
out.push(...groups);
|
|
31
|
+
}
|
|
32
|
+
return der(0x06, Buffer.from(out));
|
|
33
|
+
};
|
|
34
|
+
const positiveInteger = (bytes) => {
|
|
35
|
+
let value = bytes;
|
|
36
|
+
while (value.length > 1 && value[0] === 0 && (value[1] & 0x80) === 0)
|
|
37
|
+
value = value.subarray(1);
|
|
38
|
+
return der(0x02, (value[0] & 0x80) === 0 ? value : Buffer.concat([Buffer.from([0]), value]));
|
|
39
|
+
};
|
|
40
|
+
const time = (date) => {
|
|
41
|
+
const iso = date.toISOString();
|
|
42
|
+
const rest = `${iso.slice(5, 7)}${iso.slice(8, 10)}${iso.slice(11, 13)}${iso.slice(14, 16)}${iso.slice(17, 19)}Z`;
|
|
43
|
+
const year = date.getUTCFullYear();
|
|
44
|
+
// RFC 5280 §4.1.2.5: UTCTime through 2049, GeneralizedTime from 2050.
|
|
45
|
+
return year < 2050 ? der(0x17, Buffer.from(`${String(year).slice(2)}${rest}`)) : der(0x18, Buffer.from(`${year}${rest}`));
|
|
46
|
+
};
|
|
47
|
+
const pem = (label, bytes) => `-----BEGIN ${label}-----\n${bytes.toString('base64').match(/.{1,64}/g).join('\n')}\n-----END ${label}-----\n`;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a hub's self-signed ECDSA P-256 certificate, valid from an hour ago
|
|
50
|
+
* (a rack's clock may lag) for ten years, naming `e3-rack-hub` only.
|
|
51
|
+
* @param now - The issue time
|
|
52
|
+
* @returns The certificate and its private key
|
|
53
|
+
* @example
|
|
54
|
+
* const { certificatePem, privateKeyPem } = createHubCertificate();
|
|
55
|
+
*/
|
|
56
|
+
export function createHubCertificate(now = new Date()) {
|
|
57
|
+
const { privateKey, publicKey } = generateKeyPairSync('ec', { namedCurve: 'P-256' });
|
|
58
|
+
const ecdsaWithSha256 = sequence(oid('1.2.840.10045.4.3.2'));
|
|
59
|
+
const name = sequence(der(0x31, sequence(oid('2.5.4.3'), der(0x0c, Buffer.from('e3 rack hub')))));
|
|
60
|
+
const serial = randomBytes(16);
|
|
61
|
+
serial[0] &= 0x7f;
|
|
62
|
+
const extensions = sequence(sequence(oid('2.5.29.19'), der(0x01, Buffer.from([0xff])), der(0x04, sequence())), // basicConstraints, critical: not a CA
|
|
63
|
+
sequence(oid('2.5.29.17'), der(0x04, sequence(der(0x82, Buffer.from('e3-rack-hub'))))));
|
|
64
|
+
const tbs = sequence(der(0xa0, positiveInteger(Buffer.from([2]))), positiveInteger(serial), ecdsaWithSha256, name, sequence(time(new Date(now.getTime() - 3_600_000)), time(new Date(now.getTime() + 3650 * 86_400_000))), name, publicKey.export({ type: 'spki', format: 'der' }), der(0xa3, extensions));
|
|
65
|
+
const certificate = sequence(tbs, ecdsaWithSha256, der(0x03, Buffer.from([0]), sign('sha256', tbs, privateKey)));
|
|
66
|
+
return { certificatePem: pem('CERTIFICATE', certificate), privateKeyPem: privateKey.export({ type: 'pkcs8', format: 'pem' }).toString() };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The SHA-256 fingerprint a rack pins: uppercase hex pairs joined by colons,
|
|
70
|
+
* as `openssl x509 -fingerprint -sha256` prints it.
|
|
71
|
+
* @param certificatePem - A PEM certificate
|
|
72
|
+
* @returns Its fingerprint
|
|
73
|
+
* @example
|
|
74
|
+
* console.log(certificateSha256(certificatePem)); // 'AB:CD:…'
|
|
75
|
+
*/
|
|
76
|
+
export function certificateSha256(certificatePem) {
|
|
77
|
+
return new X509Certificate(certificatePem).fingerprint256;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reads a pinned fingerprint as an operator writes it: 64 hex digits, with or
|
|
81
|
+
* without colons, in either case.
|
|
82
|
+
* @param text - The fingerprint
|
|
83
|
+
* @returns It in {@link certificateSha256}'s form
|
|
84
|
+
* @throws {Error} When it is not a SHA-256 fingerprint
|
|
85
|
+
* @example
|
|
86
|
+
* parseCertificateSha256('abcd…'); // 'AB:CD:…'
|
|
87
|
+
*/
|
|
88
|
+
export function parseCertificateSha256(text) {
|
|
89
|
+
const hex = text.trim().replaceAll(':', '').toUpperCase();
|
|
90
|
+
if (!/^[0-9A-F]{64}$/.test(hex))
|
|
91
|
+
throw new Error('A certificate SHA-256 fingerprint is 64 hex digits, colons optional');
|
|
92
|
+
return hex.match(/../g).join(':');
|
|
93
|
+
}
|
|
94
|
+
/** Reads the certificate a TLS server presents, sending no application data. */
|
|
95
|
+
function presentedCertificate(host, port, timeoutMs) {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const socket = connect({ host, port, rejectUnauthorized: false, ...(isIP(host) === 0 ? { servername: host } : {}) }, () => {
|
|
98
|
+
const raw = socket.getPeerCertificate(true).raw;
|
|
99
|
+
socket.destroy();
|
|
100
|
+
if (raw === undefined)
|
|
101
|
+
reject(new Error(`${host}:${port} presented no certificate`));
|
|
102
|
+
else
|
|
103
|
+
resolve(pem('CERTIFICATE', raw));
|
|
104
|
+
});
|
|
105
|
+
socket.setTimeout(timeoutMs, () => socket.destroy(new Error(`TLS handshake with ${host}:${port} timed out`)));
|
|
106
|
+
socket.once('error', reject);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Makes an HTTPS agent that trusts exactly the certificate whose SHA-256 is
|
|
111
|
+
* pinned. A first handshake, which sends nothing, reads the certificate the
|
|
112
|
+
* origin presents; it is refused unless its fingerprint is the pin. Every
|
|
113
|
+
* request then verifies the server against that certificate alone, and its
|
|
114
|
+
* fingerprint again, so a different certificate is refused before any
|
|
115
|
+
* request or credential is written.
|
|
116
|
+
* @param origin - An https:// origin
|
|
117
|
+
* @param certificateSha256 - The pinned fingerprint
|
|
118
|
+
* @param timeoutMs - The first handshake's deadline
|
|
119
|
+
* @returns The agent
|
|
120
|
+
* @throws {Error} When the origin is not HTTPS or presents another certificate
|
|
121
|
+
* @example
|
|
122
|
+
* const agent = await pinnedHttpsAgent('https://10.0.0.2:7331', pin);
|
|
123
|
+
*/
|
|
124
|
+
export async function pinnedHttpsAgent(origin, certificateSha256, timeoutMs = 10_000) {
|
|
125
|
+
const url = new URL(origin);
|
|
126
|
+
if (url.protocol !== 'https:')
|
|
127
|
+
throw new Error(`A pinned certificate needs an https:// origin, not ${url.protocol}`);
|
|
128
|
+
const pin = parseCertificateSha256(certificateSha256);
|
|
129
|
+
const host = url.hostname.replace(/^\[(.*)\]$/, '$1');
|
|
130
|
+
const presented = await presentedCertificate(host, Number(url.port || 443), timeoutMs);
|
|
131
|
+
const found = new X509Certificate(presented).fingerprint256;
|
|
132
|
+
if (found !== pin)
|
|
133
|
+
throw new Error(`${url.host} presented certificate SHA-256 ${found}, not the pinned ${pin}`);
|
|
134
|
+
return new Agent({ ca: presented, keepAlive: true, checkServerIdentity: (_host, certificate) => certificate.fingerprint256 === pin ? undefined : Object.assign(new Error(`${url.host} presented certificate SHA-256 ${certificate.fingerprint256}, not the pinned ${pin}`), { code: 'ERR_RACK_CERTIFICATE_PIN' }) });
|
|
135
|
+
}
|
|
136
|
+
/** OpenSSL's refusals of a server certificate that is not the pinned one, and the pin's own. */
|
|
137
|
+
const CERTIFICATE_REFUSALS = new Set(['ERR_RACK_CERTIFICATE_PIN', 'DEPTH_ZERO_SELF_SIGNED_CERT', 'SELF_SIGNED_CERT_IN_CHAIN',
|
|
138
|
+
'UNABLE_TO_VERIFY_LEAF_SIGNATURE', 'UNABLE_TO_GET_ISSUER_CERT', 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY', 'CERT_SIGNATURE_FAILURE']);
|
|
139
|
+
/**
|
|
140
|
+
* A fetch that reaches HTTPS origins only through {@link pinnedHttpsAgent},
|
|
141
|
+
* one agent per origin; any other scheme is refused.
|
|
142
|
+
* @param certificateSha256 - The pinned fingerprint
|
|
143
|
+
* @returns A fetch-compatible function
|
|
144
|
+
* @throws {Error} When the fingerprint is malformed
|
|
145
|
+
* @example
|
|
146
|
+
* const response = await pinnedFetch(pin)('https://10.0.0.2:7331/api/rack/heartbeat', { method: 'POST', body });
|
|
147
|
+
*/
|
|
148
|
+
export function pinnedFetch(certificateSha256) {
|
|
149
|
+
const pin = parseCertificateSha256(certificateSha256);
|
|
150
|
+
const agents = new Map();
|
|
151
|
+
const agentFor = (url) => {
|
|
152
|
+
let agent = agents.get(url.origin);
|
|
153
|
+
if (agent === undefined) {
|
|
154
|
+
agent = pinnedHttpsAgent(url.origin, pin);
|
|
155
|
+
agents.set(url.origin, agent);
|
|
156
|
+
agent.catch(() => { agents.delete(url.origin); });
|
|
157
|
+
}
|
|
158
|
+
return agent;
|
|
159
|
+
};
|
|
160
|
+
return async (input, init = {}) => {
|
|
161
|
+
const url = new URL(input instanceof Request ? input.url : input.toString());
|
|
162
|
+
if (url.protocol !== 'https:')
|
|
163
|
+
throw new TypeError(`A pinned certificate needs an https:// URL, not ${url.protocol}`);
|
|
164
|
+
const agent = await agentFor(url);
|
|
165
|
+
const method = init.method ?? 'GET';
|
|
166
|
+
return new Promise((resolve, reject) => {
|
|
167
|
+
const req = request(url, { method, agent, headers: Object.fromEntries(new Headers(init.headers)), signal: init.signal ?? undefined }, (res) => {
|
|
168
|
+
const headers = new Headers();
|
|
169
|
+
for (const [name, value] of Object.entries(res.headers))
|
|
170
|
+
if (value !== undefined)
|
|
171
|
+
headers.set(name, Array.isArray(value) ? value.join(', ') : value);
|
|
172
|
+
const status = res.statusCode ?? 502;
|
|
173
|
+
const empty = method === 'HEAD' || status === 204 || status === 304;
|
|
174
|
+
if (empty)
|
|
175
|
+
res.resume();
|
|
176
|
+
resolve(new Response(empty ? null : Readable.toWeb(res), { status, statusText: res.statusMessage, headers }));
|
|
177
|
+
});
|
|
178
|
+
// A certificate that does not chain to the pinned one is refused by
|
|
179
|
+
// the handshake itself, before the fingerprint is compared: either way
|
|
180
|
+
// it is not the pinned certificate.
|
|
181
|
+
req.once('error', (error) => reject(error.code !== undefined && CERTIFICATE_REFUSALS.has(error.code)
|
|
182
|
+
? new Error(`${url.host} presented a certificate other than the pinned SHA-256 ${pin}`, { cause: error })
|
|
183
|
+
: error));
|
|
184
|
+
const body = init.body;
|
|
185
|
+
if (body === undefined || body === null)
|
|
186
|
+
req.end();
|
|
187
|
+
else if (typeof body === 'string' || body instanceof Uint8Array)
|
|
188
|
+
req.end(body);
|
|
189
|
+
else if (body instanceof ArrayBuffer)
|
|
190
|
+
req.end(Buffer.from(body));
|
|
191
|
+
else if (body instanceof ReadableStream)
|
|
192
|
+
Readable.fromWeb(body).once('error', (err) => req.destroy(err)).pipe(req);
|
|
193
|
+
else {
|
|
194
|
+
req.destroy();
|
|
195
|
+
reject(new TypeError('A pinned fetch sends a string, bytes or a stream'));
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=tls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls.js","sourceRoot":"","sources":["../../../src/tls/tls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls.spec.d.ts","sourceRoot":"","sources":["../../../src/tls/tls.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
import { afterEach, it } from 'node:test';
|
|
6
|
+
import assert from 'node:assert/strict';
|
|
7
|
+
import { X509Certificate, createPrivateKey } from 'node:crypto';
|
|
8
|
+
import { once } from 'node:events';
|
|
9
|
+
import { createServer } from 'node:https';
|
|
10
|
+
import { certificateSha256, createHubCertificate, parseCertificateSha256, pinnedFetch, pinnedHttpsAgent } from './tls.js';
|
|
11
|
+
const cleanup = [];
|
|
12
|
+
afterEach(async () => { for (const close of cleanup.splice(0).reverse())
|
|
13
|
+
await close(); });
|
|
14
|
+
async function serve(certificate = createHubCertificate(), port = 0) {
|
|
15
|
+
const requests = [];
|
|
16
|
+
const server = createServer({ cert: certificate.certificatePem, key: certificate.privateKeyPem }, (req, res) => {
|
|
17
|
+
const chunks = [];
|
|
18
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
19
|
+
req.on('end', () => {
|
|
20
|
+
requests.push(`${req.method} ${req.url} ${Buffer.concat(chunks).toString()}`);
|
|
21
|
+
if (req.url === '/empty') {
|
|
22
|
+
res.writeHead(204);
|
|
23
|
+
res.end();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
res.writeHead(200, { 'content-type': 'text/plain', 'x-echo': req.headers['x-echo'] ?? '' });
|
|
27
|
+
res.end(`${req.method} ${Buffer.concat(chunks).toString()}`);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
server.listen(port, '127.0.0.1');
|
|
31
|
+
await once(server, 'listening');
|
|
32
|
+
const close = async () => { if (!server.listening)
|
|
33
|
+
return; server.closeAllConnections(); server.close(); await once(server, 'close'); };
|
|
34
|
+
cleanup.push(close);
|
|
35
|
+
const address = server.address();
|
|
36
|
+
assert(address && typeof address !== 'string');
|
|
37
|
+
return { origin: `https://127.0.0.1:${address.port}`, port: address.port, pin: certificateSha256(certificate.certificatePem), requests, close };
|
|
38
|
+
}
|
|
39
|
+
it('creates a self-signed P-256 certificate naming e3-rack-hub, valid from an hour ago for ten years', () => {
|
|
40
|
+
const now = new Date('2030-01-01T00:00:00Z');
|
|
41
|
+
const { certificatePem, privateKeyPem } = createHubCertificate(now);
|
|
42
|
+
const certificate = new X509Certificate(certificatePem);
|
|
43
|
+
assert.equal(certificate.subject, 'CN=e3 rack hub');
|
|
44
|
+
assert.equal(certificate.issuer, certificate.subject);
|
|
45
|
+
assert.equal(certificate.subjectAltName, 'DNS:e3-rack-hub');
|
|
46
|
+
assert.equal(certificate.ca, false);
|
|
47
|
+
assert.equal(certificate.publicKey.asymmetricKeyDetails?.namedCurve, 'prime256v1');
|
|
48
|
+
assert(certificate.checkPrivateKey(createPrivateKey(privateKeyPem)));
|
|
49
|
+
assert(certificate.verify(certificate.publicKey));
|
|
50
|
+
assert.equal(new Date(certificate.validFrom).getTime(), now.getTime() - 3_600_000);
|
|
51
|
+
assert.equal(new Date(certificate.validTo).getTime(), now.getTime() + 3650 * 86_400_000);
|
|
52
|
+
assert.notEqual(certificateSha256(createHubCertificate(now).certificatePem), certificateSha256(certificatePem));
|
|
53
|
+
});
|
|
54
|
+
it('writes a validity past 2049 as GeneralizedTime', () => {
|
|
55
|
+
const certificate = new X509Certificate(createHubCertificate(new Date('2045-06-01T00:00:00Z')).certificatePem);
|
|
56
|
+
assert.equal(new Date(certificate.validTo).getUTCFullYear(), 2055);
|
|
57
|
+
});
|
|
58
|
+
it('reads a fingerprint with or without colons, in either case, and refuses anything else', () => {
|
|
59
|
+
const pin = certificateSha256(createHubCertificate().certificatePem);
|
|
60
|
+
assert.match(pin, /^([0-9A-F]{2}:){31}[0-9A-F]{2}$/);
|
|
61
|
+
assert.equal(parseCertificateSha256(pin.toLowerCase()), pin);
|
|
62
|
+
assert.equal(parseCertificateSha256(` ${pin.replaceAll(':', '')} `), pin);
|
|
63
|
+
for (const bad of ['', pin.slice(3), `${pin}:00`, pin.replace(/^../, 'ZZ')])
|
|
64
|
+
assert.throws(() => parseCertificateSha256(bad), /64 hex digits/);
|
|
65
|
+
});
|
|
66
|
+
it('fetches through the pinned certificate, with headers, bodies and empty responses', async () => {
|
|
67
|
+
const hub = await serve();
|
|
68
|
+
const fetch = pinnedFetch(hub.pin);
|
|
69
|
+
const posted = await fetch(`${hub.origin}/echo`, { method: 'POST', headers: { 'x-echo': 'yes' }, body: new TextEncoder().encode('payload') });
|
|
70
|
+
assert.equal(posted.status, 200);
|
|
71
|
+
assert.equal(posted.headers.get('x-echo'), 'yes');
|
|
72
|
+
assert.equal(await posted.text(), 'POST payload');
|
|
73
|
+
const streamed = await fetch(`${hub.origin}/stream`, { method: 'PUT', body: new Blob(['streamed']).stream(), duplex: 'half' });
|
|
74
|
+
assert.equal(await streamed.text(), 'PUT streamed');
|
|
75
|
+
const empty = await fetch(`${hub.origin}/empty`);
|
|
76
|
+
assert.equal(empty.status, 204);
|
|
77
|
+
assert.equal(await empty.text(), '');
|
|
78
|
+
});
|
|
79
|
+
it('refuses another certificate before any request reaches it, and refuses plain HTTP', async () => {
|
|
80
|
+
const hub = await serve();
|
|
81
|
+
const other = certificateSha256(createHubCertificate().certificatePem);
|
|
82
|
+
await assert.rejects(pinnedFetch(other)(`${hub.origin}/secret`, { method: 'POST', body: 'token' }), /not the pinned/);
|
|
83
|
+
await assert.rejects(pinnedHttpsAgent(hub.origin, other), /not the pinned/);
|
|
84
|
+
assert.deepEqual(hub.requests, [], 'nothing was sent to the wrong certificate');
|
|
85
|
+
await assert.rejects(pinnedFetch(hub.pin)('http://127.0.0.1:1/x'), /https:\/\//);
|
|
86
|
+
await assert.rejects(pinnedHttpsAgent('http://127.0.0.1:1', hub.pin), /https:\/\//);
|
|
87
|
+
// An unpinned client refuses the self-signed certificate outright.
|
|
88
|
+
await assert.rejects(globalThis.fetch(`${hub.origin}/secret`));
|
|
89
|
+
assert.deepEqual(hub.requests, []);
|
|
90
|
+
});
|
|
91
|
+
it('refuses an origin it trusted once that origin presents another certificate', async () => {
|
|
92
|
+
const first = await serve();
|
|
93
|
+
const fetch = pinnedFetch(first.pin);
|
|
94
|
+
// A connection kept alive would outlive the server; the next request must handshake again.
|
|
95
|
+
assert.equal((await fetch(`${first.origin}/a`, { headers: { connection: 'close' } })).status, 200);
|
|
96
|
+
await first.close();
|
|
97
|
+
const replaced = await serve(createHubCertificate(), first.port);
|
|
98
|
+
await assert.rejects(fetch(`${replaced.origin}/b`, { method: 'POST', body: 'token' }), /other than the pinned SHA-256/);
|
|
99
|
+
assert.deepEqual(replaced.requests, [], 'the agent the first certificate made refuses the second');
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=tls.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls.spec.js","sourceRoot":"","sources":["../../../src/tls/tls.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/** The lockstep release whose repository formats this session reads. */
|
|
6
|
+
export declare const E3_RACK_VERSION: string;
|
|
7
|
+
/** The hub/session protocol this release implements; negotiated exactly. */
|
|
8
|
+
export declare const HUB_PROTOCOL = 2;
|
|
9
|
+
//# sourceMappingURL=version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,wEAAwE;AACxE,eAAO,MAAM,eAAe,EAAE,MAAmB,CAAC;AAClD,4EAA4E;AAC5E,eAAO,MAAM,YAAY,IAAI,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
import { E3_RELEASE } from '@elaraai/e3-types';
|
|
6
|
+
/** The lockstep release whose repository formats this session reads. */
|
|
7
|
+
export const E3_RACK_VERSION = E3_RELEASE;
|
|
8
|
+
/** The hub/session protocol this release implements; negotiated exactly. */
|
|
9
|
+
export const HUB_PROTOCOL = 2;
|
|
10
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAW,UAAU,CAAC;AAClD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,75 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elaraai/e3-rack",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
3
|
+
"version": "1.0.94",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Rack protocol, local hub and task delegation for e3",
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
|
+
"types": "dist/src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/src/index.d.ts",
|
|
11
|
+
"default": "./dist/src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./protocol": {
|
|
14
|
+
"types": "./dist/src/protocol/index.d.ts",
|
|
15
|
+
"default": "./dist/src/protocol/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./server": {
|
|
18
|
+
"types": "./dist/src/server/index.d.ts",
|
|
19
|
+
"default": "./dist/src/server/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./testing": {
|
|
22
|
+
"types": "./dist/src/testing/index.d.ts",
|
|
23
|
+
"default": "./dist/src/testing/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./hub-main": {
|
|
26
|
+
"types": "./dist/src/hub/main.d.ts",
|
|
27
|
+
"default": "./dist/src/hub/main.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist/src",
|
|
32
|
+
"!dist/src/**/*.spec.*",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE.md"
|
|
35
|
+
],
|
|
36
|
+
"keywords": [
|
|
37
|
+
"east",
|
|
38
|
+
"e3",
|
|
39
|
+
"rack"
|
|
40
|
+
],
|
|
5
41
|
"license": "BUSL-1.1",
|
|
6
42
|
"repository": {
|
|
7
43
|
"type": "git",
|
|
8
44
|
"url": "git+https://github.com/elaraai/east-workspace.git",
|
|
9
45
|
"directory": "libs/e3/packages/e3-rack"
|
|
10
46
|
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=22.2.0"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@hono/node-server": "^1.13.0",
|
|
52
|
+
"hono": "^4.6.0",
|
|
53
|
+
"@elaraai/e3": "1.0.94",
|
|
54
|
+
"@elaraai/e3-types": "1.0.94",
|
|
55
|
+
"@elaraai/e3-core": "1.0.94"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@elaraai/east": "1.0.94"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/node": "^22.0.0",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
|
63
|
+
"@typescript-eslint/parser": "^8.47.0",
|
|
64
|
+
"eslint": "^9.0.0",
|
|
65
|
+
"eslint-plugin-headers": "^1.3.3",
|
|
66
|
+
"typescript": "^5.6.0",
|
|
67
|
+
"@elaraai/east-node-cli": "1.0.94"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "tsc",
|
|
71
|
+
"test": "npm run build && node --enable-source-maps --test-reporter=spec --test-timeout=480000 --test 'dist/src/**/*.spec.js'",
|
|
72
|
+
"lint": "eslint .",
|
|
73
|
+
"lint:fix": "eslint . --fix"
|
|
74
|
+
}
|
|
75
|
+
}
|