@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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE argv parse behind every OMEGA CLI: node's `util.parseArgs` wearing
|
|
3
|
+
* the yargs conveniences the bins and their commands still read
|
|
4
|
+
* ([#920](https://github.com/Omega-JS-Stack/omega/issues/920)). yargs is gone;
|
|
5
|
+
* this is what replaced it, in one place so the five CLIs can never drift.
|
|
6
|
+
*
|
|
7
|
+
* THE VALUE RULE, which is yargs' own: a flag named in `booleans` never takes a
|
|
8
|
+
* value, and every OTHER flag takes the next token as its value when that token
|
|
9
|
+
* exists and does not start with `-`, else it is `true`. `--flag=value` always
|
|
10
|
+
* wins. So a CLI declares only what is value-LESS, and a flag nobody declared
|
|
11
|
+
* still carries its value: the brand root forwards `omega test --filter foo` to
|
|
12
|
+
* a target whose parse has never heard of `--filter`, and CI really runs
|
|
13
|
+
* `sign-windows --in release --out release/signed`.
|
|
14
|
+
*
|
|
15
|
+
* What the builtin does NOT do, and what this adds back:
|
|
16
|
+
* 1. THE VALUE RULE above. To `parseArgs` an undeclared flag is boolean, so
|
|
17
|
+
* the value of a space-separated `--only hosting` would fall through to
|
|
18
|
+
* `_`. The line is PRE-SCANNED to build the options map instead: every
|
|
19
|
+
* flag whose occurrence takes a value is declared a string, and parseArgs
|
|
20
|
+
* still does all the tokenizing.
|
|
21
|
+
* 2. CAMEL TWINS. `parseArgs` gives `--dry-run` exactly one key, `dry-run`.
|
|
22
|
+
* Dozens of reads say `options.dryRun` (the manager alone has ~100) with
|
|
23
|
+
* no kebab fallback, and the manager's buildForwardedFlags skips a camel
|
|
24
|
+
* key whose kebab original exists. So every kebab flag lands under BOTH
|
|
25
|
+
* spellings, exactly as yargs minted them.
|
|
26
|
+
* 3. NEGATION. `parseArgs` reads `--no-secrets` as a flag NAMED `no-secrets`.
|
|
27
|
+
* Every consumer tests the positive key against false (`argv.https !==
|
|
28
|
+
* false`, the deploy precheck's `secrets: false`), so `--no-<x>` sets
|
|
29
|
+
* `<x>` to false, never takes a value, and never survives under its own
|
|
30
|
+
* name.
|
|
31
|
+
* 4. REPEATS. A repeated flag overwrites unless it is declared `multiple`,
|
|
32
|
+
* and an array of one is not what a single occurrence meant: the backend's
|
|
33
|
+
* `--where` accumulates into an array and one clause stays a string.
|
|
34
|
+
*
|
|
35
|
+
* Positionals come back at `_`, the key every consumer already reads. The
|
|
36
|
+
* router owns `--help`/`--version`, so nothing here is reserved.
|
|
37
|
+
*
|
|
38
|
+
* Stdlib-only on purpose: a vendored module must not push a dependency onto
|
|
39
|
+
* its hosts.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
const { parseArgs } = require('node:util');
|
|
43
|
+
|
|
44
|
+
/** `signed-dir` becomes `signedDir` (the twin yargs minted beside every kebab key). */
|
|
45
|
+
function camelCase(name) {
|
|
46
|
+
return name.replace(/-([a-z0-9])/g, (match, character) => character.toUpperCase());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The options map for THIS line: the declarations plus every flag the value
|
|
51
|
+
* rule says carries a value. A clustered short (`-abc`) declares nothing and
|
|
52
|
+
* stays boolean, and so does a `--no-<x>` negation.
|
|
53
|
+
*/
|
|
54
|
+
function declarationsFor(args, booleans, multiples) {
|
|
55
|
+
const options = {};
|
|
56
|
+
|
|
57
|
+
for (const name of booleans) options[name] = { type: 'boolean' };
|
|
58
|
+
for (const name of multiples) options[name] = { type: 'string', multiple: true };
|
|
59
|
+
|
|
60
|
+
for (let index = 0; index < args.length; index++) {
|
|
61
|
+
const token = args[index];
|
|
62
|
+
if (token === '--') break;
|
|
63
|
+
|
|
64
|
+
const flag = /^--([^-=][^=]*)(=)?/.exec(token) || /^-([^-])$/.exec(token);
|
|
65
|
+
if (!flag) continue;
|
|
66
|
+
|
|
67
|
+
const name = flag[1];
|
|
68
|
+
if (options[name]) continue;
|
|
69
|
+
|
|
70
|
+
// A negation is value-LESS (rule 3), so the token behind it is a positional
|
|
71
|
+
// of its own and never the negation's value.
|
|
72
|
+
if (name.startsWith('no-')) {
|
|
73
|
+
options[name] = { type: 'boolean' };
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const next = args[index + 1];
|
|
78
|
+
if (flag[2] || (next !== undefined && !next.startsWith('-'))) {
|
|
79
|
+
options[name] = { type: 'string' };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return options;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Parse a CLI argv tail into the yargs-shaped options object the CLIs read.
|
|
88
|
+
*
|
|
89
|
+
* @param {string[]} args - The argv tail (`process.argv.slice(2)`).
|
|
90
|
+
* @param {object} [declarations]
|
|
91
|
+
* @param {string[]} [declarations.booleans] - Value-less flags (`--extended`), the only list a CLI owes
|
|
92
|
+
* @param {string[]} [declarations.multiples] - Value flags that accumulate when repeated (`--where`).
|
|
93
|
+
* @returns {object} `{ _: positionals, ...flags }`, each kebab flag under both spellings
|
|
94
|
+
*/
|
|
95
|
+
function parseArgv(args, declarations = {}) {
|
|
96
|
+
const { booleans = [], multiples = [] } = declarations;
|
|
97
|
+
const options = declarationsFor(args, booleans, multiples);
|
|
98
|
+
|
|
99
|
+
const { values, positionals } = parseArgs({ args, options, strict: false, allowPositionals: true });
|
|
100
|
+
const parsed = { _: positionals };
|
|
101
|
+
|
|
102
|
+
for (const [key, raw] of Object.entries(values)) {
|
|
103
|
+
// A repeated declaration always answers an array; one occurrence is one value.
|
|
104
|
+
const value = Array.isArray(raw) && raw.length === 1 ? raw[0] : raw;
|
|
105
|
+
|
|
106
|
+
// Negation names the POSITIVE flag. Last spelling wins, as it did under
|
|
107
|
+
// yargs: `--secrets --no-secrets` ends false.
|
|
108
|
+
const negated = key.startsWith('no-');
|
|
109
|
+
const name = negated ? key.slice(3) : key;
|
|
110
|
+
|
|
111
|
+
parsed[name] = negated ? false : value;
|
|
112
|
+
if (name.includes('-')) parsed[camelCase(name)] = parsed[name];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return parsed;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = { parseArgv };
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
// That is what keeps an inner tee's detach from stealing (and truncating) the outer tee's
|
|
23
23
|
// file, the way a shared singleton did.
|
|
24
24
|
//
|
|
25
|
+
// Two attaches on ONE tee stack the same way: a second attach of a DIFFERENT path pushes a
|
|
26
|
+
// layer on top of the first instead of replacing it, so a verb that runs another verb in
|
|
27
|
+
// process (`omega test` running `omega deploy`) keeps teeing both files. Detach order is
|
|
28
|
+
// LIFO, and the module-level `detach()` pops the newest layer.
|
|
29
|
+
//
|
|
25
30
|
// Skipped in CI: a runner has its own log capture and wants no logs/ left in the workspace.
|
|
26
31
|
// `attachInCI: true` opts out of that skip, for a sink whose file is the POINT rather than
|
|
27
32
|
// workspace debris (the signing box's own runner log, which a CI job is exactly when it
|
|
@@ -51,8 +56,10 @@ function isCI(env) {
|
|
|
51
56
|
|
|
52
57
|
// Factory — each call returns an independent tee with its own closure state.
|
|
53
58
|
function createTee() {
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
// One entry per live attach on this tee, oldest first. A layer restores the writers it
|
|
60
|
+
// captured, so detaching in LIFO order (the only order a nested run produces) unwinds
|
|
61
|
+
// the patches exactly as they went on.
|
|
62
|
+
const layers = [];
|
|
56
63
|
let warned = false;
|
|
57
64
|
|
|
58
65
|
function attach(filePath, options) {
|
|
@@ -63,10 +70,10 @@ function createTee() {
|
|
|
63
70
|
|
|
64
71
|
const abs = path.resolve(filePath);
|
|
65
72
|
|
|
66
|
-
// Already teeing this exact file
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
if (
|
|
73
|
+
// Already teeing this exact file, so do not patch a second time (that would write every
|
|
74
|
+
// line twice, and reopen the file truncated under the live layer).
|
|
75
|
+
const live = layers.find((layer) => layer.path === abs);
|
|
76
|
+
if (live) { return live.detach; }
|
|
70
77
|
|
|
71
78
|
let fd;
|
|
72
79
|
try {
|
|
@@ -125,21 +132,22 @@ function createTee() {
|
|
|
125
132
|
process.off('exit', closeFd);
|
|
126
133
|
closeFd();
|
|
127
134
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
activeDetach = null;
|
|
131
|
-
}
|
|
135
|
+
const index = layers.indexOf(layer);
|
|
136
|
+
if (index !== -1) { layers.splice(index, 1); }
|
|
132
137
|
}
|
|
133
138
|
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
const layer = { path: abs, detach };
|
|
140
|
+
layers.push(layer);
|
|
136
141
|
|
|
137
142
|
return detach;
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
return {
|
|
141
146
|
attach,
|
|
142
|
-
detach: () => {
|
|
147
|
+
detach: () => {
|
|
148
|
+
const newest = layers[layers.length - 1];
|
|
149
|
+
if (newest) { newest.detach(); }
|
|
150
|
+
},
|
|
143
151
|
};
|
|
144
152
|
}
|
|
145
153
|
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand accent ramp — derives the accent token family from ONE brand color
|
|
3
|
+
* (C3/D6: `brand.color` drives the accent everywhere via CSS custom
|
|
4
|
+
* properties; the static token sheet's neutral placeholder stands when the
|
|
5
|
+
* brand has no usable color).
|
|
6
|
+
*
|
|
7
|
+
* Pure math, dependency-free: hex → RGB → HSL round-trips plus WCAG
|
|
8
|
+
* relative luminance for the on-accent ink pick. Every output value is
|
|
9
|
+
* regenerated from parsed numbers, so nothing from config reaches the
|
|
10
|
+
* emitted <style> block verbatim.
|
|
11
|
+
*
|
|
12
|
+
* ONE home for three surfaces ([#912](https://github.com/Omega-JS-Stack/omega/issues/912)):
|
|
13
|
+
* @omega.js/web inlines the ramp into <head> after its bundles, and
|
|
14
|
+
* @omega.js/desktop and @omega.js/extension write `renderBrandScss()` below to
|
|
15
|
+
* a generated `_brand.scss` their sass tasks compile. Same hex in, same ramp
|
|
16
|
+
* out, on every target.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const INK_DARK = '#111213';
|
|
20
|
+
const INK_LIGHT = '#ffffff';
|
|
21
|
+
|
|
22
|
+
// WCAG contrast-vs-white beats contrast-vs-black below this luminance
|
|
23
|
+
const INK_LUMINANCE_THRESHOLD = 0.1791;
|
|
24
|
+
|
|
25
|
+
// Accents darker than this lightness brighten on hover instead of darkening
|
|
26
|
+
const HOVER_LIGHTEN_THRESHOLD = 0.25;
|
|
27
|
+
|
|
28
|
+
// Dark-mode variant: accents at/above this lightness are already legible on
|
|
29
|
+
// charcoal and pass through; darker ones lift into the floor..ceil band.
|
|
30
|
+
const DARK_KEEP_THRESHOLD = 0.66;
|
|
31
|
+
const DARK_LIGHTNESS_FLOOR = 0.55;
|
|
32
|
+
const DARK_LIGHTNESS_CEIL = 0.78;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse a 3- or 6-digit hex color.
|
|
36
|
+
* @param {*} value - candidate color
|
|
37
|
+
* @returns {{ r: number, g: number, b: number }|null} channels 0-255, or null
|
|
38
|
+
*/
|
|
39
|
+
function parseHex(value) {
|
|
40
|
+
if (typeof value !== 'string') {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const match = value.trim().match(/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i);
|
|
45
|
+
if (!match) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let hex = match[1].toLowerCase();
|
|
50
|
+
if (hex.length === 3) {
|
|
51
|
+
hex = hex.split('').map((c) => c + c).join('');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
r: parseInt(hex.slice(0, 2), 16),
|
|
56
|
+
g: parseInt(hex.slice(2, 4), 16),
|
|
57
|
+
b: parseInt(hex.slice(4, 6), 16),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* WCAG 2.x relative luminance.
|
|
63
|
+
* @param {{ r: number, g: number, b: number }} rgb - channels 0-255
|
|
64
|
+
* @returns {number} 0 (black) to 1 (white)
|
|
65
|
+
*/
|
|
66
|
+
function relativeLuminance({ r, g, b }) {
|
|
67
|
+
const linearize = (channel) => {
|
|
68
|
+
const c = channel / 255;
|
|
69
|
+
return c <= 0.03928 ? c / 12.92 : (((c + 0.055) / 1.055) ** 2.4);
|
|
70
|
+
};
|
|
71
|
+
return (0.2126 * linearize(r)) + (0.7152 * linearize(g)) + (0.0722 * linearize(b));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* RGB (0-255) → HSL (h 0-360, s/l 0-1).
|
|
76
|
+
*/
|
|
77
|
+
function rgbToHsl({ r, g, b }) {
|
|
78
|
+
const rn = r / 255;
|
|
79
|
+
const gn = g / 255;
|
|
80
|
+
const bn = b / 255;
|
|
81
|
+
const max = Math.max(rn, gn, bn);
|
|
82
|
+
const min = Math.min(rn, gn, bn);
|
|
83
|
+
const l = (max + min) / 2;
|
|
84
|
+
|
|
85
|
+
if (max === min) {
|
|
86
|
+
return { h: 0, s: 0, l };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const d = max - min;
|
|
90
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
91
|
+
let h;
|
|
92
|
+
if (max === rn) {
|
|
93
|
+
h = ((gn - bn) / d) + (gn < bn ? 6 : 0);
|
|
94
|
+
} else if (max === gn) {
|
|
95
|
+
h = ((bn - rn) / d) + 2;
|
|
96
|
+
} else {
|
|
97
|
+
h = ((rn - gn) / d) + 4;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { h: h * 60, s, l };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* HSL (h 0-360, s/l 0-1) → RGB (0-255).
|
|
105
|
+
*/
|
|
106
|
+
function hslToRgb({ h, s, l }) {
|
|
107
|
+
if (s === 0) {
|
|
108
|
+
const v = Math.round(l * 255);
|
|
109
|
+
return { r: v, g: v, b: v };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const hueToChannel = (p, q, t) => {
|
|
113
|
+
let tn = t;
|
|
114
|
+
if (tn < 0) tn += 1;
|
|
115
|
+
if (tn > 1) tn -= 1;
|
|
116
|
+
if (tn < 1 / 6) return p + ((q - p) * 6 * tn);
|
|
117
|
+
if (tn < 1 / 2) return q;
|
|
118
|
+
if (tn < 2 / 3) return p + ((q - p) * ((2 / 3) - tn) * 6);
|
|
119
|
+
return p;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - (l * s);
|
|
123
|
+
const p = (2 * l) - q;
|
|
124
|
+
const hn = h / 360;
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
r: Math.round(hueToChannel(p, q, hn + (1 / 3)) * 255),
|
|
128
|
+
g: Math.round(hueToChannel(p, q, hn) * 255),
|
|
129
|
+
b: Math.round(hueToChannel(p, q, hn - (1 / 3)) * 255),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* RGB → #rrggbb.
|
|
135
|
+
*/
|
|
136
|
+
function toHex({ r, g, b }) {
|
|
137
|
+
const pair = (v) => v.toString(16).padStart(2, '0');
|
|
138
|
+
return `#${pair(r)}${pair(g)}${pair(b)}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Shift an accent's lightness for hover/active states — darker accents
|
|
143
|
+
* brighten, everything else deepens, clamped to [0, 1].
|
|
144
|
+
* @param {{ h: number, s: number, l: number }} hsl - base accent
|
|
145
|
+
* @param {number} amount - lightness delta (positive magnitude)
|
|
146
|
+
* @returns {string} #rrggbb
|
|
147
|
+
*/
|
|
148
|
+
function shiftLightness(hsl, amount) {
|
|
149
|
+
const direction = hsl.l < HOVER_LIGHTEN_THRESHOLD ? 1 : -1;
|
|
150
|
+
const l = Math.min(1, Math.max(0, hsl.l + (direction * amount)));
|
|
151
|
+
return toHex(hslToRgb({ ...hsl, l }));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Compose one accent ramp from resolved channels.
|
|
156
|
+
* @param {{ r: number, g: number, b: number }} rgb - accent channels
|
|
157
|
+
* @param {{ h: number, s: number, l: number }} hsl - same accent in HSL
|
|
158
|
+
* @param {number} subtleAlpha - alpha for the subtle wash (mode-specific)
|
|
159
|
+
* @returns {object} { accent, accentHover, accentActive, accentSubtle, accentInk, accentRing }
|
|
160
|
+
*/
|
|
161
|
+
function composeRamp(rgb, hsl, subtleAlpha) {
|
|
162
|
+
const luminance = relativeLuminance(rgb);
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
accent: toHex(rgb),
|
|
166
|
+
accentHover: shiftLightness(hsl, 0.07),
|
|
167
|
+
accentActive: shiftLightness(hsl, 0.11),
|
|
168
|
+
accentSubtle: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${subtleAlpha})`,
|
|
169
|
+
accentInk: luminance > INK_LUMINANCE_THRESHOLD ? INK_DARK : INK_LIGHT,
|
|
170
|
+
accentRing: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.35)`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Compose the accent token families from a brand color: the light ramp plus
|
|
176
|
+
* a dark-mode variant (darker brand colors lift so they stay legible on the
|
|
177
|
+
* charcoal ground — the classy v2 per-mode accent).
|
|
178
|
+
* @param {string} color - brand color (#rgb or #rrggbb)
|
|
179
|
+
* @returns {object|null} ramps for the head emitter, or null when unusable:
|
|
180
|
+
* { accent, accentHover, accentActive, accentSubtle, accentInk, accentRing,
|
|
181
|
+
* dark: { …same shape… } }
|
|
182
|
+
*/
|
|
183
|
+
function composeBrandTokens(color) {
|
|
184
|
+
const rgb = parseHex(color);
|
|
185
|
+
if (!rgb) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const hsl = rgbToHsl(rgb);
|
|
190
|
+
const darkHsl = hsl.l >= DARK_KEEP_THRESHOLD
|
|
191
|
+
? hsl
|
|
192
|
+
: { ...hsl, l: Math.min(Math.max(hsl.l + 0.1, DARK_LIGHTNESS_FLOOR), DARK_LIGHTNESS_CEIL) };
|
|
193
|
+
const darkRgb = hslToRgb(darkHsl);
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
...composeRamp(rgb, hsl, 0.12),
|
|
197
|
+
dark: composeRamp(darkRgb, darkHsl, 0.16),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The ONE accent default. `brand.color` unset (or unusable) lands here, and the
|
|
203
|
+
* classy theme's `$primary: … !default` carries the same hex purely as the Sass
|
|
204
|
+
* mechanism that lets this partial win: the VALUE is decided here.
|
|
205
|
+
*/
|
|
206
|
+
const DEFAULT_BRAND_COLOR = '#2563EB';
|
|
207
|
+
|
|
208
|
+
/** The six properties, in the order head.html emits them on web. */
|
|
209
|
+
const RAMP_PROPERTIES = [
|
|
210
|
+
['--omega-accent', 'accent'],
|
|
211
|
+
['--omega-accent-hover', 'accentHover'],
|
|
212
|
+
['--omega-accent-active', 'accentActive'],
|
|
213
|
+
['--omega-accent-subtle', 'accentSubtle'],
|
|
214
|
+
['--omega-accent-ink', 'accentInk'],
|
|
215
|
+
['--omega-accent-ring', 'accentRing'],
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* One `selector { … }` block of the ramp, indented for the mixin body.
|
|
220
|
+
* @param {string} selector - the rule's selector
|
|
221
|
+
* @param {object} ramp - one ramp (the light half, or `.dark`)
|
|
222
|
+
* @param {number} depth - indent depth in 2-space steps
|
|
223
|
+
* @returns {string} the rule text
|
|
224
|
+
*/
|
|
225
|
+
function renderRampBlock(selector, ramp, depth) {
|
|
226
|
+
const pad = ' '.repeat(depth);
|
|
227
|
+
const declarations = RAMP_PROPERTIES.map(([property, key]) => `${pad} ${property}: ${ramp[key]};`);
|
|
228
|
+
|
|
229
|
+
return [`${pad}${selector} {`, ...declarations, `${pad}}`].join('\n');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Render the generated `_brand.scss` a target's sass task writes beside its
|
|
234
|
+
* compile: `$primary` for the compile-time accent Bootstrap derives from, plus
|
|
235
|
+
* a `ramp` mixin carrying the runtime `--omega-accent` family under the SAME
|
|
236
|
+
* three stamps the token sheet uses (bare `:root`, the OS preference, the
|
|
237
|
+
* explicit `data-bs-theme` stamp both ways).
|
|
238
|
+
*
|
|
239
|
+
* The css rides a MIXIN rather than the file root on purpose: a used module's
|
|
240
|
+
* css is emitted at its LOAD position, which is ahead of the framework module's
|
|
241
|
+
* token sheet and theme, so a root-level rule here would lose the cascade to
|
|
242
|
+
* the very placeholders it replaces. The consumer's `@include brand.ramp;` puts
|
|
243
|
+
* it after both.
|
|
244
|
+
*
|
|
245
|
+
* @param {object|null} tokens - composeBrandTokens() output, or null for the default
|
|
246
|
+
* @returns {string} the partial's full text
|
|
247
|
+
*/
|
|
248
|
+
function renderBrandScss(tokens) {
|
|
249
|
+
const ramp = tokens || composeBrandTokens(DEFAULT_BRAND_COLOR);
|
|
250
|
+
|
|
251
|
+
return `// AUTO-GENERATED by the sass task from \`brand.color\` in omega.json5.
|
|
252
|
+
// Every build rewrites it: edit the config, not this file.
|
|
253
|
+
//
|
|
254
|
+
// \`$primary\` is the compile-time accent (Bootstrap's color ramp compiles from
|
|
255
|
+
// it) and \`ramp\` is the runtime --omega-accent family, the same light and dark
|
|
256
|
+
// ramps @omega.js/web inlines into <head>. Include the mixin AFTER the
|
|
257
|
+
// framework module so the ramp wins the cascade over the token sheet's
|
|
258
|
+
// placeholders.
|
|
259
|
+
|
|
260
|
+
$primary: ${ramp.accent};
|
|
261
|
+
|
|
262
|
+
@mixin ramp {
|
|
263
|
+
${renderRampBlock(':root', ramp, 1)}
|
|
264
|
+
|
|
265
|
+
// The OS preference carries when no explicit choice is stamped...
|
|
266
|
+
@media (prefers-color-scheme: dark) {
|
|
267
|
+
${renderRampBlock(':root', ramp.dark, 2)}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ...and the appearance stamp beats the OS in BOTH directions.
|
|
271
|
+
${renderRampBlock(":root[data-bs-theme='dark']", ramp.dark, 1)}
|
|
272
|
+
|
|
273
|
+
${renderRampBlock(":root[data-bs-theme='light']", ramp, 1)}
|
|
274
|
+
}
|
|
275
|
+
`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
module.exports = { composeBrandTokens, parseHex, relativeLuminance, renderBrandScss, DEFAULT_BRAND_COLOR };
|