adispatchd 0.0.0-stage → 0.1.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 +195 -2
- package/dist/api/errors.d.ts +27 -0
- package/dist/api/errors.js +80 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.js +11 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/logs.d.ts +30 -0
- package/dist/api/logs.js +88 -0
- package/dist/api/logs.js.map +1 -0
- package/dist/api/serialize.d.ts +65 -0
- package/dist/api/serialize.js +135 -0
- package/dist/api/serialize.js.map +1 -0
- package/dist/api/server.d.ts +89 -0
- package/dist/api/server.js +751 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/sse.d.ts +90 -0
- package/dist/api/sse.js +225 -0
- package/dist/api/sse.js.map +1 -0
- package/dist/app.d.ts +173 -0
- package/dist/app.js +488 -0
- package/dist/app.js.map +1 -0
- package/dist/cli/config-validate.d.ts +6 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/config-validate.js.map +1 -0
- package/dist/cli/control.d.ts +31 -0
- package/dist/cli/control.js +186 -0
- package/dist/cli/control.js.map +1 -0
- package/dist/cli/http-client.d.ts +57 -0
- package/dist/cli/http-client.js +174 -0
- package/dist/cli/http-client.js.map +1 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-templates.d.ts +20 -0
- package/dist/cli/init-templates.js +275 -0
- package/dist/cli/init-templates.js.map +1 -0
- package/dist/cli/init.d.ts +16 -0
- package/dist/cli/init.js +107 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +25 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/program.d.ts +102 -0
- package/dist/cli/program.js +133 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/service.d.ts +27 -0
- package/dist/cli/service.js +252 -0
- package/dist/cli/service.js.map +1 -0
- package/dist/cli/start.d.ts +22 -0
- package/dist/cli/start.js +133 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/workflow-dry-run.d.ts +13 -0
- package/dist/cli/workflow-dry-run.js +144 -0
- package/dist/cli/workflow-dry-run.js.map +1 -0
- package/dist/config/field-catalog.d.ts +33 -0
- package/dist/config/field-catalog.js +172 -0
- package/dist/config/field-catalog.js.map +1 -0
- package/dist/config/filter-schema.d.ts +15 -0
- package/dist/config/filter-schema.js +110 -0
- package/dist/config/filter-schema.js.map +1 -0
- package/dist/config/index.d.ts +15 -0
- package/dist/config/index.js +16 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/issues.d.ts +27 -0
- package/dist/config/issues.js +37 -0
- package/dist/config/issues.js.map +1 -0
- package/dist/config/loader.d.ts +32 -0
- package/dist/config/loader.js +250 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/manager.d.ts +153 -0
- package/dist/config/manager.js +276 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/model.d.ts +334 -0
- package/dist/config/model.js +2 -0
- package/dist/config/model.js.map +1 -0
- package/dist/config/normalize.d.ts +20 -0
- package/dist/config/normalize.js +200 -0
- package/dist/config/normalize.js.map +1 -0
- package/dist/config/parameters.d.ts +37 -0
- package/dist/config/parameters.js +71 -0
- package/dist/config/parameters.js.map +1 -0
- package/dist/config/public.d.ts +6 -0
- package/dist/config/public.js +18 -0
- package/dist/config/public.js.map +1 -0
- package/dist/config/schemas.d.ts +538 -0
- package/dist/config/schemas.js +321 -0
- package/dist/config/schemas.js.map +1 -0
- package/dist/config/snapshot.d.ts +38 -0
- package/dist/config/snapshot.js +57 -0
- package/dist/config/snapshot.js.map +1 -0
- package/dist/config/validate.d.ts +33 -0
- package/dist/config/validate.js +346 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/core/clock.d.ts +59 -0
- package/dist/core/clock.js +133 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/duration.d.ts +19 -0
- package/dist/core/duration.js +93 -0
- package/dist/core/duration.js.map +1 -0
- package/dist/core/errors.d.ts +62 -0
- package/dist/core/errors.js +72 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/ids.d.ts +57 -0
- package/dist/core/ids.js +88 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +100 -0
- package/dist/core/logger.js +207 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/paths.d.ts +84 -0
- package/dist/core/paths.js +138 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/template.d.ts +136 -0
- package/dist/core/template.js +261 -0
- package/dist/core/template.js.map +1 -0
- package/dist/core/types.d.ts +544 -0
- package/dist/core/types.js +172 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/worker-protocol.d.ts +102 -0
- package/dist/core/worker-protocol.js +75 -0
- package/dist/core/worker-protocol.js.map +1 -0
- package/dist/executors/classify.d.ts +25 -0
- package/dist/executors/classify.js +54 -0
- package/dist/executors/classify.js.map +1 -0
- package/dist/executors/dispatcher.d.ts +35 -0
- package/dist/executors/dispatcher.js +68 -0
- package/dist/executors/dispatcher.js.map +1 -0
- package/dist/executors/env.d.ts +43 -0
- package/dist/executors/env.js +66 -0
- package/dist/executors/env.js.map +1 -0
- package/dist/executors/event-parser.d.ts +41 -0
- package/dist/executors/event-parser.js +106 -0
- package/dist/executors/event-parser.js.map +1 -0
- package/dist/executors/index.d.ts +12 -0
- package/dist/executors/index.js +13 -0
- package/dist/executors/index.js.map +1 -0
- package/dist/executors/log-capture.d.ts +57 -0
- package/dist/executors/log-capture.js +155 -0
- package/dist/executors/log-capture.js.map +1 -0
- package/dist/executors/process-identity.d.ts +124 -0
- package/dist/executors/process-identity.js +283 -0
- package/dist/executors/process-identity.js.map +1 -0
- package/dist/executors/shell-executor.d.ts +35 -0
- package/dist/executors/shell-executor.js +478 -0
- package/dist/executors/shell-executor.js.map +1 -0
- package/dist/executors/stream-redactor.d.ts +40 -0
- package/dist/executors/stream-redactor.js +89 -0
- package/dist/executors/stream-redactor.js.map +1 -0
- package/dist/executors/workspace.d.ts +10 -0
- package/dist/executors/workspace.js +47 -0
- package/dist/executors/workspace.js.map +1 -0
- package/dist/filters/evaluate.d.ts +77 -0
- package/dist/filters/evaluate.js +187 -0
- package/dist/filters/evaluate.js.map +1 -0
- package/dist/filters/fields.d.ts +35 -0
- package/dist/filters/fields.js +75 -0
- package/dist/filters/fields.js.map +1 -0
- package/dist/filters/index.d.ts +11 -0
- package/dist/filters/index.js +12 -0
- package/dist/filters/index.js.map +1 -0
- package/dist/filters/iso-date.d.ts +10 -0
- package/dist/filters/iso-date.js +42 -0
- package/dist/filters/iso-date.js.map +1 -0
- package/dist/filters/predicate.d.ts +24 -0
- package/dist/filters/predicate.js +178 -0
- package/dist/filters/predicate.js.map +1 -0
- package/dist/filters/priority.d.ts +51 -0
- package/dist/filters/priority.js +91 -0
- package/dist/filters/priority.js.map +1 -0
- package/dist/filters/pushdown.d.ts +12 -0
- package/dist/filters/pushdown.js +52 -0
- package/dist/filters/pushdown.js.map +1 -0
- package/dist/filters/shell-check.d.ts +110 -0
- package/dist/filters/shell-check.js +304 -0
- package/dist/filters/shell-check.js.map +1 -0
- package/dist/identities/github-app.d.ts +55 -0
- package/dist/identities/github-app.js +160 -0
- package/dist/identities/github-app.js.map +1 -0
- package/dist/identities/index.d.ts +6 -0
- package/dist/identities/index.js +7 -0
- package/dist/identities/index.js.map +1 -0
- package/dist/identities/jwt.d.ts +24 -0
- package/dist/identities/jwt.js +56 -0
- package/dist/identities/jwt.js.map +1 -0
- package/dist/lifecycle/context.d.ts +106 -0
- package/dist/lifecycle/context.js +170 -0
- package/dist/lifecycle/context.js.map +1 -0
- package/dist/lifecycle/engine.d.ts +185 -0
- package/dist/lifecycle/engine.js +551 -0
- package/dist/lifecycle/engine.js.map +1 -0
- package/dist/lifecycle/errors.d.ts +48 -0
- package/dist/lifecycle/errors.js +71 -0
- package/dist/lifecycle/errors.js.map +1 -0
- package/dist/lifecycle/index.d.ts +9 -0
- package/dist/lifecycle/index.js +9 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle/performers.d.ts +66 -0
- package/dist/lifecycle/performers.js +179 -0
- package/dist/lifecycle/performers.js.map +1 -0
- package/dist/notifications/index.d.ts +6 -0
- package/dist/notifications/index.js +7 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/service.d.ts +115 -0
- package/dist/notifications/service.js +364 -0
- package/dist/notifications/service.js.map +1 -0
- package/dist/persistence/actions.d.ts +101 -0
- package/dist/persistence/actions.js +196 -0
- package/dist/persistence/actions.js.map +1 -0
- package/dist/persistence/blocks.d.ts +85 -0
- package/dist/persistence/blocks.js +140 -0
- package/dist/persistence/blocks.js.map +1 -0
- package/dist/persistence/cron-firings.d.ts +55 -0
- package/dist/persistence/cron-firings.js +70 -0
- package/dist/persistence/cron-firings.js.map +1 -0
- package/dist/persistence/database.d.ts +60 -0
- package/dist/persistence/database.js +124 -0
- package/dist/persistence/database.js.map +1 -0
- package/dist/persistence/errors.d.ts +39 -0
- package/dist/persistence/errors.js +67 -0
- package/dist/persistence/errors.js.map +1 -0
- package/dist/persistence/events.d.ts +72 -0
- package/dist/persistence/events.js +83 -0
- package/dist/persistence/events.js.map +1 -0
- package/dist/persistence/index.d.ts +18 -0
- package/dist/persistence/index.js +19 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/instance-lock.d.ts +26 -0
- package/dist/persistence/instance-lock.js +117 -0
- package/dist/persistence/instance-lock.js.map +1 -0
- package/dist/persistence/lock-keys.d.ts +16 -0
- package/dist/persistence/lock-keys.js +41 -0
- package/dist/persistence/lock-keys.js.map +1 -0
- package/dist/persistence/notifications.d.ts +113 -0
- package/dist/persistence/notifications.js +213 -0
- package/dist/persistence/notifications.js.map +1 -0
- package/dist/persistence/persistence.d.ts +46 -0
- package/dist/persistence/persistence.js +42 -0
- package/dist/persistence/persistence.js.map +1 -0
- package/dist/persistence/retention.d.ts +46 -0
- package/dist/persistence/retention.js +68 -0
- package/dist/persistence/retention.js.map +1 -0
- package/dist/persistence/runs.d.ts +215 -0
- package/dist/persistence/runs.js +417 -0
- package/dist/persistence/runs.js.map +1 -0
- package/dist/persistence/schema.d.ts +16 -0
- package/dist/persistence/schema.js +183 -0
- package/dist/persistence/schema.js.map +1 -0
- package/dist/persistence/sqlite.d.ts +49 -0
- package/dist/persistence/sqlite.js +131 -0
- package/dist/persistence/sqlite.js.map +1 -0
- package/dist/runs/controller.d.ts +307 -0
- package/dist/runs/controller.js +1078 -0
- package/dist/runs/controller.js.map +1 -0
- package/dist/runs/errors.d.ts +20 -0
- package/dist/runs/errors.js +26 -0
- package/dist/runs/errors.js.map +1 -0
- package/dist/runs/event-bus.d.ts +106 -0
- package/dist/runs/event-bus.js +105 -0
- package/dist/runs/event-bus.js.map +1 -0
- package/dist/runs/execution.d.ts +39 -0
- package/dist/runs/execution.js +146 -0
- package/dist/runs/execution.js.map +1 -0
- package/dist/runs/index.d.ts +12 -0
- package/dist/runs/index.js +13 -0
- package/dist/runs/index.js.map +1 -0
- package/dist/runs/retention.d.ts +64 -0
- package/dist/runs/retention.js +232 -0
- package/dist/runs/retention.js.map +1 -0
- package/dist/runs/workspace.d.ts +20 -0
- package/dist/runs/workspace.js +51 -0
- package/dist/runs/workspace.js.map +1 -0
- package/dist/scheduler/checks.d.ts +41 -0
- package/dist/scheduler/checks.js +111 -0
- package/dist/scheduler/checks.js.map +1 -0
- package/dist/scheduler/cron.d.ts +32 -0
- package/dist/scheduler/cron.js +83 -0
- package/dist/scheduler/cron.js.map +1 -0
- package/dist/scheduler/dry-run.d.ts +42 -0
- package/dist/scheduler/dry-run.js +167 -0
- package/dist/scheduler/dry-run.js.map +1 -0
- package/dist/scheduler/errors.d.ts +22 -0
- package/dist/scheduler/errors.js +22 -0
- package/dist/scheduler/errors.js.map +1 -0
- package/dist/scheduler/format.d.ts +3 -0
- package/dist/scheduler/format.js +73 -0
- package/dist/scheduler/format.js.map +1 -0
- package/dist/scheduler/index.d.ts +13 -0
- package/dist/scheduler/index.js +14 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/planner.d.ts +88 -0
- package/dist/scheduler/planner.js +255 -0
- package/dist/scheduler/planner.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +160 -0
- package/dist/scheduler/scheduler.js +680 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/scheduler/state.d.ts +26 -0
- package/dist/scheduler/state.js +25 -0
- package/dist/scheduler/state.js.map +1 -0
- package/dist/scheduler/types.d.ts +197 -0
- package/dist/scheduler/types.js +3 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/service/index.d.ts +6 -0
- package/dist/service/index.js +7 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/install.d.ts +93 -0
- package/dist/service/install.js +250 -0
- package/dist/service/install.js.map +1 -0
- package/dist/service/unit.d.ts +50 -0
- package/dist/service/unit.js +171 -0
- package/dist/service/unit.js.map +1 -0
- package/dist/sources/github/adapter.d.ts +99 -0
- package/dist/sources/github/adapter.js +336 -0
- package/dist/sources/github/adapter.js.map +1 -0
- package/dist/sources/github/fields.d.ts +8 -0
- package/dist/sources/github/fields.js +72 -0
- package/dist/sources/github/fields.js.map +1 -0
- package/dist/sources/github/http.d.ts +100 -0
- package/dist/sources/github/http.js +288 -0
- package/dist/sources/github/http.js.map +1 -0
- package/dist/sources/github/index.d.ts +6 -0
- package/dist/sources/github/index.js +7 -0
- package/dist/sources/github/index.js.map +1 -0
- package/dist/sources/github/pushdown.d.ts +14 -0
- package/dist/sources/github/pushdown.js +47 -0
- package/dist/sources/github/pushdown.js.map +1 -0
- package/dist/sources/github/refs.d.ts +17 -0
- package/dist/sources/github/refs.js +61 -0
- package/dist/sources/github/refs.js.map +1 -0
- package/dist/sources/index.d.ts +6 -0
- package/dist/sources/index.js +7 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +28 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/public/app.css +239 -0
- package/dist/ui/public/favicon.svg +1 -0
- package/dist/ui/public/index.html +27 -0
- package/dist/ui/public/js/client/api.js +68 -0
- package/dist/ui/public/js/client/app.js +146 -0
- package/dist/ui/public/js/client/dom.js +34 -0
- package/dist/ui/public/js/client/page.js +9 -0
- package/dist/ui/public/js/client/pages/actions.js +35 -0
- package/dist/ui/public/js/client/pages/blocked.js +58 -0
- package/dist/ui/public/js/client/pages/config.js +126 -0
- package/dist/ui/public/js/client/pages/dashboard.js +175 -0
- package/dist/ui/public/js/client/pages/run.js +367 -0
- package/dist/ui/public/js/client/pages/runs.js +72 -0
- package/dist/ui/public/js/client/pages/views.js +80 -0
- package/dist/ui/public/js/client/pages/workflows.js +291 -0
- package/dist/ui/public/js/client/types.js +1 -0
- package/dist/ui/public/js/client/ui.js +152 -0
- package/dist/ui/public/js/shared/dom.js +77 -0
- package/dist/ui/public/js/shared/events.js +88 -0
- package/dist/ui/public/js/shared/format.js +213 -0
- package/dist/ui/public/js/shared/logs.js +136 -0
- package/dist/ui/public/js/shared/params.js +90 -0
- package/dist/ui/public/js/shared/route.js +36 -0
- package/dist/ui/public/js/shared/sort.js +70 -0
- package/dist/ui/public/js/shared/sse.js +193 -0
- package/dist/ui/public/js/shared/throttle.js +65 -0
- package/dist/views/index.d.ts +6 -0
- package/dist/views/index.js +7 -0
- package/dist/views/index.js.map +1 -0
- package/dist/views/service.d.ts +131 -0
- package/dist/views/service.js +264 -0
- package/dist/views/service.js.map +1 -0
- package/docs/api.md +239 -0
- package/docs/configuration-reference.md +1785 -0
- package/docs/cron-semantics.md +96 -0
- package/docs/manual-tests/github-fixture/config.yaml +30 -0
- package/docs/manual-tests/github-fixture/helpers.sh +93 -0
- package/docs/manual-tests/github-fixture/identities.yaml +18 -0
- package/docs/manual-tests/github-fixture/scripts/test-worker.sh +100 -0
- package/docs/manual-tests/github-fixture/sources.yaml +13 -0
- package/docs/manual-tests/github-fixture/views/dependencies.yaml +17 -0
- package/docs/manual-tests/github-fixture/views/needs-maintainer.yaml +17 -0
- package/docs/manual-tests/github-fixture/workflows/coder-test.yaml +76 -0
- package/docs/manual-tests/github-fixture/workflows/commenter-test.yaml +63 -0
- package/docs/manual-tests/github-fixture/workflows/reviewer-test.yaml +60 -0
- package/docs/manual-tests/github.md +1308 -0
- package/docs/manual-tests/process-supervision.md +64 -0
- package/docs/manual-tests/systemd.md +68 -0
- package/docs/operations.md +271 -0
- package/docs/worker-protocol.md +145 -0
- package/examples/config/config.yaml +35 -0
- package/examples/config/identities.yaml +18 -0
- package/examples/config/sources.yaml +13 -0
- package/examples/config/views/needs-maintainer.yaml +15 -0
- package/examples/config/workflows/coder-project-a.yaml +86 -0
- package/examples/config/workflows/deploy.yaml +15 -0
- package/examples/config/workflows/nightly-maintenance.yaml +13 -0
- package/package.json +75 -4
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal structured logging with secret redaction (Spec §20.4).
|
|
3
|
+
*
|
|
4
|
+
* Log records are JSON lines: `{ time, level, msg, ...bindings, ...fields }`.
|
|
5
|
+
* Every record passes through a redaction hook before it is written, so known
|
|
6
|
+
* secret values and secret-looking keys never reach the sink.
|
|
7
|
+
*/
|
|
8
|
+
import { systemClock } from './clock.js';
|
|
9
|
+
/** Replacement text for redacted values. */
|
|
10
|
+
export const REDACTED = '[REDACTED]';
|
|
11
|
+
const LEVEL_ORDER = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
12
|
+
/**
|
|
13
|
+
* Keys whose values are always redacted, regardless of content. Matched
|
|
14
|
+
* against the key's last segment (camelCase is split first), so `GH_TOKEN`,
|
|
15
|
+
* `accessToken`, `x-api-key`, `privateKey`, `password` match, while
|
|
16
|
+
* `passed`, `bypass` or `tokens_used` do not.
|
|
17
|
+
*/
|
|
18
|
+
const SECRET_KEY_PATTERN = /(^|[_.-])(pass(word|wd)?|secret|token|private[_-]?key|authorization|api[_-]?key|credentials?|cookie)$/i;
|
|
19
|
+
/** True if values under `key` must always be redacted. */
|
|
20
|
+
export function isSecretKey(key) {
|
|
21
|
+
return SECRET_KEY_PATTERN.test(key.replace(/([a-z0-9])([A-Z])/g, '$1_$2'));
|
|
22
|
+
}
|
|
23
|
+
/** Minimum length of a registered secret; shorter values would cause excessive false positives. */
|
|
24
|
+
const MIN_SECRET_LENGTH = 4;
|
|
25
|
+
/**
|
|
26
|
+
* Registry of known secret values (tokens, keys) that are replaced by
|
|
27
|
+
* {@link REDACTED} in any text passed through {@link SecretRedactor.redact}.
|
|
28
|
+
* Best-effort only: a secret that is transformed (e.g. base64) is not detected (Spec §24.8).
|
|
29
|
+
*/
|
|
30
|
+
export class SecretRedactor {
|
|
31
|
+
secrets = new Set();
|
|
32
|
+
/** Register a secret value. Values shorter than 4 characters are ignored. */
|
|
33
|
+
add(secret) {
|
|
34
|
+
if (!secret || secret.length < MIN_SECRET_LENGTH)
|
|
35
|
+
return;
|
|
36
|
+
this.secrets.add(secret);
|
|
37
|
+
// PEM keys are often passed with literal "\n" escapes; register both forms.
|
|
38
|
+
if (secret.includes('\n'))
|
|
39
|
+
this.secrets.add(secret.replace(/\n/g, '\\n'));
|
|
40
|
+
}
|
|
41
|
+
/** Forget a secret value (e.g. after token expiry). */
|
|
42
|
+
remove(secret) {
|
|
43
|
+
this.secrets.delete(secret);
|
|
44
|
+
this.secrets.delete(secret.replace(/\n/g, '\\n'));
|
|
45
|
+
}
|
|
46
|
+
/** Number of registered secrets. */
|
|
47
|
+
get size() {
|
|
48
|
+
return this.secrets.size;
|
|
49
|
+
}
|
|
50
|
+
/** Replace every registered secret occurring in `text`. Longest secrets are replaced first. */
|
|
51
|
+
redact(text) {
|
|
52
|
+
if (this.secrets.size === 0 || text.length === 0)
|
|
53
|
+
return text;
|
|
54
|
+
let out = text;
|
|
55
|
+
const ordered = [...this.secrets].sort((a, b) => b.length - a.length);
|
|
56
|
+
for (const secret of ordered) {
|
|
57
|
+
if (out.includes(secret))
|
|
58
|
+
out = out.split(secret).join(REDACTED);
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
/** The redactor as a plain function, suitable for {@link ConsoleLoggerOptions.redact}. */
|
|
63
|
+
asRedactor = (text) => this.redact(text);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Deep-copy `value`, replacing values under secret-looking keys and applying
|
|
67
|
+
* `redact` to every string. Handles cycles and Error objects.
|
|
68
|
+
*/
|
|
69
|
+
export function redactValue(value, redact = (s) => s, seen = new WeakSet()) {
|
|
70
|
+
if (typeof value === 'string')
|
|
71
|
+
return redact(value);
|
|
72
|
+
if (value === null || typeof value !== 'object')
|
|
73
|
+
return value;
|
|
74
|
+
if (value instanceof Date)
|
|
75
|
+
return value.toISOString();
|
|
76
|
+
// `seen` tracks only the current path: shared (non-cyclic) references are
|
|
77
|
+
// rendered every time, true cycles become "[Circular]".
|
|
78
|
+
if (seen.has(value))
|
|
79
|
+
return '[Circular]';
|
|
80
|
+
seen.add(value);
|
|
81
|
+
try {
|
|
82
|
+
if (value instanceof Error) {
|
|
83
|
+
const out = { name: value.name, message: redact(value.message) };
|
|
84
|
+
const { code, details } = value;
|
|
85
|
+
if (code !== undefined)
|
|
86
|
+
out.code = code;
|
|
87
|
+
if (details !== undefined)
|
|
88
|
+
out.details = redactValue(details, redact, seen);
|
|
89
|
+
if (value.stack)
|
|
90
|
+
out.stack = redact(value.stack);
|
|
91
|
+
if (value.cause !== undefined)
|
|
92
|
+
out.cause = redactValue(value.cause, redact, seen);
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
if (Array.isArray(value))
|
|
96
|
+
return value.map((v) => redactValue(v, redact, seen));
|
|
97
|
+
const out = {};
|
|
98
|
+
for (const [key, v] of Object.entries(value)) {
|
|
99
|
+
out[key] =
|
|
100
|
+
isSecretKey(key) && v !== null && v !== undefined && typeof v !== 'boolean'
|
|
101
|
+
? REDACTED
|
|
102
|
+
: redactValue(v, redact, seen);
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
seen.delete(value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Logger writing one JSON object per line (default: stderr). */
|
|
111
|
+
export class ConsoleLogger {
|
|
112
|
+
level;
|
|
113
|
+
clock;
|
|
114
|
+
redact;
|
|
115
|
+
sink;
|
|
116
|
+
bindings;
|
|
117
|
+
constructor(options = {}) {
|
|
118
|
+
this.level = options.level ?? 'info';
|
|
119
|
+
this.clock = options.clock ?? systemClock;
|
|
120
|
+
this.redact = options.redact ?? ((s) => s);
|
|
121
|
+
this.sink = options.sink ?? ((line) => process.stderr.write(`${line}\n`));
|
|
122
|
+
this.bindings = options.bindings ?? {};
|
|
123
|
+
}
|
|
124
|
+
debug(msg, fields) {
|
|
125
|
+
this.write('debug', msg, fields);
|
|
126
|
+
}
|
|
127
|
+
info(msg, fields) {
|
|
128
|
+
this.write('info', msg, fields);
|
|
129
|
+
}
|
|
130
|
+
warn(msg, fields) {
|
|
131
|
+
this.write('warn', msg, fields);
|
|
132
|
+
}
|
|
133
|
+
error(msg, fields) {
|
|
134
|
+
this.write('error', msg, fields);
|
|
135
|
+
}
|
|
136
|
+
child(bindings) {
|
|
137
|
+
return new ConsoleLogger({
|
|
138
|
+
level: this.level,
|
|
139
|
+
clock: this.clock,
|
|
140
|
+
redact: this.redact,
|
|
141
|
+
sink: this.sink,
|
|
142
|
+
bindings: { ...this.bindings, ...bindings },
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
write(level, msg, fields) {
|
|
146
|
+
if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level])
|
|
147
|
+
return;
|
|
148
|
+
const record = {
|
|
149
|
+
time: this.clock.now().toISOString(),
|
|
150
|
+
level,
|
|
151
|
+
msg,
|
|
152
|
+
...this.bindings,
|
|
153
|
+
...(fields ?? {}),
|
|
154
|
+
};
|
|
155
|
+
let line;
|
|
156
|
+
try {
|
|
157
|
+
line = JSON.stringify(redactValue(record, this.redact));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
line = JSON.stringify({ time: record.time, level, msg: this.redact(msg), logError: 'unserialisable fields' });
|
|
161
|
+
}
|
|
162
|
+
this.sink(line);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** Logger that keeps (redacted) records in memory; intended for tests. */
|
|
166
|
+
export class MemoryLogger {
|
|
167
|
+
redact;
|
|
168
|
+
bindings;
|
|
169
|
+
/** Shared record list (children append to the root's list). */
|
|
170
|
+
records;
|
|
171
|
+
constructor(redact = (s) => s, bindings = {}, records) {
|
|
172
|
+
this.redact = redact;
|
|
173
|
+
this.bindings = bindings;
|
|
174
|
+
this.records = records ?? [];
|
|
175
|
+
}
|
|
176
|
+
debug(msg, fields) {
|
|
177
|
+
this.push('debug', msg, fields);
|
|
178
|
+
}
|
|
179
|
+
info(msg, fields) {
|
|
180
|
+
this.push('info', msg, fields);
|
|
181
|
+
}
|
|
182
|
+
warn(msg, fields) {
|
|
183
|
+
this.push('warn', msg, fields);
|
|
184
|
+
}
|
|
185
|
+
error(msg, fields) {
|
|
186
|
+
this.push('error', msg, fields);
|
|
187
|
+
}
|
|
188
|
+
child(bindings) {
|
|
189
|
+
return new MemoryLogger(this.redact, { ...this.bindings, ...bindings }, this.records);
|
|
190
|
+
}
|
|
191
|
+
push(level, msg, fields) {
|
|
192
|
+
this.records.push({
|
|
193
|
+
level,
|
|
194
|
+
msg: this.redact(msg),
|
|
195
|
+
fields: redactValue({ ...this.bindings, ...(fields ?? {}) }, this.redact),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Logger that discards everything. */
|
|
200
|
+
export const noopLogger = {
|
|
201
|
+
debug: () => undefined,
|
|
202
|
+
info: () => undefined,
|
|
203
|
+
warn: () => undefined,
|
|
204
|
+
error: () => undefined,
|
|
205
|
+
child: () => noopLogger,
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** Resolve `p` to a normalised absolute path (relative to `cwd`, default `process.cwd()`). */
|
|
2
|
+
export declare function toAbsolutePath(p: string, cwd?: string): string;
|
|
3
|
+
/** True when `target` equals `base` or lies below it (both are resolved first; purely lexical). */
|
|
4
|
+
export declare function isWithin(base: string, target: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve `rel` against the absolute directory `base` and guarantee that the
|
|
7
|
+
* result stays inside `base` (or equals it).
|
|
8
|
+
*
|
|
9
|
+
* - `base` must be absolute; it is normalised.
|
|
10
|
+
* - `rel` may be relative or absolute; an absolute `rel` is accepted only if it
|
|
11
|
+
* lies within `base`.
|
|
12
|
+
* - `..` segments are allowed only as long as the normalised result stays inside.
|
|
13
|
+
* - NUL bytes are rejected.
|
|
14
|
+
*
|
|
15
|
+
* The check is lexical; use {@link assertRealPathWithin} before destructive
|
|
16
|
+
* operations on existing paths to also catch symlink escapes.
|
|
17
|
+
*
|
|
18
|
+
* @throws PathTraversalError
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveWithin(base: string, rel: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Like {@link resolveWithin}, but additionally requires the result to be
|
|
23
|
+
* strictly below `base` (never `base` itself). Use for per-run directories
|
|
24
|
+
* that may be deleted.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveStrictlyWithin(base: string, rel: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Verify (following symlinks) that the existing path `target` lies within the
|
|
29
|
+
* existing directory `base`. Intended as a guard before deleting workspaces.
|
|
30
|
+
*
|
|
31
|
+
* @throws PathTraversalError if the real path escapes `base`
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertRealPathWithin(base: string, target: string): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Validate a single path segment (file or directory name) without
|
|
36
|
+
* transforming it: non-empty, no separators, not `.`/`..`, no NUL.
|
|
37
|
+
*
|
|
38
|
+
* @throws PathTraversalError
|
|
39
|
+
*/
|
|
40
|
+
export declare function assertSafeSegment(segment: string): string;
|
|
41
|
+
/** Maximum length of a sanitised path segment. */
|
|
42
|
+
export declare const MAX_SEGMENT_LENGTH = 128;
|
|
43
|
+
/**
|
|
44
|
+
* Turn an arbitrary value into a single safe path segment: every character
|
|
45
|
+
* outside `[A-Za-z0-9._@+=,-]` (including `/`, `\`, NUL, whitespace) becomes
|
|
46
|
+
* `_`; a result consisting only of dots becomes underscores; empty becomes `_`;
|
|
47
|
+
* length is capped at {@link MAX_SEGMENT_LENGTH}.
|
|
48
|
+
*/
|
|
49
|
+
export declare function sanitizeSegment(value: string): string;
|
|
50
|
+
/** Per-run file locations below `data-dir` (Spec §4). */
|
|
51
|
+
export interface RunPaths {
|
|
52
|
+
/** `runs/<run-id>/` */
|
|
53
|
+
runDir: string;
|
|
54
|
+
/** `runs/<run-id>/context.json` (`ADISPATCH_CONTEXT_FILE`) */
|
|
55
|
+
contextFile: string;
|
|
56
|
+
/** `runs/<run-id>/result.json` (`ADISPATCH_RESULT_FILE`) */
|
|
57
|
+
resultFile: string;
|
|
58
|
+
/** `logs/<run-id>/` */
|
|
59
|
+
logDir: string;
|
|
60
|
+
/** `logs/<run-id>/stdout.log` */
|
|
61
|
+
stdoutLog: string;
|
|
62
|
+
/** `logs/<run-id>/stderr.log` */
|
|
63
|
+
stderrLog: string;
|
|
64
|
+
}
|
|
65
|
+
/** Canonical `data-dir` layout (Spec §4). All paths are absolute. */
|
|
66
|
+
export interface DataPaths {
|
|
67
|
+
dataDir: string;
|
|
68
|
+
/** `adispatchd.sqlite` */
|
|
69
|
+
database: string;
|
|
70
|
+
/** Instance lock file next to the database. */
|
|
71
|
+
instanceLock: string;
|
|
72
|
+
/** `logs/` */
|
|
73
|
+
logsDir: string;
|
|
74
|
+
/** `runs/` */
|
|
75
|
+
runsDir: string;
|
|
76
|
+
/** Default `workspace.base_dir`: `workspaces/` */
|
|
77
|
+
defaultWorkspacesDir: string;
|
|
78
|
+
/** `backups/` */
|
|
79
|
+
backupsDir: string;
|
|
80
|
+
/** Paths of one run; `runId` must be a safe path segment. */
|
|
81
|
+
run(runId: string): RunPaths;
|
|
82
|
+
}
|
|
83
|
+
/** Build the {@link DataPaths} for an (absolute or cwd-relative) `dataDir`. */
|
|
84
|
+
export declare function dataPaths(dataDir: string): DataPaths;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path helpers with path-traversal protection (Spec §4, §9, §20) and the
|
|
3
|
+
* canonical `data-dir` layout shared by persistence, executor, API and CLI.
|
|
4
|
+
*/
|
|
5
|
+
import { realpath } from 'node:fs/promises';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { PathTraversalError } from './errors.js';
|
|
8
|
+
function assertNoNul(value, what) {
|
|
9
|
+
if (value.includes('\0'))
|
|
10
|
+
throw new PathTraversalError(`${what} contains a NUL byte`);
|
|
11
|
+
}
|
|
12
|
+
/** Resolve `p` to a normalised absolute path (relative to `cwd`, default `process.cwd()`). */
|
|
13
|
+
export function toAbsolutePath(p, cwd = process.cwd()) {
|
|
14
|
+
assertNoNul(p, 'Path');
|
|
15
|
+
if (p.trim() === '')
|
|
16
|
+
throw new PathTraversalError('Path must not be empty');
|
|
17
|
+
return path.resolve(cwd, p);
|
|
18
|
+
}
|
|
19
|
+
/** True when `target` equals `base` or lies below it (both are resolved first; purely lexical). */
|
|
20
|
+
export function isWithin(base, target) {
|
|
21
|
+
const b = path.resolve(base);
|
|
22
|
+
const t = path.resolve(b, target);
|
|
23
|
+
const rel = path.relative(b, t);
|
|
24
|
+
return rel === '' || (!rel.startsWith(`..${path.sep}`) && rel !== '..' && !path.isAbsolute(rel));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve `rel` against the absolute directory `base` and guarantee that the
|
|
28
|
+
* result stays inside `base` (or equals it).
|
|
29
|
+
*
|
|
30
|
+
* - `base` must be absolute; it is normalised.
|
|
31
|
+
* - `rel` may be relative or absolute; an absolute `rel` is accepted only if it
|
|
32
|
+
* lies within `base`.
|
|
33
|
+
* - `..` segments are allowed only as long as the normalised result stays inside.
|
|
34
|
+
* - NUL bytes are rejected.
|
|
35
|
+
*
|
|
36
|
+
* The check is lexical; use {@link assertRealPathWithin} before destructive
|
|
37
|
+
* operations on existing paths to also catch symlink escapes.
|
|
38
|
+
*
|
|
39
|
+
* @throws PathTraversalError
|
|
40
|
+
*/
|
|
41
|
+
export function resolveWithin(base, rel) {
|
|
42
|
+
assertNoNul(base, 'Base path');
|
|
43
|
+
assertNoNul(rel, 'Path');
|
|
44
|
+
if (!path.isAbsolute(base))
|
|
45
|
+
throw new PathTraversalError(`Base path must be absolute: ${base}`, { base });
|
|
46
|
+
const normBase = path.resolve(base);
|
|
47
|
+
const resolved = path.resolve(normBase, rel);
|
|
48
|
+
if (!isWithin(normBase, resolved)) {
|
|
49
|
+
throw new PathTraversalError(`Path "${rel}" escapes base directory "${normBase}"`, { base: normBase, path: rel });
|
|
50
|
+
}
|
|
51
|
+
return resolved;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Like {@link resolveWithin}, but additionally requires the result to be
|
|
55
|
+
* strictly below `base` (never `base` itself). Use for per-run directories
|
|
56
|
+
* that may be deleted.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveStrictlyWithin(base, rel) {
|
|
59
|
+
const resolved = resolveWithin(base, rel);
|
|
60
|
+
if (resolved === path.resolve(base)) {
|
|
61
|
+
throw new PathTraversalError(`Path "${rel}" must not be the base directory itself`, { base, path: rel });
|
|
62
|
+
}
|
|
63
|
+
return resolved;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Verify (following symlinks) that the existing path `target` lies within the
|
|
67
|
+
* existing directory `base`. Intended as a guard before deleting workspaces.
|
|
68
|
+
*
|
|
69
|
+
* @throws PathTraversalError if the real path escapes `base`
|
|
70
|
+
*/
|
|
71
|
+
export async function assertRealPathWithin(base, target) {
|
|
72
|
+
const lexical = resolveWithin(base, target);
|
|
73
|
+
const [realBase, realTarget] = await Promise.all([realpath(base), realpath(lexical)]);
|
|
74
|
+
if (!isWithin(realBase, realTarget)) {
|
|
75
|
+
throw new PathTraversalError(`Path "${target}" resolves outside "${base}" via symlink`, { base, path: target });
|
|
76
|
+
}
|
|
77
|
+
return realTarget;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate a single path segment (file or directory name) without
|
|
81
|
+
* transforming it: non-empty, no separators, not `.`/`..`, no NUL.
|
|
82
|
+
*
|
|
83
|
+
* @throws PathTraversalError
|
|
84
|
+
*/
|
|
85
|
+
export function assertSafeSegment(segment) {
|
|
86
|
+
assertNoNul(segment, 'Path segment');
|
|
87
|
+
if (segment === '' || segment === '.' || segment === '..' || /[/\\]/.test(segment)) {
|
|
88
|
+
throw new PathTraversalError(`Unsafe path segment "${segment}"`, { segment });
|
|
89
|
+
}
|
|
90
|
+
return segment;
|
|
91
|
+
}
|
|
92
|
+
/** Maximum length of a sanitised path segment. */
|
|
93
|
+
export const MAX_SEGMENT_LENGTH = 128;
|
|
94
|
+
/**
|
|
95
|
+
* Turn an arbitrary value into a single safe path segment: every character
|
|
96
|
+
* outside `[A-Za-z0-9._@+=,-]` (including `/`, `\`, NUL, whitespace) becomes
|
|
97
|
+
* `_`; a result consisting only of dots becomes underscores; empty becomes `_`;
|
|
98
|
+
* length is capped at {@link MAX_SEGMENT_LENGTH}.
|
|
99
|
+
*/
|
|
100
|
+
export function sanitizeSegment(value) {
|
|
101
|
+
let out = value.replace(/[^A-Za-z0-9._@+=,-]/g, '_');
|
|
102
|
+
if (/^\.*$/.test(out))
|
|
103
|
+
out = out.replace(/\./g, '_');
|
|
104
|
+
if (out === '')
|
|
105
|
+
out = '_';
|
|
106
|
+
if (out.length > MAX_SEGMENT_LENGTH)
|
|
107
|
+
out = out.slice(0, MAX_SEGMENT_LENGTH);
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
/** Build the {@link DataPaths} for an (absolute or cwd-relative) `dataDir`. */
|
|
111
|
+
export function dataPaths(dataDir) {
|
|
112
|
+
const root = toAbsolutePath(dataDir);
|
|
113
|
+
const logsDir = path.join(root, 'logs');
|
|
114
|
+
const runsDir = path.join(root, 'runs');
|
|
115
|
+
return {
|
|
116
|
+
dataDir: root,
|
|
117
|
+
database: path.join(root, 'adispatchd.sqlite'),
|
|
118
|
+
instanceLock: path.join(root, 'adispatchd.lock'),
|
|
119
|
+
logsDir,
|
|
120
|
+
runsDir,
|
|
121
|
+
defaultWorkspacesDir: path.join(root, 'workspaces'),
|
|
122
|
+
backupsDir: path.join(root, 'backups'),
|
|
123
|
+
run(runId) {
|
|
124
|
+
assertSafeSegment(runId);
|
|
125
|
+
const runDir = path.join(runsDir, runId);
|
|
126
|
+
const logDir = path.join(logsDir, runId);
|
|
127
|
+
return {
|
|
128
|
+
runDir,
|
|
129
|
+
contextFile: path.join(runDir, 'context.json'),
|
|
130
|
+
resultFile: path.join(runDir, 'result.json'),
|
|
131
|
+
logDir,
|
|
132
|
+
stdoutLog: path.join(logDir, 'stdout.log'),
|
|
133
|
+
stderrLog: path.join(logDir, 'stderr.log'),
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { OrchestratorError } from './errors.js';
|
|
2
|
+
import type { BusinessResult, TerminalStatus, TriggerType } from './types.js';
|
|
3
|
+
/** Interpolation mode, see module docs. */
|
|
4
|
+
export type TemplateMode = 'text' | 'path';
|
|
5
|
+
/**
|
|
6
|
+
* Availability group of a placeholder:
|
|
7
|
+
* - `always`: every workflow context
|
|
8
|
+
* - `run`: once a run exists (not in filters/conditions before reservation)
|
|
9
|
+
* - `workspace`: after the workspace path is known (not in `working_directory` itself)
|
|
10
|
+
* - `source`: only for source-bound runs (source trigger or manual with item)
|
|
11
|
+
* - `result`: only in completion hooks (`on_success`, `on_failure`, ...)
|
|
12
|
+
* - `parameters`: declared manual-start parameters, `{parameters.<name>}`
|
|
13
|
+
*/
|
|
14
|
+
export type PlaceholderGroup = 'always' | 'run' | 'workspace' | 'source' | 'result' | 'parameters';
|
|
15
|
+
/** Definition of a whitelisted placeholder. */
|
|
16
|
+
export interface PlaceholderDefinition {
|
|
17
|
+
name: string;
|
|
18
|
+
group: PlaceholderGroup;
|
|
19
|
+
description: string;
|
|
20
|
+
/** Trusted absolute directory; in `path` mode only allowed at the start of a template. */
|
|
21
|
+
pathRoot?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** The fixed placeholder whitelist (Spec §5.4, §9, §20.5). */
|
|
24
|
+
export declare const PLACEHOLDERS: readonly PlaceholderDefinition[];
|
|
25
|
+
/** Prefix of parameter placeholders: `{parameters.<name>}`. */
|
|
26
|
+
export declare const PARAMETER_PLACEHOLDER_PREFIX = "parameters.";
|
|
27
|
+
/** Error thrown for template syntax errors, unknown/unavailable placeholders or unsafe values. */
|
|
28
|
+
export declare class TemplateError extends OrchestratorError {
|
|
29
|
+
readonly placeholder?: string | undefined;
|
|
30
|
+
constructor(code: TemplateIssueCode, message: string, placeholder?: string | undefined);
|
|
31
|
+
}
|
|
32
|
+
/** Problem codes reported by {@link validateTemplate}. */
|
|
33
|
+
export type TemplateIssueCode = 'syntax' | 'unknown_placeholder' | 'unavailable_placeholder' | 'path_root_position' | 'missing_value' | 'unsafe_value';
|
|
34
|
+
/** One validation problem. */
|
|
35
|
+
export interface TemplateIssue {
|
|
36
|
+
code: TemplateIssueCode;
|
|
37
|
+
message: string;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Parsed template part. */
|
|
41
|
+
export type TemplatePart = {
|
|
42
|
+
kind: 'literal';
|
|
43
|
+
text: string;
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'placeholder';
|
|
46
|
+
name: string;
|
|
47
|
+
index: number;
|
|
48
|
+
};
|
|
49
|
+
/** Parse a template into literal and placeholder parts. Throws {@link TemplateError} (`syntax`). */
|
|
50
|
+
export declare function parseTemplate(template: string): TemplatePart[];
|
|
51
|
+
/** Distinct placeholder names used in `template`, in order of first use. Throws on syntax errors. */
|
|
52
|
+
export declare function extractPlaceholders(template: string): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Which placeholder groups are available in a given config context.
|
|
55
|
+
* Omitted flags default to `false`; `always` placeholders are always available.
|
|
56
|
+
*/
|
|
57
|
+
export interface TemplateScope {
|
|
58
|
+
run?: boolean;
|
|
59
|
+
workspace?: boolean;
|
|
60
|
+
source?: boolean;
|
|
61
|
+
result?: boolean;
|
|
62
|
+
/** Declared parameter names usable as `{parameters.<name>}`. */
|
|
63
|
+
parameters?: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
/** Scope with every group enabled (parameters: none). */
|
|
66
|
+
export declare const FULL_TEMPLATE_SCOPE: Readonly<TemplateScope>;
|
|
67
|
+
/** True if `name` is on the whitelist (parameters: any syntactically valid name). */
|
|
68
|
+
export declare function isKnownPlaceholder(name: string): boolean;
|
|
69
|
+
/** Options for {@link validateTemplate} and {@link renderTemplate}. */
|
|
70
|
+
export interface TemplateOptions {
|
|
71
|
+
mode: TemplateMode;
|
|
72
|
+
/** Available placeholder groups; default {@link FULL_TEMPLATE_SCOPE}. */
|
|
73
|
+
scope?: TemplateScope;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validate a template without values (for config validation). Returns all
|
|
77
|
+
* problems; an empty array means valid.
|
|
78
|
+
*/
|
|
79
|
+
export declare function validateTemplate(template: string, options: TemplateOptions): TemplateIssue[];
|
|
80
|
+
/** Scalar value of a placeholder. */
|
|
81
|
+
export type TemplateScalar = string | number | boolean | null;
|
|
82
|
+
/** Flat map from placeholder name (e.g. `run.id`, `parameters.version`) to value. */
|
|
83
|
+
export type TemplateValues = Readonly<Record<string, TemplateScalar | undefined>>;
|
|
84
|
+
/** Options for {@link renderTemplate}. */
|
|
85
|
+
export interface RenderOptions extends TemplateOptions {
|
|
86
|
+
/**
|
|
87
|
+
* Behaviour for whitelisted placeholders without a value: `error` (default
|
|
88
|
+
* in `path` mode) or `empty` (default in `text` mode).
|
|
89
|
+
*/
|
|
90
|
+
missing?: 'error' | 'empty';
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Interpolate `template` with `values`.
|
|
94
|
+
*
|
|
95
|
+
* @throws TemplateError for syntax errors, unknown/unavailable placeholders,
|
|
96
|
+
* missing values (per `missing`), misplaced/relative path roots or NUL bytes.
|
|
97
|
+
*/
|
|
98
|
+
export declare function renderTemplate(template: string, values: TemplateValues, options: RenderOptions): string;
|
|
99
|
+
/** Structured input for {@link buildTemplateValues}. */
|
|
100
|
+
export interface TemplateInput {
|
|
101
|
+
workflow: {
|
|
102
|
+
id: string;
|
|
103
|
+
name?: string;
|
|
104
|
+
};
|
|
105
|
+
workspace?: {
|
|
106
|
+
baseDir?: string;
|
|
107
|
+
path?: string;
|
|
108
|
+
};
|
|
109
|
+
run?: {
|
|
110
|
+
id: string;
|
|
111
|
+
trigger?: TriggerType;
|
|
112
|
+
startedAt?: Date;
|
|
113
|
+
};
|
|
114
|
+
source?: {
|
|
115
|
+
id: string;
|
|
116
|
+
type: string;
|
|
117
|
+
repository?: string;
|
|
118
|
+
};
|
|
119
|
+
item?: {
|
|
120
|
+
id: string;
|
|
121
|
+
key?: string;
|
|
122
|
+
number?: number | string;
|
|
123
|
+
url?: string;
|
|
124
|
+
title?: string;
|
|
125
|
+
};
|
|
126
|
+
result?: {
|
|
127
|
+
status?: TerminalStatus;
|
|
128
|
+
result?: BusinessResult;
|
|
129
|
+
message?: string;
|
|
130
|
+
};
|
|
131
|
+
parameters?: Readonly<Record<string, string | number | boolean>>;
|
|
132
|
+
/** Time used for `{timestamp}`. */
|
|
133
|
+
now: Date;
|
|
134
|
+
}
|
|
135
|
+
/** Build the flat {@link TemplateValues} map from structured run data. */
|
|
136
|
+
export declare function buildTemplateValues(input: TemplateInput): TemplateValues;
|