@phreshos/cli 0.1.13 → 0.1.15
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 +5 -4
- package/dist/program-release.js +29 -22
- package/dist/style.js +0 -1
- package/dist/template/README.md +9 -4
- package/dist/template/api-docs.md +10 -4
- package/dist/template/gitignore +3 -15
- package/dist/template/package.json +14 -8
- package/dist/template/phresh.config.ts +4 -9
- package/dist/template/{source → scripts}/build.ts +7 -4
- package/dist/template/source/client/main.tsx +2 -2
- package/dist/template/source/server/core/counter.ts +13 -0
- package/dist/template/source/server/main.ts +2 -13
- package/dist/template/source/server/view/view.ts +16 -0
- package/dist/template/tsconfig.json +4 -0
- package/dist/template/{vite.server.ts → vite.config.ts} +1 -2
- package/dist/template.json +2 -1
- package/package.json +6 -4
- package/dist/build-template.js +0 -90
- package/dist/template/source/client/dec.d.ts +0 -1
- package/dist/template/source/server/dec.d.ts +0 -1
- /package/dist/template/source/client/{app.tsx → view/app.tsx} +0 -0
package/README.md
CHANGED
|
@@ -78,10 +78,11 @@ Windows; neither becomes a network endpoint or introduces a bearer secret.
|
|
|
78
78
|
|
|
79
79
|
`phresh create <directory>` creates a complete Server and Client Program from
|
|
80
80
|
the maintained `PhreshOS/phresh-program` repository. The CLI build downloads
|
|
81
|
-
|
|
82
|
-
repository-only material, and bundles
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
the newest complete stable release, validates its source identity and version,
|
|
82
|
+
removes repository-only material, and bundles that exact authoring project.
|
|
83
|
+
The resolved source digest is recorded with the bundle. The installed CLI
|
|
84
|
+
therefore creates projects offline without reading a live branch or maintaining
|
|
85
|
+
a release pin or second template by hand.
|
|
85
86
|
|
|
86
87
|
The directory name becomes the stable kebab-case Program identity. In a
|
|
87
88
|
terminal, `create` asks for the directory when it is omitted, the readable
|
package/dist/program-release.js
CHANGED
|
@@ -4,16 +4,6 @@ import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
6
6
|
import AdmZip from "adm-zip";
|
|
7
|
-
const officialPrograms = {
|
|
8
|
-
phresh: {
|
|
9
|
-
identity: "phresh-program",
|
|
10
|
-
repository: "PhreshOS/phresh-program"
|
|
11
|
-
},
|
|
12
|
-
setup: {
|
|
13
|
-
identity: "setup",
|
|
14
|
-
repository: "PhreshOS/setup-program"
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
7
|
/** Resolve, verify, and unpack one official production Program release. */
|
|
18
8
|
export async function prepareOfficialProgram(name, fetcher = fetch) {
|
|
19
9
|
const release = await resolveOfficialProgramRelease(name, fetcher);
|
|
@@ -34,10 +24,8 @@ export async function prepareOfficialProgram(name, fetcher = fetch) {
|
|
|
34
24
|
}
|
|
35
25
|
}
|
|
36
26
|
export async function resolveOfficialProgramRelease(name, fetcher = fetch) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
throw new Error(`No official Program is named "${name}"`);
|
|
40
|
-
const response = await fetcher(`https://api.github.com/repos/${official.repository}/releases?per_page=100`, {
|
|
27
|
+
const requested = programName(name);
|
|
28
|
+
const response = await fetcher(`https://api.github.com/repos/PhreshOS/${requested}-program/releases?per_page=100`, {
|
|
41
29
|
headers: {
|
|
42
30
|
Accept: "application/vnd.github+json",
|
|
43
31
|
"User-Agent": "@phreshos/cli"
|
|
@@ -46,26 +34,24 @@ export async function resolveOfficialProgramRelease(name, fetcher = fetch) {
|
|
|
46
34
|
});
|
|
47
35
|
if (!response.ok)
|
|
48
36
|
throw new Error(`The ${name} release list could not be read (${response.status} ${response.statusText})`);
|
|
49
|
-
return selectProgramRelease(
|
|
37
|
+
return selectProgramRelease(await response.json());
|
|
50
38
|
}
|
|
51
|
-
export function selectProgramRelease(
|
|
39
|
+
export function selectProgramRelease(value) {
|
|
52
40
|
if (!Array.isArray(value))
|
|
53
|
-
throw new Error(
|
|
41
|
+
throw new Error("The Program release list is invalid");
|
|
54
42
|
const releases = value.flatMap(function (item) {
|
|
55
43
|
if (!record(item) || item.draft === true || item.prerelease === true || typeof item.tag_name !== "string" || !Array.isArray(item.assets))
|
|
56
44
|
return [];
|
|
57
45
|
const version = parseVersion(item.tag_name);
|
|
58
46
|
if (!version)
|
|
59
47
|
return [];
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const checksum = asset(item.assets, `${archiveName}.sha256`);
|
|
63
|
-
return archive && checksum ? [{ identity, version, archive, checksum }] : [];
|
|
48
|
+
const files = programAssets(item.assets, version);
|
|
49
|
+
return files ? [{ version, ...files }] : [];
|
|
64
50
|
});
|
|
65
51
|
releases.sort((left, right) => compare(right.version, left.version));
|
|
66
52
|
const selected = releases[0];
|
|
67
53
|
if (!selected)
|
|
68
|
-
throw new Error(
|
|
54
|
+
throw new Error("No stable Program release is available");
|
|
69
55
|
return selected;
|
|
70
56
|
}
|
|
71
57
|
export async function downloadProgramRelease(release, fetcher = fetch) {
|
|
@@ -150,6 +136,27 @@ function asset(assets, name) {
|
|
|
150
136
|
const found = assets.find(item => record(item) && item.name === name && typeof item.browser_download_url === "string");
|
|
151
137
|
return record(found) && typeof found.browser_download_url === "string" ? found.browser_download_url : undefined;
|
|
152
138
|
}
|
|
139
|
+
function programAssets(assets, version) {
|
|
140
|
+
const suffix = `@${version}.zip`;
|
|
141
|
+
const archives = assets.flatMap(function (item) {
|
|
142
|
+
if (!record(item) || typeof item.name !== "string" || typeof item.browser_download_url !== "string" || !item.name.endsWith(suffix))
|
|
143
|
+
return [];
|
|
144
|
+
const identity = item.name.slice(0, -suffix.length);
|
|
145
|
+
if (!validProgramName(identity))
|
|
146
|
+
return [];
|
|
147
|
+
const checksum = asset(assets, `${item.name}.sha256`);
|
|
148
|
+
return checksum ? [{ identity, archive: item.browser_download_url, checksum }] : [];
|
|
149
|
+
});
|
|
150
|
+
return archives.length === 1 ? archives[0] : undefined;
|
|
151
|
+
}
|
|
152
|
+
function programName(name) {
|
|
153
|
+
if (!validProgramName(name))
|
|
154
|
+
throw new Error(`The official Program name "${name}" is invalid`);
|
|
155
|
+
return name;
|
|
156
|
+
}
|
|
157
|
+
function validProgramName(name) {
|
|
158
|
+
return name.length <= 64 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);
|
|
159
|
+
}
|
|
153
160
|
function parseVersion(tag) {
|
|
154
161
|
const match = /^v(\d+)\.(\d+)\.(\d+)$/.exec(tag);
|
|
155
162
|
return match ? tag.slice(1) : undefined;
|
package/dist/style.js
CHANGED
package/dist/template/README.md
CHANGED
|
@@ -16,13 +16,18 @@ production shape, build and attach the Program with:
|
|
|
16
16
|
bun phresh start
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
The structure
|
|
19
|
+
The structure keeps composition, representation, and authoritative behavior distinct:
|
|
20
20
|
|
|
21
21
|
```text
|
|
22
22
|
source/
|
|
23
|
-
├── client/
|
|
24
|
-
|
|
23
|
+
├── client/
|
|
24
|
+
│ ├── main.tsx Client composition
|
|
25
|
+
│ └── view/ React representation
|
|
26
|
+
└── server/
|
|
27
|
+
├── main.ts Server composition
|
|
28
|
+
├── core/ authoritative counter
|
|
29
|
+
└── view/ endpoint adapter and service exposure
|
|
25
30
|
```
|
|
26
31
|
|
|
27
32
|
The Program declaration lives in `phresh.config.ts`. Its own service contract
|
|
28
|
-
is documented in `api-docs.md`.
|
|
33
|
+
is exposed as the `counter` Server service and documented in `api-docs.md`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Counter API
|
|
2
2
|
|
|
3
|
-
The Server owns one number for the lifetime of its Process.
|
|
3
|
+
The `counter` Server service owns one number for the lifetime of its Process.
|
|
4
4
|
|
|
5
5
|
| Name | Destination | Interaction | Payload | Answer |
|
|
6
6
|
| --- | --- | --- | --- | --- |
|
|
@@ -14,7 +14,13 @@ The Server owns one number for the lifetime of its Process.
|
|
|
14
14
|
current counter value.
|
|
15
15
|
|
|
16
16
|
```ts
|
|
17
|
-
const
|
|
17
|
+
const counter = host.service({
|
|
18
|
+
program: "phresh-program",
|
|
19
|
+
endpoint: "server",
|
|
20
|
+
name: "counter"
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const count = await counter.channel.ask<number>("read")
|
|
18
24
|
```
|
|
19
25
|
|
|
20
26
|
## Publish to the Server: `increment`
|
|
@@ -23,7 +29,7 @@ const count = await process.server.ask<number>("read")
|
|
|
23
29
|
to the counter and does not produce an answer.
|
|
24
30
|
|
|
25
31
|
```ts
|
|
26
|
-
|
|
32
|
+
counter.channel.publish("increment")
|
|
27
33
|
```
|
|
28
34
|
|
|
29
35
|
## Subscribe to the Server: `changed`
|
|
@@ -32,7 +38,7 @@ After an increment, the Server emits `changed` with the new counter value.
|
|
|
32
38
|
Anyone holding that Process's Server can subscribe to the event.
|
|
33
39
|
|
|
34
40
|
```ts
|
|
35
|
-
|
|
41
|
+
counter.channel.subscribe<number>("changed", count => {
|
|
36
42
|
// use the latest count
|
|
37
43
|
})
|
|
38
44
|
```
|
package/dist/template/gitignore
CHANGED
|
@@ -1,31 +1,19 @@
|
|
|
1
1
|
# Logs
|
|
2
2
|
logs
|
|
3
3
|
*.log
|
|
4
|
-
npm-debug.log*
|
|
5
|
-
yarn-debug.log*
|
|
6
|
-
yarn-error.log*
|
|
7
|
-
pnpm-debug.log*
|
|
8
|
-
lerna-debug.log*
|
|
9
4
|
|
|
10
5
|
node_modules
|
|
11
6
|
dist
|
|
12
|
-
dist-ssr
|
|
13
7
|
*.local
|
|
14
8
|
|
|
15
9
|
# Made by `phresh pack` in the project root.
|
|
16
10
|
/*.zip
|
|
17
11
|
/*.zip.sha256
|
|
18
12
|
|
|
19
|
-
# Editor
|
|
20
|
-
.vscode
|
|
21
|
-
!.vscode/extensions.json
|
|
13
|
+
# Editor and machine residue.
|
|
14
|
+
.vscode
|
|
22
15
|
.idea
|
|
23
16
|
.DS_Store
|
|
24
|
-
*.suo
|
|
25
|
-
*.ntvs*
|
|
26
|
-
*.njsproj
|
|
27
|
-
*.sln
|
|
28
|
-
*.sw?
|
|
29
17
|
|
|
30
|
-
# Made by the system the first time this
|
|
18
|
+
# Made by the system the first time this Program runs.
|
|
31
19
|
storage
|
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phresh-program",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6",
|
|
5
|
+
"description": "The official PhreshOS starter Program.",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"dev": "phresh dev",
|
|
8
9
|
"start": "phresh start"
|
|
9
10
|
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"phreshos",
|
|
13
|
+
"program",
|
|
14
|
+
"starter"
|
|
15
|
+
],
|
|
10
16
|
"dependencies": {
|
|
11
|
-
"@phreshos/client": "^0.1.
|
|
12
|
-
"@phreshos/
|
|
13
|
-
"@phreshos/
|
|
14
|
-
"@phreshos/
|
|
17
|
+
"@phreshos/client": "^0.1.9",
|
|
18
|
+
"@phreshos/core": "^0.1.6",
|
|
19
|
+
"@phreshos/react": "^0.1.5",
|
|
20
|
+
"@phreshos/server": "^0.1.7",
|
|
15
21
|
"react": "^19.2.8",
|
|
16
22
|
"react-dom": "^19.2.8"
|
|
17
23
|
},
|
|
18
24
|
"devDependencies": {
|
|
19
|
-
"@phreshos/cli": "^0.1.
|
|
25
|
+
"@phreshos/cli": "^0.1.15",
|
|
20
26
|
"@types/node": "^26.2.0",
|
|
21
27
|
"@types/react": "^19.2.18",
|
|
22
28
|
"@types/react-dom": "^19.2.4",
|
|
23
29
|
"@vitejs/plugin-react": "^6.0.5",
|
|
24
|
-
"tsx": "^4.23.12",
|
|
25
30
|
"typescript": "^7.0.2",
|
|
26
|
-
"vite": "^8.2.1"
|
|
31
|
+
"vite": "^8.2.1",
|
|
32
|
+
"vite-node": "^6.0.0"
|
|
27
33
|
}
|
|
28
34
|
}
|
|
@@ -22,12 +22,7 @@ export default defineConfig({
|
|
|
22
22
|
// these values determines the Program's identity.
|
|
23
23
|
name: "Phresh Program",
|
|
24
24
|
description: "A simple counter whose state lives on the Server.",
|
|
25
|
-
version: "0.1.
|
|
26
|
-
|
|
27
|
-
// Markdown entry point for the API owned by this Program. It documents the
|
|
28
|
-
// counter service contract; PhreshOS endpoint mechanics belong in PhreshOS
|
|
29
|
-
// documentation instead of being repeated here.
|
|
30
|
-
apiDocs: "api-docs.md",
|
|
25
|
+
version: "0.1.6",
|
|
31
26
|
|
|
32
27
|
// One authored PNG. Installation gives it a canonical name and the system
|
|
33
28
|
// derives the standard hosted icon sizes from it.
|
|
@@ -36,7 +31,7 @@ export default defineConfig({
|
|
|
36
31
|
// Prepares both production endpoint directories. The CLI runs it from this
|
|
37
32
|
// project before `phresh start`, `phresh install`, and `phresh pack`.
|
|
38
33
|
// `phresh dev` does not build and uses the declarations below instead.
|
|
39
|
-
buildCommand: "node
|
|
34
|
+
buildCommand: "vite-node scripts/build.ts",
|
|
40
35
|
|
|
41
36
|
// A Process may run its Server and Client independently. This declaration
|
|
42
37
|
// says how the Server is prepared and started; it does not merge Server
|
|
@@ -62,8 +57,8 @@ export default defineConfig({
|
|
|
62
57
|
|
|
63
58
|
// `phresh dev` runs this command from the project directory. The
|
|
64
59
|
// project directory becomes the development Server location, so
|
|
65
|
-
// source imports
|
|
66
|
-
startCommand: "node
|
|
60
|
+
// source imports work without a production build.
|
|
61
|
+
startCommand: "vite-node source/server/main.ts"
|
|
67
62
|
}
|
|
68
63
|
},
|
|
69
64
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { externalDependencies } from "@/vite.
|
|
1
|
+
import { externalDependencies } from "@/vite.config"
|
|
2
2
|
import { writeFile } from "node:fs/promises"
|
|
3
3
|
import packageConfig from "@/package.json"
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
process.env.NODE_ENV = "production"
|
|
6
|
+
|
|
7
|
+
const { build } = await import("vite")
|
|
5
8
|
|
|
6
9
|
const dependencies: Partial<typeof packageConfig.dependencies> = {}
|
|
7
10
|
|
|
@@ -10,8 +13,8 @@ for (const externalDependency of externalDependencies) {
|
|
|
10
13
|
dependencies[externalDependency] = packageConfig.dependencies[externalDependency]
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
await build({ configFile: "vite.
|
|
16
|
+
await build({ configFile: "vite.config.ts", ssr: { noExternal: true } })
|
|
14
17
|
|
|
15
18
|
await build({ configFile: "vite.client.ts" })
|
|
16
19
|
|
|
17
|
-
await writeFile("dist/server/package.json", JSON.stringify({ type: "module", dependencies }))
|
|
20
|
+
await writeFile("dist/server/package.json", JSON.stringify({ type: "module", dependencies }))
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import client from "react-dom/client"
|
|
2
2
|
import { StrictMode } from "react"
|
|
3
|
-
import App from "./app"
|
|
3
|
+
import App from "./view/app"
|
|
4
4
|
import "./style.css"
|
|
5
5
|
|
|
6
6
|
const root = client.createRoot(document.body)
|
|
7
7
|
|
|
8
|
-
root.render(<StrictMode><App /></StrictMode>)
|
|
8
|
+
root.render(<StrictMode><App /></StrictMode>)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Owns the Program's authoritative counter state without knowing how it is exposed. */
|
|
2
|
+
export default class Counter {
|
|
3
|
+
private value = 0
|
|
4
|
+
|
|
5
|
+
public read() {
|
|
6
|
+
return this.value
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public increment() {
|
|
10
|
+
this.value += 1
|
|
11
|
+
return this.value
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
import
|
|
1
|
+
import view from "./view/view"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
// this same state instead of keeping an independent browser counter.
|
|
5
|
-
let count = 0
|
|
6
|
-
|
|
7
|
-
current.answer("read", () => count)
|
|
8
|
-
|
|
9
|
-
current.subscribe("increment", async function () {
|
|
10
|
-
|
|
11
|
-
count += 1
|
|
12
|
-
|
|
13
|
-
current.publish("changed", count)
|
|
14
|
-
})
|
|
3
|
+
await view()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { current } from "@phreshos/server"
|
|
2
|
+
import docs from "@/api-docs.md?raw"
|
|
3
|
+
import Counter from "@server/core/counter"
|
|
4
|
+
|
|
5
|
+
const counter = new Counter()
|
|
6
|
+
|
|
7
|
+
/** Exposes the counter core through this Server endpoint. */
|
|
8
|
+
export default async function view() {
|
|
9
|
+
current.answer("read", () => counter.read())
|
|
10
|
+
|
|
11
|
+
current.subscribe("increment", () => {
|
|
12
|
+
current.publish("changed", counter.increment())
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
await current.enableService({ name: "counter", docs })
|
|
16
|
+
}
|
package/dist/template.json
CHANGED
package/package.json
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
|
|
4
|
+
"version": "0.1.15",
|
|
5
5
|
"description": "The Phresh command-line interface for Program projects and system management.",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.10"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
11
|
+
"check:scripts": "tsc --project tsconfig.scripts.json",
|
|
11
12
|
"compile": "tsc --noEmit false --outDir dist --rootDir source --rewriteRelativeImportExtensions true --allowImportingTsExtensions true",
|
|
12
|
-
"build": "node --run clean && node --run compile && node
|
|
13
|
+
"build": "node --run clean && node --run compile && vite-node scripts/build-template.ts",
|
|
13
14
|
"test": "node --run compile && node --test tests/*.test.mjs",
|
|
14
|
-
"verify": "node --run build && node --run test && node scripts/verify-package.mjs",
|
|
15
|
+
"verify": "node --run check:scripts && node --run build && node --run test && node scripts/verify-package.mjs",
|
|
15
16
|
"verify:system-release": "node --run compile && node scripts/verify-system-release.mjs",
|
|
16
17
|
"verify:linux-service": "node --run compile && node scripts/verify-linux-service.mjs",
|
|
17
18
|
"verify:macos-service": "node --run compile && node scripts/verify-macos-service.mjs",
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@types/adm-zip": "^0.5.8",
|
|
58
59
|
"@types/node": "^25.9.5",
|
|
59
|
-
"typescript": "^6.0.3"
|
|
60
|
+
"typescript": "^6.0.3",
|
|
61
|
+
"vite-node": "^6.0.0"
|
|
60
62
|
}
|
|
61
63
|
}
|
package/dist/build-template.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import metadata from "../package.json" with { type: "json" };
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { dirname, resolve, sep } from "node:path";
|
|
5
|
-
import AdmZip from "adm-zip";
|
|
6
|
-
import { readConfig } from "./project.js";
|
|
7
|
-
const repository = "PhreshOS/phresh-program";
|
|
8
|
-
const release = {
|
|
9
|
-
version: "0.1.2",
|
|
10
|
-
sha256: "dba5f1fd868c46501d5bd3463f4f8f0e098c7a231cb75794c01118689dc69339"
|
|
11
|
-
};
|
|
12
|
-
const archiveUrl = `https://github.com/${repository}/archive/refs/tags/v${release.version}.zip`;
|
|
13
|
-
const output = resolve(import.meta.dirname, "template");
|
|
14
|
-
const descriptionOutput = resolve(import.meta.dirname, "template.json");
|
|
15
|
-
const excludedDirectories = new Set([".github", "dist", "node_modules", "scripts", "storage"]);
|
|
16
|
-
const excludedRootFiles = new Set(["CONTRIBUTING.md", "LICENSE", "SECURITY.md"]);
|
|
17
|
-
const excludedFiles = new Set([
|
|
18
|
-
".DS_Store",
|
|
19
|
-
"bun.lock",
|
|
20
|
-
"bun.lockb",
|
|
21
|
-
"package-lock.json",
|
|
22
|
-
"pnpm-lock.yaml",
|
|
23
|
-
"yarn.lock"
|
|
24
|
-
]);
|
|
25
|
-
const response = await fetch(archiveUrl);
|
|
26
|
-
if (!response.ok)
|
|
27
|
-
throw new Error(`Could not download ${repository} v${release.version}: ${response.status} ${response.statusText}`);
|
|
28
|
-
const bytes = Buffer.from(await response.arrayBuffer());
|
|
29
|
-
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
30
|
-
if (digest !== release.sha256)
|
|
31
|
-
throw new Error(`The ${repository} v${release.version} source archive failed integrity verification`);
|
|
32
|
-
const archive = new AdmZip(bytes);
|
|
33
|
-
const entries = archive.getEntries();
|
|
34
|
-
const roots = new Set(entries.map(entry => entry.entryName.split("/")[0]).filter(Boolean));
|
|
35
|
-
if (roots.size !== 1)
|
|
36
|
-
throw new Error(`The ${repository} v${release.version} source archive has an invalid root`);
|
|
37
|
-
const root = [...roots][0];
|
|
38
|
-
rmSync(output, { recursive: true, force: true });
|
|
39
|
-
rmSync(descriptionOutput, { force: true });
|
|
40
|
-
for (const entry of entries) {
|
|
41
|
-
if (entry.isDirectory || !entry.entryName.startsWith(`${root}/`))
|
|
42
|
-
continue;
|
|
43
|
-
const local = entry.entryName.slice(root.length + 1);
|
|
44
|
-
if (!included(local))
|
|
45
|
-
continue;
|
|
46
|
-
const target = local === ".gitignore" ? "gitignore" : local;
|
|
47
|
-
const path = resolve(output, target);
|
|
48
|
-
if (!path.startsWith(`${output}${sep}`))
|
|
49
|
-
throw new Error(`The ${repository} source archive contains an unsafe path: ${local}`);
|
|
50
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
51
|
-
writeFileSync(path, entry.getData());
|
|
52
|
-
}
|
|
53
|
-
const manifestPath = resolve(output, "package.json");
|
|
54
|
-
const manifestSource = archive.readFile(`${root}/package.json`);
|
|
55
|
-
if (!manifestSource)
|
|
56
|
-
throw new Error(`The ${repository} v${release.version} source archive has no package.json`);
|
|
57
|
-
const manifest = JSON.parse(manifestSource.toString("utf8"));
|
|
58
|
-
manifest.scripts = select(manifest.scripts, ["dev", "start"]);
|
|
59
|
-
manifest.devDependencies = {
|
|
60
|
-
...manifest.devDependencies,
|
|
61
|
-
"@phreshos/cli": `^${metadata.version}`
|
|
62
|
-
};
|
|
63
|
-
delete manifest.author;
|
|
64
|
-
delete manifest.license;
|
|
65
|
-
delete manifest.repository;
|
|
66
|
-
delete manifest.bugs;
|
|
67
|
-
delete manifest.homepage;
|
|
68
|
-
delete manifest.packageManager;
|
|
69
|
-
writeFileSync(manifestPath, JSON.stringify(manifest, null, 4) + "\n");
|
|
70
|
-
const config = await readConfig(output);
|
|
71
|
-
if (config.identity !== "phresh-program" || config.name !== "Phresh Program")
|
|
72
|
-
throw new Error(`The ${repository} release is not Phresh Program`);
|
|
73
|
-
writeFileSync(descriptionOutput, JSON.stringify({
|
|
74
|
-
repository,
|
|
75
|
-
version: release.version,
|
|
76
|
-
development: Boolean(config.server?.development || config.client?.development)
|
|
77
|
-
}, null, 4) + "\n");
|
|
78
|
-
function included(local) {
|
|
79
|
-
const parts = local.split("/");
|
|
80
|
-
if (parts.some(part => excludedDirectories.has(part)))
|
|
81
|
-
return false;
|
|
82
|
-
if (parts.length === 1 && excludedRootFiles.has(local))
|
|
83
|
-
return false;
|
|
84
|
-
if (excludedFiles.has(parts.at(-1)))
|
|
85
|
-
return false;
|
|
86
|
-
return !local.endsWith(".zip");
|
|
87
|
-
}
|
|
88
|
-
function select(source, names) {
|
|
89
|
-
return Object.fromEntries(names.flatMap(name => source?.[name] === undefined ? [] : [[name, source[name]]]));
|
|
90
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="@types/node" />
|
|
File without changes
|