@hsuite/smart-engines-cli 1.2.0 → 1.4.0
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 +272 -58
- package/dist/_lib/attestation.d.ts +45 -0
- package/dist/_lib/attestation.d.ts.map +1 -0
- package/dist/_lib/attestation.js +76 -0
- package/dist/_lib/attestation.js.map +1 -0
- package/dist/_lib/baas.d.ts +147 -0
- package/dist/_lib/baas.d.ts.map +1 -0
- package/dist/_lib/baas.js +260 -0
- package/dist/_lib/baas.js.map +1 -0
- package/dist/_lib/cluster.d.ts +6 -6
- package/dist/_lib/cluster.js +6 -6
- package/dist/_lib/deploy-manifest.d.ts +69 -0
- package/dist/_lib/deploy-manifest.d.ts.map +1 -0
- package/dist/_lib/deploy-manifest.js +87 -0
- package/dist/_lib/deploy-manifest.js.map +1 -0
- package/dist/_lib/docker.d.ts +82 -0
- package/dist/_lib/docker.d.ts.map +1 -0
- package/dist/_lib/docker.js +164 -0
- package/dist/_lib/docker.js.map +1 -0
- package/dist/_lib/faucet-client.d.ts +64 -0
- package/dist/_lib/faucet-client.d.ts.map +1 -0
- package/dist/_lib/faucet-client.js +92 -0
- package/dist/_lib/faucet-client.js.map +1 -0
- package/dist/_lib/framework.d.ts +59 -0
- package/dist/_lib/framework.d.ts.map +1 -0
- package/dist/_lib/framework.js +149 -0
- package/dist/_lib/framework.js.map +1 -0
- package/dist/_lib/frontend-guard.d.ts +13 -0
- package/dist/_lib/frontend-guard.d.ts.map +1 -0
- package/dist/_lib/frontend-guard.js +41 -0
- package/dist/_lib/frontend-guard.js.map +1 -0
- package/dist/_lib/registry-fanout.d.ts +114 -0
- package/dist/_lib/registry-fanout.d.ts.map +1 -0
- package/dist/_lib/registry-fanout.js +435 -0
- package/dist/_lib/registry-fanout.js.map +1 -0
- package/dist/_lib/runtime-env.d.ts +126 -0
- package/dist/_lib/runtime-env.d.ts.map +1 -0
- package/dist/_lib/runtime-env.js +172 -0
- package/dist/_lib/runtime-env.js.map +1 -0
- package/dist/_lib/subscription-client.d.ts +129 -18
- package/dist/_lib/subscription-client.d.ts.map +1 -1
- package/dist/_lib/subscription-client.js +268 -40
- package/dist/_lib/subscription-client.js.map +1 -1
- package/dist/_lib/subscription-env.d.ts +35 -0
- package/dist/_lib/subscription-env.d.ts.map +1 -1
- package/dist/_lib/subscription-env.js +46 -2
- package/dist/_lib/subscription-env.js.map +1 -1
- package/dist/commands/app-lifecycle.d.ts +36 -0
- package/dist/commands/app-lifecycle.d.ts.map +1 -0
- package/dist/commands/app-lifecycle.js +145 -0
- package/dist/commands/app-lifecycle.js.map +1 -0
- package/dist/commands/ci.d.ts +45 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +174 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/deploy-manifest.d.ts +11 -0
- package/dist/commands/deploy-manifest.d.ts.map +1 -0
- package/dist/commands/deploy-manifest.js +17 -0
- package/dist/commands/deploy-manifest.js.map +1 -0
- package/dist/commands/deploy.d.ts +70 -11
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +263 -150
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/downgrade.js +1 -1
- package/dist/commands/downgrade.js.map +1 -1
- package/dist/commands/faucet.d.ts +78 -0
- package/dist/commands/faucet.d.ts.map +1 -0
- package/dist/commands/faucet.js +184 -0
- package/dist/commands/faucet.js.map +1 -0
- package/dist/commands/governance.js +1 -1
- package/dist/commands/governance.js.map +1 -1
- package/dist/commands/hist-balance.d.ts +1 -1
- package/dist/commands/hist-balance.js +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/list.d.ts +22 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +81 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/personhood.js +2 -2
- package/dist/commands/personhood.js.map +1 -1
- package/dist/commands/redeploy.d.ts +62 -0
- package/dist/commands/redeploy.d.ts.map +1 -0
- package/dist/commands/redeploy.js +310 -0
- package/dist/commands/redeploy.js.map +1 -0
- package/dist/commands/status.d.ts +51 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +138 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/subscribe.d.ts +94 -17
- package/dist/commands/subscribe.d.ts.map +1 -1
- package/dist/commands/subscribe.js +450 -81
- package/dist/commands/subscribe.js.map +1 -1
- package/dist/commands/transactions.d.ts +60 -0
- package/dist/commands/transactions.d.ts.map +1 -0
- package/dist/commands/transactions.js +101 -0
- package/dist/commands/transactions.js.map +1 -0
- package/dist/commands/update.d.ts +40 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +114 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.d.ts +10 -19
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +14 -23
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -3
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin, testable wrappers around the `docker` CLI for the trusted-deploy flow.
|
|
3
|
+
*
|
|
4
|
+
* Before the trusted-deploy arc the CLI only PRINTED the `docker login` /
|
|
5
|
+
* `docker push` commands and waited for the developer to run them by hand.
|
|
6
|
+
* Now the CLI wraps the push so it can RECORD the pushed image's registry
|
|
7
|
+
* manifest digest — the `imageDigest` field every {@link BuildAttestation}
|
|
8
|
+
* binds to. The digest must be the value the registry returns for the pushed
|
|
9
|
+
* tag, not a locally-computed one, so the attestation references the exact
|
|
10
|
+
* bytes the cluster will pull.
|
|
11
|
+
*
|
|
12
|
+
* Every function shells out via `child_process.spawn` with an argv array (never
|
|
13
|
+
* a shell string), so registry passwords and tags can't be shell-injected.
|
|
14
|
+
* Secrets (the one-time push password) are fed on stdin, never on argv.
|
|
15
|
+
*/
|
|
16
|
+
import { spawn } from 'child_process';
|
|
17
|
+
/** Registry coordinates returned by `deployment.init` (`BaasInitResponse`). */
|
|
18
|
+
export type RegistryCreds = {
|
|
19
|
+
server: string;
|
|
20
|
+
username: string;
|
|
21
|
+
password: string;
|
|
22
|
+
repository: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The fully-qualified image reference for a pushed artifact:
|
|
26
|
+
* `<server>/<repository>:<tag>`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function imageRef(registry: RegistryCreds, tag: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* `docker login` to the per-app Harbor registry using the one-time push robot
|
|
31
|
+
* credentials. Password is fed on stdin (`--password-stdin`).
|
|
32
|
+
*
|
|
33
|
+
* @throws if docker exits non-zero (login failed).
|
|
34
|
+
*/
|
|
35
|
+
export declare function dockerLogin(registry: RegistryCreds, spawnFn?: typeof spawn): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Parse the registry manifest digest from `docker push` output.
|
|
38
|
+
*
|
|
39
|
+
* A successful `docker push` prints a summary line of the shape:
|
|
40
|
+
* `v0.1.0: digest: sha256:<64hex> size: 1786`
|
|
41
|
+
* We extract the `sha256:<64hex>` token. This is the registry's MANIFEST
|
|
42
|
+
* digest for the pushed tag — exactly what the attestation's `imageDigest`
|
|
43
|
+
* must carry.
|
|
44
|
+
*
|
|
45
|
+
* @returns the `sha256:<64hex>` digest, or `null` if no digest line is present.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parsePushDigest(output: string): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Pick the `sha256:<64hex>` digest for `<server>/<repository>` out of a parsed
|
|
50
|
+
* `docker inspect ... .RepoDigests` array. Pure — shared by both the
|
|
51
|
+
* `spawn`-based {@link inspectImageDigest} and the `execFileSync`-based
|
|
52
|
+
* `buildAndPush` inspect-fallback in `_lib/baas.ts`, so the matching rule lives
|
|
53
|
+
* in exactly one place.
|
|
54
|
+
*
|
|
55
|
+
* `stdout` is the raw `{{json .RepoDigests}}` output (a JSON string array).
|
|
56
|
+
* Returns the lower-cased `sha256:<64hex>` of the first entry whose ref matches
|
|
57
|
+
* `<server>/<repository>@`, or `null` if the JSON is unparseable / no entry
|
|
58
|
+
* matches.
|
|
59
|
+
*/
|
|
60
|
+
export declare function parseRepoDigests(stdout: string, registry: RegistryCreds): string | null;
|
|
61
|
+
/**
|
|
62
|
+
* `docker inspect` fallback to recover a pushed image's registry digest from
|
|
63
|
+
* its `RepoDigests` when the push output didn't surface one (older docker,
|
|
64
|
+
* buildx drivers, or an externally-run push we're inspecting after the fact).
|
|
65
|
+
*
|
|
66
|
+
* Returns the `sha256:<64hex>` portion of the matching `RepoDigests` entry, or
|
|
67
|
+
* `null` if none matches `<server>/<repository>`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function inspectImageDigest(registry: RegistryCreds, tag: string, spawnFn?: typeof spawn): Promise<string | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Push the backend image and RECORD its registry manifest digest.
|
|
72
|
+
*
|
|
73
|
+
* Runs `docker push <server>/<repository>:<tag>`, parses the digest from the
|
|
74
|
+
* push summary, and falls back to {@link inspectImageDigest} if the summary
|
|
75
|
+
* didn't carry one. Throws if the push fails OR if no digest can be recovered
|
|
76
|
+
* (a deploy must never proceed with an unverifiable artifact reference).
|
|
77
|
+
*
|
|
78
|
+
* @returns the `sha256:<64hex>` manifest digest of the pushed tag.
|
|
79
|
+
* @throws if push exits non-zero, or if no manifest digest can be determined.
|
|
80
|
+
*/
|
|
81
|
+
export declare function dockerPush(registry: RegistryCreds, tag: string, spawnFn?: typeof spawn): Promise<string>;
|
|
82
|
+
//# sourceMappingURL=docker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/_lib/docker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAwCF;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI7D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAiBvF;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC,MAAM,CAAC,CAiBjB"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.imageRef = imageRef;
|
|
4
|
+
exports.dockerLogin = dockerLogin;
|
|
5
|
+
exports.parsePushDigest = parsePushDigest;
|
|
6
|
+
exports.parseRepoDigests = parseRepoDigests;
|
|
7
|
+
exports.inspectImageDigest = inspectImageDigest;
|
|
8
|
+
exports.dockerPush = dockerPush;
|
|
9
|
+
/**
|
|
10
|
+
* Thin, testable wrappers around the `docker` CLI for the trusted-deploy flow.
|
|
11
|
+
*
|
|
12
|
+
* Before the trusted-deploy arc the CLI only PRINTED the `docker login` /
|
|
13
|
+
* `docker push` commands and waited for the developer to run them by hand.
|
|
14
|
+
* Now the CLI wraps the push so it can RECORD the pushed image's registry
|
|
15
|
+
* manifest digest — the `imageDigest` field every {@link BuildAttestation}
|
|
16
|
+
* binds to. The digest must be the value the registry returns for the pushed
|
|
17
|
+
* tag, not a locally-computed one, so the attestation references the exact
|
|
18
|
+
* bytes the cluster will pull.
|
|
19
|
+
*
|
|
20
|
+
* Every function shells out via `child_process.spawn` with an argv array (never
|
|
21
|
+
* a shell string), so registry passwords and tags can't be shell-injected.
|
|
22
|
+
* Secrets (the one-time push password) are fed on stdin, never on argv.
|
|
23
|
+
*/
|
|
24
|
+
const child_process_1 = require("child_process");
|
|
25
|
+
/**
|
|
26
|
+
* Run `docker <args>` to completion, capturing stdout/stderr. `passwordStdin`,
|
|
27
|
+
* when set, is written to the child's stdin then the stream is closed — used to
|
|
28
|
+
* feed `docker login --password-stdin` without the secret ever touching argv or
|
|
29
|
+
* the process table.
|
|
30
|
+
*
|
|
31
|
+
* Injectable `spawnFn` keeps this unit-testable: specs pass a fake that returns
|
|
32
|
+
* canned output without a real docker daemon.
|
|
33
|
+
*/
|
|
34
|
+
async function runDocker(args, opts = {}) {
|
|
35
|
+
const spawnImpl = opts.spawnFn ?? child_process_1.spawn;
|
|
36
|
+
return new Promise((resolvePromise, reject) => {
|
|
37
|
+
const child = spawnImpl('docker', args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
38
|
+
let stdout = '';
|
|
39
|
+
let stderr = '';
|
|
40
|
+
child.stdout?.on('data', (d) => {
|
|
41
|
+
stdout += d.toString();
|
|
42
|
+
});
|
|
43
|
+
child.stderr?.on('data', (d) => {
|
|
44
|
+
stderr += d.toString();
|
|
45
|
+
});
|
|
46
|
+
child.on('error', (err) => reject(err));
|
|
47
|
+
child.on('close', (code) => resolvePromise({ code: code ?? 0, stdout, stderr }));
|
|
48
|
+
if (opts.passwordStdin !== undefined) {
|
|
49
|
+
child.stdin?.write(opts.passwordStdin);
|
|
50
|
+
}
|
|
51
|
+
child.stdin?.end();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The fully-qualified image reference for a pushed artifact:
|
|
56
|
+
* `<server>/<repository>:<tag>`.
|
|
57
|
+
*/
|
|
58
|
+
function imageRef(registry, tag) {
|
|
59
|
+
return `${registry.server}/${registry.repository}:${tag}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* `docker login` to the per-app Harbor registry using the one-time push robot
|
|
63
|
+
* credentials. Password is fed on stdin (`--password-stdin`).
|
|
64
|
+
*
|
|
65
|
+
* @throws if docker exits non-zero (login failed).
|
|
66
|
+
*/
|
|
67
|
+
async function dockerLogin(registry, spawnFn) {
|
|
68
|
+
const { code, stderr } = await runDocker(['login', registry.server, '-u', registry.username, '--password-stdin'], { passwordStdin: registry.password, spawnFn });
|
|
69
|
+
if (code !== 0) {
|
|
70
|
+
throw new Error(`docker login failed (exit ${code}): ${stderr.trim()}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse the registry manifest digest from `docker push` output.
|
|
75
|
+
*
|
|
76
|
+
* A successful `docker push` prints a summary line of the shape:
|
|
77
|
+
* `v0.1.0: digest: sha256:<64hex> size: 1786`
|
|
78
|
+
* We extract the `sha256:<64hex>` token. This is the registry's MANIFEST
|
|
79
|
+
* digest for the pushed tag — exactly what the attestation's `imageDigest`
|
|
80
|
+
* must carry.
|
|
81
|
+
*
|
|
82
|
+
* @returns the `sha256:<64hex>` digest, or `null` if no digest line is present.
|
|
83
|
+
*/
|
|
84
|
+
function parsePushDigest(output) {
|
|
85
|
+
// `digest:` precedes the manifest digest in docker's push summary.
|
|
86
|
+
const m = output.match(/digest:\s*(sha256:[a-f0-9]{64})/i);
|
|
87
|
+
return m ? m[1].toLowerCase() : null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Pick the `sha256:<64hex>` digest for `<server>/<repository>` out of a parsed
|
|
91
|
+
* `docker inspect ... .RepoDigests` array. Pure — shared by both the
|
|
92
|
+
* `spawn`-based {@link inspectImageDigest} and the `execFileSync`-based
|
|
93
|
+
* `buildAndPush` inspect-fallback in `_lib/baas.ts`, so the matching rule lives
|
|
94
|
+
* in exactly one place.
|
|
95
|
+
*
|
|
96
|
+
* `stdout` is the raw `{{json .RepoDigests}}` output (a JSON string array).
|
|
97
|
+
* Returns the lower-cased `sha256:<64hex>` of the first entry whose ref matches
|
|
98
|
+
* `<server>/<repository>@`, or `null` if the JSON is unparseable / no entry
|
|
99
|
+
* matches.
|
|
100
|
+
*/
|
|
101
|
+
function parseRepoDigests(stdout, registry) {
|
|
102
|
+
let repoDigests;
|
|
103
|
+
try {
|
|
104
|
+
repoDigests = JSON.parse(stdout.trim());
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
if (!Array.isArray(repoDigests))
|
|
110
|
+
return null;
|
|
111
|
+
const repoPrefix = `${registry.server}/${registry.repository}@`;
|
|
112
|
+
for (const rd of repoDigests) {
|
|
113
|
+
if (typeof rd === 'string' && rd.startsWith(repoPrefix)) {
|
|
114
|
+
const at = rd.indexOf('@');
|
|
115
|
+
const digest = rd.slice(at + 1);
|
|
116
|
+
if (/^sha256:[a-f0-9]{64}$/i.test(digest))
|
|
117
|
+
return digest.toLowerCase();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* `docker inspect` fallback to recover a pushed image's registry digest from
|
|
124
|
+
* its `RepoDigests` when the push output didn't surface one (older docker,
|
|
125
|
+
* buildx drivers, or an externally-run push we're inspecting after the fact).
|
|
126
|
+
*
|
|
127
|
+
* Returns the `sha256:<64hex>` portion of the matching `RepoDigests` entry, or
|
|
128
|
+
* `null` if none matches `<server>/<repository>`.
|
|
129
|
+
*/
|
|
130
|
+
async function inspectImageDigest(registry, tag, spawnFn) {
|
|
131
|
+
const ref = imageRef(registry, tag);
|
|
132
|
+
const { code, stdout } = await runDocker(['inspect', '--format', '{{json .RepoDigests}}', ref], { spawnFn });
|
|
133
|
+
if (code !== 0)
|
|
134
|
+
return null;
|
|
135
|
+
return parseRepoDigests(stdout, registry);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Push the backend image and RECORD its registry manifest digest.
|
|
139
|
+
*
|
|
140
|
+
* Runs `docker push <server>/<repository>:<tag>`, parses the digest from the
|
|
141
|
+
* push summary, and falls back to {@link inspectImageDigest} if the summary
|
|
142
|
+
* didn't carry one. Throws if the push fails OR if no digest can be recovered
|
|
143
|
+
* (a deploy must never proceed with an unverifiable artifact reference).
|
|
144
|
+
*
|
|
145
|
+
* @returns the `sha256:<64hex>` manifest digest of the pushed tag.
|
|
146
|
+
* @throws if push exits non-zero, or if no manifest digest can be determined.
|
|
147
|
+
*/
|
|
148
|
+
async function dockerPush(registry, tag, spawnFn) {
|
|
149
|
+
const ref = imageRef(registry, tag);
|
|
150
|
+
const { code, stdout, stderr } = await runDocker(['push', ref], { spawnFn });
|
|
151
|
+
if (code !== 0) {
|
|
152
|
+
throw new Error(`docker push failed (exit ${code}): ${stderr.trim()}`);
|
|
153
|
+
}
|
|
154
|
+
const fromPush = parsePushDigest(stdout + '\n' + stderr);
|
|
155
|
+
if (fromPush)
|
|
156
|
+
return fromPush;
|
|
157
|
+
const fromInspect = await inspectImageDigest(registry, tag, spawnFn);
|
|
158
|
+
if (fromInspect)
|
|
159
|
+
return fromInspect;
|
|
160
|
+
throw new Error(`pushed ${ref} but could not determine its registry manifest digest ` +
|
|
161
|
+
'(no `digest: sha256:...` in push output and no matching RepoDigests). ' +
|
|
162
|
+
'Re-run with --image-digest <sha256:...> to supply it explicitly.');
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/_lib/docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type FaucetChallengeResponse = {
|
|
2
|
+
challengeId: string;
|
|
3
|
+
message: string;
|
|
4
|
+
expiresInSeconds: number;
|
|
5
|
+
};
|
|
6
|
+
export type FaucetDispensedResponse = {
|
|
7
|
+
status: 'dispensed';
|
|
8
|
+
amount: string;
|
|
9
|
+
txHash: string;
|
|
10
|
+
remainingDailyAllowance: string;
|
|
11
|
+
};
|
|
12
|
+
export type FaucetTrustLineRequiredResponse = {
|
|
13
|
+
status: 'trustline_required';
|
|
14
|
+
trustLine: {
|
|
15
|
+
currency: string;
|
|
16
|
+
issuer: string;
|
|
17
|
+
limit: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type FaucetRateLimitedResponse = {
|
|
21
|
+
status: 'rate_limited';
|
|
22
|
+
retryAfterSeconds: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Discriminated union of the three documented `POST /faucet/hsuite` outcomes.
|
|
26
|
+
* Switch on `.status`.
|
|
27
|
+
*/
|
|
28
|
+
export type FaucetDispenseResponse = FaucetDispensedResponse | FaucetTrustLineRequiredResponse | FaucetRateLimitedResponse;
|
|
29
|
+
export type FaucetStatusResponse = {
|
|
30
|
+
dispensedToday: string;
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
};
|
|
33
|
+
export type FetchLike = (input: string, init?: any) => Promise<{
|
|
34
|
+
ok: boolean;
|
|
35
|
+
status: number;
|
|
36
|
+
text(): Promise<string>;
|
|
37
|
+
json(): Promise<unknown>;
|
|
38
|
+
}>;
|
|
39
|
+
export type FaucetClientOptions = {
|
|
40
|
+
baseUrl: string;
|
|
41
|
+
fetchImpl?: FetchLike;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Validator-side HSUITE-testnet faucet HTTP client.
|
|
45
|
+
*
|
|
46
|
+
* Adapter over the SDK's {@link SdkFaucetClient}. All HTTP/fetch logic lives in
|
|
47
|
+
* the SDK's `createHttpClient`; this class only adapts method names + base URL
|
|
48
|
+
* and (for tests) bridges the injectable `fetchImpl` seam onto the SDK's
|
|
49
|
+
* `HttpClient` interface.
|
|
50
|
+
*/
|
|
51
|
+
export declare class FaucetClient {
|
|
52
|
+
private readonly sdk;
|
|
53
|
+
constructor(opts: FaucetClientOptions);
|
|
54
|
+
challenge(chain: string, recipientAddress: string): Promise<FaucetChallengeResponse>;
|
|
55
|
+
dispense(payload: {
|
|
56
|
+
chain: string;
|
|
57
|
+
recipientAddress: string;
|
|
58
|
+
challengeId: string;
|
|
59
|
+
signature: string;
|
|
60
|
+
publicKey: string;
|
|
61
|
+
}): Promise<FaucetDispenseResponse>;
|
|
62
|
+
status(chain: string, recipientAddress: string): Promise<FaucetStatusResponse>;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=faucet-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faucet-client.d.ts","sourceRoot":"","sources":["../../src/_lib/faucet-client.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,cAAc,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAC9B,uBAAuB,GACvB,+BAA+B,GAC/B,yBAAyB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;IAC7D,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;gBAE1B,IAAI,EAAE,mBAAmB;IAW/B,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAIpF,QAAQ,CAAC,OAAO,EAAE;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAI7B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAGrF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FaucetClient = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Thin CLI adapter over the SDK's faucet client.
|
|
6
|
+
*
|
|
7
|
+
* The validator-side `/api/v3/faucet/hsuite/*` surface is already covered by
|
|
8
|
+
* `FaucetClient` in `@hsuite/smart-engines-sdk`. To avoid the CLI drifting from
|
|
9
|
+
* the SDK, this file no longer hand-rolls its own `fetch`/HTTP plumbing — it
|
|
10
|
+
* delegates every call to the SDK client, built on the SDK's shared
|
|
11
|
+
* `createHttpClient` (whose `baseUrl` carries the `/api/v3` prefix the SDK paths
|
|
12
|
+
* omit).
|
|
13
|
+
*
|
|
14
|
+
* The public shape (class name, method names, response types) is preserved so
|
|
15
|
+
* the CLI command bodies (`commands/faucet.ts`) and their unit tests are
|
|
16
|
+
* unchanged. Tests still inject a fake via the constructor's `fetchImpl` seam.
|
|
17
|
+
*/
|
|
18
|
+
const smart_engines_sdk_1 = require("@hsuite/smart-engines-sdk");
|
|
19
|
+
/**
|
|
20
|
+
* Validator-side HSUITE-testnet faucet HTTP client.
|
|
21
|
+
*
|
|
22
|
+
* Adapter over the SDK's {@link SdkFaucetClient}. All HTTP/fetch logic lives in
|
|
23
|
+
* the SDK's `createHttpClient`; this class only adapts method names + base URL
|
|
24
|
+
* and (for tests) bridges the injectable `fetchImpl` seam onto the SDK's
|
|
25
|
+
* `HttpClient` interface.
|
|
26
|
+
*/
|
|
27
|
+
class FaucetClient {
|
|
28
|
+
sdk;
|
|
29
|
+
constructor(opts) {
|
|
30
|
+
// The SDK faucet paths are `/faucet/hsuite/...` — carry the `/api/v3`
|
|
31
|
+
// prefix on the HttpClient baseUrl so the full URL matches the validator
|
|
32
|
+
// controller mount.
|
|
33
|
+
const baseUrl = `${opts.baseUrl.replace(/\/$/, '')}/api/v3`;
|
|
34
|
+
const http = opts.fetchImpl
|
|
35
|
+
? makeFetchBackedHttpClient(baseUrl, opts.fetchImpl)
|
|
36
|
+
: (0, smart_engines_sdk_1.createHttpClient)({ baseUrl });
|
|
37
|
+
this.sdk = new smart_engines_sdk_1.FaucetClient(http);
|
|
38
|
+
}
|
|
39
|
+
async challenge(chain, recipientAddress) {
|
|
40
|
+
return this.sdk.requestChallenge(chain, recipientAddress);
|
|
41
|
+
}
|
|
42
|
+
async dispense(payload) {
|
|
43
|
+
return this.sdk.dispense(payload);
|
|
44
|
+
}
|
|
45
|
+
async status(chain, recipientAddress) {
|
|
46
|
+
return this.sdk.getStatus(chain, recipientAddress);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.FaucetClient = FaucetClient;
|
|
50
|
+
/**
|
|
51
|
+
* Adapt an injected `fetchImpl` (the CLI's test seam) onto the SDK's
|
|
52
|
+
* {@link HttpClient} interface so the SDK client can be exercised with a fake
|
|
53
|
+
* transport. Only the verbs the faucet client uses (`get`/`post`) are
|
|
54
|
+
* implemented; the rest throw if called.
|
|
55
|
+
*
|
|
56
|
+
* Mirrors the SDK's own `createHttpClient` request/error semantics (JSON body,
|
|
57
|
+
* `${method} ${path} → ${status}` error on non-2xx) so behavior is identical.
|
|
58
|
+
*/
|
|
59
|
+
function makeFetchBackedHttpClient(baseUrl, fetchImpl) {
|
|
60
|
+
async function send(method, path, body) {
|
|
61
|
+
const url = `${baseUrl}${path}`;
|
|
62
|
+
const init = {
|
|
63
|
+
method,
|
|
64
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
65
|
+
};
|
|
66
|
+
if (body !== undefined)
|
|
67
|
+
init.body = JSON.stringify(body);
|
|
68
|
+
const res = await fetchImpl(url, init);
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
const text = await res.text().catch(() => '');
|
|
71
|
+
throw new Error(`${method} ${path} → ${res.status}: ${text.slice(0, 500)}`);
|
|
72
|
+
}
|
|
73
|
+
return (await res.json());
|
|
74
|
+
}
|
|
75
|
+
const unsupported = (verb) => () => {
|
|
76
|
+
throw new Error(`fetch-backed HttpClient: ${verb} not supported in CLI faucet adapter`);
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
get: (path) => send('GET', path),
|
|
80
|
+
post: (path, b) => send('POST', path, b),
|
|
81
|
+
put: unsupported('put'),
|
|
82
|
+
patch: unsupported('patch'),
|
|
83
|
+
delete: unsupported('delete'),
|
|
84
|
+
getText: unsupported('getText'),
|
|
85
|
+
getBinary: unsupported('getBinary'),
|
|
86
|
+
getBinaryWithMeta: unsupported('getBinaryWithMeta'),
|
|
87
|
+
upload: unsupported('upload'),
|
|
88
|
+
setAuthToken: () => undefined,
|
|
89
|
+
getAuthToken: () => undefined,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=faucet-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faucet-client.js","sourceRoot":"","sources":["../../src/_lib/faucet-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** The frameworks the SDK can build, mirroring `BuildAttestation['framework']`. */
|
|
2
|
+
export type Framework = 'ionic' | 'nestjs';
|
|
3
|
+
/**
|
|
4
|
+
* The result of classifying a project directory.
|
|
5
|
+
*
|
|
6
|
+
* `framework` is the attestation framework; `sdkPackage` is the specific
|
|
7
|
+
* HSUITE dependency that proved SDK usage (recorded for diagnostics);
|
|
8
|
+
* `sdkVersion` is the declared semver range of that dependency, used as the
|
|
9
|
+
* attestation's `sdkVersion` when the installed version can't be resolved.
|
|
10
|
+
*/
|
|
11
|
+
export type FrameworkDetection = {
|
|
12
|
+
framework: Framework;
|
|
13
|
+
sdkPackage: string;
|
|
14
|
+
sdkVersionRange: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Thrown when a deploy target carries no HSUITE SDK dependency. Carrying a
|
|
18
|
+
* distinct error type lets the deploy command surface the actionable message
|
|
19
|
+
* AND lets specs assert the abort without string-matching console output.
|
|
20
|
+
*/
|
|
21
|
+
export declare class NoSdkError extends Error {
|
|
22
|
+
constructor(message: string);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Classify a project directory's framework, or abort.
|
|
26
|
+
*
|
|
27
|
+
* Classification order (most-specific dependency wins):
|
|
28
|
+
* 1. `@hsuite/native-connect-*` (+ `@ionic/angular`) → `ionic` frontend.
|
|
29
|
+
* 2. `@hsuite/smart-engines-sdk` (+ `@nestjs/core`) → `nestjs` backend.
|
|
30
|
+
* 3. neither HSUITE dependency present → {@link NoSdkError}.
|
|
31
|
+
*
|
|
32
|
+
* The `@ionic/angular` / `@nestjs/core` co-presence is REQUIRED so a project
|
|
33
|
+
* that merely lists an HSUITE package without the matching framework runtime
|
|
34
|
+
* is reported precisely rather than silently mis-bucketed.
|
|
35
|
+
*
|
|
36
|
+
* @param projectDir - Smart-app project root (the dir containing package.json).
|
|
37
|
+
* @returns The detected framework + the proving SDK dependency.
|
|
38
|
+
* @throws {NoSdkError} if no HSUITE SDK dependency is present.
|
|
39
|
+
* @throws {Error} if the framework runtime that pairs with the HSUITE dep is
|
|
40
|
+
* missing (actionable: "add @nestjs/core" / "add @ionic/angular").
|
|
41
|
+
*/
|
|
42
|
+
export declare function detectFramework(projectDir: string): FrameworkDetection;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the SDK version the project is actually built against, for the
|
|
45
|
+
* attestation's `sdkVersion` field.
|
|
46
|
+
*
|
|
47
|
+
* Prefers the INSTALLED version (`node_modules/<pkg>/package.json`'s `version`)
|
|
48
|
+
* because that is the exact code the artifact links; falls back to the declared
|
|
49
|
+
* semver range from the project `package.json` when `node_modules` isn't
|
|
50
|
+
* present (e.g. a CI image that built elsewhere). The fallback range is still a
|
|
51
|
+
* meaningful, signable claim about the intended SDK.
|
|
52
|
+
*
|
|
53
|
+
* @param projectDir - Smart-app project root.
|
|
54
|
+
* @param detection - The result of {@link detectFramework} (carries the package
|
|
55
|
+
* name + declared range).
|
|
56
|
+
* @returns The installed exact version if resolvable, else the declared range.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveSdkVersion(projectDir: string, detection: FrameworkDetection): string;
|
|
59
|
+
//# sourceMappingURL=framework.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framework.d.ts","sourceRoot":"","sources":["../../src/_lib/framework.ts"],"names":[],"mappings":"AAqBA,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAQ3C;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAOF;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CA4CtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,kBAAkB,GAC5B,MAAM,CAgBR"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoSdkError = void 0;
|
|
4
|
+
exports.detectFramework = detectFramework;
|
|
5
|
+
exports.resolveSdkVersion = resolveSdkVersion;
|
|
6
|
+
/**
|
|
7
|
+
* Client-side half of the SDK restriction: prove a deploy target was built
|
|
8
|
+
* with the HSUITE Smart Engines SDK and classify which framework it uses.
|
|
9
|
+
*
|
|
10
|
+
* The trusted-deploy arc signs a {@link BuildAttestation} over every deploy;
|
|
11
|
+
* one of its fields is `framework: 'ionic' | 'nestjs'`. This module derives
|
|
12
|
+
* that field — and, critically, ABORTS the deploy when the project carries NO
|
|
13
|
+
* HSUITE SDK dependency at all. A smart-app that wasn't built against the SDK
|
|
14
|
+
* has nothing to attest and must never reach the signing path.
|
|
15
|
+
*
|
|
16
|
+
* Detection is dependency-driven (reads the project's `package.json`), matching
|
|
17
|
+
* the real showcase apps:
|
|
18
|
+
* - backend (`apps/showcase/smart-app`): `@hsuite/smart-engines-sdk` + `@nestjs/core`
|
|
19
|
+
* - frontend (`apps/showcase/ionic-dapp`): `@hsuite/native-connect-*` + `@ionic/angular`
|
|
20
|
+
*
|
|
21
|
+
* This module is PURE I/O-at-the-edge: it reads one file and classifies. No
|
|
22
|
+
* network, no clock.
|
|
23
|
+
*/
|
|
24
|
+
const fs_1 = require("fs");
|
|
25
|
+
const path_1 = require("path");
|
|
26
|
+
/** The `@hsuite/native-connect-*` packages an Ionic smart-app frontend pulls in. */
|
|
27
|
+
const NATIVE_CONNECT_PREFIX = '@hsuite/native-connect-';
|
|
28
|
+
/** The backend SDK package. Its presence is the nestjs (backend) signal. */
|
|
29
|
+
const BACKEND_SDK = '@hsuite/smart-engines-sdk';
|
|
30
|
+
/**
|
|
31
|
+
* Thrown when a deploy target carries no HSUITE SDK dependency. Carrying a
|
|
32
|
+
* distinct error type lets the deploy command surface the actionable message
|
|
33
|
+
* AND lets specs assert the abort without string-matching console output.
|
|
34
|
+
*/
|
|
35
|
+
class NoSdkError extends Error {
|
|
36
|
+
constructor(message) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = 'NoSdkError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.NoSdkError = NoSdkError;
|
|
42
|
+
/**
|
|
43
|
+
* Merge `dependencies` + `devDependencies` into one lookup. Smart-apps declare
|
|
44
|
+
* the SDK under `dependencies`, but tooling/CI templates occasionally place a
|
|
45
|
+
* build-time SDK under `devDependencies`; treat both as "the project uses the
|
|
46
|
+
* SDK" for detection purposes.
|
|
47
|
+
*/
|
|
48
|
+
function allDeps(pkg) {
|
|
49
|
+
return { ...(pkg.devDependencies ?? {}), ...(pkg.dependencies ?? {}) };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Read + parse the `package.json` at `projectDir`. Throws a clear error if the
|
|
53
|
+
* directory has no `package.json` (deploy target must be a real project root).
|
|
54
|
+
*/
|
|
55
|
+
function readPackageJson(projectDir) {
|
|
56
|
+
const pkgPath = (0, path_1.join)((0, path_1.resolve)(projectDir), 'package.json');
|
|
57
|
+
if (!(0, fs_1.existsSync)(pkgPath)) {
|
|
58
|
+
throw new Error(`no package.json found at ${pkgPath} — point --project at the smart-app root`);
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf8'));
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
throw new Error(`failed to parse ${pkgPath}: ${err.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Classify a project directory's framework, or abort.
|
|
69
|
+
*
|
|
70
|
+
* Classification order (most-specific dependency wins):
|
|
71
|
+
* 1. `@hsuite/native-connect-*` (+ `@ionic/angular`) → `ionic` frontend.
|
|
72
|
+
* 2. `@hsuite/smart-engines-sdk` (+ `@nestjs/core`) → `nestjs` backend.
|
|
73
|
+
* 3. neither HSUITE dependency present → {@link NoSdkError}.
|
|
74
|
+
*
|
|
75
|
+
* The `@ionic/angular` / `@nestjs/core` co-presence is REQUIRED so a project
|
|
76
|
+
* that merely lists an HSUITE package without the matching framework runtime
|
|
77
|
+
* is reported precisely rather than silently mis-bucketed.
|
|
78
|
+
*
|
|
79
|
+
* @param projectDir - Smart-app project root (the dir containing package.json).
|
|
80
|
+
* @returns The detected framework + the proving SDK dependency.
|
|
81
|
+
* @throws {NoSdkError} if no HSUITE SDK dependency is present.
|
|
82
|
+
* @throws {Error} if the framework runtime that pairs with the HSUITE dep is
|
|
83
|
+
* missing (actionable: "add @nestjs/core" / "add @ionic/angular").
|
|
84
|
+
*/
|
|
85
|
+
function detectFramework(projectDir) {
|
|
86
|
+
const pkg = readPackageJson(projectDir);
|
|
87
|
+
const deps = allDeps(pkg);
|
|
88
|
+
const names = Object.keys(deps);
|
|
89
|
+
const nativeConnect = names.find((n) => n.startsWith(NATIVE_CONNECT_PREFIX));
|
|
90
|
+
const hasBackendSdk = BACKEND_SDK in deps;
|
|
91
|
+
// No HSUITE SDK dependency of either flavor → abort before any signing.
|
|
92
|
+
if (!nativeConnect && !hasBackendSdk) {
|
|
93
|
+
throw new NoSdkError('smart-apps must be built with the HSUITE SDK — install ' +
|
|
94
|
+
`\`${BACKEND_SDK}\` (NestJS backend) or the \`${NATIVE_CONNECT_PREFIX}*\` ` +
|
|
95
|
+
'packages (Ionic frontend). See https://docs.hsuite.network/sdk');
|
|
96
|
+
}
|
|
97
|
+
// Ionic frontend: native-connect packages present.
|
|
98
|
+
if (nativeConnect) {
|
|
99
|
+
if (!('@ionic/angular' in deps)) {
|
|
100
|
+
throw new Error(`found ${nativeConnect} but no \`@ionic/angular\` — an Ionic smart-app ` +
|
|
101
|
+
'frontend must depend on @ionic/angular');
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
framework: 'ionic',
|
|
105
|
+
sdkPackage: nativeConnect,
|
|
106
|
+
sdkVersionRange: deps[nativeConnect],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// NestJS backend: smart-engines-sdk present.
|
|
110
|
+
if (!('@nestjs/core' in deps)) {
|
|
111
|
+
throw new Error(`found ${BACKEND_SDK} but no \`@nestjs/core\` — a NestJS smart-app ` +
|
|
112
|
+
'backend must depend on @nestjs/core');
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
framework: 'nestjs',
|
|
116
|
+
sdkPackage: BACKEND_SDK,
|
|
117
|
+
sdkVersionRange: deps[BACKEND_SDK],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Resolve the SDK version the project is actually built against, for the
|
|
122
|
+
* attestation's `sdkVersion` field.
|
|
123
|
+
*
|
|
124
|
+
* Prefers the INSTALLED version (`node_modules/<pkg>/package.json`'s `version`)
|
|
125
|
+
* because that is the exact code the artifact links; falls back to the declared
|
|
126
|
+
* semver range from the project `package.json` when `node_modules` isn't
|
|
127
|
+
* present (e.g. a CI image that built elsewhere). The fallback range is still a
|
|
128
|
+
* meaningful, signable claim about the intended SDK.
|
|
129
|
+
*
|
|
130
|
+
* @param projectDir - Smart-app project root.
|
|
131
|
+
* @param detection - The result of {@link detectFramework} (carries the package
|
|
132
|
+
* name + declared range).
|
|
133
|
+
* @returns The installed exact version if resolvable, else the declared range.
|
|
134
|
+
*/
|
|
135
|
+
function resolveSdkVersion(projectDir, detection) {
|
|
136
|
+
const installed = (0, path_1.join)((0, path_1.resolve)(projectDir), 'node_modules', detection.sdkPackage, 'package.json');
|
|
137
|
+
if ((0, fs_1.existsSync)(installed)) {
|
|
138
|
+
try {
|
|
139
|
+
const pkg = JSON.parse((0, fs_1.readFileSync)(installed, 'utf8'));
|
|
140
|
+
if (pkg.version)
|
|
141
|
+
return pkg.version;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// fall through to the declared range
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return detection.sdkVersionRange;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=framework.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framework.js","sourceRoot":"","sources":["../../src/_lib/framework.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed guard shared by `deploy` and `redeploy`: refuse to ship a
|
|
3
|
+
* frontend build that still carries unfilled `REPLACE_BEFORE_DEPLOY:<key>`
|
|
4
|
+
* placeholders (per-deployment config that was never set).
|
|
5
|
+
*
|
|
6
|
+
* Such a build refuses to bootstrap in the browser (the SPA's `main.ts` asserts
|
|
7
|
+
* on the sentinel prefix), and the CLI would otherwise tar+upload it silently —
|
|
8
|
+
* which is exactly how a non-bootstrapping bundle reached production. Matches
|
|
9
|
+
* the sentinel VALUE form (`:` followed by a letter) so it does NOT
|
|
10
|
+
* false-positive on the guard's own comparison literal (`REPLACE_BEFORE_DEPLOY:"`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function assertNoDeploySentinels(dir: string): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=frontend-guard.d.ts.map
|