@omega.js/desktop 0.50.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/assets/css/tokens/_index.scss +1 -1
- package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +4 -1
- package/dist/assets/themes/base/_includes/frontend/sections/footer.html +13 -6
- package/dist/assets/themes/base/_includes/frontend/sections/nav.html +10 -8
- package/dist/assets/themes/base/_includes/global/sections/account.html +3 -1
- package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +12 -8
- package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +10 -6
- package/dist/assets/themes/base/_includes/global/sections/page-header.html +8 -4
- package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +24 -24
- package/dist/assets/themes/base/_layouts/frontend/pages/about.html +10 -10
- package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +35 -35
- package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +4 -4
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +4 -4
- package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +3 -3
- package/dist/assets/themes/base/_layouts/frontend/pages/download.html +34 -33
- package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +11 -11
- package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/status.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +9 -7
- package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +5 -3
- package/dist/assets/themes/base/_sections/about/letter/section.html +1 -1
- package/dist/assets/themes/base/_sections/about/letter/section.json5 +4 -4
- package/dist/assets/themes/base/_sections/marketing/bento/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +15 -15
- package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +1 -1
- package/dist/assets/themes/base/_sections/marketing/hero/section.html +6 -6
- package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +10 -10
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +2 -2
- package/dist/assets/themes/base/_sections/marketing/stats/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +5 -5
- package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +2 -2
- package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +10 -10
- package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +10 -10
- package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +2 -2
- package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +4 -4
- package/dist/build.js +69 -28
- package/dist/cli-run.js +20 -13
- package/dist/cli.js +12 -7
- package/dist/commands/build.js +0 -2
- package/dist/commands/deploy.js +116 -44
- package/dist/commands/finalize-release.js +5 -4
- package/dist/commands/launch.js +12 -7
- package/dist/commands/lib/deploy-precheck.js +66 -34
- package/dist/commands/lib/ensure-target.js +71 -11
- package/dist/commands/package.js +0 -1
- package/dist/commands/publish.js +11 -4
- package/dist/commands/release.js +99 -252
- package/dist/commands/runner.js +42 -5
- package/dist/commands/sign-windows.js +46 -15
- package/dist/commands/test.js +4 -3
- package/dist/commands/validate-certs.js +291 -115
- package/dist/config/page-template.html +4 -0
- package/dist/defaults/.github/workflows/build.yml +144 -64
- package/dist/defaults/AGENTS.md +3 -2
- package/dist/defaults/_.gitignore +4 -2
- package/dist/defaults/config/certs/README.md +25 -45
- package/dist/defaults/config/omega.json5 +66 -32
- package/dist/defaults/hooks/deploy/pre.js +10 -0
- package/dist/defaults/src/assets/scss/main.scss +12 -2
- package/dist/defaults/src/integrations/tray/index.js +1 -1
- package/dist/gulp/main.js +13 -25
- package/dist/gulp/tasks/audit.js +18 -6
- package/dist/gulp/tasks/build-config.js +181 -60
- package/dist/gulp/tasks/bundle.js +108 -36
- package/dist/gulp/tasks/release.js +86 -4
- package/dist/gulp/tasks/sass.js +11 -0
- package/dist/hooks/lib/notarize-tools.js +137 -0
- package/dist/hooks/notarize-artifacts.js +57 -0
- package/dist/hooks/notarize.js +59 -8
- package/dist/lib/auth-persistence.js +25 -0
- package/dist/lib/client-bridge.js +11 -8
- package/dist/lib/deep-link.js +15 -8
- package/dist/lib/protocol.js +7 -1
- package/dist/lib/restart-manager/install.js +6 -3
- package/dist/lib/sign-helpers/auto-unlock.js +65 -27
- package/dist/lib/sign-helpers/console-lock.js +34 -0
- package/dist/lib/sign-helpers/exec-with-limit.js +68 -0
- package/dist/lib/sign-helpers/resolve-icons.js +6 -4
- package/dist/lib/tray.js +7 -6
- package/dist/main.js +16 -0
- package/dist/preload.js +8 -0
- package/dist/renderer.js +16 -4
- package/dist/runner/job-started.js +104 -0
- package/dist/test/fixtures/consumer-app/config/omega.json5 +7 -0
- package/dist/test/fixtures/consumer-app/package.json +1 -1
- package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +7 -1
- package/dist/test/harness/main-entry.js +2 -1
- package/dist/test/harness/renderer-preload.js +12 -3
- package/dist/test/runners/boot.js +101 -11
- package/dist/test/runners/electron.js +1 -1
- package/dist/test/suites/boot/consumer-app-boots.test.js +54 -0
- package/dist/test/suites/build/audit.test.js +45 -6
- package/dist/test/suites/build/auth-persistence-resolve.test.js +119 -0
- package/dist/test/suites/build/auto-unlock.test.js +105 -0
- package/dist/test/suites/build/boot-runner-timeout.test.js +253 -0
- package/dist/test/suites/build/brand-scss.test.js +106 -0
- package/dist/test/suites/build/build-config.test.js +146 -36
- package/dist/test/suites/build/build-json-bake.test.js +245 -0
- package/dist/test/suites/build/build-verbs.test.js +48 -15
- package/dist/test/suites/build/build-workflow-jobs.test.js +190 -0
- package/dist/test/suites/build/cli.test.js +32 -10
- package/dist/test/suites/build/config-schema.test.js +4 -4
- package/dist/test/suites/build/console-lock.test.js +51 -0
- package/dist/test/suites/build/defaults-scaffold.test.js +71 -2
- package/dist/test/suites/build/deploy-direct.test.js +241 -0
- package/dist/test/suites/build/deploy-dispatch.test.js +287 -0
- package/dist/test/suites/build/deploy-hook.test.js +169 -0
- package/dist/test/suites/build/ensure-target.test.js +14 -2
- package/dist/test/suites/build/env-delivery.test.js +19 -10
- package/dist/test/suites/build/env-watch.test.js +18 -7
- package/dist/test/suites/build/esm-only-dependency.test.js +127 -0
- package/dist/test/suites/build/exec-with-limit.test.js +53 -0
- package/dist/test/suites/build/finalize-release.test.js +2 -2
- package/dist/test/suites/build/get-config.test.js +120 -8
- package/dist/test/suites/build/github-utils.test.js +12 -6
- package/dist/test/suites/build/license-stamp.test.js +6 -4
- package/dist/test/suites/build/manager.test.js +88 -68
- package/dist/test/suites/build/manifest-deps.test.js +116 -0
- package/dist/test/suites/build/merge-line-files.test.js +27 -4
- package/dist/test/suites/build/notarize-artifacts.test.js +135 -0
- package/dist/test/suites/build/notarize-tools.test.js +38 -0
- package/dist/test/suites/build/notarize.test.js +207 -0
- package/dist/test/suites/build/release-pipeline.test.js +38 -60
- package/dist/test/suites/build/release-skipped-upload.test.js +107 -0
- package/dist/test/suites/build/resolve-icons.test.js +35 -35
- package/dist/test/suites/build/runner-job-guard.test.js +182 -0
- package/dist/test/suites/build/runner.test.js +25 -2
- package/dist/test/suites/build/sentry.test.js +8 -3
- package/dist/test/suites/build/setup-scripts.test.js +3 -0
- package/dist/test/suites/build/sign-windows.test.js +92 -8
- package/dist/test/suites/build/test-stealth.test.js +17 -11
- package/dist/test/suites/build/url-helpers.test.js +37 -17
- package/dist/test/suites/build/validate-certs.test.js +428 -55
- package/dist/test/suites/build/validate-config.test.js +3 -3
- package/dist/test/suites/main/auth-flow.test.js +12 -0
- package/dist/test/suites/main/auth-persistence.test.js +14 -17
- package/dist/test/suites/main/auto-updater.test.js +2 -2
- package/dist/test/suites/main/boot-sequence.test.js +1 -1
- package/dist/test/suites/main/client-bridge.integration.test.js +5 -82
- package/dist/test/suites/main/client-bridge.test.js +19 -6
- package/dist/test/suites/main/deep-link.test.js +54 -0
- package/dist/test/suites/main/startup-paths-and-ua.test.js +1 -1
- package/dist/test/suites/main/url-helpers.test.js +81 -72
- package/dist/test/suites/renderer/cross-context-helpers.test.js +19 -16
- package/dist/utils/build-pipeline.js +10 -10
- package/dist/utils/github.js +12 -51
- package/dist/utils/load-env.js +66 -0
- package/dist/utils/mode-helpers.js +43 -111
- package/dist/utils/platform.js +37 -0
- package/dist/utils/runner-env.js +2 -1
- package/dist/utils/runner-job-guard.js +149 -0
- package/dist/utils/ship-keys.js +52 -0
- package/dist/utils/test-stealth.js +5 -3
- package/dist/utils/url-helpers.js +33 -17
- package/dist/vendor/config/bundle-id.js +53 -0
- package/dist/vendor/config/client-config.js +141 -0
- package/dist/vendor/config/company.js +334 -15
- package/dist/vendor/config/dev-facts.js +48 -0
- package/dist/vendor/config/env-delivery.js +231 -9
- package/dist/vendor/config/env-retired.js +137 -0
- package/dist/vendor/config/env-rules.js +22 -3
- package/dist/vendor/config/env-schema.js +234 -117
- package/dist/vendor/config/env.js +55 -26
- package/dist/vendor/config/environment.js +189 -0
- package/dist/vendor/config/hooks.js +13 -11
- package/dist/vendor/config/index.js +121 -44
- package/dist/vendor/config/load.js +366 -115
- package/dist/vendor/config/merge.js +2 -2
- package/dist/vendor/config/order.js +3 -3
- package/dist/vendor/config/platforms.js +276 -0
- package/dist/vendor/config/repo.js +226 -104
- package/dist/vendor/config/retired-keys.js +232 -27
- package/dist/vendor/config/schema.js +525 -99
- package/dist/vendor/config/site-global.js +63 -53
- package/dist/vendor/config/targets.js +187 -0
- package/dist/vendor/config/validate.js +119 -59
- package/dist/vendor/devkit/argv.js +118 -0
- package/dist/vendor/devkit/attach-log-file.js +21 -13
- package/dist/vendor/devkit/brand-tokens.js +278 -0
- package/dist/vendor/devkit/brand-version.js +264 -0
- package/dist/vendor/devkit/build-json.js +91 -0
- package/dist/vendor/devkit/bundle.js +48 -0
- package/dist/vendor/devkit/certificate-expiry.js +108 -0
- package/dist/vendor/devkit/certs.js +16 -194
- package/dist/vendor/devkit/ci-workflows.js +124 -8
- package/dist/vendor/devkit/cli-router.js +3 -3
- package/dist/vendor/devkit/defaults-engine.js +69 -7
- package/dist/vendor/devkit/deploy-follow.js +297 -0
- package/dist/vendor/devkit/deploy-precheck.js +23 -8
- package/dist/vendor/devkit/deploy-record.js +11 -27
- package/dist/vendor/devkit/deploy-snapshot.js +661 -0
- package/dist/vendor/devkit/deploy.js +445 -75
- package/dist/vendor/devkit/git-auth.js +73 -0
- package/dist/vendor/devkit/git-remote.js +95 -0
- package/dist/vendor/devkit/github-repo.js +290 -0
- package/dist/vendor/devkit/local.js +47 -0
- package/dist/vendor/devkit/merge-line-files.js +23 -16
- package/dist/vendor/devkit/omega-bin.js +18 -3
- package/dist/vendor/devkit/pack-local.js +391 -0
- package/dist/vendor/devkit/preludes/index.js +120 -0
- package/dist/vendor/devkit/preludes/origin-heal.js +156 -0
- package/dist/vendor/devkit/service-account.js +43 -0
- package/dist/vendor/devkit/ship-plan.js +112 -0
- package/dist/vendor/devkit/signing-env.js +180 -0
- package/dist/vendor/devkit/signing-tree.js +92 -0
- package/dist/vendor/devkit/target-seams.js +142 -0
- package/dist/vendor/devkit/target-secrets.js +235 -50
- package/dist/vendor/devkit/test/esm-only-fixture.js +48 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/browser.js +19 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/index.js +20 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/package.json +13 -0
- package/dist/vendor/monitoring/env.js +20 -10
- package/dist/vendor/monitoring/main.js +1 -1
- package/dist/vendor/monitoring/preload.js +1 -1
- package/dist/vendor/monitoring/renderer.js +1 -1
- package/docs/analytics.md +1 -1
- package/docs/auto-updater.md +5 -5
- package/docs/boot-sequence.md +1 -1
- package/docs/build-system.md +15 -7
- package/docs/client-bridge.md +9 -7
- package/docs/config-schema.md +4 -4
- package/docs/css.md +8 -2
- package/docs/deep-link.md +12 -4
- package/docs/environment-detection.md +32 -24
- package/docs/hooks.md +3 -1
- package/docs/icons.md +7 -7
- package/docs/index.md +61 -23
- package/docs/installer-options.md +24 -21
- package/docs/logging.md +5 -5
- package/docs/releasing.md +25 -17
- package/docs/runner.md +40 -5
- package/docs/shared/brands.md +12 -6
- package/docs/shared/breaking-changes.md +375 -21
- package/docs/shared/config.md +757 -199
- package/docs/shared/deploys.md +194 -91
- package/docs/shared/icons.md +18 -0
- package/docs/shared/local-dev.md +24 -6
- package/docs/shared/logging.md +9 -6
- package/docs/shared/monitoring.md +27 -13
- package/docs/shared/publishing.md +3 -3
- package/docs/shared/rulings.md +2 -2
- package/docs/shared/testing.md +1 -1
- package/docs/shared/theming.md +26 -1
- package/docs/shared/translation.md +49 -7
- package/docs/shared/updates.md +1 -1
- package/docs/signing.md +59 -33
- package/docs/test-framework.md +10 -5
- package/docs/themes.md +15 -1
- package/package.json +18 -12
- package/bin/omega-desktop +0 -2
- package/dist/commands/push-secrets.js +0 -141
- package/dist/test/suites/build/deliver-certs.test.js +0 -95
- package/dist/test/suites/build/derive-signing-env.test.js +0 -122
- package/dist/test/suites/build/push-secrets.test.js +0 -226
- package/dist/test/suites/build/resolve-signing-cert.test.js +0 -342
- package/dist/utils/deliver-certs.js +0 -69
- package/dist/utils/derive-signing-env.js +0 -56
- package/dist/utils/resolve-signing-cert.js +0 -175
- package/dist/vendor/config/desktop-artifacts.js +0 -110
- package/dist/vendor/config/instances.js +0 -208
- /package/dist/defaults/config/icons/{macos → mac}/dmg.png +0 -0
- /package/dist/defaults/config/icons/{macos → mac}/icon.png +0 -0
- /package/dist/defaults/config/icons/{macos → mac}/tray.png +0 -0
|
@@ -1,31 +1,350 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The company layer's ONE
|
|
2
|
+
* The company layer's ONE resolver
|
|
3
|
+
* ([#677](https://github.com/Omega-JS-Stack/omega/issues/677)).
|
|
3
4
|
*
|
|
4
|
-
* A brand
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* A brand joins its company with ONE typed key, outside `brand` (Ian
|
|
6
|
+
* 2026-09-12: "brand key is for things about this brand, and the
|
|
7
|
+
* parent/company/organization key is OUTSIDE of that"):
|
|
8
|
+
*
|
|
9
|
+
* company: { id: 'itw-creative-works' } // a sub-brand: the parent's brand.id
|
|
10
|
+
* company: { id: 'self' } // the company brand itself
|
|
11
|
+
*
|
|
12
|
+
* The resolver fills that SAME object at load (`{ id, name, url, images,
|
|
13
|
+
* webhooks }`), so one name carries one shape at every level and no reader
|
|
14
|
+
* needs a fallback: a brand with no `company` key resolves to `{ id: null,
|
|
15
|
+
* name: brand.name, url: brand.url, images: {}, webhooks: true }`.
|
|
16
|
+
*
|
|
17
|
+
* `webhooks` is the one key beside `id` a brand TYPES (#677): `false` says the
|
|
18
|
+
* provider ACCOUNT is somebody else's and the manage walk never repoints its
|
|
19
|
+
* one account-level webhook. It is the brand's own statement, so it is read
|
|
20
|
+
* from the brand config at every branch, never from the company.
|
|
21
|
+
*
|
|
22
|
+
* The COMPANY TREE is a `company/` folder INSIDE the parent brand's repo,
|
|
23
|
+
* shaped like a brand (`company/config/omega.json5`, `company/.env`,
|
|
24
|
+
* `company/.omega/certificates/apple/`). Inheritance is ONE rule: a
|
|
25
|
+
* brand-level file the child lacks resolves from the parent's `company/` at the
|
|
26
|
+
* same relative path, through `file(relPath)`. Everything that cascades (the
|
|
27
|
+
* config chain in load.js, the .env chain in env.js, owner hooks in hooks.js,
|
|
28
|
+
* the desktop signing tree) asks that one function, so a NEW kind of inherited
|
|
29
|
+
* file costs zero code.
|
|
30
|
+
*
|
|
31
|
+
* WHERE the parent lives on this machine is a cache, never configuration: the
|
|
32
|
+
* machine registry `~/.omega/brands.json` (`OMEGA_HOME` overrides the home),
|
|
33
|
+
* keyed by `brand.id`, which every loadConfig() refreshes for its own brand.
|
|
34
|
+
* Nobody maintains it, and a parent that has never been loaded here is simply
|
|
35
|
+
* inheritance-off with one loud line.
|
|
36
|
+
*
|
|
37
|
+
* OFF-LAPTOP the tree never travels: the machine that dispatches a deploy
|
|
38
|
+
* resolves the company and writes `config/company-resolved.json5` beside the
|
|
39
|
+
* brand config (@omega.js/devkit's deploy snapshot), and a runner reads that
|
|
40
|
+
* generated layer instead of a registry it has no lines in.
|
|
9
41
|
*/
|
|
10
42
|
|
|
11
43
|
const fs = require('node:fs');
|
|
44
|
+
const os = require('node:os');
|
|
12
45
|
const path = require('node:path');
|
|
46
|
+
const JSON5 = require('json5');
|
|
13
47
|
|
|
14
|
-
//
|
|
15
|
-
|
|
48
|
+
// The company TREE inside the parent brand's repo, and the file an off-laptop
|
|
49
|
+
// run reads the resolved layer from: GENERATED beside the brand config by the
|
|
50
|
+
// deploy snapshot, which is the only place a brand-wide generated file can sit
|
|
51
|
+
// and still ride a mirror (git cannot re-include a path under an excluded
|
|
52
|
+
// directory, and every brand ignores `.omega/` whole).
|
|
53
|
+
const COMPANY_DIR = 'company';
|
|
54
|
+
const COMPANY_RESOLVED_FILE = path.join('config', 'company-resolved.json5');
|
|
55
|
+
|
|
56
|
+
// The machine registry: brand id to { root, name, url, updatedAt }. One file,
|
|
57
|
+
// no other company-shaped state.
|
|
58
|
+
const REGISTRY_FILE = 'brands.json';
|
|
59
|
+
|
|
60
|
+
// `company: { id: 'self' }` is the company brand itself, kept visible on
|
|
61
|
+
// purpose (Ian 2026-09-12: "dont retire parent field for parent brands, i like
|
|
62
|
+
// it visible").
|
|
63
|
+
const SELF = 'self';
|
|
64
|
+
|
|
65
|
+
// The legacy stamp #677 retired, warned about where an owner still carries one.
|
|
66
|
+
const RETIRED_MARKER = path.join('.omega', 'company.json');
|
|
67
|
+
|
|
68
|
+
// One warning per company id per process: a missing parent is a state a whole
|
|
69
|
+
// build runs in, never a per-call event.
|
|
70
|
+
const warned = new Set();
|
|
16
71
|
|
|
17
72
|
/**
|
|
18
|
-
* The
|
|
19
|
-
*
|
|
20
|
-
* @returns {string
|
|
73
|
+
* The machine home OMEGA keeps its per-machine state in. `OMEGA_HOME` moves it
|
|
74
|
+
* (the test lanes point it at a temp dir so a fixture never writes a real line).
|
|
75
|
+
* @returns {string} Absolute path.
|
|
21
76
|
*/
|
|
22
|
-
function
|
|
77
|
+
function omegaHome() {
|
|
78
|
+
return process.env.OMEGA_HOME || path.join(os.homedir(), '.omega');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The machine registry file.
|
|
83
|
+
* @returns {string} Absolute path to `<home>/brands.json`.
|
|
84
|
+
*/
|
|
85
|
+
function registryFile() {
|
|
86
|
+
return path.join(omegaHome(), REGISTRY_FILE);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Parse a JSON/JSON5 file, tolerantly: this whole module is a CACHE plus a
|
|
91
|
+
* parent's own public facts, and neither may ever fail a brand's load.
|
|
92
|
+
* @param {string} file - Absolute path.
|
|
93
|
+
* @returns {object|null} The parsed object, or null.
|
|
94
|
+
*/
|
|
95
|
+
function readJson(file) {
|
|
23
96
|
try {
|
|
24
|
-
const
|
|
25
|
-
return typeof
|
|
97
|
+
const parsed = JSON5.parse(fs.readFileSync(file, 'utf8'));
|
|
98
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
26
99
|
} catch {
|
|
27
100
|
return null;
|
|
28
101
|
}
|
|
29
102
|
}
|
|
30
103
|
|
|
31
|
-
|
|
104
|
+
/**
|
|
105
|
+
* A brand root's authored omega.json5.
|
|
106
|
+
* @param {string|null} root - Brand root.
|
|
107
|
+
* @returns {object|null}
|
|
108
|
+
*/
|
|
109
|
+
function readBrandConfig(root) {
|
|
110
|
+
return root ? readJson(path.join(root, 'config', 'omega.json5')) : null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The whole machine registry.
|
|
115
|
+
* @returns {Object<string, { root: string, name: string, url: string, updatedAt: string }>}
|
|
116
|
+
*/
|
|
117
|
+
function readRegistry() {
|
|
118
|
+
return readJson(registryFile()) || {};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Record (or refresh) ONE brand's line in the machine registry, which is what
|
|
123
|
+
* every `loadConfig()` does for the brand it just loaded: the map of where the
|
|
124
|
+
* brands are on this machine maintains itself.
|
|
125
|
+
*
|
|
126
|
+
* IDEMPOTENT: a line whose root, name and url already match is left untouched
|
|
127
|
+
* (`updatedAt` is not churn). It NEVER throws: a read-only home, a racing
|
|
128
|
+
* writer, or a machine with no home directory at all is a registry that misses,
|
|
129
|
+
* which is inheritance-off, never a failed load.
|
|
130
|
+
*
|
|
131
|
+
* A write also PRUNES the lines whose root is gone: the file is a map of what
|
|
132
|
+
* is on this machine, so a brand that was deleted or moved (and re-recorded
|
|
133
|
+
* under its new root) leaves nothing behind to resolve into.
|
|
134
|
+
*
|
|
135
|
+
* @param {object} entry - The brand's own facts.
|
|
136
|
+
* @param {string} entry.id - `brand.id` (the key).
|
|
137
|
+
* @param {string} entry.root - The brand root on this machine.
|
|
138
|
+
* @param {string} [entry.name] - `brand.name`.
|
|
139
|
+
* @param {string} [entry.url] - `brand.url`.
|
|
140
|
+
* @returns {boolean} True when a line was written.
|
|
141
|
+
*/
|
|
142
|
+
function recordBrand({ id, root, name, url }) {
|
|
143
|
+
if (!id || !root) return false;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const registry = readRegistry();
|
|
147
|
+
const current = registry[id];
|
|
148
|
+
const line = { root, name: name || null, url: url || null };
|
|
149
|
+
|
|
150
|
+
if (current && current.root === line.root && (current.name || null) === line.name && (current.url || null) === line.url) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
registry[id] = { ...line, updatedAt: new Date().toISOString() };
|
|
155
|
+
|
|
156
|
+
const live = Object.fromEntries(Object.entries(registry).filter(([, entry]) => entry && isDir(entry.root)));
|
|
157
|
+
|
|
158
|
+
fs.mkdirSync(omegaHome(), { recursive: true });
|
|
159
|
+
fs.writeFileSync(registryFile(), `${JSON.stringify(live, null, 2)}\n`);
|
|
160
|
+
return true;
|
|
161
|
+
} catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The generated layer an off-laptop run reads: what the dispatching machine
|
|
168
|
+
* resolved, written beside the brand config and carried by the deploy snapshot.
|
|
169
|
+
* @param {string|null} brandRoot
|
|
170
|
+
* @returns {{ config: object, company: object }|null}
|
|
171
|
+
*/
|
|
172
|
+
function readResolvedLayer(brandRoot) {
|
|
173
|
+
const generated = brandRoot ? readJson(path.join(brandRoot, COMPANY_RESOLVED_FILE)) : null;
|
|
174
|
+
return generated && generated.company ? generated : null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** A directory that is really there. */
|
|
178
|
+
function isDir(dir) {
|
|
179
|
+
try {
|
|
180
|
+
return fs.statSync(dir).isDirectory();
|
|
181
|
+
} catch {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* The one line a missing parent prints, once per company id per process.
|
|
188
|
+
* @param {string} id
|
|
189
|
+
*/
|
|
190
|
+
function warnMissing(id) {
|
|
191
|
+
if (warned.has(id)) return;
|
|
192
|
+
warned.add(id);
|
|
193
|
+
console.warn(`Company ${id} is not on this machine: inheritance off. Clone it and run any omega verb inside it once.`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The company tree (`<root>/company`), when the root has one.
|
|
198
|
+
* @param {string|null} root
|
|
199
|
+
* @returns {string|null}
|
|
200
|
+
*/
|
|
201
|
+
function companyDir(root) {
|
|
202
|
+
const dir = root ? path.join(root, COMPANY_DIR) : null;
|
|
203
|
+
return dir && isDir(dir) ? dir : null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The wordmark a company hands its brands: the company brand's OWN wordmark.
|
|
208
|
+
* @param {object} brand - A brand block.
|
|
209
|
+
* @returns {{ wordmark?: string }}
|
|
210
|
+
*/
|
|
211
|
+
function wordmarkOf(brand) {
|
|
212
|
+
const wordmark = brand && brand.images ? brand.images.wordmark : null;
|
|
213
|
+
return wordmark ? { wordmark } : {};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The resolver's answer, built around ONE inheritance rule.
|
|
218
|
+
* @param {object} facts - The company's id/name/url/images/root, the brand's
|
|
219
|
+
* own `webhooks` statement, plus the generated layer's config when there is
|
|
220
|
+
* no tree to read one from.
|
|
221
|
+
* @returns {object} The resolveCompany() shape.
|
|
222
|
+
*/
|
|
223
|
+
function answer({ id = null, name = null, url = null, images = {}, root = null, config = null, configFile = null, webhooks = true }) {
|
|
224
|
+
const dir = companyDir(root);
|
|
225
|
+
|
|
226
|
+
// The WHOLE inheritance rule: a brand-level file the child lacks resolves
|
|
227
|
+
// from the parent's company/ at the same relative path. Nothing per-kind.
|
|
228
|
+
const file = (relPath) => {
|
|
229
|
+
const candidate = dir && relPath ? path.join(dir, relPath) : null;
|
|
230
|
+
return candidate && fs.existsSync(candidate) ? candidate : null;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const layerFile = configFile || file(path.join('config', 'omega.json5'));
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
id,
|
|
237
|
+
name: name || null,
|
|
238
|
+
url: url || null,
|
|
239
|
+
images: images || {},
|
|
240
|
+
// Typed boolean, default true: only an explicit `false` opts out.
|
|
241
|
+
webhooks: webhooks !== false,
|
|
242
|
+
root: root || null,
|
|
243
|
+
dir,
|
|
244
|
+
file,
|
|
245
|
+
// The company CONFIG layer itself, already parsed: read from the parent's
|
|
246
|
+
// company/config/omega.json5 here, handed over by the generated layer on a
|
|
247
|
+
// runner.
|
|
248
|
+
config: config || (layerFile ? readJson(layerFile) : null),
|
|
249
|
+
configFile: layerFile || null,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The company a brand belongs to, filled from its typed `company.id`.
|
|
255
|
+
*
|
|
256
|
+
* @param {string} brandRoot - The brand (or standalone-project) root.
|
|
257
|
+
* @param {object} [brandConfig] - The brand's authored config, when the caller
|
|
258
|
+
* already has it (loadConfig does); read from the brand root otherwise.
|
|
259
|
+
* @returns {{ id: string|null, name: string|null, url: string|null, images: object, webhooks: boolean, root: string|null, dir: string|null, file: Function, config: object|null, configFile: string|null }}
|
|
260
|
+
* `root` is the parent brand's root ON THIS MACHINE (null = inheritance off),
|
|
261
|
+
* `file(relPath)` that path inside the parent's `company/` when it exists.
|
|
262
|
+
*/
|
|
263
|
+
function resolveCompany(brandRoot, brandConfig) {
|
|
264
|
+
const root = brandRoot ? path.resolve(brandRoot) : null;
|
|
265
|
+
const config = brandConfig || readBrandConfig(root) || {};
|
|
266
|
+
const brand = config.brand || {};
|
|
267
|
+
const id = config.company && typeof config.company.id === 'string' ? config.company.id.trim() : '';
|
|
268
|
+
|
|
269
|
+
// The brand's OWN statement, on every branch: whose provider account the
|
|
270
|
+
// manage walk may repoint (#677). Never the company's to answer.
|
|
271
|
+
const webhooks = !(config.company && config.company.webhooks === false);
|
|
272
|
+
const reply = (facts) => answer({ ...facts, webhooks });
|
|
273
|
+
|
|
274
|
+
// A brand still carrying the retired stamp hears about it once: the folder it
|
|
275
|
+
// points at is not read any more, by anything.
|
|
276
|
+
if (root && fs.existsSync(path.join(root, RETIRED_MARKER)) && !warned.has(RETIRED_MARKER)) {
|
|
277
|
+
warned.add(RETIRED_MARKER);
|
|
278
|
+
console.warn(`${path.join(root, RETIRED_MARKER)} is retired (#677): delete it, and name the company with company: { id: '<parent brand.id>' } in omega.json5.`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// No company: the brand IS the whole entity, so the company facts are its own
|
|
282
|
+
// and every reader works without a fallback.
|
|
283
|
+
if (!id) {
|
|
284
|
+
return reply({ id: null, name: brand.name, url: brand.url });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// The company brand itself: its own company/ tree, its own public facts.
|
|
288
|
+
if (id === SELF) {
|
|
289
|
+
return reply({ id: SELF, name: brand.name, url: brand.url, images: wordmarkOf(brand), root });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const line = readRegistry()[id];
|
|
293
|
+
const parentRoot = line && typeof line.root === 'string' && isDir(line.root) ? line.root : null;
|
|
294
|
+
|
|
295
|
+
if (parentRoot) {
|
|
296
|
+
const parentBrand = (readBrandConfig(parentRoot) || {}).brand || {};
|
|
297
|
+
|
|
298
|
+
return reply({
|
|
299
|
+
id,
|
|
300
|
+
name: parentBrand.name || line.name,
|
|
301
|
+
url: parentBrand.url || line.url,
|
|
302
|
+
images: wordmarkOf(parentBrand),
|
|
303
|
+
root: parentRoot,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Off this laptop: what the dispatching machine resolved rides beside the
|
|
308
|
+
// brand config. Never a tree, so `file()` answers null for everything.
|
|
309
|
+
const generated = readResolvedLayer(root);
|
|
310
|
+
if (generated) {
|
|
311
|
+
const company = generated.company || {};
|
|
312
|
+
return reply({
|
|
313
|
+
id: company.id || id,
|
|
314
|
+
name: company.name,
|
|
315
|
+
url: company.url,
|
|
316
|
+
images: company.images || {},
|
|
317
|
+
config: generated.config || {},
|
|
318
|
+
configFile: path.join(root, COMPANY_RESOLVED_FILE),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// A config that already CARRIES the facts: composeTargetConfig freezes the
|
|
323
|
+
// resolved company into a target's upload (the deployed backend has no
|
|
324
|
+
// registry, no company tree and no brand walk-up above it), so what the
|
|
325
|
+
// dispatching machine resolved is the answer here. A human never authors
|
|
326
|
+
// these keys: the loader refuses them in a brand file.
|
|
327
|
+
const frozen = config.company || {};
|
|
328
|
+
if (frozen.name || frozen.url || frozen.images) {
|
|
329
|
+
return reply({ id, name: frozen.name, url: frozen.url, images: frozen.images || {} });
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Known in the registry but the folder is gone: the cached public facts still
|
|
333
|
+
// stand, the tree does not.
|
|
334
|
+
if (line) {
|
|
335
|
+
return reply({ id, name: line.name, url: line.url });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
warnMissing(id);
|
|
339
|
+
return reply({ id });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
module.exports = {
|
|
343
|
+
resolveCompany,
|
|
344
|
+
recordBrand,
|
|
345
|
+
readRegistry,
|
|
346
|
+
registryFile,
|
|
347
|
+
COMPANY_DIR,
|
|
348
|
+
COMPANY_RESOLVED_FILE,
|
|
349
|
+
COMPANY_SELF: SELF,
|
|
350
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dev-facts.js, the ONE missing-dev-fact error
|
|
3
|
+
* ([#834](https://github.com/Omega-JS-Stack/omega/issues/834)).
|
|
4
|
+
*
|
|
5
|
+
* The classic port numbers are DEFINED once, in `CLASSIC_PORTS` beside this
|
|
6
|
+
* file, and the allocator resolves a live map from them at boot (N7). Browser
|
|
7
|
+
* code can read neither env nor files, so every surface BAKES that resolved map
|
|
8
|
+
* into its build json under one key, `OMEGA_BUILD_JSON.config.dev`
|
|
9
|
+
* ([#300](https://github.com/Omega-JS-Stack/omega/issues/300),
|
|
10
|
+
* [#262](https://github.com/Omega-JS-Stack/omega/issues/262)).
|
|
11
|
+
*
|
|
12
|
+
* What this file exists to stop is the OTHER half of that arrangement: the same
|
|
13
|
+
* numbers hand-typed a second time as a browser-side fallback for "a build made
|
|
14
|
+
* with no stack up". @omega.js/client carried four of them plus a classic dev
|
|
15
|
+
* origin, @omega.js/desktop's url-helpers and client-bridge carried three more,
|
|
16
|
+
* and @omega.js/extension's url-helpers and background worker two. Every one of
|
|
17
|
+
* them was a guess about a port nothing identity-checks, so a neighbouring
|
|
18
|
+
* project's emulator holding 9099 read as an auth mystery for hours rather than
|
|
19
|
+
* as a port problem, and a wrong API base read as a silent connection refusal.
|
|
20
|
+
*
|
|
21
|
+
* So there is no fallback anywhere any more. A real build always carries the
|
|
22
|
+
* map; a read that finds none throws THIS error, which names the fact it wanted
|
|
23
|
+
* and the build step that writes it.
|
|
24
|
+
*
|
|
25
|
+
* This module requires NOTHING: it is bundled into browser artifacts (the
|
|
26
|
+
* desktop renderer, every extension bundle) and vendored into @omega.js/client.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// The ONE key every surface bakes the resolved map under, spelled once.
|
|
30
|
+
const DEV_FACT_CHANNEL = 'OMEGA_BUILD_JSON.config.dev';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The error a surface throws when a dev fact it needs was never baked.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} what - the fact, named the way the caller wanted it
|
|
36
|
+
* ("dev port for `auth`", "dev website origin").
|
|
37
|
+
* @param {string} writer - the build step that writes the map on THIS surface,
|
|
38
|
+
* so the message says where to go and not just what is absent.
|
|
39
|
+
* @returns {Error} the error to throw.
|
|
40
|
+
*/
|
|
41
|
+
function devFactMissing(what, writer) {
|
|
42
|
+
return new Error(`No resolved ${what} in ${DEV_FACT_CHANNEL}. It is written by ${writer}. A build made without it is not a build, so nothing here assumes a classic port.`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
DEV_FACT_CHANNEL,
|
|
47
|
+
devFactMissing,
|
|
48
|
+
};
|