@omega.js/desktop 0.53.0 → 0.54.1
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 +38 -38
- package/dist/cli-run.js +4 -1
- package/dist/cli.js +2 -2
- package/dist/commands/cdp/client.js +1 -1
- package/dist/commands/cdp.js +1 -1
- package/dist/commands/clean.js +2 -3
- package/dist/commands/dev.js +25 -0
- package/dist/commands/lib/ensure-target.js +12 -17
- package/dist/commands/lib/migrate.js +17 -0
- package/dist/commands/logs.js +1 -1
- package/dist/commands/release.js +1 -1
- package/dist/commands/test.js +4 -4
- package/dist/commands/update.js +5 -4
- package/dist/defaults/.github/workflows/build.yml +18 -18
- package/dist/defaults/_.gitignore +0 -2
- package/dist/defaults/_mas/README.md +3 -3
- package/dist/defaults/config/certs/README.md +1 -1
- package/dist/defaults/config/omega.json5 +36 -36
- package/dist/defaults/docs/README.md +3 -3
- package/dist/defaults/gulpfile.js +1 -1
- package/dist/defaults/hooks/build/post.js +1 -1
- package/dist/defaults/hooks/build/pre.js +1 -1
- package/dist/defaults/hooks/notarize/post.js +2 -2
- package/dist/defaults/hooks/release/post.js +1 -1
- package/dist/defaults/hooks/release/pre.js +1 -1
- package/dist/defaults/src/assets/scss/pages/about.scss +1 -1
- package/dist/defaults/src/assets/scss/pages/main.scss +1 -1
- package/dist/defaults/src/assets/scss/pages/settings.scss +1 -1
- package/dist/defaults/src/integrations/context-menu/index.js +11 -11
- package/dist/defaults/src/integrations/menu/index.js +5 -5
- package/dist/defaults/src/integrations/tray/index.js +9 -9
- package/dist/defaults/src/main.js +2 -2
- package/dist/defaults/src/preload.js +1 -1
- package/dist/defaults/test/README.md +3 -3
- package/dist/defaults/test/_init.js +1 -1
- package/dist/gulp/tasks/audit.js +5 -8
- package/dist/lib/restart-manager/index.js +1 -1
- package/dist/lib/restart-manager/install.js +1 -1
- package/dist/lib/restart-manager/protocol.js +1 -1
- package/dist/main.js +4 -3
- package/dist/preload.js +1 -1
- package/dist/test/suites/build/audit.test.js +20 -7
- package/dist/test/suites/build/build-workflow-jobs.test.js +2 -2
- package/dist/test/suites/build/cli.test.js +28 -0
- package/dist/test/suites/build/defaults-em-dash.test.js +22 -0
- package/dist/test/suites/build/defaults-scaffold.test.js +19 -5
- package/dist/test/suites/build/deploy-direct.test.js +7 -5
- package/dist/test/suites/build/deploy-dispatch.test.js +2 -1
- package/dist/test/suites/build/deploy-hook.test.js +4 -2
- package/dist/test/suites/build/dev-verb.test.js +67 -0
- package/dist/test/suites/build/ensure-target.test.js +11 -3
- package/dist/test/suites/build/merge-line-files.test.js +6 -6
- package/dist/test/suites/build/migrate.test.js +29 -0
- package/dist/test/suites/build/project-scripts-deps.test.js +6 -10
- package/dist/test/suites/build/runner-env-write.test.js +73 -0
- package/dist/test/suites/build/runner.test.js +9 -8
- package/dist/test/suites/build/setup-scripts.test.js +27 -0
- package/dist/test/suites/build/validate-config.test.js +13 -2
- package/dist/test/suites/build/verb-logs.test.js +20 -0
- package/dist/test/suites/renderer/window-desktop-surface.test.js +1 -1
- package/dist/utils/build-pipeline.js +4 -4
- package/dist/utils/runner-env.js +13 -28
- package/dist/vendor/config/company.js +46 -14
- package/dist/vendor/config/defaults.js +30 -7
- package/dist/vendor/config/edit.js +25 -3
- package/dist/vendor/config/env-delivery.js +1 -1
- package/dist/vendor/config/env-schema.js +3 -6
- package/dist/vendor/config/env.js +34 -22
- package/dist/vendor/config/index.js +13 -17
- package/dist/vendor/config/load.js +15 -7
- package/dist/vendor/config/repo.js +10 -27
- package/dist/vendor/config/schema-client.js +64 -0
- package/dist/vendor/config/schema-cloud.js +38 -0
- package/dist/vendor/config/schema-manager.js +118 -0
- package/dist/vendor/config/schema-overrides.js +68 -0
- package/dist/vendor/config/schema.js +99 -152
- package/dist/vendor/config/validate.js +97 -77
- package/dist/vendor/devkit/agents-md.js +233 -0
- package/dist/vendor/devkit/attach-log-file.js +15 -1
- package/dist/vendor/devkit/ci-workflows.js +30 -30
- package/dist/vendor/devkit/cli-router.js +13 -7
- package/dist/vendor/devkit/defaults-engine.js +9 -43
- package/dist/vendor/devkit/deploy-snapshot.js +44 -9
- package/dist/vendor/devkit/env-lines.js +183 -0
- package/dist/vendor/devkit/local.js +62 -10
- package/dist/vendor/devkit/lockfile.js +32 -13
- package/dist/vendor/devkit/logger.js +7 -2
- package/dist/vendor/devkit/merge-line-files.js +219 -176
- package/dist/vendor/devkit/omega-bin.js +208 -111
- package/dist/vendor/devkit/preludes/docs-sync.js +52 -0
- package/dist/vendor/devkit/preludes/index.js +1 -0
- package/dist/vendor/devkit/target-picker.js +45 -0
- package/dist/vendor/devkit/test/dashed-files.js +37 -0
- package/dist/vendor/devkit/test/run-verb-under-tee.js +71 -0
- package/dist/vendor/devkit/update.js +15 -15
- package/dist/vendor/devkit/verb-scripts.js +40 -0
- package/dist/vendor/devkit/verbs.js +170 -0
- package/package.json +18 -24
- package/dist/commands/install.js +0 -37
- package/dist/defaults/AGENTS.md +0 -119
- package/dist/defaults/CLAUDE.md +0 -1
- package/dist/vendor/config/env-retired.js +0 -137
- package/dist/vendor/config/retired-keys.js +0 -635
- package/docs/analytics.md +0 -140
- package/docs/app-state.md +0 -92
- package/docs/audit.md +0 -69
- package/docs/auth.md +0 -284
- package/docs/auto-updater.md +0 -243
- package/docs/boot-sequence.md +0 -44
- package/docs/build-system.md +0 -169
- package/docs/cdp-debugging.md +0 -169
- package/docs/common-mistakes.md +0 -21
- package/docs/config-schema.md +0 -120
- package/docs/context-menu.md +0 -112
- package/docs/context.md +0 -81
- package/docs/css.md +0 -84
- package/docs/deep-link.md +0 -186
- package/docs/environment-detection.md +0 -112
- package/docs/fontawesome.md +0 -109
- package/docs/hooks.md +0 -89
- package/docs/icons.md +0 -79
- package/docs/index.md +0 -328
- package/docs/installer-options.md +0 -165
- package/docs/ipc.md +0 -61
- package/docs/lib-modules.md +0 -53
- package/docs/logging.md +0 -227
- package/docs/menu.md +0 -160
- package/docs/releasing.md +0 -239
- package/docs/remote-config.md +0 -118
- package/docs/remote-scripts.md +0 -144
- package/docs/restart-manager.md +0 -144
- package/docs/runner.md +0 -290
- package/docs/sentry.md +0 -97
- package/docs/shared/agent-docs.md +0 -89
- package/docs/shared/analytics.md +0 -612
- package/docs/shared/brands.md +0 -57
- package/docs/shared/breaking-changes.md +0 -917
- package/docs/shared/config.md +0 -1948
- package/docs/shared/deploys.md +0 -341
- package/docs/shared/icons.md +0 -219
- package/docs/shared/local-dev.md +0 -167
- package/docs/shared/logging.md +0 -205
- package/docs/shared/monitoring.md +0 -167
- package/docs/shared/publishing.md +0 -187
- package/docs/shared/rulings.md +0 -34
- package/docs/shared/testing.md +0 -147
- package/docs/shared/theming.md +0 -629
- package/docs/shared/translation.md +0 -342
- package/docs/shared/updates.md +0 -61
- package/docs/signing.md +0 -293
- package/docs/startup.md +0 -142
- package/docs/storage.md +0 -59
- package/docs/templating.md +0 -101
- package/docs/test-boot-layer.md +0 -157
- package/docs/test-framework.md +0 -362
- package/docs/themes.md +0 -149
- package/docs/tooltips.md +0 -99
- package/docs/tray.md +0 -164
- package/docs/usage.md +0 -58
- package/docs/verts.md +0 -62
- package/docs/windows.md +0 -149
package/docs/signing.md
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
# Code Signing
|
|
2
|
-
|
|
3
|
-
Cross-platform code signing reference. Covers macOS (sign + notarize), Windows (EV token + cloud), and Linux (no signing required for AppImage/deb but Snap/Flatpak have their own conventions).
|
|
4
|
-
|
|
5
|
-
## tl;dr
|
|
6
|
-
|
|
7
|
-
- **macOS production**: Developer ID Application cert + notarization API key. Files in `config/certs/`. Env vars point at them.
|
|
8
|
-
- **Windows production**: EV USB token (self-hosted runner now) or cloud signing (future, pluggable via `platforms.windows.signing.strategy`).
|
|
9
|
-
- **Linux**: No signing for AppImage/deb. Snap/Flatpak have their own pipelines.
|
|
10
|
-
|
|
11
|
-
## Where files live
|
|
12
|
-
|
|
13
|
-
Signing material lives in the SIGNING TREE and is read there, never copied into a target
|
|
14
|
-
([#891](https://github.com/Omega-JS-Stack/omega/issues/891)):
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
<company brand>/company/.omega/certificates/apple/ # read FIRST when the brand names a company
|
|
18
|
-
<your brand>/.omega/certificates/apple/ # read second
|
|
19
|
-
certificates/DEVELOPER_ID_APPLICATION_G2.p12 # what CSC_LINK resolves to
|
|
20
|
-
certificates/DEVELOPER_ID_INSTALLER_G2.p12 # only for .pkg builds (rare)
|
|
21
|
-
AuthKey_XXXXXXXXXX.p8 # what APPLE_API_KEY resolves to
|
|
22
|
-
|
|
23
|
-
<your-app>/
|
|
24
|
-
build/
|
|
25
|
-
entitlements.mac.plist # universal, already in @omega.js/desktop defaults
|
|
26
|
-
icon.icns / icon.ico / icon.png # per-brand
|
|
27
|
-
config/certs/ # gitignored, and EMPTY on a developer machine:
|
|
28
|
-
# only a RUNNER writes here, decoding the pushed secrets
|
|
29
|
-
README.md # the ONE tracked file here (#913): it says the above
|
|
30
|
-
.env # gitignored, and it carries no signing PATH: only
|
|
31
|
-
# CSC_KEY_PASSWORD and the APPLE_* three
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
There is no provisioning profile: Developer ID is direct distribution, signed and notarized,
|
|
35
|
-
and needs none.
|
|
36
|
-
|
|
37
|
-
## Where the macOS certificate comes from (the lookup order)
|
|
38
|
-
|
|
39
|
-
Every desktop boot (the CLI and gulp alike) derives the signing paths ONCE, at the env load
|
|
40
|
-
([src/utils/load-env.js](../src/utils/load-env.js), over `@omega.js/devkit/signing-env`), so
|
|
41
|
-
the build, `omega validate-certs` and the deploy precheck's secret publish all read the same
|
|
42
|
-
answer. `CSC_LINK` resolves in ONE order:
|
|
43
|
-
|
|
44
|
-
1. **`CSC_LINK`** in the environment — an explicit answer always wins.
|
|
45
|
-
2. **The COMPANY's tree**, when the brand names a company and that company is on this
|
|
46
|
-
machine: a brand with a company reuses the company tree first, always
|
|
47
|
-
([#677](https://github.com/Omega-JS-Stack/omega/issues/677)). A brand is NEVER physically
|
|
48
|
-
inside its company folder: membership is its `company: { id }` key, resolved through
|
|
49
|
-
`@omega.js/config`'s `resolveCompany` (the ecosystem's one company rule), and the
|
|
50
|
-
certificate is one more relative path through that resolver's `file()`:
|
|
51
|
-
`<company>/company/.omega/certificates/apple/certificates/DEVELOPER_ID_APPLICATION_G2.p12`.
|
|
52
|
-
3. **The brand's own signing tree**:
|
|
53
|
-
`<brandRoot>/.omega/certificates/apple/certificates/DEVELOPER_ID_APPLICATION_G2.p12`.
|
|
54
|
-
`@omega.js/manager`'s certificates service produces the tree
|
|
55
|
-
([the manager guide](../../../docs/manager/index.md)).
|
|
56
|
-
4. **The macOS Keychain** — electron-builder's own identity auto-discovery.
|
|
57
|
-
|
|
58
|
-
`APPLE_API_KEY` follows the same order for `AuthKey_<APPLE_API_KEY_ID>.p8` (an unset id means
|
|
59
|
-
there is no filename to look for). Both derived values are ABSOLUTE paths into the tree.
|
|
60
|
-
|
|
61
|
-
**Keychain stays the default.** A project with no signing tree resolves to it exactly as
|
|
62
|
-
before. Two further conditions keep a build on the Keychain rather than handing
|
|
63
|
-
electron-builder a file it cannot use: no `CSC_KEY_PASSWORD` in the `.env` cascade, or a
|
|
64
|
-
password that does not OPEN the `.p12` (verified with `openssl pkcs12 -legacy`, retried
|
|
65
|
-
without the flag for LibreSSL — stock macOS openssl — which reads legacy containers but
|
|
66
|
-
rejects the flag; a missing openssl is named as the reason rather than blamed on the
|
|
67
|
-
password). Either case prints one line naming the file and the reason, and leaves the key
|
|
68
|
-
unset rather than failing the build silently.
|
|
69
|
-
|
|
70
|
-
The password itself never lives in config: `CSC_KEY_PASSWORD` comes from the `.env`
|
|
71
|
-
cascade (the manager's certificates service writes it to the signing root's `.env`),
|
|
72
|
-
and the config validator hard-fails secret-shaped config keys.
|
|
73
|
-
|
|
74
|
-
## What's universal vs per-brand
|
|
75
|
-
|
|
76
|
-
**Universal (one set per Apple Developer team / Windows signing identity):**
|
|
77
|
-
- Developer ID Application cert (`.p12`)
|
|
78
|
-
- Developer ID Installer cert (`.p12`) — only if you ship `.pkg`
|
|
79
|
-
- App Store Connect API key (`.p8`)
|
|
80
|
-
- Apple Team ID, Apple ID, app-specific password
|
|
81
|
-
- Windows EV USB token
|
|
82
|
-
|
|
83
|
-
**Per-brand (per app):**
|
|
84
|
-
- Provisioning profile (`.provisionprofile`) — only required if your app uses entitlements that Apple gates (push notifications, in-app purchase, network extensions, app groups, etc.)
|
|
85
|
-
- Icons (`icon.icns`, `icon.ico`, `icon.png`)
|
|
86
|
-
|
|
87
|
-
So for ITW Creative Works' multiple Electron apps signed by the same team, you generally drop the **same** `developer-id-application.p12` and `AuthKey_*.p8` into each app's `config/certs/`. The `.env` paths are identical across apps. Provisioning profiles, if needed, are unique per app.
|
|
88
|
-
|
|
89
|
-
## macOS setup
|
|
90
|
-
|
|
91
|
-
### 1. Apple Developer prerequisites
|
|
92
|
-
|
|
93
|
-
You need an Apple Developer Program membership ($99/year). Inside it:
|
|
94
|
-
|
|
95
|
-
1. **Developer ID Application certificate** — generate at Apple Developer → Certificates → `+` → "Developer ID Application." Download the `.cer`, install in Keychain Access, then export from Keychain as `.p12` with a password.
|
|
96
|
-
2. **App Store Connect API key** (preferred over Apple ID + app-specific password) — generate at App Store Connect → Users and Access → Keys → `+`. Download the `.p8` once (Apple won't show it again). Note the Key ID and Issuer ID shown on the page.
|
|
97
|
-
3. **Team ID** — visible in your Apple Developer membership page.
|
|
98
|
-
|
|
99
|
-
### 2. Drop files into your project
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
cp ~/Downloads/developer-id-application.p12 config/certs/
|
|
103
|
-
cp ~/Downloads/AuthKey_XXXXXXXXXX.p8 config/certs/
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### 3. Wire `.env`
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
CSC_LINK=config/certs/developer-id-application.p12
|
|
110
|
-
CSC_KEY_PASSWORD=<password you set on export>
|
|
111
|
-
|
|
112
|
-
APPLE_API_KEY=config/certs/AuthKey_XXXXXXXXXX.p8
|
|
113
|
-
APPLE_API_KEY_ID=XXXXXXXXXX # 10 chars from key filename
|
|
114
|
-
APPLE_API_ISSUER=00000000-0000-... # UUID from App Store Connect
|
|
115
|
-
|
|
116
|
-
APPLE_TEAM_ID=XXXXXXXXXX
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
> **Empty values are safe.** The `.env` template ships these as `CSC_LINK=""` placeholders; @omega.js/desktop deletes empty/whitespace signing vars after loading `.env` (`src/utils/sanitize-signing-env.js`) so they read as *unset*. Without the guard, app-builder-lib only null-checks — `importCertificate('')` resolves `''` to the project root and the build dies with `"<projectRoot> not a file"`. With no `CSC_LINK` at all, electron-builder falls back to Keychain identity auto-discovery (so local `package:quick` builds still sign when an identity is installed).
|
|
120
|
-
|
|
121
|
-
### 4. Verify
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
npx omega validate-certs
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
This checks:
|
|
128
|
-
- `CSC_LINK` exists and is readable
|
|
129
|
-
- macOS Keychain has a Developer ID Application identity, and ONLY when `CSC_LINK` names no certificate: electron-builder imports a `CSC_LINK` `.p12` into its own temporary keychain, so the login keychain says nothing about that build
|
|
130
|
-
- API key file exists and `APPLE_API_KEY_ID` + `APPLE_API_ISSUER` are set
|
|
131
|
-
- Team ID matches the cert
|
|
132
|
-
|
|
133
|
-
The electron-builder floor is 26.16.1 (the package's peer range, installed by ensure-target for any consumer below it): every earlier build hands the `.p12` password to `security set-key-partition-list`, which wants the temporary keychain's own password, and the macos-26 runner image rejects the mismatch (electron-userland/electron-builder#10066, [#891](https://github.com/Omega-JS-Stack/omega/issues/891)).
|
|
134
|
-
|
|
135
|
-
### 5. Build + notarize
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
npm run release # signs + notarizes + publishes
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
@omega.js/desktop's built-in notarize is wired as electron-builder's `afterSign` hook (via `gulp/build-config`) and calls `@electron/notarize` with the API key creds. Consumers can extend it with an optional `hooks/notarize/post.js` for post-notarization work.
|
|
142
|
-
|
|
143
|
-
## Windows setup
|
|
144
|
-
|
|
145
|
-
### Self-hosted EV USB token (recommended for v1)
|
|
146
|
-
|
|
147
|
-
Buy an EV code-signing cert from Sectigo, DigiCert, SSL.com, etc. Get a physical USB token (you can't transfer EV certs).
|
|
148
|
-
|
|
149
|
-
1. Plug the token into a Windows machine that's registered as a self-hosted GitHub Actions runner labeled `windows`, `self-hosted`, `ev-token`.
|
|
150
|
-
2. Install the token's middleware (SafeNet, etc.) on that machine.
|
|
151
|
-
3. Set `.env` (on the runner):
|
|
152
|
-
```
|
|
153
|
-
# config: { signing: { windows: { strategy: 'self-hosted' } } }
|
|
154
|
-
WIN_EV_TOKEN_PATH=<cert SHA1 thumbprint, or a path to a .pfx>
|
|
155
|
-
WIN_CSC_KEY_PASSWORD=<token PIN>
|
|
156
|
-
```
|
|
157
|
-
4. The `windows-sign` job in `.github/workflows/build.yml` will route signing to that runner.
|
|
158
|
-
|
|
159
|
-
Setting the box up is [`docs/runner.md`](runner.md) — the runner home, the Startup-folder auto-start, the commands, and the Session 0 / Session 1 rule that makes the whole thing work.
|
|
160
|
-
|
|
161
|
-
**A workflow that targets this runner fires only on dispatch**, and the box enforces it ([#875](https://github.com/Omega-JS-Stack/omega/issues/875)). Whoever can start a job on the signing box can sign binaries with your token, so two guards stand between a stray trigger and `signtool`:
|
|
162
|
-
|
|
163
|
-
- The generated `windows-sign` job runs only on `workflow_dispatch`, the one trigger the file carries (#923). A `push` or `pull_request` trigger added to `build.yml` later SKIPS the sign job instead of signing what it built.
|
|
164
|
-
- The runner itself refuses the job before its first step. `npx omega runner install` writes `job-started.js` beside the runner and points every registration's `ACTIONS_RUNNER_HOOK_JOB_STARTED` at it; the hook exits nonzero, failing the job with one line, unless the event is a dispatch, `GITHUB_REPOSITORY` is on `allowed-repos.txt` and `GITHUB_ACTOR` is on `allowed-actors.txt`. Both lists live on the box and are yours to edit ([`docs/runner.md`](runner.md) § One-time setup).
|
|
165
|
-
|
|
166
|
-
`npx omega sign-windows` is the strategy-aware command that drives `signtool` (or the cloud provider CLI).
|
|
167
|
-
|
|
168
|
-
Every signed artifact is verified before the job moves on, and the log says so in the mac lane's shape: `Signed and verified: signtool verify /pa accepts <file>.` mirrors macOS's `Stapled and verified: ...`, so a deploy log proves the Windows signature was checked rather than only claimed ([#918](https://github.com/Omega-JS-Stack/omega/issues/918)). A failing verify aborts the job.
|
|
169
|
-
|
|
170
|
-
### Cloud signing (future migration)
|
|
171
|
-
|
|
172
|
-
Once the framework's cloud strategy is finalized, set the provider in `config/omega.json5`:
|
|
173
|
-
```
|
|
174
|
-
# config: { targets: { win: { signing: { strategy: 'cloud', cloud: { provider: 'azure' } } } } }
|
|
175
|
-
# provider: 'azure' | 'sslcom' | 'digicert'
|
|
176
|
-
# Provider-specific creds (secrets) live in .env Custom Values section.
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
Provider modules will live in `src/lib/sign-providers/{azure,sslcom,digicert}.js` (Pass 3 work).
|
|
180
|
-
|
|
181
|
-
### Local (developer-machine) fallback
|
|
182
|
-
|
|
183
|
-
If no signing runner is available, CI uploads the unsigned `.exe` and a developer manually signs locally:
|
|
184
|
-
```
|
|
185
|
-
# config: { signing: { windows: { strategy: 'local' } } }
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## Pushing secrets to GitHub Actions
|
|
189
|
-
|
|
190
|
-
`omega deploy`'s precheck pushes the target's **composed env** to the repo's GitHub Actions secrets over the same shared `gh` transport the other three frameworks use (`gh secret set`, which seals each value with the repo's public key locally): values travel on **stdin**, never in argv, and are never logged. For env vars whose value is a path to a local file (`.p12`, `.p8`, etc.), the secret value pushed is the **base64-encoded file contents**: the workflow decodes back to a temp file at job start.
|
|
191
|
-
|
|
192
|
-
There is no standalone `omega push-secrets` verb ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): the push happens where it matters, and the manage walk's `repo` service runs the same function when you want a brand brought current without deploying.
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
# Make sure the brand root's .env holds CSC_KEY_PASSWORD + the APPLE_* three, and `gh auth login` has run
|
|
196
|
-
npx omega deploy
|
|
197
|
-
|
|
198
|
-
# See exactly what it would publish, and publish nothing
|
|
199
|
-
npx omega deploy --dry-run
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
Behavior:
|
|
203
|
-
- The source is `composeTargetEnv({ targetDir, target: 'desktop' })`: company `.env` ← brand `.env` ← target `.env`, the brand-side layers filtered by the env schema to the keys the desktop target reads. The brand root's `.env` is the one file you keep; a target `.env` is an optional per-key override.
|
|
204
|
-
- Files only — the shell is never a source — and an empty value never claims a key, so unset keys simply don't publish.
|
|
205
|
-
- Auto-detects "is this a path?" — relative or absolute paths ending in `.p12`/`.pem`/`.cer`/`.p8`/`.provisionprofile`/`.crt`/`.key`/`.json` that exist on disk (target root first, brand root second) get base64-encoded.
|
|
206
|
-
- Publishes to the brand's SOURCE repo (`repo.org` + `brand.id` → `<brand.id>-omega`), and REFUSES unless the checkout's own remote IS that repo: a fork, a template clone or a vendored target never arms a stranger's Actions with your certificates.
|
|
207
|
-
- No PAT: the credential is `gh`'s auth session. A missing or signed-out `gh` fails loudly with install/auth instructions; a CI run, an empty cascade or a remote-less checkout skips loudly, and a checkout whose `origin` is not the derived source repo refuses on the one drift line (`origin is <slug> but config derives <derived>: ...`, [#934](https://github.com/Omega-JS-Stack/omega/issues/934)).
|
|
208
|
-
- `CSC_LINK` and `APPLE_API_KEY` are DERIVED, never typed ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): the env load resolved them from the signing tree, so the publish sends those files' bytes. A typed value still wins, and a key the config REQUIRES that neither the tree nor the `.env` answers STOPS the deploy (the step is `fatal`), naming both tier paths it looked in and `omega manage --service certificates`, publishing nothing.
|
|
209
|
-
- Logs key NAMES only; never a value.
|
|
210
|
-
|
|
211
|
-
The corresponding decode step in CI looks like:
|
|
212
|
-
|
|
213
|
-
```yaml
|
|
214
|
-
- name: Decode signing assets
|
|
215
|
-
run: |
|
|
216
|
-
mkdir -p config/certs
|
|
217
|
-
echo "${{ secrets.CSC_LINK }}" | base64 --decode > config/certs/dev-id.p12
|
|
218
|
-
echo "${{ secrets.APPLE_API_KEY }}" | base64 --decode > config/certs/AuthKey.p8
|
|
219
|
-
env:
|
|
220
|
-
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
|
221
|
-
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
|
222
|
-
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
|
223
|
-
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
224
|
-
- name: Build and release
|
|
225
|
-
run: npm run release
|
|
226
|
-
env:
|
|
227
|
-
CSC_LINK: config/certs/dev-id.p12
|
|
228
|
-
APPLE_API_KEY: config/certs/AuthKey.p8
|
|
229
|
-
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
This is wired up automatically in the `.github/workflows/build.yml` template (lands in Pass 3).
|
|
233
|
-
|
|
234
|
-
## CI integration
|
|
235
|
-
|
|
236
|
-
GitHub Actions secrets you'll need (per repo):
|
|
237
|
-
|
|
238
|
-
| Secret | Purpose |
|
|
239
|
-
|---|---|
|
|
240
|
-
| `GH_TOKEN` | Cross-repo publish, secret rotation |
|
|
241
|
-
| `CSC_LINK` | base64-encoded `.p12` (workflow decodes to file) |
|
|
242
|
-
| `CSC_KEY_PASSWORD` | `.p12` password |
|
|
243
|
-
| `APPLE_API_KEY` | base64-encoded `.p8` |
|
|
244
|
-
| `APPLE_API_KEY_ID` | Plain string |
|
|
245
|
-
| `APPLE_API_ISSUER` | UUID |
|
|
246
|
-
| `APPLE_TEAM_ID` | 10-char team ID |
|
|
247
|
-
| `WIN_EV_TOKEN_PATH` | Windows EV cert reference — SHA1 thumbprint or `.pfx` path (the ONE name; the `WIN_CSC_LINK` alias is gone) |
|
|
248
|
-
| `WIN_CSC_KEY_PASSWORD` | EV token PIN / `.pfx` password |
|
|
249
|
-
| `SIGNTOOL_PATH` | Full path to `signtool.exe` on the signing runner |
|
|
250
|
-
|
|
251
|
-
The workflow base64-decodes secrets into temp files inside `config/certs/` at job start, runs the build, and the runner's ephemeral filesystem cleans up afterward. Local `.env` files are never committed and never shipped.
|
|
252
|
-
|
|
253
|
-
## Troubleshooting
|
|
254
|
-
|
|
255
|
-
### "Could not find a certificate" on macOS
|
|
256
|
-
- Run `security find-identity -v -p codesigning` — you should see "Developer ID Application: <your name> (TEAMID)".
|
|
257
|
-
- If absent: re-import your `.p12` to Keychain Access and make sure the private key is included.
|
|
258
|
-
|
|
259
|
-
### Notarization timeout
|
|
260
|
-
- Confirm `APPLE_API_KEY` is the absolute path (or relative to repo root) and the file is readable.
|
|
261
|
-
- Confirm `APPLE_API_KEY_ID` matches the `XXXXXXXXXX` portion of the `AuthKey_XXXXXXXXXX.p8` filename.
|
|
262
|
-
- Confirm `APPLE_API_ISSUER` is the issuer UUID from App Store Connect → Users and Access → Keys.
|
|
263
|
-
|
|
264
|
-
### "Hardened runtime requires entitlements"
|
|
265
|
-
- @omega.js/desktop generates `dist/config/entitlements.mac.plist` from defaults + your overrides at build time.
|
|
266
|
-
- Defaults cover Electron's needs (allow-jit, network client/server, library validation off, etc.).
|
|
267
|
-
- To override or add: set the `entitlements.mac` block in `config/omega.json5`. Setting a key to `null` removes a default.
|
|
268
|
-
```json5
|
|
269
|
-
entitlements: {
|
|
270
|
-
mac: {
|
|
271
|
-
'com.apple.security.cs.allow-jit': false, // override default
|
|
272
|
-
'com.apple.security.device.camera': true, // add a key
|
|
273
|
-
'com.apple.security.network.server': null, // remove a default
|
|
274
|
-
},
|
|
275
|
-
}
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
### Windows: "SignTool Error: No certificates were found"
|
|
279
|
-
- Token unplugged or middleware not running.
|
|
280
|
-
- For cloud: verify `platforms.windows.signing.cloud.provider` in `config/omega.json5` matches the provider whose creds are in `.env`.
|
|
281
|
-
|
|
282
|
-
## What lives in `build/`
|
|
283
|
-
|
|
284
|
-
`build/` in a consumer project holds **only** code-signing certificate files. Everything else (entitlements, icons, electron-builder config) is generated by @omega.js/desktop into `dist/config/` at build time.
|
|
285
|
-
|
|
286
|
-
- **`config/certs/`** — `.p12`, `.p8`, `.cer`, `.mobileprovision` files. Per-developer / per-CI-runner. **Never commit** — `.gitignore` blocks them.
|
|
287
|
-
- **Nothing else.** `entitlements.mac.plist` is generated. App icons + DMG background + tray icons resolve from `config/icons/` (consumer override) → @omega.js/desktop's bundled defaults. `electron-builder.yml` is generated.
|
|
288
|
-
|
|
289
|
-
The only file you usually create yourself in `build/` is the cert files in `config/certs/`. See [`config/certs/README.md`](../src/defaults/config/certs/README.md) for the full inventory.
|
|
290
|
-
|
|
291
|
-
## Related docs
|
|
292
|
-
|
|
293
|
-
- [`config/certs/README.md`](../src/defaults/config/certs/README.md) — cert file inventory
|
package/docs/startup.md
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# Startup
|
|
2
|
-
|
|
3
|
-
Controls how the app launches: full normal launch vs completely hidden background app. Also handles open-at-login.
|
|
4
|
-
|
|
5
|
-
## Config
|
|
6
|
-
|
|
7
|
-
```jsonc
|
|
8
|
-
"startup": {
|
|
9
|
-
"mode": "normal", // user-launch behavior: 'normal' | 'hidden'
|
|
10
|
-
"openAtLogin": {
|
|
11
|
-
"enabled": true, // OS auto-launches the app at login
|
|
12
|
-
"mode": "hidden" // login-launch behavior (defaults to 'hidden')
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
`mode` is what happens when **the user launches the app directly** (clicks the dock icon / Start menu / etc.).
|
|
18
|
-
|
|
19
|
-
`openAtLogin.mode` is what happens when **the OS auto-launches the app at login**. It applies *only* when the launch is detected as a login-launch (macOS: `wasOpenedAtLogin` flag; Windows/Linux: presence of the `--omega-launched-at-login` arg @omega.js/desktop passes when registering the login item).
|
|
20
|
-
|
|
21
|
-
The default behavior — `mode: 'normal'` + `openAtLogin: { enabled: true, mode: 'hidden' }` — means: the app auto-starts at login but stays out of the way until the user opens it themselves. User-direct launches show the main window like any normal app.
|
|
22
|
-
|
|
23
|
-
## Modes
|
|
24
|
-
|
|
25
|
-
### `normal` (default)
|
|
26
|
-
|
|
27
|
-
Standard app behavior. Your `main.js` calls `windows.create('main', { show: !startup.isLaunchHidden() })` from inside `omega.initialize().then(...)`. In `normal` mode, `show` is `true` so the window appears immediately. Dock visible (macOS), taskbar entry (win/linux).
|
|
28
|
-
|
|
29
|
-
### `hidden`
|
|
30
|
-
|
|
31
|
-
App launches **completely invisible**: no dock icon, no Cmd+Tab presence, no taskbar entry. Tray + notifications + networking + IPC + auto-update all still work — only the visible UI is suppressed. Production builds inject `LSUIElement: true` into `Info.plist` (via `gulp/build-config`) so macOS treats the process as a background agent from launch — **zero dock bounce**.
|
|
32
|
-
|
|
33
|
-
The `main` window is still created (just with `show: false`) so it sits in @omega.js/desktop's window registry. When the user double-clicks the running app's icon, @omega.js/desktop's `app.on('activate')` (macOS) / `app.on('second-instance')` (win/linux) handler finds `main` in the registry and calls `windows.show('main')` — which auto-runs `app.dock.show()` so the dock icon appears alongside the window. Same thing happens when the consumer manually surfaces UI from a tray click / deep-link / IPC event via `windows.show('main')`. The reverse is automatic too: hiding the last visible window (including the hide-on-close X) re-runs `app.dock.hide()`, so a hidden-mode app never strands a dock icon after its UI is dismissed.
|
|
34
|
-
|
|
35
|
-
Use this for: menubar apps, agent apps (clipboard managers, time trackers, system monitors), apps that should be invisible at boot but available on demand.
|
|
36
|
-
|
|
37
|
-
**Dev caveat**: in dev (`npm start` / `electron .`), the packaged `Info.plist` isn't in effect, so macOS will still briefly bounce. Production builds (`npm run build` / `npm run release`) get the real zero-bounce behavior.
|
|
38
|
-
|
|
39
|
-
> **Note:** the deprecated `'tray-only'` mode is no longer valid — its behavior was always identical to `'hidden'`, so they've been folded into one. Old `tray-only` configs fall back to `'normal'` per `getMode()` validation.
|
|
40
|
-
|
|
41
|
-
## Public API on `omega.startup`
|
|
42
|
-
|
|
43
|
-
```js
|
|
44
|
-
omega.startup.getMode() // user-launch mode: 'normal' | 'hidden'
|
|
45
|
-
omega.startup.isLaunchHidden() // true if THIS launch is hidden: combines
|
|
46
|
-
// user-launch mode + login-launch detection.
|
|
47
|
-
// Use this in main.js to gate windows.create().
|
|
48
|
-
omega.startup.wasLaunchedAtLogin() // true if the OS auto-launched us at login
|
|
49
|
-
omega.startup.applyEarly() // calls app.dock.hide() if needed (called by main.js boot)
|
|
50
|
-
|
|
51
|
-
omega.startup.setOpenAtLogin(true) // back-compat boolean form
|
|
52
|
-
omega.startup.setOpenAtLogin({ enabled: true, mode: 'hidden' }) // object form
|
|
53
|
-
omega.startup.isOpenAtLogin() // read live OS state
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Typical main.js pattern
|
|
57
|
-
|
|
58
|
-
```js
|
|
59
|
-
omega.initialize().then(() => {
|
|
60
|
-
// Always create the main window. In hidden launches, `show: false` keeps it
|
|
61
|
-
// invisible until something explicitly calls windows.show('main') — but it's
|
|
62
|
-
// in the registry, so @omega.js/desktop's activate/second-instance handlers can find and
|
|
63
|
-
// surface it when the user double-clicks the running app.
|
|
64
|
-
omega.windows.create('main', {
|
|
65
|
-
show: !omega.startup.isLaunchHidden(),
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Don't conditionally skip `create()` for hidden launches — without `main` in the registry, the dock-click / re-launch handlers have nothing to surface, and the user double-clicking the running app appears to do nothing.
|
|
71
|
-
|
|
72
|
-
## Boot order
|
|
73
|
-
|
|
74
|
-
`startup.applyEarly()` is the **first** call in `omega.initialize()`: before `whenReady`, before any other lib. The goal: spend as little time as possible in the dock-bounce window.
|
|
75
|
-
|
|
76
|
-
Sequence: applyEarly → before-quit hook → ipc → storage → theme → fontawesome → sentry → protocol → deep-link → auth-flow → app-state → context → usage → whenReady → updater → tray/menu/contextMenu → startup.initialize → auth → remote-config → remote-scripts → analytics → restart-manager → windows.initialize (full list: [boot-sequence.md](boot-sequence.md)). **@omega.js/desktop does not auto-create the main window**: your `main.js` does that inside the `.then()` callback after `initialize()` resolves.
|
|
77
|
-
|
|
78
|
-
## How zero-bounce works on macOS
|
|
79
|
-
|
|
80
|
-
`LSUIElement` is an `Info.plist` key that tells macOS *before launch* "this app is a background agent — don't put it in the dock or app switcher." Setting it at runtime (`app.dock.hide()`) is too late — by the time JS runs, the dock-bounce animation has already started.
|
|
81
|
-
|
|
82
|
-
@omega.js/desktop handles this at build time:
|
|
83
|
-
1. `gulp/build-config` reads `config/omega.json5`.
|
|
84
|
-
2. If `startup.mode === 'hidden'` **or** `startup.openAtLogin.mode === 'hidden'`, it injects `mac.extendInfo.LSUIElement: true` into the materialized `dist/electron-builder.yml`. (The openAtLogin case matters for `mode: 'normal'` apps that launch hidden at login — without the plist key, the login launch flashes the dock before `applyEarly()`'s `dock.hide()` can run.)
|
|
85
|
-
3. `electron-builder` packages the app with that key in the final `Info.plist`.
|
|
86
|
-
|
|
87
|
-
With the key baked, a MANUAL launch also starts dockless — the dock icon appears the moment the main window surfaces (every surface path runs `_ensureDockVisible()` → `app.dock.show()`), so the visible difference is only that the bounce animation is replaced by the icon appearing when the window is ready.
|
|
88
|
-
|
|
89
|
-
At runtime, when the consumer first calls `omega.windows.show()` (or the `windows.create()` call resolves with `show: true`), @omega.js/desktop calls `app.dock.show()` so the dock icon appears alongside the window. Reverses cleanly via `app.dock.hide()` if you want to go back to invisible.
|
|
90
|
-
|
|
91
|
-
The injection is YAML-text-level (preserves comments, idempotent, merges with existing `extendInfo`). See `src/gulp/tasks/build-config.js`.
|
|
92
|
-
|
|
93
|
-
## Re-surfacing on user re-launch
|
|
94
|
-
|
|
95
|
-
When the user double-clicks a running hidden-mode app (or clicks its dock icon on macOS), @omega.js/desktop transparently surfaces the main window — no consumer wiring needed. Mechanisms:
|
|
96
|
-
|
|
97
|
-
- **macOS**: `window-manager.initialize()` registers `app.on('activate')` which calls `windows.show('main')` if `main` is in the registry.
|
|
98
|
-
- **Windows / Linux**: `deep-link.initialize()` registers `app.on('second-instance')` which does the same. (The OS spawns a duplicate process, the single-instance lock kills it, and forwards its argv to the original instance.)
|
|
99
|
-
|
|
100
|
-
Both handlers are no-ops if `main` isn't in the registry, so consumers who genuinely never want a window can omit `windows.create('main', ...)` entirely. Otherwise, with the canonical pattern (`windows.create('main', { show: !isLaunchHidden() })`), hidden-mode apps come back to life on a second click — like CleanMyMac, Rectangle, etc.
|
|
101
|
-
|
|
102
|
-
## Testing the login-launch path locally
|
|
103
|
-
|
|
104
|
-
Pass `--omega-launched-at-login` as a command-line arg when launching the .app; @omega.js/desktop treats it identically to a real OS-driven login launch (`startup.wasLaunchedAtLogin()` returns `true`, with `via:argv-flag` in the boot summary log). Useful for testing hidden-mode behavior without configuring login items + rebooting.
|
|
105
|
-
|
|
106
|
-
The easiest way is `mgr launch`, which auto-strips `ELECTRON_RUN_AS_NODE` and uses `open -n` under the hood:
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
# Auto-discover the most recent `mgr package:quick` build:
|
|
110
|
-
npx omega launch --args="--omega-launched-at-login"
|
|
111
|
-
|
|
112
|
-
# Or pass an explicit path:
|
|
113
|
-
npx omega launch /Applications/MyApp.app --args="--omega-launched-at-login"
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
If you'd rather call `open` directly, remember to strip `ELECTRON_RUN_AS_NODE` first (the variable leaks into shells from common host processes like VS Code's Claude Code extension and silently breaks Electron):
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
unset ELECTRON_RUN_AS_NODE
|
|
120
|
-
open -n /path/to/MyApp.app --args --omega-launched-at-login
|
|
121
|
-
|
|
122
|
-
# Windows / Linux — direct binary launch
|
|
123
|
-
"/path/to/MyApp.exe" --omega-launched-at-login
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
The boot summary log written by the `startup` lib distinguishes a real login launch (`via:macos-wasOpenedAtLogin`) from a flag-based simulation (`via:argv-flag`).
|
|
127
|
-
|
|
128
|
-
## Pairing with tray/window patterns
|
|
129
|
-
|
|
130
|
-
Hidden / agent apps usually want:
|
|
131
|
-
|
|
132
|
-
```jsonc
|
|
133
|
-
"startup": { "mode": "hidden" }
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
And in `src/integrations/tray/index.js`:
|
|
137
|
-
|
|
138
|
-
```js
|
|
139
|
-
tray.update('open', { click: () => omega.windows.show('main') });
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The window is created at boot but invisible. When the user clicks the tray's "Open" item (or double-clicks the app icon), `windows.show('main')` runs, @omega.js/desktop calls `app.dock.show()`, and the user sees both the dock icon and the window appear together.
|
package/docs/storage.md
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# Storage
|
|
2
|
-
|
|
3
|
-
Persistent KV store accessible from both main and renderer. Backed by [`electron-store`](https://github.com/sindresorhus/electron-store) under the hood.
|
|
4
|
-
|
|
5
|
-
## File location
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
macOS: ~/Library/Application Support/<productName>/omega-storage.json
|
|
9
|
-
Windows: %APPDATA%/<productName>/omega-storage.json
|
|
10
|
-
Linux: ~/.config/<productName>/omega-storage.json
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Main-process API (sync, direct disk-backed)
|
|
14
|
-
|
|
15
|
-
```js
|
|
16
|
-
omega.storage.get(key, defaultValue) // any
|
|
17
|
-
omega.storage.set(key, value)
|
|
18
|
-
omega.storage.delete(key)
|
|
19
|
-
omega.storage.has(key) // boolean
|
|
20
|
-
omega.storage.clear()
|
|
21
|
-
omega.storage.onChange(key, fn) // returns unsubscribe fn
|
|
22
|
-
omega.storage.getPath() // absolute path to omega-storage.json
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Renderer-process API (async, proxied through preload + IPC)
|
|
26
|
-
|
|
27
|
-
```js
|
|
28
|
-
await window.desktop.storage.get(key, defaultValue)
|
|
29
|
-
await window.desktop.storage.set(key, value)
|
|
30
|
-
await window.desktop.storage.delete(key)
|
|
31
|
-
await window.desktop.storage.has(key)
|
|
32
|
-
await window.desktop.storage.clear()
|
|
33
|
-
|
|
34
|
-
const off = window.desktop.storage.onChange(key, ({ value, previous }) => { ... });
|
|
35
|
-
// pass '*' as key to receive all changes
|
|
36
|
-
off();
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Dot-notation paths
|
|
40
|
-
|
|
41
|
-
Keys support dot-notation for nested objects natively:
|
|
42
|
-
|
|
43
|
-
```js
|
|
44
|
-
omega.storage.set('window.main.bounds', { x: 10, y: 20, w: 800, h: 600 });
|
|
45
|
-
omega.storage.get('window.main.bounds.w'); // → 800
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Change broadcasts
|
|
49
|
-
|
|
50
|
-
Every `set` / `delete` / `clear` in main broadcasts an `desktop:storage:change` IPC event to all renderer windows. The renderer's `window.desktop.storage.onChange` filters by key locally.
|
|
51
|
-
|
|
52
|
-
In main, `omega.storage.onChange(key, fn)` registers a callback fired with `(value, previous)`.
|
|
53
|
-
|
|
54
|
-
## Implementation notes
|
|
55
|
-
|
|
56
|
-
- Storage initialization is async: `omega.initialize()` `await`s it before any other lib boots, since features like `app-state` and `windows` rely on it.
|
|
57
|
-
- IPC handlers (`desktop:storage:get` etc.) are registered on the @omega.js/desktop `ipc` bus, not directly on `ipcMain`. See [ipc.md](ipc.md).
|
|
58
|
-
- The store uses `name: 'omega-storage'` (filename `omega-storage.json`). Don't reuse this name in a separate `electron-store` instance.
|
|
59
|
-
- `electron-store@11` is ESM-only. The bundler inlines it INTO `main.bundle.js` (the static-specifier `import()` in `lib/storage.js`) — consumers install NOTHING; packaged apps carry it inside the bundle with no runtime resolution. (It used to be a runtime import the bundler was told to ignore, which silently no-op'd storage in packaged consumers — @omega.js/desktop is a devDependency and never ships in the asar.)
|
package/docs/templating.md
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# Templating
|
|
2
|
-
|
|
3
|
-
Light token-replacement engine for HTML pages. Uses `{{ var }}` syntax, dot-notation paths, brand/app values from config.
|
|
4
|
-
|
|
5
|
-
## How it works
|
|
6
|
-
|
|
7
|
-
1. Consumer authors `src/views/<name>/index.html` as the **body** of the page (no `<html>`, `<head>`, `<body>` tags).
|
|
8
|
-
2. @omega.js/desktop ships a default page template at `<em>/dist/config/page-template.html`. Consumers can override with their own at `<consumer>/config/page-template.html` if they want to.
|
|
9
|
-
3. At build time, `gulp/html`:
|
|
10
|
-
- Reads each `src/views/<name>/index.html`
|
|
11
|
-
- Templates its body with the page vars (so the body can use `{{ brand.name }}` etc.)
|
|
12
|
-
- Injects the rendered body into the page template's `{{ content }}` slot
|
|
13
|
-
- Writes the final HTML to `dist/views/<name>/index.html`
|
|
14
|
-
4. The page template auto-includes:
|
|
15
|
-
- `assets/css/main.bundle.css` (compiled from `src/assets/scss/main.scss` by gulp/sass) — present on every page
|
|
16
|
-
- `assets/js/components/<page.name>.bundle.js` (per-view renderer bundle) — only the JS for this page
|
|
17
|
-
|
|
18
|
-
## Page name
|
|
19
|
-
|
|
20
|
-
`page.name` is derived from the view's path under `src/views/`:
|
|
21
|
-
- `src/views/main/index.html` → `page.name = 'main'`
|
|
22
|
-
- `src/views/settings/index.html` → `page.name = 'settings'`
|
|
23
|
-
- `src/views/blog/post.html` → `page.name = 'blog/post'`
|
|
24
|
-
|
|
25
|
-
This naming lines up with the renderer bundle's entry naming so the JS bundle path resolves correctly.
|
|
26
|
-
|
|
27
|
-
## Page template variables
|
|
28
|
-
|
|
29
|
-
| Var | Source | Example |
|
|
30
|
-
|---|---|---|
|
|
31
|
-
| `{{ brand.id }}`, `{{ brand.name }}`, `{{ brand.url }}` | `config.brand.*` | `myapp` / `MyApp` |
|
|
32
|
-
| `{{ app.productName }}`, `{{ app.appId }}`, `{{ app.copyright }}` | `config.app.*` | `MyApp` / `com.itwcw.myapp` |
|
|
33
|
-
| `{{ page.name }}` | derived from view path | `main` / `settings` |
|
|
34
|
-
| `{{ page.title }}` | `extras.title` (in gulp/html) or `app.productName` | `MyApp` |
|
|
35
|
-
| `{{ theme.appearance }}` | `config.theme.appearance` (default `'system'`) | `light` / `dark` / `system` — runtime applier replaces with the resolved value ([themes.md](themes.md)) |
|
|
36
|
-
| `{{ cacheBust }}` | build timestamp | `1777515223640` |
|
|
37
|
-
| `{{ content }}` | rendered body (set by gulp/html) | `<main>...</main>` |
|
|
38
|
-
|
|
39
|
-
## Default page template
|
|
40
|
-
|
|
41
|
-
```html
|
|
42
|
-
<!doctype html>
|
|
43
|
-
<html data-bs-theme="{{ theme.appearance }}">
|
|
44
|
-
<head>
|
|
45
|
-
<meta charset="utf-8">
|
|
46
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
47
|
-
<title>{{ page.title }}</title>
|
|
48
|
-
<link href="../../assets/css/main.bundle.css?cb={{ cacheBust }}" rel="stylesheet">
|
|
49
|
-
</head>
|
|
50
|
-
<body>
|
|
51
|
-
{{ content }}
|
|
52
|
-
<script src="../../assets/js/components/{{ page.name }}.bundle.js?cb={{ cacheBust }}"></script>
|
|
53
|
-
</body>
|
|
54
|
-
</html>
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Overriding the page template
|
|
58
|
-
|
|
59
|
-
Drop your own `config/page-template.html` in your project root. @omega.js/desktop picks it up before falling back to its own default.
|
|
60
|
-
|
|
61
|
-
```html
|
|
62
|
-
<!-- consumer/config/page-template.html -->
|
|
63
|
-
<!doctype html>
|
|
64
|
-
<html data-bs-theme="{{ theme.appearance }}" lang="en">
|
|
65
|
-
<head>
|
|
66
|
-
<meta charset="utf-8">
|
|
67
|
-
<title>{{ page.title }} | {{ brand.name }}</title>
|
|
68
|
-
<link href="../../assets/css/main.bundle.css?cb={{ cacheBust }}" rel="stylesheet">
|
|
69
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700" rel="stylesheet">
|
|
70
|
-
</head>
|
|
71
|
-
<body class="custom-shell">
|
|
72
|
-
{{ content }}
|
|
73
|
-
<script src="../../assets/js/components/{{ page.name }}.bundle.js?cb={{ cacheBust }}"></script>
|
|
74
|
-
</body>
|
|
75
|
-
</html>
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Runtime API
|
|
79
|
-
|
|
80
|
-
`omega.templating` is also available at runtime if you need to template a string yourself (e.g. dynamic deep-link routes):
|
|
81
|
-
|
|
82
|
-
```js
|
|
83
|
-
omega.templating.render('Hello {{ user.name }}', { user: { name: 'Ian' } });
|
|
84
|
-
// → 'Hello Ian'
|
|
85
|
-
|
|
86
|
-
omega.templating.render('Custom [name]', { name: 'X' }, { brackets: ['[', ']'] });
|
|
87
|
-
// → 'Custom X'
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Future
|
|
91
|
-
|
|
92
|
-
The current page template hardcodes the asset paths. A future pass adds:
|
|
93
|
-
- Per-page CSS bundles (currently only one shared `main.bundle.css`)
|
|
94
|
-
- Source map references in dev mode
|
|
95
|
-
- Inline critical CSS for fast first paint
|
|
96
|
-
|
|
97
|
-
For theme-related styling (Bootstrap, classy theme), see [docs/themes.md](themes.md) when that pass lands.
|
|
98
|
-
|
|
99
|
-
## Tests
|
|
100
|
-
|
|
101
|
-
`src/test/suites/build/templating.test.js` — render, dot-notation, custom brackets, buildPageVars, renderPage end-to-end. 8 tests.
|