@phreshos/cli 0.1.14 → 0.1.16
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/template/README.md +14 -16
- package/dist/template/gitignore +3 -15
- package/dist/template/package.json +14 -8
- package/dist/template/phresh.config.ts +4 -87
- package/dist/template/scripts/build.ts +10 -0
- package/dist/template/source/client/app.tsx +7 -21
- package/dist/template/source/client/index.html +3 -3
- package/dist/template/source/client/main.tsx +2 -2
- package/dist/template/source/client/style.css +24 -31
- package/dist/template/source/server/main.ts +1 -5
- package/dist/template/tsconfig.json +4 -11
- package/dist/template/vite.config.ts +14 -0
- package/dist/template.json +2 -1
- package/package.json +7 -5
- package/dist/build-template.js +0 -90
- package/dist/template/api-docs.md +0 -38
- package/dist/template/source/build.ts +0 -17
- package/dist/template/source/client/dec.d.ts +0 -1
- package/dist/template/source/server/dec.d.ts +0 -1
- package/dist/template/vite.server.ts +0 -27
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/template/README.md
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
# Phresh Program
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
state.
|
|
3
|
+
The minimal PhreshOS starter Program. Its Client talks directly to its Server;
|
|
4
|
+
there is no MVC structure and no registered service.
|
|
6
5
|
|
|
7
6
|
```bash
|
|
8
7
|
bun install
|
|
9
|
-
bun
|
|
8
|
+
bun run dev
|
|
10
9
|
```
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
production shape, build and attach the Program with:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
bun phresh start
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
The structure follows the endpoint boundaries directly:
|
|
11
|
+
The complete authored source is intentionally small:
|
|
20
12
|
|
|
21
13
|
```text
|
|
22
14
|
source/
|
|
23
|
-
├── client/
|
|
24
|
-
|
|
15
|
+
├── client/
|
|
16
|
+
│ ├── app.tsx
|
|
17
|
+
│ ├── index.html
|
|
18
|
+
│ ├── main.tsx
|
|
19
|
+
│ └── style.css
|
|
20
|
+
└── server/
|
|
21
|
+
└── main.ts
|
|
25
22
|
```
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
`app.tsx` owns the small React component. The two `main` files are direct
|
|
25
|
+
endpoint entries: the Client renders the app and the Server owns the counter.
|
|
26
|
+
Nothing registers or exposes a named service.
|
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.10",
|
|
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.11",
|
|
18
|
+
"@phreshos/core": "^0.1.7",
|
|
19
|
+
"@phreshos/react": "^0.1.6",
|
|
20
|
+
"@phreshos/server": "^0.1.9",
|
|
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.16",
|
|
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
|
}
|
|
@@ -1,107 +1,24 @@
|
|
|
1
1
|
import { defineConfig } from "@phreshos/core"
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* This is the Program's authoring declaration, not a runtime configuration
|
|
5
|
-
* loaded by either endpoint. The Phresh CLI reads it and derives the concrete
|
|
6
|
-
* Program description needed for development, production, installation, or
|
|
7
|
-
* packaging.
|
|
8
|
-
*
|
|
9
|
-
* Production uses the built locations and commands declared below.
|
|
10
|
-
* Development replaces only each endpoint's location and start command with
|
|
11
|
-
* its `development` declaration. Packaging relocates the production files
|
|
12
|
-
* into the Program archive. Relative paths begin at this project directory.
|
|
13
|
-
*/
|
|
14
3
|
export default defineConfig({
|
|
15
|
-
|
|
16
|
-
// Permanent public address of the Program. It is kebab-case because the
|
|
17
|
-
// system also uses it as the installed directory name. Unlike `name`, it
|
|
18
|
-
// is an identifier and must remain stable across releases.
|
|
19
4
|
identity: "phresh-program",
|
|
20
|
-
|
|
21
|
-
// Human-facing metadata shown by the desktop and authoring tools. None of
|
|
22
|
-
// these values determines the Program's identity.
|
|
23
5
|
name: "Phresh Program",
|
|
24
|
-
description: "A
|
|
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",
|
|
31
|
-
|
|
32
|
-
// One authored PNG. Installation gives it a canonical name and the system
|
|
33
|
-
// derives the standard hosted icon sizes from it.
|
|
6
|
+
description: "A minimal counter with direct Client and Server endpoints.",
|
|
7
|
+
version: "0.1.10",
|
|
34
8
|
icon: "icon.png",
|
|
35
|
-
|
|
36
|
-
// Prepares both production endpoint directories. The CLI runs it from this
|
|
37
|
-
// project before `phresh start`, `phresh install`, and `phresh pack`.
|
|
38
|
-
// `phresh dev` does not build and uses the declarations below instead.
|
|
39
|
-
buildCommand: "node --import tsx source/build.ts",
|
|
40
|
-
|
|
41
|
-
// A Process may run its Server and Client independently. This declaration
|
|
42
|
-
// says how the Server is prepared and started; it does not merge Server
|
|
43
|
-
// code into the browser Client.
|
|
9
|
+
buildCommand: "vite-node scripts/build.ts",
|
|
44
10
|
server: {
|
|
45
|
-
|
|
46
|
-
// Production directory containing the Server artifact. The start
|
|
47
|
-
// command runs with this directory as its working directory.
|
|
48
11
|
location: "dist/server",
|
|
49
12
|
startCommand: "node main.js",
|
|
50
|
-
|
|
51
|
-
// An install command is optional and runs inside `location` when the
|
|
52
|
-
// Program is installed. This build bundles its Server dependencies, so
|
|
53
|
-
// the example does not need one.
|
|
54
|
-
// installCommand: "npm install",
|
|
55
|
-
|
|
56
|
-
// Declared endpoints start in a default Process unless `start` is
|
|
57
|
-
// false. A false value keeps the capability available for an explicit
|
|
58
|
-
// Process launch without starting it automatically.
|
|
59
|
-
// start: false,
|
|
60
|
-
|
|
61
13
|
development: {
|
|
62
|
-
|
|
63
|
-
// `phresh dev` runs this command from the project directory. The
|
|
64
|
-
// project directory becomes the development Server location, so
|
|
65
|
-
// source imports and watch mode work without a production build.
|
|
66
|
-
startCommand: "node --watch --import tsx source/server/main.ts"
|
|
14
|
+
startCommand: "vite-node source/server/main.ts"
|
|
67
15
|
}
|
|
68
16
|
},
|
|
69
|
-
|
|
70
|
-
// The Client declaration also defines the initial Window created for it.
|
|
71
|
-
// It contains presentation defaults only; live Window state belongs to
|
|
72
|
-
// each running Process.
|
|
73
17
|
client: {
|
|
74
|
-
|
|
75
|
-
// Production directory containing the browser application's
|
|
76
|
-
// `index.html` and all files reachable from it.
|
|
77
18
|
location: "dist/client",
|
|
78
|
-
|
|
79
|
-
// Initial Window values. The title defaults to the Program name when
|
|
80
|
-
// omitted. This example chooses a fixed initial size while leaving
|
|
81
|
-
// placement, layer, and minimized state to their system defaults.
|
|
82
19
|
title: "Phresh Program",
|
|
83
20
|
size: { width: 600, height: 500 },
|
|
84
|
-
|
|
85
|
-
// Geometry accepts finite pixel numbers or linear values. Fractions
|
|
86
|
-
// and percentages are relative to the selected desktop layer, and a
|
|
87
|
-
// pixel offset may be combined with either form.
|
|
88
|
-
// size: { width: "50% + 20", height: 440 },
|
|
89
|
-
// position: { x: "1/2 + 10", y: 40 },
|
|
90
|
-
|
|
91
|
-
// `window` is the ordinary framed layer. `under` and `over` are
|
|
92
|
-
// structurally isolated, frameless desktop layers.
|
|
93
|
-
// layer: "window",
|
|
94
|
-
|
|
95
|
-
// The initial Window may also be declared to open minimized.
|
|
96
|
-
// minimize: true,
|
|
97
|
-
|
|
98
21
|
development: {
|
|
99
|
-
|
|
100
|
-
// Development Clients are addressed by URL rather than a local
|
|
101
|
-
// artifact directory. The CLI starts this optional tool, waits for
|
|
102
|
-
// the URL to respond, and only then launches the Program. The dev
|
|
103
|
-
// server must allow the desktop origin through CORS; this project's
|
|
104
|
-
// Vite configuration does so.
|
|
105
22
|
url: "http://localhost:5200/",
|
|
106
23
|
startCommand: "vite dev --config vite.client.ts"
|
|
107
24
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { rm, writeFile } from "node:fs/promises"
|
|
2
|
+
|
|
3
|
+
process.env.NODE_ENV = "production"
|
|
4
|
+
|
|
5
|
+
const { build } = await import("vite")
|
|
6
|
+
|
|
7
|
+
await rm("dist", { recursive: true, force: true })
|
|
8
|
+
await build({ configFile: "vite.config.ts", ssr: { noExternal: true } })
|
|
9
|
+
await build({ configFile: "vite.client.ts" })
|
|
10
|
+
await writeFile("dist/server/package.json", JSON.stringify({ type: "module" }))
|
|
@@ -1,36 +1,22 @@
|
|
|
1
|
+
import { current } from "@phreshos/client"
|
|
1
2
|
import { useSubscribe } from "@phreshos/react"
|
|
2
3
|
import { useEffect, useState } from "react"
|
|
3
|
-
import { current } from "@phreshos/client"
|
|
4
|
-
|
|
5
|
-
function Counter() {
|
|
6
4
|
|
|
5
|
+
export default function App() {
|
|
7
6
|
const [count, setCount] = useState<number>()
|
|
8
7
|
|
|
9
8
|
useSubscribe(current.server, "changed", setCount)
|
|
10
9
|
|
|
11
|
-
useEffect(
|
|
12
|
-
|
|
10
|
+
useEffect(() => {
|
|
13
11
|
current.server.ask<number>("read").then(setCount)
|
|
14
|
-
|
|
15
12
|
}, [])
|
|
16
13
|
|
|
17
|
-
return <main
|
|
18
|
-
|
|
19
|
-
<span className="label">Phresh Program</span>
|
|
20
|
-
|
|
14
|
+
return <main>
|
|
15
|
+
<span>PhreshOS</span>
|
|
21
16
|
<h1>Server counter</h1>
|
|
22
|
-
|
|
23
|
-
<p>The value belongs to the Server and is shared with every Client representation.</p>
|
|
24
|
-
|
|
17
|
+
<p>The Client talks directly to its Process Server.</p>
|
|
25
18
|
<button type="button" onClick={() => current.server.publish("increment")}>
|
|
26
|
-
|
|
27
|
-
count is {count ?? "…"}
|
|
28
|
-
|
|
19
|
+
Count is {count ?? "…"}
|
|
29
20
|
</button>
|
|
30
|
-
|
|
31
21
|
</main>
|
|
32
22
|
}
|
|
33
|
-
|
|
34
|
-
export default function App() {
|
|
35
|
-
return <Counter />
|
|
36
|
-
}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Phresh Program</title>
|
|
7
8
|
<script type="module" src="./main.tsx"></script>
|
|
8
9
|
</head>
|
|
9
10
|
|
|
10
|
-
<body>
|
|
11
|
-
</body>
|
|
11
|
+
<body></body>
|
|
12
12
|
|
|
13
|
-
</html>
|
|
13
|
+
</html>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import client from "react-dom/client"
|
|
2
1
|
import { StrictMode } from "react"
|
|
2
|
+
import client from "react-dom/client"
|
|
3
3
|
import App from "./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>)
|
|
@@ -1,38 +1,37 @@
|
|
|
1
1
|
:root {
|
|
2
2
|
color: #20242a;
|
|
3
|
-
background:
|
|
4
|
-
font-family: Inter, ui-sans-serif, system-ui,
|
|
3
|
+
background: transparent;
|
|
4
|
+
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
|
5
5
|
font-synthesis: none;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
* {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
body { margin: 0; }
|
|
8
|
+
* {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
html,
|
|
13
|
+
body {
|
|
14
|
+
min-height: 100%;
|
|
15
|
+
background: transparent;
|
|
16
|
+
}
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
17
20
|
min-height: 100vh;
|
|
18
21
|
display: grid;
|
|
19
22
|
place-items: center;
|
|
20
23
|
padding: 1.5rem;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
main {
|
|
24
27
|
width: min(26rem, 100%);
|
|
25
|
-
display: grid;
|
|
26
|
-
justify-items: start;
|
|
27
|
-
gap: 1rem;
|
|
28
28
|
padding: 2rem;
|
|
29
29
|
border: 1px solid #d8dce2;
|
|
30
30
|
border-radius: 0.75rem;
|
|
31
31
|
background: #ffffff;
|
|
32
|
-
margin: auto;
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
span {
|
|
36
35
|
color: #646b76;
|
|
37
36
|
font-size: 0.72rem;
|
|
38
37
|
font-weight: 700;
|
|
@@ -41,21 +40,19 @@ button { font: inherit; }
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
h1 {
|
|
44
|
-
margin: 0;
|
|
43
|
+
margin: 0.75rem 0;
|
|
45
44
|
font-size: 2rem;
|
|
46
45
|
line-height: 1.1;
|
|
47
|
-
letter-spacing: -0.04em;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
p {
|
|
51
49
|
margin: 0;
|
|
52
50
|
color: #5f6670;
|
|
53
|
-
|
|
54
|
-
line-height: 1.55;
|
|
51
|
+
line-height: 1.5;
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
button {
|
|
55
|
+
margin-top: 1.5rem;
|
|
59
56
|
min-height: 2.75rem;
|
|
60
57
|
padding: 0.65rem 1rem;
|
|
61
58
|
border: 0;
|
|
@@ -63,19 +60,15 @@ p {
|
|
|
63
60
|
color: #ffffff;
|
|
64
61
|
background: #2563eb;
|
|
65
62
|
cursor: pointer;
|
|
63
|
+
font: inherit;
|
|
66
64
|
font-weight: 650;
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.counter-card button:active { background: #1e40af; }
|
|
72
|
-
|
|
73
|
-
.counter-card button:focus-visible {
|
|
74
|
-
outline: 3px solid rgba(37, 99, 235, 0.25);
|
|
75
|
-
outline-offset: 2px;
|
|
67
|
+
button:hover {
|
|
68
|
+
background: #1d4ed8;
|
|
76
69
|
}
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
button:focus-visible {
|
|
72
|
+
outline: 3px solid rgb(37 99 235 / 25%);
|
|
73
|
+
outline-offset: 2px;
|
|
81
74
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { current } from "@phreshos/server"
|
|
2
2
|
|
|
3
|
-
// The Server owns the value. Every Client representation reads and changes
|
|
4
|
-
// this same state instead of keeping an independent browser counter.
|
|
5
3
|
let count = 0
|
|
6
4
|
|
|
7
5
|
current.answer("read", () => count)
|
|
8
6
|
|
|
9
|
-
current.subscribe("increment",
|
|
10
|
-
|
|
7
|
+
current.subscribe("increment", () => {
|
|
11
8
|
count += 1
|
|
12
|
-
|
|
13
9
|
current.publish("changed", count)
|
|
14
10
|
})
|
|
@@ -9,16 +9,9 @@
|
|
|
9
9
|
"jsx": "react-jsx",
|
|
10
10
|
"strict": true,
|
|
11
11
|
"noEmit": true,
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"@server/*": [
|
|
17
|
-
"./source/server/*"
|
|
18
|
-
],
|
|
19
|
-
"@client/*": [
|
|
20
|
-
"./source/client/*"
|
|
21
|
-
]
|
|
22
|
-
}
|
|
12
|
+
"types": [
|
|
13
|
+
"@types/node",
|
|
14
|
+
"vite/client"
|
|
15
|
+
]
|
|
23
16
|
}
|
|
24
17
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineConfig } from "vite"
|
|
2
|
+
import { resolve } from "node:path"
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
root: "source/server",
|
|
6
|
+
build: {
|
|
7
|
+
ssr: true,
|
|
8
|
+
emptyOutDir: true,
|
|
9
|
+
outDir: resolve(import.meta.dirname, "dist/server"),
|
|
10
|
+
rolldownOptions: {
|
|
11
|
+
input: "main.ts"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
})
|
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
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.16",
|
|
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",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"packageManager": "bun@1.3.14",
|
|
49
50
|
"dependencies": {
|
|
50
51
|
"@clack/prompts": "^1.7.0",
|
|
51
|
-
"@phreshos/core": "^0.1.
|
|
52
|
+
"@phreshos/core": "^0.1.9",
|
|
52
53
|
"adm-zip": "^0.6.0",
|
|
53
54
|
"commander": "^15.0.0",
|
|
54
55
|
"picocolors": "^1.1.1"
|
|
@@ -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,38 +0,0 @@
|
|
|
1
|
-
# Counter API
|
|
2
|
-
|
|
3
|
-
The Server owns one number for the lifetime of its Process.
|
|
4
|
-
|
|
5
|
-
| Name | Destination | Interaction | Payload | Answer |
|
|
6
|
-
| --- | --- | --- | --- | --- |
|
|
7
|
-
| `read` | Server | `ask()` | `undefined` | `number` |
|
|
8
|
-
| `increment` | Server | `publish()` | `undefined` | None |
|
|
9
|
-
| `changed` | Server | `subscribe()` | `number` | None |
|
|
10
|
-
|
|
11
|
-
## Ask the Server: `read`
|
|
12
|
-
|
|
13
|
-
`read` is a question addressed to this Process's Server. It answers with the
|
|
14
|
-
current counter value.
|
|
15
|
-
|
|
16
|
-
```ts
|
|
17
|
-
const count = await process.server.ask<number>("read")
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Publish to the Server: `increment`
|
|
21
|
-
|
|
22
|
-
`increment` is a one-way event addressed to this Process's Server. It adds one
|
|
23
|
-
to the counter and does not produce an answer.
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
process.server.publish("increment")
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Subscribe to the Server: `changed`
|
|
30
|
-
|
|
31
|
-
After an increment, the Server emits `changed` with the new counter value.
|
|
32
|
-
Anyone holding that Process's Server can subscribe to the event.
|
|
33
|
-
|
|
34
|
-
```ts
|
|
35
|
-
process.server.subscribe<number>("changed", count => {
|
|
36
|
-
// use the latest count
|
|
37
|
-
})
|
|
38
|
-
```
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { externalDependencies } from "@/vite.server"
|
|
2
|
-
import { writeFile } from "node:fs/promises"
|
|
3
|
-
import packageConfig from "@/package.json"
|
|
4
|
-
import { build } from "vite"
|
|
5
|
-
|
|
6
|
-
const dependencies: Partial<typeof packageConfig.dependencies> = {}
|
|
7
|
-
|
|
8
|
-
for (const externalDependency of externalDependencies) {
|
|
9
|
-
|
|
10
|
-
dependencies[externalDependency] = packageConfig.dependencies[externalDependency]
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
await build({ configFile: "vite.server.ts" })
|
|
14
|
-
|
|
15
|
-
await build({ configFile: "vite.client.ts" })
|
|
16
|
-
|
|
17
|
-
await writeFile("dist/server/package.json", JSON.stringify({ type: "module", dependencies }))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="@types/node" />
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import packageConfig from "./package.json" with { type: "json" }
|
|
2
|
-
import { defineConfig } from "vite"
|
|
3
|
-
import { resolve } from "node:path"
|
|
4
|
-
|
|
5
|
-
export const externalDependencies: (keyof typeof packageConfig.dependencies)[] = [
|
|
6
|
-
|
|
7
|
-
// Add packages here that should NOT be bundled during server, Example: "sqlite3"
|
|
8
|
-
]
|
|
9
|
-
|
|
10
|
-
export default defineConfig({
|
|
11
|
-
root: "source/server",
|
|
12
|
-
resolve: {
|
|
13
|
-
tsconfigPaths: true
|
|
14
|
-
},
|
|
15
|
-
ssr: {
|
|
16
|
-
noExternal: true,
|
|
17
|
-
external: externalDependencies
|
|
18
|
-
},
|
|
19
|
-
build: {
|
|
20
|
-
ssr: true,
|
|
21
|
-
emptyOutDir: true,
|
|
22
|
-
outDir: resolve(import.meta.dirname, "dist/server"),
|
|
23
|
-
rolldownOptions: {
|
|
24
|
-
input: "main.ts"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
})
|