@omega.js/desktop 0.50.0 → 0.52.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 +2 -2
- 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 +176 -42
- 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 +146 -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 +635 -5
- 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 +125 -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 +249 -103
- 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 +425 -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 +505 -75
- package/dist/vendor/devkit/git-auth.js +73 -0
- package/dist/vendor/devkit/git-remote.js +172 -0
- package/dist/vendor/devkit/github-repo.js +290 -0
- package/dist/vendor/devkit/local.js +98 -3
- package/dist/vendor/devkit/lockfile.js +127 -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 +388 -0
- package/dist/vendor/devkit/preludes/index.js +120 -0
- package/dist/vendor/devkit/preludes/origin-heal.js +142 -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 +231 -57
- 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 +29 -19
- package/docs/runner.md +51 -14
- package/docs/shared/brands.md +12 -6
- package/docs/shared/breaking-changes.md +375 -21
- package/docs/shared/config.md +764 -199
- package/docs/shared/deploys.md +217 -91
- package/docs/shared/icons.md +18 -0
- package/docs/shared/local-dev.md +28 -8
- 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 +60 -34
- 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
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
// callers normally pass NO argument. An explicit `environment` arg is an override
|
|
12
12
|
// (used mainly by tests to pin a specific environment's mapping).
|
|
13
13
|
|
|
14
|
+
// The missing-fact error is the ONE shared one (#834). Its module requires
|
|
15
|
+
// nothing, so it rides the renderer bundle exactly as this file does.
|
|
16
|
+
const { devFactMissing } = require('../vendor/config/dev-facts.js');
|
|
17
|
+
|
|
14
18
|
// Guarded the way @omega.js/extension's twin is: the renderer bundle is a
|
|
15
19
|
// browser context, and only the Node-side contexts (main, preload, build, the
|
|
16
20
|
// test harness) carry the resolved-port env channel.
|
|
@@ -20,14 +24,9 @@ function envPort(name) {
|
|
|
20
24
|
: undefined;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
// The
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
// Node-only package (fs, net, json5); @omega.js/client mirrors the same constant
|
|
27
|
-
// for the same reason. The build suite pins this copy to the config package's
|
|
28
|
-
// value, so a drift fails a test instead of a user
|
|
29
|
-
// ([#747](https://github.com/Omega-JS-Stack/omega/issues/747)).
|
|
30
|
-
const CLASSIC_DEV_ORIGIN = 'https://localhost:4000';
|
|
27
|
+
// The step that writes the resolved dev map into this surface's artifact, named
|
|
28
|
+
// in every missing-fact error below (#834).
|
|
29
|
+
const DEV_FACT_WRITER = "@omega.js/desktop's bundle task, from the live stack's ports file plus the OMEGA_*_PORT env channel";
|
|
31
30
|
|
|
32
31
|
// One local port, from whichever channel this context has: the env var first
|
|
33
32
|
// (the CLI that booted the stack publishes it, N7), then the `dev.ports` map
|
|
@@ -35,10 +34,26 @@ const CLASSIC_DEV_ORIGIN = 'https://localhost:4000';
|
|
|
35
34
|
// parent env, so a bumped emulator port reaches it only that way
|
|
36
35
|
// ([#745](https://github.com/Omega-JS-Stack/omega/issues/745)). Same helper,
|
|
37
36
|
// same order as @omega.js/extension's src/utils/url-helpers.js.
|
|
37
|
+
//
|
|
38
|
+
// There is no third step any more (#834): the classic numbers used to be
|
|
39
|
+
// hand-typed here as a last resort for "a build made with no stack up", which
|
|
40
|
+
// is a guess about a port nothing identity-checks. A miss is loud instead.
|
|
38
41
|
function localPort(context, envName, name) {
|
|
39
42
|
return envPort(envName) || context?.config?.dev?.ports?.[name];
|
|
40
43
|
}
|
|
41
44
|
|
|
45
|
+
// The same read, REQUIRED: the one every URL getter below uses, because every
|
|
46
|
+
// one of them is answering with a real local address or not at all.
|
|
47
|
+
function requiredPort(context, envName, name) {
|
|
48
|
+
const port = localPort(context, envName, name);
|
|
49
|
+
|
|
50
|
+
if (!port) {
|
|
51
|
+
throw devFactMissing(`dev port for \`${name}\``, DEV_FACT_WRITER);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return port;
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
function getFunctionsUrl(environment) {
|
|
43
58
|
const env = environment || this.getEnvironment();
|
|
44
59
|
const projectId = this?.config?.cloud?.config?.projectId;
|
|
@@ -51,7 +66,7 @@ function getFunctionsUrl(environment) {
|
|
|
51
66
|
// same three-step chain getApiUrl() walks: the OMEGA_*_PORT env channel (N7),
|
|
52
67
|
// then the baked map, then the classic default.
|
|
53
68
|
if (env === 'development' || env === 'testing') {
|
|
54
|
-
const port =
|
|
69
|
+
const port = requiredPort(this, 'OMEGA_FUNCTIONS_PORT', 'functions');
|
|
55
70
|
return `http://localhost:${port}/${projectId}/us-central1`;
|
|
56
71
|
}
|
|
57
72
|
|
|
@@ -64,12 +79,13 @@ function getApiUrl(environment) {
|
|
|
64
79
|
// Local for development OR testing; production otherwise. Mirrors
|
|
65
80
|
// @omega.js/backend's getApiUrl (N7): a resolved OMEGA_HTTPS_PORT means
|
|
66
81
|
// `mgr serve`'s mkcert proxy is up (https); otherwise plain http to the
|
|
67
|
-
// hosting emulator (env port, baked port
|
|
82
|
+
// hosting emulator (env port, then baked port). Neither resolved is a broken
|
|
83
|
+
// build, not a case to assume the classic 5002 through (#834).
|
|
68
84
|
if (env === 'development' || env === 'testing') {
|
|
69
85
|
const httpsPort = localPort(this, 'OMEGA_HTTPS_PORT', 'https');
|
|
70
86
|
return httpsPort
|
|
71
87
|
? `https://localhost:${httpsPort}`
|
|
72
|
-
: `http://localhost:${
|
|
88
|
+
: `http://localhost:${requiredPort(this, 'OMEGA_HOSTING_PORT', 'hosting')}`;
|
|
73
89
|
}
|
|
74
90
|
|
|
75
91
|
// Prod: api.<brand host>. Mirrors @omega.js/client.getApiUrl. Never derive
|
|
@@ -89,9 +105,10 @@ function getApiUrl(environment) {
|
|
|
89
105
|
// ([#262](https://github.com/Omega-JS-Stack/omega/issues/262)): the baked
|
|
90
106
|
// `dev.origin` the live website published (scheme, host AND port, the complete
|
|
91
107
|
// fact) wins, else a port from the usual chain (OMEGA_WEBSITE_PORT, then the baked
|
|
92
|
-
// `dev.ports.website`) composed over https
|
|
93
|
-
//
|
|
94
|
-
//
|
|
108
|
+
// `dev.ports.website`) composed over https. There is no classic-origin fallback
|
|
109
|
+
// any more (#834): a wrong dev origin fails as a silent connection refusal. The
|
|
110
|
+
// scheme is https because `omega dev` fronts its public port with the mkcert
|
|
111
|
+
// proxy by default, so a port alone can never say it
|
|
95
112
|
// ([#747](https://github.com/Omega-JS-Stack/omega/issues/747)).
|
|
96
113
|
// Prod → `config.brand.url`. Use this whenever app code wants to link out to "the
|
|
97
114
|
// website" (Help → Website tray/menu items, "Open in browser," billing portal
|
|
@@ -106,8 +123,7 @@ function getWebsiteUrl(environment) {
|
|
|
106
123
|
return origin;
|
|
107
124
|
}
|
|
108
125
|
|
|
109
|
-
|
|
110
|
-
return port ? `https://localhost:${port}` : CLASSIC_DEV_ORIGIN;
|
|
126
|
+
return `https://localhost:${requiredPort(this, 'OMEGA_WEBSITE_PORT', 'website')}`;
|
|
111
127
|
}
|
|
112
128
|
|
|
113
129
|
const url = this?.config?.brand?.url;
|
|
@@ -160,8 +176,8 @@ function attachTo(Manager) {
|
|
|
160
176
|
|
|
161
177
|
module.exports = {
|
|
162
178
|
attachTo,
|
|
163
|
-
CLASSIC_DEV_ORIGIN,
|
|
164
179
|
localPort,
|
|
180
|
+
requiredPort,
|
|
165
181
|
getFunctionsUrl,
|
|
166
182
|
getApiUrl,
|
|
167
183
|
getWebsiteUrl,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle-identifier policy — reverse-DNS ids derived from the brand's web
|
|
3
|
+
* identity (Ian 2026-07-14: the prefix is the reverse domain, autoset at
|
|
4
|
+
* onboarding from the parent company when one exists, else the brand's own
|
|
5
|
+
* url).
|
|
6
|
+
*
|
|
7
|
+
* deriveBundleIdPrefix('https://itwcreativeworks.com') → 'com.itwcreativeworks'
|
|
8
|
+
* deriveBundleIdPrefix('https://playground.omegajs.dev') → 'dev.omegajs.playground'
|
|
9
|
+
* composeBundleId('com.itwcreativeworks', 'daily-build')
|
|
10
|
+
* → 'com.itwcreativeworks.daily.build'
|
|
11
|
+
*
|
|
12
|
+
* Sanitization keeps ids legal on BOTH stores: Apple allows hyphens but
|
|
13
|
+
* Android package segments don't, so domain labels drop non-alphanumerics
|
|
14
|
+
* and brand-id hyphens become dots (each id word is its own segment).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reverse-DNS prefix from a company/brand url, or null when the url does
|
|
19
|
+
* not parse. `www.` is dropped; every other hostname label survives (a
|
|
20
|
+
* subdomain brand derives a subdomain-scoped prefix).
|
|
21
|
+
*
|
|
22
|
+
* @param {string} url - http(s) url (brand.url shape)
|
|
23
|
+
* @returns {string|null}
|
|
24
|
+
*/
|
|
25
|
+
function deriveBundleIdPrefix(url) {
|
|
26
|
+
let hostname;
|
|
27
|
+
try {
|
|
28
|
+
hostname = new URL(url).hostname;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const labels = hostname
|
|
34
|
+
.replace(/^www\./, '')
|
|
35
|
+
.split('.')
|
|
36
|
+
.map((label) => label.toLowerCase().replace(/[^a-z0-9]/g, ''))
|
|
37
|
+
.filter(Boolean);
|
|
38
|
+
|
|
39
|
+
return labels.length > 0 ? labels.reverse().join('.') : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Full bundle identifier: prefix + the brand id with hyphens as dots.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} prefix - reverse-DNS prefix (config or derived)
|
|
46
|
+
* @param {string} brandId - brand.id ('daily-build')
|
|
47
|
+
* @returns {string}
|
|
48
|
+
*/
|
|
49
|
+
function composeBundleId(prefix, brandId) {
|
|
50
|
+
return `${prefix}.${String(brandId).toLowerCase().replace(/-+/g, '.')}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { deriveBundleIdPrefix, composeBundleId };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* client-config.js: the ONE browser-safe subset of a resolved config
|
|
3
|
+
* ([#894](https://github.com/Omega-JS-Stack/omega/issues/894)).
|
|
4
|
+
*
|
|
5
|
+
* Three surfaces bake a config into an artifact a browser can read: web's page
|
|
6
|
+
* chrome, desktop's renderer bundle, and every extension bundle. Each one used
|
|
7
|
+
* to decide what may go public on its own, three different ways (desktop
|
|
8
|
+
* shipped the WHOLE resolved config, the extension kept a hand-written allow
|
|
9
|
+
* list, web wired a subset together in its engine and its foot include), so a
|
|
10
|
+
* new public value had to be added in three places and every one of them could
|
|
11
|
+
* be the place it leaked.
|
|
12
|
+
*
|
|
13
|
+
* This is that decision, once. The schema's `CLIENT_SECTIONS` says which
|
|
14
|
+
* sections a browser may see (and how much of each), the list below says which
|
|
15
|
+
* BUILD FACTS ride beside them, and nothing else leaves the build. All three
|
|
16
|
+
* bakes call this and wrap the answer identically:
|
|
17
|
+
*
|
|
18
|
+
* OMEGA_BUILD_JSON = { config: clientConfig(resolved), package, mode, license, builtAt }
|
|
19
|
+
*
|
|
20
|
+
* Secrets are a loud failure, not a filter: a secret-shaped key inside a
|
|
21
|
+
* section that is about to ship means the resolved config carried one at all,
|
|
22
|
+
* which loadConfig() and validateConfig() already refuse. A value a build is
|
|
23
|
+
* SANCTIONED to bake (`publicAtRest`, docs/shared/config.md) comes from .env
|
|
24
|
+
* and is added by that build after this gate, never carried through it.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const { CLIENT_SECTIONS } = require('./schema.js');
|
|
28
|
+
const { findSecretKeys } = require('./secrets.js');
|
|
29
|
+
const { resolveWinbackOffer } = require('./winback.js');
|
|
30
|
+
|
|
31
|
+
// The per-build facts a surface composes ON TOP of the resolved config before
|
|
32
|
+
// baking it. They are not config sections (nothing authors them in
|
|
33
|
+
// omega.json5): each is a fact about the artifact, spelled the same on every
|
|
34
|
+
// surface so one read answers everywhere.
|
|
35
|
+
//
|
|
36
|
+
// runtime 'web' | 'browser-extension' | 'electron'
|
|
37
|
+
// environment the build's verdict ('development' | 'testing' | 'production')
|
|
38
|
+
// version the target's own package version, the release tag every error
|
|
39
|
+
// report carries as `<brand.id>@<version>` (#380)
|
|
40
|
+
// buildTime the build stamp (ms)
|
|
41
|
+
// target WHICH target this artifact IS, by name (#887)
|
|
42
|
+
// url this target's own resolved public url (#588)
|
|
43
|
+
// dev the local stack's resolved map: ports + the sibling website's
|
|
44
|
+
// origin, development builds only (#300/#262). The extension's
|
|
45
|
+
// live-reload port rides here too (#896), the one home of a
|
|
46
|
+
// local-stack number.
|
|
47
|
+
const CLIENT_FACT_KEYS = ['runtime', 'environment', 'version', 'buildTime', 'target', 'url', 'dev'];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Copy exactly `paths` out of a section (dot-paths, deep).
|
|
51
|
+
* @param {*} section - the resolved section value.
|
|
52
|
+
* @param {string[]} paths - dot-paths inside it.
|
|
53
|
+
* @returns {object|undefined} the picked sub-tree, or undefined when it is empty.
|
|
54
|
+
*/
|
|
55
|
+
function pickPaths(section, paths) {
|
|
56
|
+
if (!section || typeof section !== 'object') {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const picked = {};
|
|
61
|
+
let found = false;
|
|
62
|
+
|
|
63
|
+
for (const path of paths) {
|
|
64
|
+
const keys = path.split('.');
|
|
65
|
+
let source = section;
|
|
66
|
+
let missing = false;
|
|
67
|
+
|
|
68
|
+
for (const key of keys) {
|
|
69
|
+
if (!source || typeof source !== 'object' || !(key in source)) {
|
|
70
|
+
missing = true;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
source = source[key];
|
|
74
|
+
}
|
|
75
|
+
if (missing) continue;
|
|
76
|
+
|
|
77
|
+
let cursor = picked;
|
|
78
|
+
for (const key of keys.slice(0, -1)) {
|
|
79
|
+
cursor[key] = cursor[key] || {};
|
|
80
|
+
cursor = cursor[key];
|
|
81
|
+
}
|
|
82
|
+
cursor[keys[keys.length - 1]] = source;
|
|
83
|
+
found = true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return found ? picked : undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The subset of a resolved config a browser may see.
|
|
91
|
+
* @param {object} [resolved] - a resolved config (Manager.getConfig() output),
|
|
92
|
+
* with this build's facts composed onto it.
|
|
93
|
+
* @returns {object} the blob every browser surface bakes as OMEGA_BUILD_JSON.config.
|
|
94
|
+
* @throws {Error} when a section that is about to ship carries a secret-shaped key.
|
|
95
|
+
*/
|
|
96
|
+
function clientConfig(resolved) {
|
|
97
|
+
const source = resolved || {};
|
|
98
|
+
const client = {};
|
|
99
|
+
|
|
100
|
+
for (const key of CLIENT_FACT_KEYS) {
|
|
101
|
+
if (key in source) client[key] = source[key];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const [section, rule] of Object.entries(CLIENT_SECTIONS)) {
|
|
105
|
+
if (!(section in source)) continue;
|
|
106
|
+
|
|
107
|
+
const value = rule === true ? source[section] : pickPaths(source[section], rule);
|
|
108
|
+
if (value === undefined) continue;
|
|
109
|
+
|
|
110
|
+
client[section] = value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// A secret in here is not a value to drop: it is a config that should never
|
|
114
|
+
// have loaded, so it fails the build at the one place that would have
|
|
115
|
+
// published it.
|
|
116
|
+
const secrets = findSecretKeys(client);
|
|
117
|
+
if (secrets.length) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`[@omega.js/config] refusing to bake a browser config carrying secret-shaped keys: ${secrets.join(', ')}. `
|
|
120
|
+
+ 'Secrets live in .env, never in config/omega.json5 (docs/shared/config.md).',
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// The one home of the cancel-flow save offer's default (#268) is
|
|
125
|
+
// @omega.js/config, so the browser is handed the RESOLVED offer: the dialog a
|
|
126
|
+
// customer reads and the coupon the backend creates can never name different
|
|
127
|
+
// numbers.
|
|
128
|
+
if (client.payment) {
|
|
129
|
+
client.payment = { ...client.payment, winback: resolveWinbackOffer(client.payment) };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Never an alias of the resolved config: what a bake stamps into an artifact
|
|
133
|
+
// cannot be something a later build step can still mutate. A JSON round trip
|
|
134
|
+
// and not structuredClone, because JSON is what a bake CAN carry: a page's
|
|
135
|
+
// resolved config arrives through a template data cascade that hangs
|
|
136
|
+
// functions and accessors off it, and those drop out here rather than
|
|
137
|
+
// crashing the build.
|
|
138
|
+
return JSON.parse(JSON.stringify(client));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = { clientConfig, CLIENT_FACT_KEYS };
|
|
@@ -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
|
+
};
|