@endora-commerce/cli 0.100.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/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/bin/endora.d.ts +28 -0
- package/dist/bin/endora.d.ts.map +1 -0
- package/dist/bin/endora.js +926 -0
- package/dist/bin/endora.js.map +1 -0
- package/dist/check/estate.d.ts +189 -0
- package/dist/check/estate.d.ts.map +1 -0
- package/dist/check/estate.js +1037 -0
- package/dist/check/estate.js.map +1 -0
- package/dist/check/hosts.d.ts +25 -0
- package/dist/check/hosts.d.ts.map +1 -0
- package/dist/check/hosts.js +1347 -0
- package/dist/check/hosts.js.map +1 -0
- package/dist/check/index.d.ts +60 -0
- package/dist/check/index.d.ts.map +1 -0
- package/dist/check/index.js +111 -0
- package/dist/check/index.js.map +1 -0
- package/dist/check/layout.d.ts +136 -0
- package/dist/check/layout.d.ts.map +1 -0
- package/dist/check/layout.js +262 -0
- package/dist/check/layout.js.map +1 -0
- package/dist/check/ledger.d.ts +98 -0
- package/dist/check/ledger.d.ts.map +1 -0
- package/dist/check/ledger.js +173 -0
- package/dist/check/ledger.js.map +1 -0
- package/dist/check/peer-owners.d.ts +116 -0
- package/dist/check/peer-owners.d.ts.map +1 -0
- package/dist/check/peer-owners.js +225 -0
- package/dist/check/peer-owners.js.map +1 -0
- package/dist/check/report.d.ts +33 -0
- package/dist/check/report.d.ts.map +1 -0
- package/dist/check/report.js +107 -0
- package/dist/check/report.js.map +1 -0
- package/dist/check/run.d.ts +147 -0
- package/dist/check/run.d.ts.map +1 -0
- package/dist/check/run.js +111 -0
- package/dist/check/run.js.map +1 -0
- package/dist/checks.d.ts +17 -0
- package/dist/checks.d.ts.map +1 -0
- package/dist/checks.js +17 -0
- package/dist/checks.js.map +1 -0
- package/dist/dev/index.d.ts +83 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +298 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/generate/divergence.d.ts +38 -0
- package/dist/generate/divergence.d.ts.map +1 -0
- package/dist/generate/divergence.js +237 -0
- package/dist/generate/divergence.js.map +1 -0
- package/dist/generate/index.d.ts +90 -0
- package/dist/generate/index.d.ts.map +1 -0
- package/dist/generate/index.js +369 -0
- package/dist/generate/index.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/inputs/declaration.d.ts +18 -0
- package/dist/inputs/declaration.d.ts.map +1 -0
- package/dist/inputs/declaration.js +64 -0
- package/dist/inputs/declaration.js.map +1 -0
- package/dist/inputs/env-file.d.ts +73 -0
- package/dist/inputs/env-file.d.ts.map +1 -0
- package/dist/inputs/env-file.js +134 -0
- package/dist/inputs/env-file.js.map +1 -0
- package/dist/inputs/prompt.d.ts +21 -0
- package/dist/inputs/prompt.d.ts.map +1 -0
- package/dist/inputs/prompt.js +59 -0
- package/dist/inputs/prompt.js.map +1 -0
- package/dist/inputs/resolve.d.ts +163 -0
- package/dist/inputs/resolve.d.ts.map +1 -0
- package/dist/inputs/resolve.js +290 -0
- package/dist/inputs/resolve.js.map +1 -0
- package/dist/install/host.d.ts +27 -0
- package/dist/install/host.d.ts.map +1 -0
- package/dist/install/host.js +90 -0
- package/dist/install/host.js.map +1 -0
- package/dist/install/index.d.ts +173 -0
- package/dist/install/index.d.ts.map +1 -0
- package/dist/install/index.js +793 -0
- package/dist/install/index.js.map +1 -0
- package/dist/install/wizard.d.ts +144 -0
- package/dist/install/wizard.d.ts.map +1 -0
- package/dist/install/wizard.js +362 -0
- package/dist/install/wizard.js.map +1 -0
- package/dist/lib/admin-artefacts.d.ts +70 -0
- package/dist/lib/admin-artefacts.d.ts.map +1 -0
- package/dist/lib/admin-artefacts.js +354 -0
- package/dist/lib/admin-artefacts.js.map +1 -0
- package/dist/lib/admin-surfaces.d.ts +298 -0
- package/dist/lib/admin-surfaces.d.ts.map +1 -0
- package/dist/lib/admin-surfaces.js +669 -0
- package/dist/lib/admin-surfaces.js.map +1 -0
- package/dist/lib/delegated-composer.d.ts +85 -0
- package/dist/lib/delegated-composer.d.ts.map +1 -0
- package/dist/lib/delegated-composer.js +241 -0
- package/dist/lib/delegated-composer.js.map +1 -0
- package/dist/lib/divergence-artefacts.d.ts +305 -0
- package/dist/lib/divergence-artefacts.d.ts.map +1 -0
- package/dist/lib/divergence-artefacts.js +828 -0
- package/dist/lib/divergence-artefacts.js.map +1 -0
- package/dist/lib/divergence.d.ts +337 -0
- package/dist/lib/divergence.d.ts.map +1 -0
- package/dist/lib/divergence.js +1005 -0
- package/dist/lib/divergence.js.map +1 -0
- package/dist/lib/docs-artefacts.d.ts +395 -0
- package/dist/lib/docs-artefacts.d.ts.map +1 -0
- package/dist/lib/docs-artefacts.js +781 -0
- package/dist/lib/docs-artefacts.js.map +1 -0
- package/dist/lib/emitted-exports.d.ts +21 -0
- package/dist/lib/emitted-exports.d.ts.map +1 -0
- package/dist/lib/emitted-exports.js +96 -0
- package/dist/lib/emitted-exports.js.map +1 -0
- package/dist/lib/emitted-freshness.d.ts +112 -0
- package/dist/lib/emitted-freshness.d.ts.map +1 -0
- package/dist/lib/emitted-freshness.js +288 -0
- package/dist/lib/emitted-freshness.js.map +1 -0
- package/dist/lib/entity-index-artefact.d.ts +95 -0
- package/dist/lib/entity-index-artefact.d.ts.map +1 -0
- package/dist/lib/entity-index-artefact.js +210 -0
- package/dist/lib/entity-index-artefact.js.map +1 -0
- package/dist/lib/instance-build-inputs.d.ts +108 -0
- package/dist/lib/instance-build-inputs.d.ts.map +1 -0
- package/dist/lib/instance-build-inputs.js +165 -0
- package/dist/lib/instance-build-inputs.js.map +1 -0
- package/dist/lib/module-docs.d.ts +473 -0
- package/dist/lib/module-docs.d.ts.map +1 -0
- package/dist/lib/module-docs.js +711 -0
- package/dist/lib/module-docs.js.map +1 -0
- package/dist/lib/module-package-subpaths.d.ts +56 -0
- package/dist/lib/module-package-subpaths.d.ts.map +1 -0
- package/dist/lib/module-package-subpaths.js +223 -0
- package/dist/lib/module-package-subpaths.js.map +1 -0
- package/dist/lib/module-packages.d.ts +200 -0
- package/dist/lib/module-packages.d.ts.map +1 -0
- package/dist/lib/module-packages.js +580 -0
- package/dist/lib/module-packages.js.map +1 -0
- package/dist/lib/module-population.d.ts +129 -0
- package/dist/lib/module-population.d.ts.map +1 -0
- package/dist/lib/module-population.js +172 -0
- package/dist/lib/module-population.js.map +1 -0
- package/dist/lib/module-roots.d.ts +337 -0
- package/dist/lib/module-roots.d.ts.map +1 -0
- package/dist/lib/module-roots.js +586 -0
- package/dist/lib/module-roots.js.map +1 -0
- package/dist/lib/nested-checkouts.d.ts +33 -0
- package/dist/lib/nested-checkouts.d.ts.map +1 -0
- package/dist/lib/nested-checkouts.js +160 -0
- package/dist/lib/nested-checkouts.js.map +1 -0
- package/dist/lib/platform-root.d.ts +43 -0
- package/dist/lib/platform-root.d.ts.map +1 -0
- package/dist/lib/platform-root.js +134 -0
- package/dist/lib/platform-root.js.map +1 -0
- package/dist/lib/platform-surface.d.ts +235 -0
- package/dist/lib/platform-surface.d.ts.map +1 -0
- package/dist/lib/platform-surface.js +393 -0
- package/dist/lib/platform-surface.js.map +1 -0
- package/dist/lib/port-registrations.d.ts +223 -0
- package/dist/lib/port-registrations.d.ts.map +1 -0
- package/dist/lib/port-registrations.js +532 -0
- package/dist/lib/port-registrations.js.map +1 -0
- package/dist/lib/read-size.d.ts +154 -0
- package/dist/lib/read-size.d.ts.map +1 -0
- package/dist/lib/read-size.js +182 -0
- package/dist/lib/read-size.js.map +1 -0
- package/dist/lib/registration-owners.d.ts +79 -0
- package/dist/lib/registration-owners.d.ts.map +1 -0
- package/dist/lib/registration-owners.js +77 -0
- package/dist/lib/registration-owners.js.map +1 -0
- package/dist/lib/release-index.d.ts +53 -0
- package/dist/lib/release-index.d.ts.map +1 -0
- package/dist/lib/release-index.js +162 -0
- package/dist/lib/release-index.js.map +1 -0
- package/dist/lib/repeating-timers.d.ts +79 -0
- package/dist/lib/repeating-timers.d.ts.map +1 -0
- package/dist/lib/repeating-timers.js +189 -0
- package/dist/lib/repeating-timers.js.map +1 -0
- package/dist/lib/source-text.d.ts +34 -0
- package/dist/lib/source-text.d.ts.map +1 -0
- package/dist/lib/source-text.js +80 -0
- package/dist/lib/source-text.js.map +1 -0
- package/dist/lib/specifiers.d.ts +20 -0
- package/dist/lib/specifiers.d.ts.map +1 -0
- package/dist/lib/specifiers.js +130 -0
- package/dist/lib/specifiers.js.map +1 -0
- package/dist/lib/sql-tables.d.ts +166 -0
- package/dist/lib/sql-tables.d.ts.map +1 -0
- package/dist/lib/sql-tables.js +464 -0
- package/dist/lib/sql-tables.js.map +1 -0
- package/dist/lib/switchable-modules.d.ts +54 -0
- package/dist/lib/switchable-modules.d.ts.map +1 -0
- package/dist/lib/switchable-modules.js +104 -0
- package/dist/lib/switchable-modules.js.map +1 -0
- package/dist/lib/tailwind-sources.d.ts +136 -0
- package/dist/lib/tailwind-sources.d.ts.map +1 -0
- package/dist/lib/tailwind-sources.js +307 -0
- package/dist/lib/tailwind-sources.js.map +1 -0
- package/dist/lib/ui-layer.d.ts +54 -0
- package/dist/lib/ui-layer.d.ts.map +1 -0
- package/dist/lib/ui-layer.js +57 -0
- package/dist/lib/ui-layer.js.map +1 -0
- package/dist/lib/workspace-packages.d.ts +186 -0
- package/dist/lib/workspace-packages.d.ts.map +1 -0
- package/dist/lib/workspace-packages.js +351 -0
- package/dist/lib/workspace-packages.js.map +1 -0
- package/dist/new-instance/deploy.d.ts +211 -0
- package/dist/new-instance/deploy.d.ts.map +1 -0
- package/dist/new-instance/deploy.js +1381 -0
- package/dist/new-instance/deploy.js.map +1 -0
- package/dist/new-instance/docs-toolchain.d.ts +66 -0
- package/dist/new-instance/docs-toolchain.d.ts.map +1 -0
- package/dist/new-instance/docs-toolchain.js +69 -0
- package/dist/new-instance/docs-toolchain.js.map +1 -0
- package/dist/new-instance/host.d.ts +124 -0
- package/dist/new-instance/host.d.ts.map +1 -0
- package/dist/new-instance/host.js +276 -0
- package/dist/new-instance/host.js.map +1 -0
- package/dist/new-instance/index.d.ts +118 -0
- package/dist/new-instance/index.d.ts.map +1 -0
- package/dist/new-instance/index.js +567 -0
- package/dist/new-instance/index.js.map +1 -0
- package/dist/new-instance/modules.d.ts +188 -0
- package/dist/new-instance/modules.d.ts.map +1 -0
- package/dist/new-instance/modules.js +392 -0
- package/dist/new-instance/modules.js.map +1 -0
- package/dist/new-instance/template.d.ts +505 -0
- package/dist/new-instance/template.d.ts.map +1 -0
- package/dist/new-instance/template.js +1886 -0
- package/dist/new-instance/template.js.map +1 -0
- package/dist/new-module/emit.d.ts +67 -0
- package/dist/new-module/emit.d.ts.map +1 -0
- package/dist/new-module/emit.js +1393 -0
- package/dist/new-module/emit.js.map +1 -0
- package/dist/new-module/host.d.ts +67 -0
- package/dist/new-module/host.d.ts.map +1 -0
- package/dist/new-module/host.js +224 -0
- package/dist/new-module/host.js.map +1 -0
- package/dist/new-module/index.d.ts +32 -0
- package/dist/new-module/index.d.ts.map +1 -0
- package/dist/new-module/index.js +193 -0
- package/dist/new-module/index.js.map +1 -0
- package/dist/new-module/spec.d.ts +188 -0
- package/dist/new-module/spec.d.ts.map +1 -0
- package/dist/new-module/spec.js +404 -0
- package/dist/new-module/spec.js.map +1 -0
- package/dist/new-module/text.d.ts +15 -0
- package/dist/new-module/text.d.ts.map +1 -0
- package/dist/new-module/text.js +22 -0
- package/dist/new-module/text.js.map +1 -0
- package/dist/new-storefront/dockerfile.d.ts +20 -0
- package/dist/new-storefront/dockerfile.d.ts.map +1 -0
- package/dist/new-storefront/dockerfile.js +131 -0
- package/dist/new-storefront/dockerfile.js.map +1 -0
- package/dist/new-storefront/gitignore.d.ts +23 -0
- package/dist/new-storefront/gitignore.d.ts.map +1 -0
- package/dist/new-storefront/gitignore.js +39 -0
- package/dist/new-storefront/gitignore.js.map +1 -0
- package/dist/new-storefront/index.d.ts +93 -0
- package/dist/new-storefront/index.d.ts.map +1 -0
- package/dist/new-storefront/index.js +329 -0
- package/dist/new-storefront/index.js.map +1 -0
- package/dist/new-storefront/npmrc.d.ts +98 -0
- package/dist/new-storefront/npmrc.d.ts.map +1 -0
- package/dist/new-storefront/npmrc.js +217 -0
- package/dist/new-storefront/npmrc.js.map +1 -0
- package/dist/new-storefront/reference.d.ts +189 -0
- package/dist/new-storefront/reference.d.ts.map +1 -0
- package/dist/new-storefront/reference.js +430 -0
- package/dist/new-storefront/reference.js.map +1 -0
- package/dist/new-storefront/rewrite.d.ts +171 -0
- package/dist/new-storefront/rewrite.d.ts.map +1 -0
- package/dist/new-storefront/rewrite.js +701 -0
- package/dist/new-storefront/rewrite.js.map +1 -0
- package/dist/release-index.json +284 -0
- package/dist/rules/action-route-permissions.d.ts +141 -0
- package/dist/rules/action-route-permissions.d.ts.map +1 -0
- package/dist/rules/action-route-permissions.js +556 -0
- package/dist/rules/action-route-permissions.js.map +1 -0
- package/dist/rules/bundle-pairing.d.ts +74 -0
- package/dist/rules/bundle-pairing.d.ts.map +1 -0
- package/dist/rules/bundle-pairing.js +281 -0
- package/dist/rules/bundle-pairing.js.map +1 -0
- package/dist/rules/channel-resolution.d.ts +17 -0
- package/dist/rules/channel-resolution.d.ts.map +1 -0
- package/dist/rules/channel-resolution.js +382 -0
- package/dist/rules/channel-resolution.js.map +1 -0
- package/dist/rules/command-coverage.d.ts +210 -0
- package/dist/rules/command-coverage.d.ts.map +1 -0
- package/dist/rules/command-coverage.js +714 -0
- package/dist/rules/command-coverage.js.map +1 -0
- package/dist/rules/container-imports.d.ts +60 -0
- package/dist/rules/container-imports.d.ts.map +1 -0
- package/dist/rules/container-imports.js +158 -0
- package/dist/rules/container-imports.js.map +1 -0
- package/dist/rules/default-language-prose.d.ts +212 -0
- package/dist/rules/default-language-prose.d.ts.map +1 -0
- package/dist/rules/default-language-prose.js +710 -0
- package/dist/rules/default-language-prose.js.map +1 -0
- package/dist/rules/diacritic-folds.d.ts +238 -0
- package/dist/rules/diacritic-folds.d.ts.map +1 -0
- package/dist/rules/diacritic-folds.js +681 -0
- package/dist/rules/diacritic-folds.js.map +1 -0
- package/dist/rules/entity-tenant-classification.d.ts +171 -0
- package/dist/rules/entity-tenant-classification.d.ts.map +1 -0
- package/dist/rules/entity-tenant-classification.js +323 -0
- package/dist/rules/entity-tenant-classification.js.map +1 -0
- package/dist/rules/entry-presence.d.ts +142 -0
- package/dist/rules/entry-presence.d.ts.map +1 -0
- package/dist/rules/entry-presence.js +339 -0
- package/dist/rules/entry-presence.js.map +1 -0
- package/dist/rules/entry-scope.d.ts +91 -0
- package/dist/rules/entry-scope.d.ts.map +1 -0
- package/dist/rules/entry-scope.js +404 -0
- package/dist/rules/entry-scope.js.map +1 -0
- package/dist/rules/env-inputs.d.ts +222 -0
- package/dist/rules/env-inputs.d.ts.map +1 -0
- package/dist/rules/env-inputs.js +951 -0
- package/dist/rules/env-inputs.js.map +1 -0
- package/dist/rules/kernel-boundary.d.ts +37 -0
- package/dist/rules/kernel-boundary.d.ts.map +1 -0
- package/dist/rules/kernel-boundary.js +195 -0
- package/dist/rules/kernel-boundary.js.map +1 -0
- package/dist/rules/nul-bytes.d.ts +233 -0
- package/dist/rules/nul-bytes.d.ts.map +1 -0
- package/dist/rules/nul-bytes.js +332 -0
- package/dist/rules/nul-bytes.js.map +1 -0
- package/dist/rules/platform-surface.d.ts +479 -0
- package/dist/rules/platform-surface.d.ts.map +1 -0
- package/dist/rules/platform-surface.js +749 -0
- package/dist/rules/platform-surface.js.map +1 -0
- package/dist/rules/port-catches.d.ts +225 -0
- package/dist/rules/port-catches.d.ts.map +1 -0
- package/dist/rules/port-catches.js +1374 -0
- package/dist/rules/port-catches.js.map +1 -0
- package/dist/rules/port-shape.d.ts +213 -0
- package/dist/rules/port-shape.d.ts.map +1 -0
- package/dist/rules/port-shape.js +670 -0
- package/dist/rules/port-shape.js.map +1 -0
- package/dist/rules/queue-names.d.ts +108 -0
- package/dist/rules/queue-names.d.ts.map +1 -0
- package/dist/rules/queue-names.js +395 -0
- package/dist/rules/queue-names.js.map +1 -0
- package/dist/rules/singleton-identity.d.ts +205 -0
- package/dist/rules/singleton-identity.d.ts.map +1 -0
- package/dist/rules/singleton-identity.js +830 -0
- package/dist/rules/singleton-identity.js.map +1 -0
- package/dist/rules/subscribe-seam.d.ts +121 -0
- package/dist/rules/subscribe-seam.d.ts.map +1 -0
- package/dist/rules/subscribe-seam.js +594 -0
- package/dist/rules/subscribe-seam.js.map +1 -0
- package/dist/rules/transaction-context.d.ts +40 -0
- package/dist/rules/transaction-context.d.ts.map +1 -0
- package/dist/rules/transaction-context.js +294 -0
- package/dist/rules/transaction-context.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,926 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `endora` — the argv layer, and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* Three properties are contract (`contracts/cli-surface.md` §1–§2):
|
|
6
|
+
*
|
|
7
|
+
* * **`node:util`'s `parseArgs`**, so the program takes no dependency to read
|
|
8
|
+
* its own arguments, and an unrecognised flag is a refusal rather than a
|
|
9
|
+
* silent ignore.
|
|
10
|
+
* * **non-interactive by default, and guaranteed** (`cli-product.md` R2.5c,
|
|
11
|
+
* amending R2.5). A value the tool invented is still a value nobody
|
|
12
|
+
* reviewed, and nothing here invents one: a command that resolves inputs
|
|
13
|
+
* prints a provenance line whose `defaulted=` is `0`. What the amendment
|
|
14
|
+
* added is a *fallback* — on a terminal, a missing **required** input is
|
|
15
|
+
* asked for rather than refused. A prompt is issued only when stdin and
|
|
16
|
+
* stdout are both TTYs, `--non-interactive` and `--dry-run` are absent and
|
|
17
|
+
* no CI marker is set; failing any of those, a missing required input is
|
|
18
|
+
* exit `1` naming every one of them and the flag that supplies each, in one
|
|
19
|
+
* refusal. No command may block on a question nobody can answer.
|
|
20
|
+
* * **one meaning per exit code.** `0` did what it was asked and found nothing
|
|
21
|
+
* to report; `1` a finding or a refusal the author can act on; `2` an input
|
|
22
|
+
* it could not read. `2` is never reported as clean and never merged into
|
|
23
|
+
* `1`: a run that could not read its input has said nothing about the tree.
|
|
24
|
+
*
|
|
25
|
+
* It reads no configuration file. There is no `.endorarc` and no environment
|
|
26
|
+
* variable that changes a verdict.
|
|
27
|
+
*/
|
|
28
|
+
import { realpathSync } from 'node:fs';
|
|
29
|
+
import { resolve } from 'node:path';
|
|
30
|
+
import { fileURLToPath } from 'node:url';
|
|
31
|
+
import { parseArgs } from 'node:util';
|
|
32
|
+
import { estateIds, runCheck } from '../check/index.js';
|
|
33
|
+
import { NotAModulePackageError } from '../check/layout.js';
|
|
34
|
+
import { runNewModule } from '../new-module/index.js';
|
|
35
|
+
import { ScaffoldHostError, ScaffoldInputError } from '../new-module/spec.js';
|
|
36
|
+
import { DeclarationLoadError } from '../inputs/declaration.js';
|
|
37
|
+
import { inputForFlag } from '../inputs/resolve.js';
|
|
38
|
+
import { MissingInputsError, runNewStorefront, storefrontInputFlags, } from '../new-storefront/index.js';
|
|
39
|
+
import { StorefrontHostError, StorefrontInputError } from '../new-storefront/reference.js';
|
|
40
|
+
import { InstanceHostError, InstanceInputError, runNewInstance, } from '../new-instance/index.js';
|
|
41
|
+
import { InstallHostError, InstallInputError, runInstall } from '../install/index.js';
|
|
42
|
+
import { DevHostError, DevInputError, runDev } from '../dev/index.js';
|
|
43
|
+
import { generateReport, GenerateHostError, GenerateInputError, runGenerate, } from '../generate/index.js';
|
|
44
|
+
const USAGE = `endora — scaffolding and conformance tooling for Endora Commerce modules.
|
|
45
|
+
|
|
46
|
+
Usage:
|
|
47
|
+
endora new module <id> --name <text> --description <text> [options]
|
|
48
|
+
endora new instance <dir> [--module <id>...] [--without <member>...] [--deployment <name>]
|
|
49
|
+
[--registry <url>] [--topology single-host|three-host] [--dry-run]
|
|
50
|
+
endora new storefront <dir> [--registry <url>] [--<input> <value>...] [--dry-run]
|
|
51
|
+
endora install [<dir>] [--admin-email <e>] [--admin-password <p>] [--admin-first-name <f>]
|
|
52
|
+
[--admin-last-name <l>] [--demo | --no-demo] [--without <member>...]
|
|
53
|
+
[--no-services] [--no-storefront] [--storefront-dir <path>]
|
|
54
|
+
[--module <id>...] [--deployment <name>] [--registry <url>]
|
|
55
|
+
[--topology single-host|three-host] [--non-interactive] [--dry-run]
|
|
56
|
+
endora generate [--dry-run]
|
|
57
|
+
endora dev [--storefront-dir <path>] [--no-storefront]
|
|
58
|
+
endora --help
|
|
59
|
+
|
|
60
|
+
\`endora new module\` writes a module package that is composed by the platform,
|
|
61
|
+
gated by the operator, discoverable in the command palette and correct on its
|
|
62
|
+
first commit. It does not author the package's package.json: that file is
|
|
63
|
+
rendered by the platform's own manifest generator, so the two cannot disagree.
|
|
64
|
+
|
|
65
|
+
Options for \`new module\`:
|
|
66
|
+
--name <text> the module's human-readable name (required)
|
|
67
|
+
--description <text> one sentence; seeds the package description (required)
|
|
68
|
+
--dir <path> where to write (default: the workspace's module tree)
|
|
69
|
+
--scope <@scope/> the npm scope; must be the workspace's own
|
|
70
|
+
--depends <id>[,<id>...] manifest dependencies (repeatable)
|
|
71
|
+
--permission <code>=<label> a permission code and its operator-facing label (repeatable)
|
|
72
|
+
--action <id>=<route> a command-palette action and the admin route it opens (repeatable)
|
|
73
|
+
--icon <KnownIconName> the icon for the emitted actions (default: Boxes)
|
|
74
|
+
--admin <navSection> emit the ./admin layer: a gated screen and the sidebar entry
|
|
75
|
+
that opens it, in the named section (main, sales, catalog,
|
|
76
|
+
inventory, pricing, customers, channels, content, messaging,
|
|
77
|
+
newsletter, analyticsAds, system). Needs a --permission
|
|
78
|
+
--entities own a table: an entity, a migration and the ./migrations subpath
|
|
79
|
+
--tenant-scope <scope> org-scoped | customer-scoped | global (default: org-scoped)
|
|
80
|
+
--ports publish a type-only ./ports subpath other modules resolve
|
|
81
|
+
--worker register a queue consumer through the module seam
|
|
82
|
+
--subscriber register an event subscription through the module seam
|
|
83
|
+
--activation-setting <code> the Setting holding the operator's on/off choice
|
|
84
|
+
(default: <id>.enabled)
|
|
85
|
+
--non-deactivatable <reason> declare that the platform cannot run without this module
|
|
86
|
+
--dry-run report what would be written; write nothing
|
|
87
|
+
|
|
88
|
+
\`endora new instance\` writes the repository that composes this platform for one
|
|
89
|
+
deployment: one workspace, one module list, and a copy of nothing. It is not a
|
|
90
|
+
fork of the platform and holds no file of it — the platform, the admin shell and
|
|
91
|
+
every module arrive as dependencies, so a fix in any of them reaches you through
|
|
92
|
+
\`pnpm update\` with no file in your tree edited.
|
|
93
|
+
|
|
94
|
+
Options for \`new instance\`:
|
|
95
|
+
<dir> where to write. Required; it must be empty, or
|
|
96
|
+
hold nothing but a \`.env\` you placed there. Its
|
|
97
|
+
basename becomes the workspace name
|
|
98
|
+
--module <id>[,<id>...] a module to install (repeatable). The set is
|
|
99
|
+
closed over the manifests' own dependencies.
|
|
100
|
+
Given none, it writes the smallest set that
|
|
101
|
+
composes — the modules the platform cannot run
|
|
102
|
+
without, closed the same way
|
|
103
|
+
--without <member>[,<member>...]
|
|
104
|
+
a member not to write (repeatable): \`admin\` or
|
|
105
|
+
\`docs\`. The default is every member; \`backend\`
|
|
106
|
+
cannot be declined, because it is what composes the
|
|
107
|
+
platform. The module list is the same either way
|
|
108
|
+
--deployment <name> the directory under \`apps/\` holding your overlay
|
|
109
|
+
modules and your divergence declaration, and the
|
|
110
|
+
value of \`DEPLOYMENT\` (default: the workspace name)
|
|
111
|
+
--registry <url> the endpoint the instance installs
|
|
112
|
+
\`@endora-commerce/*\` from. It writes an \`.npmrc\`
|
|
113
|
+
naming that endpoint, with the token as an
|
|
114
|
+
environment reference and never as a value
|
|
115
|
+
--topology <name> which machine layout the EXAMPLE deployment files
|
|
116
|
+
in \`deploy/\` describe: \`single-host\` (default) puts
|
|
117
|
+
every layer on one machine, \`three-host\` writes one
|
|
118
|
+
compose example and one \`.env.example\` per machine
|
|
119
|
+
for a backend, a storefront and an admin that scale
|
|
120
|
+
apart. It selects; it records nothing — no file in
|
|
121
|
+
the tree carries the value and nothing reads it back
|
|
122
|
+
--dry-run report every file it would write, the resolved
|
|
123
|
+
module set with its closure, and every omission;
|
|
124
|
+
write nothing
|
|
125
|
+
|
|
126
|
+
\`endora install\` is the one command: it writes the instance (and, from inside a
|
|
127
|
+
checkout of this repository, the storefront beside it), starts the development
|
|
128
|
+
services, installs, generates, builds, migrates, installs every module and creates
|
|
129
|
+
the administrator — the sequence \`endora new instance\` prints and nothing else. It
|
|
130
|
+
composes the two \`new\` commands and reimplements neither, and every step is echoed
|
|
131
|
+
before it runs so an operator can reproduce any one of them by hand. A failing step
|
|
132
|
+
exits with that step's own code and prints what is left.
|
|
133
|
+
|
|
134
|
+
With no \`--module\` it installs every module of the open-source set, each switched
|
|
135
|
+
on; switch any of them off later under Modules in the admin, without losing its
|
|
136
|
+
data. Run where nothing of ours is installed beside the target — from an empty
|
|
137
|
+
directory, or through \`npx\` — it first installs this release's packages into a
|
|
138
|
+
temporary directory to read the module set from, and removes it when done.
|
|
139
|
+
|
|
140
|
+
At a terminal it asks what the flags below did not answer — at most seven questions,
|
|
141
|
+
and Enter takes the recommendation wherever there is one: every part, the services
|
|
142
|
+
started. The demo question has no recommendation and the administrator has no
|
|
143
|
+
default. Every question has a flag; with \`--non-interactive\`, \`--dry-run\`, a CI
|
|
144
|
+
marker or no terminal it asks nothing, and a missing answer is one refusal naming
|
|
145
|
+
every flag still owed.
|
|
146
|
+
|
|
147
|
+
Options for \`install\`:
|
|
148
|
+
<dir> where the instance goes; it must be empty, or hold
|
|
149
|
+
nothing but a \`.env\` you placed there. Asked at a
|
|
150
|
+
terminal (recommending \`./endora-commerce\`),
|
|
151
|
+
required everywhere else
|
|
152
|
+
--admin-email <address> the administrator you sign in as. All four are
|
|
153
|
+
--admin-password <secret> required: nothing else creates an account, and the
|
|
154
|
+
--admin-first-name <text> password is never generated — it is the one value
|
|
155
|
+
--admin-last-name <text> you have to remember
|
|
156
|
+
--demo | --no-demo whether to seed every installed module's example
|
|
157
|
+
data. Required — asked at a terminal — and
|
|
158
|
+
deliberately with no default:
|
|
159
|
+
an instance you will sell from wants none of it and
|
|
160
|
+
one you are evaluating wants it before the first
|
|
161
|
+
screen. Seeding runs last and a failure in it does
|
|
162
|
+
not fail the install
|
|
163
|
+
--no-services do not start PostgreSQL, Redis, Meilisearch and the
|
|
164
|
+
mail catcher, and do not write their addresses into
|
|
165
|
+
the instance's \`.env\`. Use it when you run those
|
|
166
|
+
services yourself
|
|
167
|
+
--without <member>[,<member>...]
|
|
168
|
+
a member of the instance not to write (\`admin\`,
|
|
169
|
+
\`docs\`), passed to \`new instance\`; the default is
|
|
170
|
+
every member
|
|
171
|
+
--no-storefront write the instance alone. The storefront is copied
|
|
172
|
+
out of a checkout of this repository, so a run from
|
|
173
|
+
anywhere else needs this flag
|
|
174
|
+
--storefront-dir <path> where the storefront goes (default: \`<dir>-storefront\`,
|
|
175
|
+
a SIBLING — inside the instance it would be swept into
|
|
176
|
+
that workspace and become a member of it)
|
|
177
|
+
--module, --deployment, --registry, --topology
|
|
178
|
+
passed through to \`new instance\` unread
|
|
179
|
+
--non-interactive ask nothing, even at a terminal
|
|
180
|
+
--dry-run report every file and every step; write nothing,
|
|
181
|
+
start nothing, run nothing and ask nothing
|
|
182
|
+
|
|
183
|
+
\`endora dev\` is what an instance's \`pnpm run dev:all\` runs: the API (\`pnpm run
|
|
184
|
+
start\`), the admin preview (\`pnpm run preview:admin\`) and, when there is one, the
|
|
185
|
+
storefront beside the instance (its \`pnpm run dev\`), in one terminal with each
|
|
186
|
+
line prefixed by its layer. Ctrl-C stops all of them; any one ending stops the
|
|
187
|
+
rest and names which. It changes no layer's own build or start command — each is
|
|
188
|
+
still deployed on its own. Run it from the instance's root.
|
|
189
|
+
|
|
190
|
+
Options for \`dev\`:
|
|
191
|
+
--storefront-dir <path> where the storefront is (default: \`<dir>-storefront\`
|
|
192
|
+
beside the instance, when it exists)
|
|
193
|
+
--no-storefront start the API and the admin preview only
|
|
194
|
+
|
|
195
|
+
\`endora generate\` renders the two files an instance's admin project is built
|
|
196
|
+
from and commits neither: the contribution registry of the module packages this
|
|
197
|
+
instance installed, and the stylesheet enumeration that makes their utility
|
|
198
|
+
classes survive Tailwind's scan. Both are facts about the install rather than
|
|
199
|
+
about the tree, so they are regenerated after every \`pnpm install\` and are
|
|
200
|
+
git-ignored. Run it anywhere inside the instance; it finds the workspace root
|
|
201
|
+
upwards. It reports every package the discovery excluded, because a linked
|
|
202
|
+
package is invisible to this instance's runtime discovery too.
|
|
203
|
+
|
|
204
|
+
\`endora new storefront\` copies the reference storefront out of this repository
|
|
205
|
+
into a directory you then own outright, and rewrites every declaration in it that
|
|
206
|
+
names something above the storefront's own directory: each \`workspace:\` range
|
|
207
|
+
into published semver, each configuration file the storefront extends into a
|
|
208
|
+
vendored standalone copy, and each glob naming the workspace's package tree into
|
|
209
|
+
the place a standalone application finds those packages. It keeps no channel back
|
|
210
|
+
to what it wrote — a scaffold that did would be a kit wearing a different name
|
|
211
|
+
(D-195). An outward declaration it cannot make standalone is a refusal, never a
|
|
212
|
+
file copied out unchanged.
|
|
213
|
+
|
|
214
|
+
Options for \`new storefront\`:
|
|
215
|
+
<dir> where to write. Required; it must be empty, or
|
|
216
|
+
hold nothing but a \`.env\` you placed there
|
|
217
|
+
--<input> <value> a value for one of the inputs the reference
|
|
218
|
+
storefront declares, named after the variable in
|
|
219
|
+
lower case with underscores as dashes —
|
|
220
|
+
\`--next-public-api-base-url https://api.example.com\`.
|
|
221
|
+
Inputs resolve in one fixed order: this flag,
|
|
222
|
+
then a \`.env\` already in the target directory,
|
|
223
|
+
then a prompt if you are on a terminal, then a
|
|
224
|
+
refusal naming every one that is still missing.
|
|
225
|
+
Run with \`--dry-run\` to see the list first
|
|
226
|
+
--non-interactive refuse rather than ask, even on a terminal
|
|
227
|
+
--registry <url> the endpoint the scaffold installs \`@endora-commerce/*\`
|
|
228
|
+
from. It writes an \`.npmrc\` naming that endpoint for the
|
|
229
|
+
scopes the storefront declares, with the token as
|
|
230
|
+
\`\${ENDORA_NPM_TOKEN}\` — an environment reference pnpm
|
|
231
|
+
expands at install time, so the file holds no secret and
|
|
232
|
+
is committable. Omitted, it writes no \`.npmrc\` at all,
|
|
233
|
+
which is what a consumer of the public registry holds
|
|
234
|
+
--dry-run report the copy, every rewrite and every omission;
|
|
235
|
+
write nothing
|
|
236
|
+
|
|
237
|
+
\`endora check\` evaluates the platform's whole static-check estate against one
|
|
238
|
+
module package. Every rule in that estate gets exactly one verdict on every run —
|
|
239
|
+
it ran (clean, or with findings), it is \`not-applicable\` because the package
|
|
240
|
+
declares no subject for it or because its subject is the platform repository, it
|
|
241
|
+
is \`unreadable\` because an input the author can supply is absent, or it is
|
|
242
|
+
\`pending\` because this build has no package-scope host for it yet. A rule that
|
|
243
|
+
is neither run nor explained is the silent skip the whole design refuses, which
|
|
244
|
+
is why the incompleteness is **printed** rather than waived: while any rule is
|
|
245
|
+
\`pending\` the run exits 2 and names the phase that lands it.
|
|
246
|
+
|
|
247
|
+
Options for \`check\`:
|
|
248
|
+
[path] the module package to check (default: the working
|
|
249
|
+
directory, or the nearest ancestor declaring
|
|
250
|
+
\`endora: { "type": "module", "id": … }\`)
|
|
251
|
+
--rule <id> evaluate only this rule (repeatable). It can only
|
|
252
|
+
remove; there is no flag that adds a rule, changes
|
|
253
|
+
a verdict or relaxes a refusal
|
|
254
|
+
--as-platform read every acknowledged finding as a finding. The
|
|
255
|
+
package's own ledger answers the author's question
|
|
256
|
+
(is my module in the state I decided it should be
|
|
257
|
+
in?) and never the platform's (does this module
|
|
258
|
+
satisfy the rules we admit modules on?)
|
|
259
|
+
--list-rules print the estate and each rule's classification
|
|
260
|
+
|
|
261
|
+
Exit codes: 0 the estate was completely evaluated and found nothing; 1 it was
|
|
262
|
+
completely evaluated and there are findings; 2 the picture is incomplete — some
|
|
263
|
+
rule could not be read, or has no host in this build. \`findings=<n>\` is on the
|
|
264
|
+
arithmetic line whatever the code is.
|
|
265
|
+
`;
|
|
266
|
+
/**
|
|
267
|
+
* `strict: true` stays, and the option table grows instead.
|
|
268
|
+
*
|
|
269
|
+
* `endora new storefront` accepts one flag per input the reference storefront
|
|
270
|
+
* **declares** (feature 117, FR-010), and those names are the storefront's
|
|
271
|
+
* rather than this program's. Parsing that subcommand leniently was the obvious
|
|
272
|
+
* alternative and is the wrong one: it would turn a typo'd
|
|
273
|
+
* `--next-public-api-base-ur` from a loud refusal into a silently unset required
|
|
274
|
+
* input, which is the class of failure this whole feature exists to remove. So
|
|
275
|
+
* the names are resolved from the declaration first and handed in here.
|
|
276
|
+
*/
|
|
277
|
+
function parse(argv, declaredInputFlags = []) {
|
|
278
|
+
const { values, positionals } = parseArgs({
|
|
279
|
+
args: [...argv],
|
|
280
|
+
allowPositionals: true,
|
|
281
|
+
strict: true,
|
|
282
|
+
options: {
|
|
283
|
+
...Object.fromEntries(declaredInputFlags.map((flag) => [flag, { type: 'string' }])),
|
|
284
|
+
help: { type: 'boolean', short: 'h' },
|
|
285
|
+
rule: { type: 'string', multiple: true },
|
|
286
|
+
'as-platform': { type: 'boolean' },
|
|
287
|
+
'list-rules': { type: 'boolean' },
|
|
288
|
+
name: { type: 'string' },
|
|
289
|
+
description: { type: 'string' },
|
|
290
|
+
dir: { type: 'string' },
|
|
291
|
+
scope: { type: 'string' },
|
|
292
|
+
depends: { type: 'string', multiple: true },
|
|
293
|
+
permission: { type: 'string', multiple: true },
|
|
294
|
+
action: { type: 'string', multiple: true },
|
|
295
|
+
icon: { type: 'string' },
|
|
296
|
+
admin: { type: 'string' },
|
|
297
|
+
entities: { type: 'boolean' },
|
|
298
|
+
ports: { type: 'boolean' },
|
|
299
|
+
worker: { type: 'boolean' },
|
|
300
|
+
subscriber: { type: 'boolean' },
|
|
301
|
+
'tenant-scope': { type: 'string' },
|
|
302
|
+
'activation-setting': { type: 'string' },
|
|
303
|
+
'non-deactivatable': { type: 'string' },
|
|
304
|
+
module: { type: 'string', multiple: true },
|
|
305
|
+
without: { type: 'string', multiple: true },
|
|
306
|
+
deployment: { type: 'string' },
|
|
307
|
+
registry: { type: 'string' },
|
|
308
|
+
topology: { type: 'string' },
|
|
309
|
+
'non-interactive': { type: 'boolean' },
|
|
310
|
+
'dry-run': { type: 'boolean' },
|
|
311
|
+
// `endora install` (feature 125). `--no-*` is a flag of its own rather
|
|
312
|
+
// than a negation `parseArgs` understands: the library has no negation,
|
|
313
|
+
// and a default that is `true` needs a name to be turned off by.
|
|
314
|
+
'no-services': { type: 'boolean' },
|
|
315
|
+
'no-storefront': { type: 'boolean' },
|
|
316
|
+
'storefront-dir': { type: 'string' },
|
|
317
|
+
'admin-email': { type: 'string' },
|
|
318
|
+
'admin-password': { type: 'string' },
|
|
319
|
+
'admin-first-name': { type: 'string' },
|
|
320
|
+
'admin-last-name': { type: 'string' },
|
|
321
|
+
'revalidate-secret': { type: 'string' },
|
|
322
|
+
demo: { type: 'boolean' },
|
|
323
|
+
'no-demo': { type: 'boolean' },
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
return { values, positionals };
|
|
327
|
+
}
|
|
328
|
+
function asString(value) {
|
|
329
|
+
return typeof value === 'string' ? value : undefined;
|
|
330
|
+
}
|
|
331
|
+
function asList(value) {
|
|
332
|
+
if (Array.isArray(value))
|
|
333
|
+
return value.flatMap((entry) => entry.split(','));
|
|
334
|
+
return typeof value === 'string' ? value.split(',') : [];
|
|
335
|
+
}
|
|
336
|
+
function asFlag(value) {
|
|
337
|
+
return value === true;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* `endora check` — the argv half.
|
|
341
|
+
*
|
|
342
|
+
* It resolves nothing and decides nothing: the package comes off the working
|
|
343
|
+
* directory (or one positional), the verdicts come from the estate, and the exit
|
|
344
|
+
* code is `runCheck`'s. A flag that could change a verdict is the configuration
|
|
345
|
+
* this program does not have.
|
|
346
|
+
*/
|
|
347
|
+
function runCheckCommand(parsed, positionals, cwd) {
|
|
348
|
+
if (asFlag(parsed.values['list-rules'])) {
|
|
349
|
+
for (const id of estateIds())
|
|
350
|
+
process.stdout.write(`${id}\n`);
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
if (positionals.length > 1) {
|
|
354
|
+
process.stderr.write(`endora: \`check\` takes one package path; got ${positionals.length} ` +
|
|
355
|
+
`(${positionals.join(', ')}).\n`);
|
|
356
|
+
return 1;
|
|
357
|
+
}
|
|
358
|
+
const rules = asList(parsed.values['rule']);
|
|
359
|
+
const known = new Set(estateIds());
|
|
360
|
+
const unknown = rules.filter((rule) => !known.has(rule));
|
|
361
|
+
if (unknown.length > 0) {
|
|
362
|
+
process.stderr.write(`endora: \`--rule\` names ${unknown.join(', ')}, which the estate does not hold. ` +
|
|
363
|
+
`Run \`endora check --list-rules\` for the ids.\n`);
|
|
364
|
+
return 1;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
const run = runCheck({
|
|
368
|
+
cwd: positionals[0] === undefined ? cwd : resolve(cwd, positionals[0]),
|
|
369
|
+
rules,
|
|
370
|
+
asPlatform: asFlag(parsed.values['as-platform']),
|
|
371
|
+
});
|
|
372
|
+
for (const line of run.lines)
|
|
373
|
+
process.stdout.write(`${line}\n`);
|
|
374
|
+
return run.report.exitCode;
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
if (error instanceof NotAModulePackageError) {
|
|
378
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
379
|
+
return 1;
|
|
380
|
+
}
|
|
381
|
+
// Anything else is an input this run could not read, and a run that could
|
|
382
|
+
// not read its input has said nothing about the tree.
|
|
383
|
+
process.stderr.write(`endora: check could not read its input: ` +
|
|
384
|
+
`${error instanceof Error ? error.message : String(error)}\n`);
|
|
385
|
+
return 2;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* `endora new storefront` — the argv half.
|
|
390
|
+
*
|
|
391
|
+
* It decides nothing. The target comes off one positional, the population and
|
|
392
|
+
* every rewrite come from the reference storefront in this checkout, and the
|
|
393
|
+
* exit code is the refusal's class: an author-fixable refusal is 1, a checkout
|
|
394
|
+
* this command cannot read a reference storefront out of is 2.
|
|
395
|
+
*/
|
|
396
|
+
async function runNewStorefrontCommand(parsed, rest, cwd, declaredInputFlags) {
|
|
397
|
+
if (rest.length > 1) {
|
|
398
|
+
process.stderr.write(`endora: \`new storefront\` takes one directory; got ${String(rest.length)} ` +
|
|
399
|
+
`(${rest.join(', ')}).\n`);
|
|
400
|
+
return 1;
|
|
401
|
+
}
|
|
402
|
+
// The inputs, keyed by **variable name**: the flag is derived from the name
|
|
403
|
+
// (`inputForFlag`), so a storefront declaring a variable this build has never
|
|
404
|
+
// heard of is supplied by its own flag with nothing here to update.
|
|
405
|
+
const inputs = {};
|
|
406
|
+
for (const flag of declaredInputFlags) {
|
|
407
|
+
const value = asString(parsed.values[flag]);
|
|
408
|
+
if (value !== undefined)
|
|
409
|
+
inputs[inputForFlag(flag)] = value;
|
|
410
|
+
}
|
|
411
|
+
try {
|
|
412
|
+
const result = await runNewStorefront({
|
|
413
|
+
...(rest[0] === undefined ? {} : { dir: rest[0] }),
|
|
414
|
+
...(asString(parsed.values['registry']) === undefined
|
|
415
|
+
? {}
|
|
416
|
+
: { registry: asString(parsed.values['registry']) }),
|
|
417
|
+
dryRun: asFlag(parsed.values['dry-run']),
|
|
418
|
+
nonInteractive: asFlag(parsed.values['non-interactive']),
|
|
419
|
+
inputs,
|
|
420
|
+
cwd,
|
|
421
|
+
});
|
|
422
|
+
const { plan } = result;
|
|
423
|
+
process.stdout.write(`endora new storefront ${result.targetDir}${result.dryRun ? ' — dry run, nothing written' : ''}\n`);
|
|
424
|
+
process.stdout.write(` ${result.dryRun ? 'would write' : 'wrote'} ${String(plan.files.length)} files, from ` +
|
|
425
|
+
`${result.reference.dir}\n`);
|
|
426
|
+
for (const range of plan.ranges) {
|
|
427
|
+
process.stdout.write(` ${range.field}.${range.name}: ${range.from} -> ${range.to}\n`);
|
|
428
|
+
}
|
|
429
|
+
for (const rewrite of plan.rewrites) {
|
|
430
|
+
process.stdout.write(` ${rewrite.file}: ${rewrite.specifier} -> ${rewrite.to}\n`);
|
|
431
|
+
}
|
|
432
|
+
for (const file of plan.files) {
|
|
433
|
+
if (file.source === null && file.path !== 'package.json') {
|
|
434
|
+
process.stdout.write(` ${file.path} — ${file.note ?? ''}\n`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
for (const omission of plan.omitted) {
|
|
438
|
+
process.stdout.write(` omitted ${omission.path} — ${omission.reason}\n`);
|
|
439
|
+
}
|
|
440
|
+
// R2.1 — one provenance line per run, accounting for every input, with a
|
|
441
|
+
// `defaulted=` count that is contract-bound to `0`. It is printed on every
|
|
442
|
+
// run including a dry one, because the claim it makes is about the run.
|
|
443
|
+
process.stdout.write(`\n${result.provenance}\n`);
|
|
444
|
+
for (const name of result.wouldPrompt) {
|
|
445
|
+
process.stdout.write(` would ask for ${name}\n`);
|
|
446
|
+
}
|
|
447
|
+
for (const name of result.wouldGenerate) {
|
|
448
|
+
process.stdout.write(` would generate ${name}\n`);
|
|
449
|
+
}
|
|
450
|
+
for (const name of result.unset) {
|
|
451
|
+
process.stdout.write(` left unset ${name} — optional\n`);
|
|
452
|
+
}
|
|
453
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
454
|
+
result.nextSteps.forEach((step, index) => {
|
|
455
|
+
process.stdout.write(` ${String(index + 1)}. ${step}\n`);
|
|
456
|
+
});
|
|
457
|
+
return 0;
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
// A missing required input is a ninth refusal class and it is exit `1`
|
|
461
|
+
// (R7.2): the operator has something to supply, and the message names every
|
|
462
|
+
// one at once rather than the first.
|
|
463
|
+
if (error instanceof MissingInputsError) {
|
|
464
|
+
process.stderr.write(`${error.message}\n`);
|
|
465
|
+
return 1;
|
|
466
|
+
}
|
|
467
|
+
if (error instanceof StorefrontInputError) {
|
|
468
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
469
|
+
return 1;
|
|
470
|
+
}
|
|
471
|
+
// A declaration this run could not read is a derivation failure, not an
|
|
472
|
+
// operator input: R7.1 — asking a human to type a value the tool failed to
|
|
473
|
+
// *read* is how a wrong value enters wearing the operator's authority.
|
|
474
|
+
if (error instanceof DeclarationLoadError) {
|
|
475
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
476
|
+
return 2;
|
|
477
|
+
}
|
|
478
|
+
if (error instanceof StorefrontHostError) {
|
|
479
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
480
|
+
return 2;
|
|
481
|
+
}
|
|
482
|
+
throw error;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* `endora new instance` — the argv half.
|
|
487
|
+
*
|
|
488
|
+
* It decides nothing. The target comes off one positional, the module set and
|
|
489
|
+
* every range come from the packages the command resolved, and the exit code is
|
|
490
|
+
* the refusal's class: an operator-fixable refusal is 1, an input the run could
|
|
491
|
+
* not read is 2 (`instance-tree.md` §4).
|
|
492
|
+
*/
|
|
493
|
+
/**
|
|
494
|
+
* `endora generate` — the artefacts an instance is built from and commits none
|
|
495
|
+
* of (`contracts/instance-tree.md` §2.6): its admin project's two, and its
|
|
496
|
+
* documentation site's navigation, module map and per-module reference pages.
|
|
497
|
+
*
|
|
498
|
+
* It takes no positional: the instance is the workspace above the working
|
|
499
|
+
* directory, so `pnpm -C admin run generate` and a client standing in the root
|
|
500
|
+
* both answer the same. A positional would be a second way to name a tree the
|
|
501
|
+
* run is already standing in.
|
|
502
|
+
*/
|
|
503
|
+
async function runGenerateCommand(parsed, rest, cwd) {
|
|
504
|
+
if (rest.length > 0) {
|
|
505
|
+
process.stderr.write(`endora: \`generate\` takes no argument; got ${rest.join(', ')}. It renders the ` +
|
|
506
|
+
`artefacts of the instance whose workspace root is above the current directory.\n`);
|
|
507
|
+
return 1;
|
|
508
|
+
}
|
|
509
|
+
try {
|
|
510
|
+
const result = await runGenerate({ cwd, dryRun: asFlag(parsed.values['dry-run']) });
|
|
511
|
+
process.stdout.write(`endora generate ${result.root}${result.dryRun ? ' — dry run, nothing written' : ''}\n`);
|
|
512
|
+
for (const line of generateReport(result))
|
|
513
|
+
process.stdout.write(` ${line}\n`);
|
|
514
|
+
process.stdout.write(` ${String(result.modules)} installed module package(s) in the population\n`);
|
|
515
|
+
return 0;
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
if (error instanceof GenerateInputError) {
|
|
519
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
520
|
+
return 1;
|
|
521
|
+
}
|
|
522
|
+
if (error instanceof GenerateHostError) {
|
|
523
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
524
|
+
return 2;
|
|
525
|
+
}
|
|
526
|
+
throw error;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async function runNewInstanceCommand(parsed, rest, cwd) {
|
|
530
|
+
if (rest.length > 1) {
|
|
531
|
+
process.stderr.write(`endora: \`new instance\` takes one directory; got ${String(rest.length)} ` +
|
|
532
|
+
`(${rest.join(', ')}).\n`);
|
|
533
|
+
return 1;
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
const result = await runNewInstance({
|
|
537
|
+
...(rest[0] === undefined ? {} : { dir: rest[0] }),
|
|
538
|
+
modules: asList(parsed.values['module']),
|
|
539
|
+
without: asList(parsed.values['without']),
|
|
540
|
+
...(asString(parsed.values['deployment']) === undefined
|
|
541
|
+
? {}
|
|
542
|
+
: { deployment: asString(parsed.values['deployment']) }),
|
|
543
|
+
...(asString(parsed.values['registry']) === undefined
|
|
544
|
+
? {}
|
|
545
|
+
: { registry: asString(parsed.values['registry']) }),
|
|
546
|
+
...(asString(parsed.values['topology']) === undefined
|
|
547
|
+
? {}
|
|
548
|
+
: { topology: asString(parsed.values['topology']) }),
|
|
549
|
+
dryRun: asFlag(parsed.values['dry-run']),
|
|
550
|
+
cwd,
|
|
551
|
+
});
|
|
552
|
+
const { plan, modules } = result;
|
|
553
|
+
process.stdout.write(`endora new instance ${result.targetDir}` +
|
|
554
|
+
`${result.dryRun ? ' — dry run, nothing written' : ''}\n`);
|
|
555
|
+
process.stdout.write(` ${result.dryRun ? 'would write' : 'wrote'} ${String(plan.files.length)} files ` +
|
|
556
|
+
`across ${plan.members.join(', ')}\n`);
|
|
557
|
+
for (const file of plan.files) {
|
|
558
|
+
process.stdout.write(` ${result.dryRun ? 'would write' : 'wrote'} ${file.path} — ${file.kind}\n`);
|
|
559
|
+
}
|
|
560
|
+
// §3 — the set, its closure and where the default came from, because a
|
|
561
|
+
// module list nobody can see the derivation of is a list.
|
|
562
|
+
process.stdout.write(`\n[modules] ${String(modules.ids.length)} in the set` +
|
|
563
|
+
`${modules.defaulted ? ' (no --module given: the modules the platform cannot run without)' : ''}` +
|
|
564
|
+
`: ${modules.ids.join(', ')}\n`);
|
|
565
|
+
if (modules.closure.length > 0) {
|
|
566
|
+
process.stdout.write(` added by closure: ${modules.closure.join(', ')}\n`);
|
|
567
|
+
}
|
|
568
|
+
if (plan.registry !== null) {
|
|
569
|
+
process.stdout.write(` installs from ${plan.registry}\n`);
|
|
570
|
+
}
|
|
571
|
+
for (const omission of plan.omitted) {
|
|
572
|
+
process.stdout.write(` omitted ${omission.path} — ${omission.reason}\n`);
|
|
573
|
+
}
|
|
574
|
+
process.stdout.write(` wiring: ${String(result.wiringLines)} lines\n`);
|
|
575
|
+
process.stdout.write(`\n${result.provenance}\n`);
|
|
576
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
577
|
+
result.nextSteps.forEach((step, index) => {
|
|
578
|
+
process.stdout.write(` ${String(index + 1)}. ${step}\n`);
|
|
579
|
+
});
|
|
580
|
+
return 0;
|
|
581
|
+
}
|
|
582
|
+
catch (error) {
|
|
583
|
+
if (error instanceof InstanceInputError) {
|
|
584
|
+
process.stderr.write(`endora: [${error.refusal}] ${error.message}\n`);
|
|
585
|
+
return 1;
|
|
586
|
+
}
|
|
587
|
+
if (error instanceof InstanceHostError) {
|
|
588
|
+
process.stderr.write(`endora: [${error.refusal}] ${error.message}\n`);
|
|
589
|
+
return 2;
|
|
590
|
+
}
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* `endora install` — the argv half (feature 125, FR-140…FR-161).
|
|
596
|
+
*
|
|
597
|
+
* It decides nothing of its own. Every value is a flag, the two trees are
|
|
598
|
+
* written by the two `new` commands, and the exit code is either the refusal's
|
|
599
|
+
* class — 1 for an operator-fixable refusal, 2 for an input it could not read —
|
|
600
|
+
* or, when the pipeline ran and a step failed, **that step's own code** (FR-156).
|
|
601
|
+
*/
|
|
602
|
+
async function runInstallCommand(parsed, rest, cwd) {
|
|
603
|
+
if (rest.length > 1) {
|
|
604
|
+
process.stderr.write(`endora: \`install\` takes one directory; got ${String(rest.length)} ` +
|
|
605
|
+
`(${rest.join(', ')}).\n`);
|
|
606
|
+
return 1;
|
|
607
|
+
}
|
|
608
|
+
// `--demo` and `--no-demo` are two flags and one answer, so a run that gives
|
|
609
|
+
// both is a run whose author believes two different things. Refused rather
|
|
610
|
+
// than resolved by precedence: a precedence rule here would silently discard
|
|
611
|
+
// half of what they typed.
|
|
612
|
+
if (asFlag(parsed.values['demo']) && asFlag(parsed.values['no-demo'])) {
|
|
613
|
+
process.stderr.write('endora: `--demo` and `--no-demo` were both given, and they are the two answers to one ' +
|
|
614
|
+
'question. Pass one.\n');
|
|
615
|
+
return 1;
|
|
616
|
+
}
|
|
617
|
+
const demo = asFlag(parsed.values['demo'])
|
|
618
|
+
? true
|
|
619
|
+
: asFlag(parsed.values['no-demo'])
|
|
620
|
+
? false
|
|
621
|
+
: undefined;
|
|
622
|
+
try {
|
|
623
|
+
const result = await runInstall({
|
|
624
|
+
...(rest[0] === undefined ? {} : { dir: rest[0] }),
|
|
625
|
+
cwd,
|
|
626
|
+
modules: asList(parsed.values['module']),
|
|
627
|
+
...(asString(parsed.values['deployment']) === undefined
|
|
628
|
+
? {}
|
|
629
|
+
: { deployment: asString(parsed.values['deployment']) }),
|
|
630
|
+
...(asString(parsed.values['registry']) === undefined
|
|
631
|
+
? {}
|
|
632
|
+
: { registry: asString(parsed.values['registry']) }),
|
|
633
|
+
...(asString(parsed.values['topology']) === undefined
|
|
634
|
+
? {}
|
|
635
|
+
: { topology: asString(parsed.values['topology']) }),
|
|
636
|
+
// A key only for a flag that was typed: the wizard reads *absent* as a
|
|
637
|
+
// question still to ask, and a `true` here would be an answer nobody gave.
|
|
638
|
+
...(asFlag(parsed.values['no-storefront']) ? { storefront: false } : {}),
|
|
639
|
+
...(asString(parsed.values['storefront-dir']) === undefined
|
|
640
|
+
? {}
|
|
641
|
+
: { storefrontDir: asString(parsed.values['storefront-dir']) }),
|
|
642
|
+
...(asFlag(parsed.values['no-services']) ? { services: false } : {}),
|
|
643
|
+
without: asList(parsed.values['without']),
|
|
644
|
+
...(demo === undefined ? {} : { demo }),
|
|
645
|
+
...(asString(parsed.values['admin-email']) === undefined
|
|
646
|
+
? {}
|
|
647
|
+
: { adminEmail: asString(parsed.values['admin-email']) }),
|
|
648
|
+
...(asString(parsed.values['admin-password']) === undefined
|
|
649
|
+
? {}
|
|
650
|
+
: { adminPassword: asString(parsed.values['admin-password']) }),
|
|
651
|
+
...(asString(parsed.values['admin-first-name']) === undefined
|
|
652
|
+
? {}
|
|
653
|
+
: { adminFirstName: asString(parsed.values['admin-first-name']) }),
|
|
654
|
+
...(asString(parsed.values['admin-last-name']) === undefined
|
|
655
|
+
? {}
|
|
656
|
+
: { adminLastName: asString(parsed.values['admin-last-name']) }),
|
|
657
|
+
...(asString(parsed.values['revalidate-secret']) === undefined
|
|
658
|
+
? {}
|
|
659
|
+
: { revalidateSecret: asString(parsed.values['revalidate-secret']) }),
|
|
660
|
+
dryRun: asFlag(parsed.values['dry-run']),
|
|
661
|
+
nonInteractive: asFlag(parsed.values['non-interactive']),
|
|
662
|
+
// The pipeline's own output is the operator's: every step inherits the
|
|
663
|
+
// descriptors, so what pnpm says is what they see, live.
|
|
664
|
+
echo: (line) => void process.stdout.write(`${line}\n`),
|
|
665
|
+
});
|
|
666
|
+
return result.exitCode;
|
|
667
|
+
}
|
|
668
|
+
catch (error) {
|
|
669
|
+
if (error instanceof InstallInputError) {
|
|
670
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
671
|
+
return 1;
|
|
672
|
+
}
|
|
673
|
+
if (error instanceof InstallHostError) {
|
|
674
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
675
|
+
// 2 for an input it could not read; a failed host install exits with
|
|
676
|
+
// that step's own code, like every other step (FR-156, D-271).
|
|
677
|
+
return error.exitCode;
|
|
678
|
+
}
|
|
679
|
+
// The two commands this one composes keep their own classes and their own
|
|
680
|
+
// codes: a refusal they make is theirs, and reporting it as this command's
|
|
681
|
+
// would lose the class `instance-tree.md` §4 assigns it.
|
|
682
|
+
if (error instanceof InstanceInputError || error instanceof StorefrontInputError) {
|
|
683
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
684
|
+
return 1;
|
|
685
|
+
}
|
|
686
|
+
if (error instanceof InstanceHostError || error instanceof StorefrontHostError) {
|
|
687
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
688
|
+
return 2;
|
|
689
|
+
}
|
|
690
|
+
if (error instanceof MissingInputsError) {
|
|
691
|
+
process.stderr.write(`${error.message}\n`);
|
|
692
|
+
return 1;
|
|
693
|
+
}
|
|
694
|
+
throw error;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* `endora dev` — the argv half of an instance's `pnpm run dev:all`
|
|
699
|
+
* (`specs/136-open-source-publication/` GAP-7, FR-060).
|
|
700
|
+
*
|
|
701
|
+
* The terminal's interrupt reaches this process and is forwarded: each layer
|
|
702
|
+
* runs in a process group of its own, so the supervisor — not the terminal —
|
|
703
|
+
* decides the order things stop in, and a backgrounded watcher inside a layer
|
|
704
|
+
* is stopped with it. An interrupt is exit 0: the operator asked for the stop.
|
|
705
|
+
*/
|
|
706
|
+
async function runDevCommand(parsed, rest, cwd) {
|
|
707
|
+
if (rest.length > 0) {
|
|
708
|
+
process.stderr.write(`endora: \`dev\` takes no argument; got ${rest.join(', ')}. Run it from the root of ` +
|
|
709
|
+
`the instance, as \`pnpm run dev:all\`.\n`);
|
|
710
|
+
return 1;
|
|
711
|
+
}
|
|
712
|
+
const storefrontDir = asString(parsed.values['storefront-dir']);
|
|
713
|
+
if (asFlag(parsed.values['no-storefront']) && storefrontDir !== undefined) {
|
|
714
|
+
process.stderr.write('endora: `--no-storefront` and `--storefront-dir` were both given, and they are two ' +
|
|
715
|
+
'answers to one question. Pass one.\n');
|
|
716
|
+
return 1;
|
|
717
|
+
}
|
|
718
|
+
const controller = new AbortController();
|
|
719
|
+
const interrupt = () => controller.abort();
|
|
720
|
+
process.on('SIGINT', interrupt);
|
|
721
|
+
process.on('SIGTERM', interrupt);
|
|
722
|
+
try {
|
|
723
|
+
return await runDev({
|
|
724
|
+
cwd,
|
|
725
|
+
storefront: asFlag(parsed.values['no-storefront']) ? false : storefrontDir,
|
|
726
|
+
write: (line) => void process.stdout.write(`${line}\n`),
|
|
727
|
+
signal: controller.signal,
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
catch (error) {
|
|
731
|
+
if (error instanceof DevInputError) {
|
|
732
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
733
|
+
return 1;
|
|
734
|
+
}
|
|
735
|
+
if (error instanceof DevHostError) {
|
|
736
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
737
|
+
return 2;
|
|
738
|
+
}
|
|
739
|
+
throw error;
|
|
740
|
+
}
|
|
741
|
+
finally {
|
|
742
|
+
process.off('SIGINT', interrupt);
|
|
743
|
+
process.off('SIGTERM', interrupt);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
export async function main(argv, cwd) {
|
|
747
|
+
// The one thing that has to happen **before** the parse: `new storefront`
|
|
748
|
+
// accepts a flag per input the reference storefront declares, and those names
|
|
749
|
+
// are the storefront's. Keyed on the two leading tokens rather than on a
|
|
750
|
+
// heuristic over the whole of argv, so the answer never depends on whether a
|
|
751
|
+
// flag's *value* happens to read like a subcommand.
|
|
752
|
+
let declaredInputFlags = [];
|
|
753
|
+
if (argv[0] === 'new' && argv[1] === 'storefront') {
|
|
754
|
+
try {
|
|
755
|
+
declaredInputFlags = await storefrontInputFlags(cwd);
|
|
756
|
+
}
|
|
757
|
+
catch (error) {
|
|
758
|
+
if (error instanceof StorefrontHostError || error instanceof DeclarationLoadError) {
|
|
759
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
760
|
+
return 2;
|
|
761
|
+
}
|
|
762
|
+
throw error;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
let parsed;
|
|
766
|
+
try {
|
|
767
|
+
parsed = parse(argv, declaredInputFlags);
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
process.stderr.write(`endora: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
771
|
+
process.stderr.write(`Run \`endora --help\` for the flags this build accepts.\n`);
|
|
772
|
+
return 1;
|
|
773
|
+
}
|
|
774
|
+
const [command, subject, ...rest] = parsed.positionals;
|
|
775
|
+
if (asFlag(parsed.values['help']) || command === undefined) {
|
|
776
|
+
process.stdout.write(USAGE);
|
|
777
|
+
return command === undefined && !asFlag(parsed.values['help']) ? 1 : 0;
|
|
778
|
+
}
|
|
779
|
+
if (command === 'check') {
|
|
780
|
+
return runCheckCommand(parsed, [subject, ...rest].filter((v) => v !== undefined), cwd);
|
|
781
|
+
}
|
|
782
|
+
if (command === 'install') {
|
|
783
|
+
return runInstallCommand(parsed, [subject, ...rest].filter((v) => v !== undefined), cwd);
|
|
784
|
+
}
|
|
785
|
+
if (command === 'generate') {
|
|
786
|
+
return runGenerateCommand(parsed, [subject, ...rest].filter((v) => v !== undefined), cwd);
|
|
787
|
+
}
|
|
788
|
+
if (command === 'dev') {
|
|
789
|
+
return runDevCommand(parsed, [subject, ...rest].filter((v) => v !== undefined), cwd);
|
|
790
|
+
}
|
|
791
|
+
if (command !== 'new') {
|
|
792
|
+
process.stderr.write(`endora: unknown command "${command}". This build provides \`new module\`, ` +
|
|
793
|
+
`\`new instance\`, \`new storefront\`, \`install\`, \`generate\`, \`dev\` and \`check\`.\n`);
|
|
794
|
+
return 1;
|
|
795
|
+
}
|
|
796
|
+
if (subject === 'storefront') {
|
|
797
|
+
return runNewStorefrontCommand(parsed, rest, cwd, declaredInputFlags);
|
|
798
|
+
}
|
|
799
|
+
if (subject === 'instance') {
|
|
800
|
+
return runNewInstanceCommand(parsed, rest, cwd);
|
|
801
|
+
}
|
|
802
|
+
if (subject !== 'module') {
|
|
803
|
+
process.stderr.write(`endora: unknown subject "${subject ?? '<none>'}" for \`new\`. This build provides ` +
|
|
804
|
+
`\`new module\`, \`new instance\` and \`new storefront\`; the other generators named ` +
|
|
805
|
+
`in the ` +
|
|
806
|
+
`scaffolding contract are not delivered.\n`);
|
|
807
|
+
return 1;
|
|
808
|
+
}
|
|
809
|
+
const id = rest[0];
|
|
810
|
+
if (rest.length > 1) {
|
|
811
|
+
process.stderr.write(`endora: \`new module\` takes one id; got ${rest.length} (${rest.join(', ')}).\n`);
|
|
812
|
+
return 1;
|
|
813
|
+
}
|
|
814
|
+
try {
|
|
815
|
+
const result = await runNewModule({
|
|
816
|
+
...(id === undefined ? {} : { id }),
|
|
817
|
+
...(asString(parsed.values['name']) === undefined
|
|
818
|
+
? {}
|
|
819
|
+
: { name: asString(parsed.values['name']) }),
|
|
820
|
+
...(asString(parsed.values['description']) === undefined
|
|
821
|
+
? {}
|
|
822
|
+
: { description: asString(parsed.values['description']) }),
|
|
823
|
+
...(asString(parsed.values['dir']) === undefined
|
|
824
|
+
? {}
|
|
825
|
+
: { dir: asString(parsed.values['dir']) }),
|
|
826
|
+
...(asString(parsed.values['scope']) === undefined
|
|
827
|
+
? {}
|
|
828
|
+
: { scope: asString(parsed.values['scope']) }),
|
|
829
|
+
dependencies: asList(parsed.values['depends']),
|
|
830
|
+
permissions: asList(parsed.values['permission']),
|
|
831
|
+
actions: asList(parsed.values['action']),
|
|
832
|
+
...(asString(parsed.values['icon']) === undefined
|
|
833
|
+
? {}
|
|
834
|
+
: { icon: asString(parsed.values['icon']) }),
|
|
835
|
+
...(asString(parsed.values['admin']) === undefined
|
|
836
|
+
? {}
|
|
837
|
+
: { admin: asString(parsed.values['admin']) }),
|
|
838
|
+
entities: asFlag(parsed.values['entities']),
|
|
839
|
+
ports: asFlag(parsed.values['ports']),
|
|
840
|
+
worker: asFlag(parsed.values['worker']),
|
|
841
|
+
subscriber: asFlag(parsed.values['subscriber']),
|
|
842
|
+
...(asString(parsed.values['tenant-scope']) === undefined
|
|
843
|
+
? {}
|
|
844
|
+
: { tenantScope: asString(parsed.values['tenant-scope']) }),
|
|
845
|
+
...(asString(parsed.values['activation-setting']) === undefined
|
|
846
|
+
? {}
|
|
847
|
+
: { activationSetting: asString(parsed.values['activation-setting']) }),
|
|
848
|
+
...(asString(parsed.values['non-deactivatable']) === undefined
|
|
849
|
+
? {}
|
|
850
|
+
: { nonDeactivatable: asString(parsed.values['non-deactivatable']) }),
|
|
851
|
+
dryRun: asFlag(parsed.values['dry-run']),
|
|
852
|
+
cwd,
|
|
853
|
+
});
|
|
854
|
+
if (result.dryRun) {
|
|
855
|
+
process.stdout.write(`endora new module ${result.spec.id} — dry run, nothing written.\n\n`);
|
|
856
|
+
for (const file of result.files) {
|
|
857
|
+
process.stdout.write(`--- ${file.path}\n${file.content}\n`);
|
|
858
|
+
}
|
|
859
|
+
process.stdout.write(`--- package.json\n(rendered by the platform's manifest generator once the sources ` +
|
|
860
|
+
`above are on disk; this command composes no field of it)\n\n`);
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
process.stdout.write(`endora new module ${result.spec.id} — ${result.packageName}\n`);
|
|
864
|
+
for (const file of result.files) {
|
|
865
|
+
process.stdout.write(` wrote ${file.path}\n`);
|
|
866
|
+
}
|
|
867
|
+
for (const written of result.renderedManifests) {
|
|
868
|
+
process.stdout.write(` rendered ${written}\n`);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
process.stdout.write(`\nNext steps, inside this repository:\n`);
|
|
872
|
+
result.nextSteps.forEach((step, index) => {
|
|
873
|
+
process.stdout.write(` ${String(index + 1)}. ${step}\n`);
|
|
874
|
+
});
|
|
875
|
+
return 0;
|
|
876
|
+
}
|
|
877
|
+
catch (error) {
|
|
878
|
+
if (error instanceof ScaffoldInputError) {
|
|
879
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
880
|
+
return 1;
|
|
881
|
+
}
|
|
882
|
+
if (error instanceof ScaffoldHostError) {
|
|
883
|
+
process.stderr.write(`endora: ${error.message}\n`);
|
|
884
|
+
return 2;
|
|
885
|
+
}
|
|
886
|
+
throw error;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Is this module the program the process was started to run?
|
|
891
|
+
*
|
|
892
|
+
* The comparison is between **realpaths**, and that is the whole content of the
|
|
893
|
+
* function. A package manager does not invoke `dist/bin/endora.js` directly: it
|
|
894
|
+
* links the `bin`, so `process.argv[1]` names the link — pnpm's shim execs
|
|
895
|
+
* `node "$basedir/../@endora-commerce/cli/dist/bin/endora.js"`, a path running
|
|
896
|
+
* through the `node_modules/@endora-commerce/cli` symlink into the
|
|
897
|
+
* content-addressed store — while Node's ESM loader resolves a module URL to
|
|
898
|
+
* its real location before evaluating it, so `import.meta.url` is the store
|
|
899
|
+
* path. Comparing the two as written is therefore false for **every** consumer
|
|
900
|
+
* who installed this package and true only in the checkout that developed it,
|
|
901
|
+
* where nothing is linked. Measured on a `pnpm pack`ed tarball installed into a
|
|
902
|
+
* scratch directory: `endora --help` printed nothing at all and exited 0.
|
|
903
|
+
*
|
|
904
|
+
* Both failure directions are worth naming. Answering *no* wrongly is the
|
|
905
|
+
* silence above. Answering *yes* wrongly would run the program on `import
|
|
906
|
+
* { main }`, which is what the `./` export exists for, so the negative case is
|
|
907
|
+
* asserted as well.
|
|
908
|
+
*
|
|
909
|
+
* An `argv[1]` naming nothing on disk answers `false` rather than throwing:
|
|
910
|
+
* `realpathSync` raises `ENOENT`, and an uncaught one here would turn a wrong
|
|
911
|
+
* guess about the invocation into a crash before any command is dispatched.
|
|
912
|
+
*/
|
|
913
|
+
export function isDirectEntry(entry, moduleUrl) {
|
|
914
|
+
if (entry === undefined)
|
|
915
|
+
return false;
|
|
916
|
+
try {
|
|
917
|
+
return realpathSync(entry) === realpathSync(fileURLToPath(moduleUrl));
|
|
918
|
+
}
|
|
919
|
+
catch {
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if (isDirectEntry(process.argv[1], import.meta.url)) {
|
|
924
|
+
process.exitCode = await main(process.argv.slice(2), process.cwd());
|
|
925
|
+
}
|
|
926
|
+
//# sourceMappingURL=endora.js.map
|