qcobjects-cli 2.5.148-beta → 2.5.153-beta
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/.github/workflows/codeql-analysis.yml +1 -1
- package/.github/workflows/{npmpublish-lts.yml → npmpublish.yml} +11 -6
- package/AGENTS.md +31 -31
- package/VERSION +1 -1
- package/build-esbuild-esm.js +22 -0
- package/docs/launch-and-server.md +253 -0
- package/docs/release-pipeline.md +122 -0
- package/package.json +2 -2
- package/public/cjs/cli-commands-version.js +11 -10
- package/public/cjs/cli-commands-version.js.map +1 -1
- package/public/cjs/cli-main.js +1 -1
- package/public/esm/backend/backend-microservice-static.mjs +1 -1
- package/public/esm/backend/backend-microservice-static.mjs.map +2 -2
- package/public/esm/cli-commands-version.mjs +19 -34
- package/public/esm/cli-commands-version.mjs.map +2 -2
- package/public/esm/cli-main.mjs +22 -24
- package/public/esm/cli-main.mjs.map +2 -2
- package/public/types/index.d.ts +2 -2
- package/src/cli-commands-version.ts +10 -11
- package/transpile.js +32 -0
- package/.github/workflows/npmpublish-beta.yml +0 -43
- package/.github/workflows/npmpublish-main.yml +0 -44
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
|
|
3
|
-
|
|
4
|
-
name: Node.js Publish LTS version
|
|
1
|
+
name: Node.js Publish
|
|
5
2
|
|
|
6
3
|
on:
|
|
7
4
|
push:
|
|
8
5
|
tags:
|
|
9
|
-
- 'v
|
|
6
|
+
- 'v*.*.*'
|
|
10
7
|
|
|
11
8
|
jobs:
|
|
12
9
|
build:
|
|
@@ -39,4 +36,12 @@ jobs:
|
|
|
39
36
|
- name: Upgrade npm for OIDC support (requires >=11.5.1)
|
|
40
37
|
run: npm install -g npm@latest
|
|
41
38
|
- run: npm i --legacy-peer-deps
|
|
42
|
-
-
|
|
39
|
+
- name: Publish to npm
|
|
40
|
+
run: |
|
|
41
|
+
if [[ "${{ github.ref }}" == *-beta ]]; then
|
|
42
|
+
npm publish --tag beta
|
|
43
|
+
elif [[ "${{ github.ref }}" == *-lts ]]; then
|
|
44
|
+
npm publish --tag lts
|
|
45
|
+
else
|
|
46
|
+
npm publish
|
|
47
|
+
fi
|
package/AGENTS.md
CHANGED
|
@@ -3,57 +3,57 @@
|
|
|
3
3
|
TypeScript CLI tool and HTTP/HTTP2 server for the [QCObjects](https://qcobjects.dev) framework.
|
|
4
4
|
Node >=22, npm >=10.
|
|
5
5
|
|
|
6
|
-
## Git workflow
|
|
7
|
-
|
|
8
|
-
See `.opencode/instructions/git-workflow.md` — topic branches from `development`, no rebase, SemVer tags.
|
|
9
|
-
|
|
10
6
|
## Commands
|
|
11
7
|
|
|
12
8
|
| Action | Command |
|
|
13
9
|
|--------|---------|
|
|
14
|
-
| Install | `npm
|
|
15
|
-
| Lint | `npm run lint`
|
|
16
|
-
| Test | `npm test`
|
|
17
|
-
| Run
|
|
18
|
-
|
|
|
19
|
-
| Build
|
|
10
|
+
| Install | `npm i --legacy-peer-deps` (peer deps don't auto-install on npm >=10) |
|
|
11
|
+
| Lint | `npm run lint` |
|
|
12
|
+
| Test (lint + jasmine) | `npm test` |
|
|
13
|
+
| Run jasmine only | `npm run test:jasmine` |
|
|
14
|
+
| Full build (types → CJS → ESM) | `npm run build` |
|
|
15
|
+
| Build CJS only (runs test first) | `npm run build:ts` |
|
|
20
16
|
| Build types only | `npm run build:ts-types` |
|
|
21
|
-
| Build ESM
|
|
22
|
-
| Dev server | `npm start` (
|
|
17
|
+
| Build ESM + browser IIFE | `npm run build:esbuild` |
|
|
18
|
+
| Dev server | `npm start` (aliases `qcobjects-shell`) |
|
|
23
19
|
|
|
24
20
|
## Architecture
|
|
25
21
|
|
|
26
22
|
- **CLI framework:** Commander (`src/cli-main.ts` — `SwitchCommander` class)
|
|
27
23
|
- **Commands** in `src/cli-commands-*.ts`, registered via `src/cli-commands.ts`
|
|
28
|
-
- **Servers:** HTTP (`src/main-http-server.ts`), HTTP/2 (`src/main-http2-server.ts`), GAE
|
|
29
|
-
- **Build pipeline:** Custom `transpile.js` (TS compiler API)
|
|
24
|
+
- **Servers:** HTTP (`src/main-http-server.ts`), HTTP/2 (`src/main-http2-server.ts`), GAE (`src/main-http-gae-server.ts`)
|
|
25
|
+
- **Build pipeline:** Custom `transpile.js` (TS compiler API) → CJS, then `build-esbuild-esm.js` → ESM + browser IIFE
|
|
30
26
|
- **Output:** `public/cjs/`, `public/esm/`, `public/browser/`, `public/types/`
|
|
27
|
+
- **Deno:** `deno.json` + `mod.ts` for Deno compatibility
|
|
28
|
+
- **Entrypoints:** `src/qcobjects-cli.ts` (CLI), `src/qcobjects-http{-2,}-server.ts` (servers), `src/qcobjects-shell.ts`, `src/qcobjects-collab.ts`
|
|
29
|
+
- **Source convention:** Most modules import `qcobjects` at top and use `InheritClass`, `Package()`, `Export()`, `CONFIG`, `logger`, `Component`, `Service`
|
|
30
|
+
- **Plugin autodiscovery:** Scans `dependencies`/`devDependencies` for packages with `qcobjects-lib`, `qcobjects-handler`, `qcobjects-command` keywords
|
|
31
31
|
|
|
32
|
-
## Testing
|
|
32
|
+
## Testing
|
|
33
33
|
|
|
34
|
-
- **Framework:** Jasmine v3.7
|
|
35
|
-
-
|
|
36
|
-
- Mock
|
|
34
|
+
- **Framework:** Jasmine v3.7, single spec at `spec/testsSpec.ts`
|
|
35
|
+
- Verifies `qcobjects` version matches between `peerDependencies` and `devDependencies`
|
|
36
|
+
- **Mock:** `tsconfig.jasmine.json` maps `qcobjects-sdk` → `spec/mocks/qcobjects-sdk.mock.ts` (file may not exist yet)
|
|
37
37
|
- Config: `stopSpecOnExpectationFailure: true`, `failSpecWithNoExpectations: true`, `random: false`
|
|
38
38
|
|
|
39
|
-
## QCObjects patterns used in source
|
|
40
|
-
|
|
41
|
-
- `InheritClass`, `Package()`, `Export()`, `CONFIG`, `logger`, `Service`, `Component`
|
|
42
|
-
- Plugin autodiscovery: scans `dependencies`/`devDependencies` for packages with `qcobjects-lib`, `qcobjects-handler`, `qcobjects-command` keywords
|
|
43
|
-
|
|
44
39
|
## Config & env
|
|
45
40
|
|
|
46
|
-
- `config.json` at root —
|
|
41
|
+
- `config.json` at root — **gitignored** (local dev only), default: `{"devmode":"debug"}`
|
|
47
42
|
- `src/defaultsettings.ts` — `$ENV(VAR)` template syntax resolved at runtime
|
|
48
43
|
- `process.env.PORT` overrides HTTP listen port
|
|
49
|
-
-
|
|
44
|
+
- Version tracked in `VERSION` file; CLI has built-in `v-patch`/`v-minor`/`v-major`/`v-sync`/`v-changelog` commands
|
|
50
45
|
|
|
51
|
-
##
|
|
46
|
+
## CI / Git
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
- **CI workflows** (`ci.yml`, `codeql-analysis.yml`) have **placeholder steps** (TODOs) — not runnable
|
|
49
|
+
- Real publish: `.github/workflows/npmpublish.yml` — triggered by `v*.*.*` tags, uses OIDC (`id-token: write`), detects `-beta`/`-lts` suffix for npm dist-tag
|
|
50
|
+
- **Branch model:** `main` ← `development` ← `feature/*`/`fix/*`/`bugfix/*`. PRs into `development` auto-created on topic-branch push. PRs to `main` must come from `development`.
|
|
51
|
+
- **No rebase.** Use `git pull` (merge).
|
|
52
|
+
- **Postversion** is `"git push"` (branch only). Tag is pushed separately by `syncGit` to avoid duplicate CI.
|
|
53
|
+
- Version sync: `v-patch --git --npm` calls `npm version` internally (triggers `preversion`/`postversion`), then `syncGit` pushes VERSION commit + tag
|
|
54
|
+
- See `.opencode/instructions/git-workflow.md` for detailed git workflow rules.
|
|
55
55
|
|
|
56
|
-
##
|
|
56
|
+
## ESLint
|
|
57
57
|
|
|
58
|
-
- `
|
|
59
|
-
-
|
|
58
|
+
- Uses `recommendedTypeChecked` but many core rules explicitly disabled (`no-explicit-any: off`, `no-unused-vars: off`, `no-var: off`, `no-unsafe-*: off`, etc.) — lint is permissive
|
|
59
|
+
- Ignores `**/*.js`, `spec/**/*`, `node_modules`
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.5.
|
|
1
|
+
2.5.153-beta
|
package/build-esbuild-esm.js
CHANGED
|
@@ -112,6 +112,28 @@ const buildConfigs = {
|
|
|
112
112
|
content = content
|
|
113
113
|
.replace(/const\s+{([^}]+)}\s*=\s*require\(['"]([^'"]+)['"]\)/g, 'import { $1 } from "$2"')
|
|
114
114
|
.replace(/const\s+([^=]+)\s*=\s*require\(['"]([^'"]+)['"]\)/g, 'import $1 from "$2"');
|
|
115
|
+
|
|
116
|
+
// Convert await import(var) → JSON.parse(fs.readFileSync(var, "utf8"))
|
|
117
|
+
// for variables referencing paths ending in .json/.jsonp/.md/.mdc/.text/.txt
|
|
118
|
+
const extensionsToConvert = ['json', 'jsonp', 'md', 'mdc', 'text', 'txt'];
|
|
119
|
+
const extPattern = extensionsToConvert.join('|');
|
|
120
|
+
const varDeclRegex = new RegExp(
|
|
121
|
+
`(?:const|let|var)\\s+(\\w+)\\s*=[^;]*?\\.(?:${extPattern})["'\`][^;]*;`,
|
|
122
|
+
'g'
|
|
123
|
+
);
|
|
124
|
+
const jsonVars = new Set();
|
|
125
|
+
let match;
|
|
126
|
+
while ((match = varDeclRegex.exec(content)) !== null) {
|
|
127
|
+
jsonVars.add(match[1]);
|
|
128
|
+
}
|
|
129
|
+
if (jsonVars.size > 0) {
|
|
130
|
+
const importRegex = new RegExp(
|
|
131
|
+
`await import\\((${[...jsonVars].join('|')})\\)`,
|
|
132
|
+
'g'
|
|
133
|
+
);
|
|
134
|
+
content = content.replace(importRegex, 'JSON.parse(fs.readFileSync($1, "utf8"))');
|
|
135
|
+
}
|
|
136
|
+
|
|
115
137
|
writeFileSync(file, content, 'utf8');
|
|
116
138
|
});
|
|
117
139
|
});
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Launch & Server Architecture
|
|
2
|
+
|
|
3
|
+
## How to launch an app
|
|
4
|
+
|
|
5
|
+
After creating a project with `qcobjects create myapp --pwa`, enter the project directory and run:
|
|
6
|
+
|
|
7
|
+
```shell
|
|
8
|
+
npm start
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This executes `npm run createcert && npm run serve` (defined in the generated `package.json`). The `serve` script runs `qcobjects-server`.
|
|
12
|
+
|
|
13
|
+
Alternatively, from anywhere:
|
|
14
|
+
|
|
15
|
+
```shell
|
|
16
|
+
qcobjects launch <appname>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Note:** The `<appname>` argument is accepted for interface consistency but is not used by the server — the server discovers the app from the current working directory.
|
|
20
|
+
|
|
21
|
+
## The launch chain
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
qcobjects launch <appname>
|
|
25
|
+
│
|
|
26
|
+
▼
|
|
27
|
+
src/cli-main.ts:455-472
|
|
28
|
+
└─ exec("qcobjects-server")
|
|
29
|
+
│
|
|
30
|
+
▼
|
|
31
|
+
package.json bin → public/cjs/qcobjects-http2-server.js
|
|
32
|
+
│
|
|
33
|
+
▼
|
|
34
|
+
src/qcobjects-http2-server.ts
|
|
35
|
+
├─ import "./defaultsettings" ← loads CONFIG defaults + project config.json
|
|
36
|
+
├─ choose server class:
|
|
37
|
+
│ CONFIG.get("useLegacyHTTP", false)
|
|
38
|
+
│ → false (default): HTTP2Server (src/main-http2-server.ts)
|
|
39
|
+
│ → true: HTTPServer (src/main-http-server.ts)
|
|
40
|
+
└─ New(serverClass).start()
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## CLI command handler
|
|
44
|
+
|
|
45
|
+
**File:** `src/cli-main.ts:455-472`
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
switchCommander.program.command("launch <appname>")
|
|
49
|
+
.description("Launches the application")
|
|
50
|
+
.action(function () {
|
|
51
|
+
logger.info("Launching...");
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
logger.info("Go to the browser and open https://localhost ");
|
|
54
|
+
logger.info("Press Ctrl-C to stop serving ");
|
|
55
|
+
exec("qcobjects-server", () => {})
|
|
56
|
+
.stdout?.on("data", data => console.log(data));
|
|
57
|
+
}, 5000);
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The handler:
|
|
62
|
+
1. Logs `"Launching..."` immediately
|
|
63
|
+
2. Waits 5 seconds
|
|
64
|
+
3. Spawns `qcobjects-server` (the HTTP/2 server binary) via `child_process.exec()`
|
|
65
|
+
4. Pipes the server's stdout to the console
|
|
66
|
+
|
|
67
|
+
The 5-second delay exists so the CLI process can exit gracefully before the server takes over.
|
|
68
|
+
|
|
69
|
+
## Server binary
|
|
70
|
+
|
|
71
|
+
The `qcobjects-server` binary is defined in `package.json`:
|
|
72
|
+
|
|
73
|
+
| Binary | Path | Source |
|
|
74
|
+
|--------|------|--------|
|
|
75
|
+
| `qcobjects-server` | `public/cjs/qcobjects-http2-server.js` | `src/qcobjects-http2-server.ts` |
|
|
76
|
+
| `qcobjects-http-server` | `public/cjs/qcobjects-http-server.js` | `src/qcobjects-http-server.ts` |
|
|
77
|
+
| `qcobjects-gae-server` | `public/cjs/qcobjects-gae-http-server.js` | `src/qcobjects-gae-http-server.ts` |
|
|
78
|
+
|
|
79
|
+
## Server selection
|
|
80
|
+
|
|
81
|
+
**File:** `src/qcobjects-http2-server.ts`
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import "./defaultsettings";
|
|
85
|
+
import { CONFIG, New } from "qcobjects";
|
|
86
|
+
import { HTTPServer } from "./main-http-server";
|
|
87
|
+
import { HTTP2Server } from "./main-http2-server";
|
|
88
|
+
|
|
89
|
+
const _ServerClass_ = CONFIG.get("useLegacyHTTP", false)
|
|
90
|
+
? HTTPServer
|
|
91
|
+
: HTTP2Server;
|
|
92
|
+
const app = New(_ServerClass_);
|
|
93
|
+
app.start();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The default is **HTTP/2** (`useLegacyHTTP: false`). Set `useLegacyHTTP: true` in the project's `config.json` to use HTTP/1.1.
|
|
97
|
+
|
|
98
|
+
| Server | File | Listens on | Default port |
|
|
99
|
+
|--------|------|------------|--------------|
|
|
100
|
+
| HTTP/2 (default) | `src/main-http2-server.ts` | HTTP redirect (port 8080 → 8443) + HTTPS/2 (port 8443) | 8443 |
|
|
101
|
+
| Legacy HTTP | `src/main-http-server.ts` | HTTP (plain, single port) | `process.env.PORT` \|\| 8080 |
|
|
102
|
+
| GAE HTTP | `src/main-http-gae-server.ts` | HTTP (single port, GAE compatible) | `process.env.PORT` \|\| 8080 |
|
|
103
|
+
|
|
104
|
+
## App discovery
|
|
105
|
+
|
|
106
|
+
The server does not use the `<appname>` argument. Instead, the app is discovered through the **current working directory**:
|
|
107
|
+
|
|
108
|
+
1. **`src/defaultsettings.ts:133`** — `CONFIG.set("projectPath", \`${process.cwd()}/\`)`
|
|
109
|
+
2. **`src/defaultsettings.ts:173-178`** — reads `<projectPath>/config.json` and merges every key into CONFIG, overriding all defaults
|
|
110
|
+
3. **`src/defaultsettings.ts:90`** — `documentRoot` defaults to `<projectPath>/public/`
|
|
111
|
+
4. **`src/main-file.ts`** — `FileDispatcher` resolves all file requests relative to `documentRoot`
|
|
112
|
+
|
|
113
|
+
The implicit contract is that the user is `cd`'d into the project directory, and the project contains a `config.json` and a `public/` directory.
|
|
114
|
+
|
|
115
|
+
## CONFIG settings reference
|
|
116
|
+
|
|
117
|
+
All defaults are set in `src/defaultsettings.ts:83-136`.
|
|
118
|
+
|
|
119
|
+
### Server & network
|
|
120
|
+
|
|
121
|
+
| Setting | Default | Description |
|
|
122
|
+
|---------|---------|-------------|
|
|
123
|
+
| `serverPortHTTP` | `"8080"` | Port for HTTP (and HTTP→HTTPS redirect in HTTP/2 mode) |
|
|
124
|
+
| `serverPortHTTPS` | `"8443"` | Port for HTTPS/HTTP/2 |
|
|
125
|
+
| `useLegacyHTTP` | `false` | Use HTTP/1.1 instead of HTTP/2 |
|
|
126
|
+
| `allowHTTP1` | `true` | Allow HTTP/1.1 fallback on the HTTP/2 server |
|
|
127
|
+
| `documentRoot` | `$config(projectPath)public/` | Root directory for static file serving |
|
|
128
|
+
| `documentRootFileIndex` | `"index.html"` | Default file served for directory paths |
|
|
129
|
+
| `cacheControl` | `"max-age=31536000"` | Cache-Control header for static files |
|
|
130
|
+
| `backendTimeout` | `20000` | Request timeout in ms (set at runtime) |
|
|
131
|
+
|
|
132
|
+
### TLS / certificates
|
|
133
|
+
|
|
134
|
+
| Setting | Default | Description |
|
|
135
|
+
|---------|---------|-------------|
|
|
136
|
+
| `private-key-pem` | `$config(domain)-privkey.pem` | TLS private key file |
|
|
137
|
+
| `private-cert-pem` | `$config(domain)-cert.pem` | TLS certificate file |
|
|
138
|
+
| `certificate_provider` | `$ENV(CERTIFICATE_PROVIDER)` | `self_signed` or `letsencrypt` |
|
|
139
|
+
|
|
140
|
+
### Backend
|
|
141
|
+
|
|
142
|
+
| Setting | Default | Description |
|
|
143
|
+
|---------|---------|-------------|
|
|
144
|
+
| `backend.db_engine.name` | `$ENV(ENGINE_NAME)` | Database engine name |
|
|
145
|
+
| `backend.auth.enabled` | `true` | Enable authentication |
|
|
146
|
+
| `backend.routes` | `[]` | Route definitions (microservice dispatch) |
|
|
147
|
+
|
|
148
|
+
### Autodiscovery
|
|
149
|
+
|
|
150
|
+
| Setting | Default | Description |
|
|
151
|
+
|---------|---------|-------------|
|
|
152
|
+
| `autodiscover` | `true` | Master switch for plugin autodiscovery |
|
|
153
|
+
| `autodiscover_commands` | `true` | Auto-load `qcobjects-command` plugins |
|
|
154
|
+
| `autodiscover_handlers` | `true` | Auto-load `qcobjects-handler` plugins |
|
|
155
|
+
|
|
156
|
+
### Config resolution order
|
|
157
|
+
|
|
158
|
+
1. Hardcoded defaults (`defaultsettings.ts:83-136`)
|
|
159
|
+
2. `$ENV(VAR)` template literals resolved at runtime from environment variables
|
|
160
|
+
3. `$config(key)` template literals resolved from CONFIG values
|
|
161
|
+
4. Project `<projectPath>/config.json` values merged on top (overrides all)
|
|
162
|
+
|
|
163
|
+
## HTTP/2 server start
|
|
164
|
+
|
|
165
|
+
**File:** `src/main-http2-server.ts:433-448`
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
start() {
|
|
169
|
+
var server = this.server;
|
|
170
|
+
|
|
171
|
+
const httpServer = http.createServer((req, res) => {
|
|
172
|
+
res.writeHead(301, {
|
|
173
|
+
Location: `https://${req.headers.host}${req.url}`
|
|
174
|
+
});
|
|
175
|
+
res.end();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
httpServer.listen(CONFIG.get("serverPortHTTP")); // 8080 → redirects to HTTPS
|
|
179
|
+
server.listen(CONFIG.get("serverPortHTTPS")); // 8443 → HTTP/2
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- Creates an HTTP redirector on `serverPortHTTP` (8080) that sends 301 redirects to HTTPS
|
|
184
|
+
- Creates the HTTP/2 secure server on `serverPortHTTPS` (8443) using `http2.createSecureServer()` with TLS key and cert from CONFIG
|
|
185
|
+
|
|
186
|
+
## Legacy HTTP server start
|
|
187
|
+
|
|
188
|
+
**File:** `src/main-http-server.ts:595-598`
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
start() {
|
|
192
|
+
var server = this.server;
|
|
193
|
+
server.listen(process.env.PORT || CONFIG.get("serverPortHTTP"));
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Single HTTP listener on `process.env.PORT || serverPortHTTP` (default 8080).
|
|
198
|
+
|
|
199
|
+
## Request handling flow
|
|
200
|
+
|
|
201
|
+
Both server variants follow the same pattern on each request:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
Incoming request
|
|
205
|
+
│
|
|
206
|
+
▼
|
|
207
|
+
Check global.get("backendAvailable")?
|
|
208
|
+
│
|
|
209
|
+
├─ Yes (project config.json has "backend" key)
|
|
210
|
+
│ │
|
|
211
|
+
│ ├─ Load interceptors from CONFIG.get("backend").interceptors
|
|
212
|
+
│ ├─ Match request path against CONFIG.get("backend").routes
|
|
213
|
+
│ │
|
|
214
|
+
│ ├─ Route matched?
|
|
215
|
+
│ │ ├─ Yes → Instantiate route.microservice + ".Microservice"
|
|
216
|
+
│ │ │ via New(microServiceClassFactory, { ...CONFIG values })
|
|
217
|
+
│ │ └─ No → Fall through to HTTPServerResponse / HTTP2ServerResponse
|
|
218
|
+
│ │
|
|
219
|
+
│ └─ Response class serves file via FileDispatcher
|
|
220
|
+
│
|
|
221
|
+
└─ No (no backend config)
|
|
222
|
+
│
|
|
223
|
+
└─ HTTPServerResponse / HTTP2ServerResponse
|
|
224
|
+
└─ FileDispatcher resolves <documentRoot>/<path>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Key files
|
|
228
|
+
|
|
229
|
+
| File | Role |
|
|
230
|
+
|------|------|
|
|
231
|
+
| `src/qcobjects-http2-server.ts` | Server bootstrap — chooses HTTP vs HTTP/2 |
|
|
232
|
+
| `src/qcobjects-http-server.ts` | Legacy HTTP bootstrap |
|
|
233
|
+
| `src/qcobjects-gae-http-server.ts` | GAE-compatible bootstrap |
|
|
234
|
+
| `src/main-http2-server.ts` | HTTP2Server, HTTP2ServerResponse, HTTP2ServerRequest |
|
|
235
|
+
| `src/main-http-server.ts` | HTTPServer, BackendMicroservice, HTTPServerResponse |
|
|
236
|
+
| `src/main-http-gae-server.ts` | GAE-compatible server classes |
|
|
237
|
+
| `src/main-file.ts` | FileDispatcher — filesystem resolution |
|
|
238
|
+
| `src/defaultsettings.ts` | All CONFIG defaults + config.json loading + plugin autodiscovery |
|
|
239
|
+
| `src/common-pipelog.ts` | PipeLog logging utility |
|
|
240
|
+
|
|
241
|
+
## Template-generated app scripts
|
|
242
|
+
|
|
243
|
+
Running `qcobjects create myapp --pwa` generates a `package.json` with these relevant scripts:
|
|
244
|
+
|
|
245
|
+
| Script | Command | Purpose |
|
|
246
|
+
|--------|---------|---------|
|
|
247
|
+
| `start` | `npm run createcert && npm run serve` | Full launch (generate TLS cert then serve) |
|
|
248
|
+
| `serve` | `qcobjects-server` | Start HTTP/2 server |
|
|
249
|
+
| `http-server` | `qcobjects-http-server` | Start legacy HTTP server |
|
|
250
|
+
| `createcert` | `qcobjects-createcert` | Generate self-signed TLS certificate |
|
|
251
|
+
| `prestart` | `npm run publish:web` | Build before launching |
|
|
252
|
+
|
|
253
|
+
The recommended workflow: `npm start` → generates a self-signed cert (if needed) → builds the web assets → starts the HTTP/2 server.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Release Pipeline
|
|
2
|
+
|
|
3
|
+
## Why this change
|
|
4
|
+
|
|
5
|
+
Previously the repo used version-specific branches (v2.3, v2.4-beta, v2.4-ts,
|
|
6
|
+
v2.5-beta) to track different architecture waves (ECMA5, ES6+JS, full TS+JS).
|
|
7
|
+
As the number of active tracks grew, branch-based management added complexity:
|
|
8
|
+
CI workflows needed per-branch configuration, PR paths were unclear, and
|
|
9
|
+
promoting a beta to LTS required manual cross-branch coordination.
|
|
10
|
+
|
|
11
|
+
## What changed
|
|
12
|
+
|
|
13
|
+
- Removed all version-specific branches (v2.3, v2.4-beta, v2.4-ts, v2.5-beta)
|
|
14
|
+
- Single active development branch: `development`
|
|
15
|
+
- Release channel is encoded in the tag suffix, not in a branch name
|
|
16
|
+
- Old version tracks preserved as archive tags
|
|
17
|
+
|
|
18
|
+
## Branch model
|
|
19
|
+
|
|
20
|
+
main ← release digest (merged from development via PR)
|
|
21
|
+
development ← single active development branch
|
|
22
|
+
feature/* ← topic branches, PR into development
|
|
23
|
+
fix/*
|
|
24
|
+
bugfix/*
|
|
25
|
+
|
|
26
|
+
No more version-specific branches. Every release is a tag on `main`.
|
|
27
|
+
|
|
28
|
+
## Release channels (tags)
|
|
29
|
+
|
|
30
|
+
| Tag pattern | npm dist-tag | Triggered by |
|
|
31
|
+
|-------------|-------------|--------------|
|
|
32
|
+
| `vX.Y.Z` | `latest` | `npmpublish.yml` (suffix detection) |
|
|
33
|
+
| `vX.Y.Z-lts` | `lts` | `npmpublish.yml` (suffix detection) |
|
|
34
|
+
| `vX.Y.Z-beta` | `beta` | `npmpublish.yml` (suffix detection) |
|
|
35
|
+
| (future) `-dev` | `dev` | (add pattern to `npmpublish.yml`) |
|
|
36
|
+
| (future) `-alpha` | `alpha` | (add pattern to `npmpublish.yml`) |
|
|
37
|
+
|
|
38
|
+
## Promotion workflow
|
|
39
|
+
|
|
40
|
+
1. Daily work on `development` branch
|
|
41
|
+
2. `v-patch --git --npm` → tag `vX.Y.Z-beta` (beta publish)
|
|
42
|
+
3. Change VERSION suffix → `v-patch --git --npm` → tag `vX.Y.Z-lts` (LTS publish)
|
|
43
|
+
4. PR `development` → `main` → merge → tag `vX.Y.Z` on `main` (latest publish)
|
|
44
|
+
|
|
45
|
+
## Branch snapshot and removal steps
|
|
46
|
+
|
|
47
|
+
These steps preserve the latest state of every old version branch as a tag,
|
|
48
|
+
then delete the branches. Run them once during migration.
|
|
49
|
+
|
|
50
|
+
### Prerequisites
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git fetch --all --prune
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 1. Archive v2.4-beta
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git tag archive/v2.4-beta origin/v2.4-beta
|
|
60
|
+
git push origin archive/v2.4-beta
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Archive v2.4-ts
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
git tag archive/v2.4-ts origin/v2.4-ts
|
|
67
|
+
git push origin archive/v2.4-ts
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3. Bootstrap development from v2.5-beta
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git checkout -b development origin/v2.5-beta
|
|
74
|
+
git push -u origin development
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 4. Delete old remote branches
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git push origin --delete v2.5-beta v2.4-beta v2.4-ts
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 5. Delete old local branches
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
git branch -d v2.5-beta
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 6. Verify
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git branch -a # should show only main, development
|
|
93
|
+
git tag -ln archive/* # should show archive/v2.4-beta, archive/v2.4-ts
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Archived tracks
|
|
97
|
+
|
|
98
|
+
v2.3 is already captured by existing tags `v2.3.1` through `v2.3.50` — no
|
|
99
|
+
separate archive tag needed. Its commits are ancestors of `main`.
|
|
100
|
+
|
|
101
|
+
| Archive tag | Source branch | Created |
|
|
102
|
+
|-------------|--------------|---------|
|
|
103
|
+
| `archive/v2.4-beta` | `origin/v2.4-beta` | migration date |
|
|
104
|
+
| `archive/v2.4-ts` | `origin/v2.4-ts` | migration date |
|
|
105
|
+
| `v2.3.50` (existing tag) | `v2.3` branch (GitLab, merged to master) | n/a |
|
|
106
|
+
|
|
107
|
+
## CI pipeline updates
|
|
108
|
+
|
|
109
|
+
| File | Change |
|
|
110
|
+
|------|--------|
|
|
111
|
+
| `.github/workflows/codeql-analysis.yml` | branch targets from `[v2.3, v2.4]` to `[main, development]` |
|
|
112
|
+
| `.github/workflows/npmpublish.yml` | consolidated single workflow (was 3 separate) |
|
|
113
|
+
| `.github/workflows/open-pr-to-development.yml` | already targets `development`, no changes needed |
|
|
114
|
+
| `.github/workflows/main-pr-source.yml` | already enforces `development` → `main`, no changes needed |
|
|
115
|
+
| `.github/workflows/ci.yml` | already targets `main`/`development`, no changes needed |
|
|
116
|
+
|
|
117
|
+
## Future considerations
|
|
118
|
+
|
|
119
|
+
- When starting a new architecture wave (e.g. v2.6), branch from `main` into a
|
|
120
|
+
feature branch, develop on `development`, and release via the same tag flow
|
|
121
|
+
- Archived branches are never deleted from git history — the tags ensure
|
|
122
|
+
the code is always accessible for reference or hotfix branching
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qcobjects-cli",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.153-beta",
|
|
4
4
|
"description": "qcobjects cli command line tool",
|
|
5
5
|
"main": "public/cjs/index.js",
|
|
6
6
|
"module": "public/esm/index.mjs",
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
"@types/node": "^22.10.2",
|
|
157
157
|
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
|
158
158
|
"@typescript-eslint/parser": "^5.58.0",
|
|
159
|
-
"esbuild": "^0.
|
|
159
|
+
"esbuild": "^0.28.0",
|
|
160
160
|
"esbuild-plugin-alias": "^0.2.1",
|
|
161
161
|
"eslint": "^8.56.0",
|
|
162
162
|
"eslint-config-prettier": "^8.10.0",
|
|
@@ -44,7 +44,7 @@ class CommandHandler extends qcobjects_1.InheritClass {
|
|
|
44
44
|
this.switchCommander = switchCommander;
|
|
45
45
|
const commandHandler = this;
|
|
46
46
|
this.choiceOption = {
|
|
47
|
-
v_major(filename, options) {
|
|
47
|
+
async v_major(filename, options) {
|
|
48
48
|
filename = (typeof filename === "undefined") ? ("VERSION") : (filename);
|
|
49
49
|
const versionString = this.getVersionStringFromFile(filename);
|
|
50
50
|
const versionSuffix = this.parseVersionSuffix(versionString);
|
|
@@ -56,10 +56,10 @@ class CommandHandler extends qcobjects_1.InheritClass {
|
|
|
56
56
|
this.saveNewVersionFile(filename, newVersion);
|
|
57
57
|
if (options.syncGit) {
|
|
58
58
|
var commitMsg = options.commitMsg || `New Version v${newVersion}`;
|
|
59
|
-
this.syncGit(newVersion, commitMsg, options.syncNpm);
|
|
59
|
+
await this.syncGit(newVersion, commitMsg, options.syncNpm);
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
|
-
v_minor(filename, options) {
|
|
62
|
+
async v_minor(filename, options) {
|
|
63
63
|
filename = (typeof filename === "undefined") ? ("VERSION") : (filename);
|
|
64
64
|
const versionString = this.getVersionStringFromFile(filename);
|
|
65
65
|
const versionSuffix = this.parseVersionSuffix(versionString);
|
|
@@ -71,10 +71,11 @@ class CommandHandler extends qcobjects_1.InheritClass {
|
|
|
71
71
|
this.saveNewVersionFile(filename, newVersion);
|
|
72
72
|
if (options.syncGit) {
|
|
73
73
|
var commitMsg = options.commitMsg || `New Version v${newVersion}`;
|
|
74
|
-
this.syncGit(newVersion, commitMsg, options.syncNpm);
|
|
74
|
+
await this.syncGit(newVersion, commitMsg, options.syncNpm);
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
|
-
v_patch(filename, options) {
|
|
77
|
+
async v_patch(filename, options) {
|
|
78
|
+
|
|
78
79
|
filename = (typeof filename === "undefined") ? ("VERSION") : (filename);
|
|
79
80
|
const versionString = this.getVersionStringFromFile(filename);
|
|
80
81
|
const versionSuffix = this.parseVersionSuffix(versionString);
|
|
@@ -86,7 +87,7 @@ class CommandHandler extends qcobjects_1.InheritClass {
|
|
|
86
87
|
this.saveNewVersionFile(filename, newVersion);
|
|
87
88
|
if (options.syncGit) {
|
|
88
89
|
var commitMsg = options.commitMsg || `New Version v${newVersion}`;
|
|
89
|
-
this.syncGit(newVersion, commitMsg, options.syncNpm);
|
|
90
|
+
await this.syncGit(newVersion, commitMsg, options.syncNpm);
|
|
90
91
|
}
|
|
91
92
|
},
|
|
92
93
|
v_sync(filename, options) {
|
|
@@ -151,7 +152,7 @@ class CommandHandler extends qcobjects_1.InheritClass {
|
|
|
151
152
|
.option("-m, --commit-msg [message]", "Commit Message")
|
|
152
153
|
.description("Semantic Versioning: Upgrade to a new major version")
|
|
153
154
|
.action(function (args, options) {
|
|
154
|
-
commandHandler.choiceOption.v_major.call(commandHandler, args, options);
|
|
155
|
+
return commandHandler.choiceOption.v_major.call(commandHandler, args, options);
|
|
155
156
|
});
|
|
156
157
|
switchCommander.program.command("v-minor [filename]")
|
|
157
158
|
.option("--git, --sync-git", "Sync with Git")
|
|
@@ -159,7 +160,7 @@ class CommandHandler extends qcobjects_1.InheritClass {
|
|
|
159
160
|
.option("-m, --commit-msg [message]", "Commit Message")
|
|
160
161
|
.description("Semantic Versioning: Upgrade to a new minor version")
|
|
161
162
|
.action(function (args, options) {
|
|
162
|
-
commandHandler.choiceOption.v_minor.call(commandHandler, args, options);
|
|
163
|
+
return commandHandler.choiceOption.v_minor.call(commandHandler, args, options);
|
|
163
164
|
});
|
|
164
165
|
switchCommander.program.command("v-patch [filename]")
|
|
165
166
|
.option("--git, --sync-git", "Sync with Git")
|
|
@@ -167,7 +168,7 @@ class CommandHandler extends qcobjects_1.InheritClass {
|
|
|
167
168
|
.option("-m, --commit-msg [message]", "Commit Message")
|
|
168
169
|
.description("Semantic Versioning: Upgrade to a new patch version")
|
|
169
170
|
.action(function (args, options) {
|
|
170
|
-
commandHandler.choiceOption.v_patch.call(commandHandler, args, options);
|
|
171
|
+
return commandHandler.choiceOption.v_patch.call(commandHandler, args, options);
|
|
171
172
|
});
|
|
172
173
|
switchCommander.program.command("v-sync [filename]")
|
|
173
174
|
.option("-m, --commit-msg [message]", "Commit Message")
|
|
@@ -198,7 +199,7 @@ class CommandHandler extends qcobjects_1.InheritClass {
|
|
|
198
199
|
_commands_.push(`git tag -a "v${versionString}" -m "${commitMsg}"`);
|
|
199
200
|
}
|
|
200
201
|
_commands_.push("git push && git push --tags");
|
|
201
|
-
this.switchCommander.shellCommands(_commands_).then(function (response) {
|
|
202
|
+
return this.switchCommander.shellCommands(_commands_).then(function (response) {
|
|
202
203
|
qcobjects_1.logger.info("Synced to Git");
|
|
203
204
|
qcobjects_1.logger.debug(response);
|
|
204
205
|
}).catch(function (e) {
|