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,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe workspace creation below `workspace.base_dir` (Spec §9, §20).
|
|
3
|
+
*/
|
|
4
|
+
import { mkdir, realpath, stat } from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { isWithin, OrchestratorError, PathTraversalError, resolveStrictlyWithin } from '../core/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Ensure `target` exists as a directory strictly below `baseDir` (never
|
|
9
|
+
* `baseDir` itself). Directories are created one segment at a time and each
|
|
10
|
+
* segment's real path is checked BEFORE anything is created below it, so a
|
|
11
|
+
* symlink inside `baseDir` cannot cause directories to be created outside.
|
|
12
|
+
* With `create: false` the existing directory is only verified.
|
|
13
|
+
*
|
|
14
|
+
* @throws PathTraversalError on lexical or symlink escapes
|
|
15
|
+
*/
|
|
16
|
+
export async function ensureWorkspaceWithin(baseDir, target, create) {
|
|
17
|
+
const resolved = resolveStrictlyWithin(baseDir, target);
|
|
18
|
+
if (create)
|
|
19
|
+
await mkdir(baseDir, { recursive: true });
|
|
20
|
+
const realBase = await realpath(baseDir);
|
|
21
|
+
const segments = path.relative(path.resolve(baseDir), resolved).split(path.sep);
|
|
22
|
+
let current = path.resolve(baseDir);
|
|
23
|
+
for (const segment of segments) {
|
|
24
|
+
current = path.join(current, segment);
|
|
25
|
+
if (create) {
|
|
26
|
+
try {
|
|
27
|
+
await mkdir(current);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err.code !== 'EEXIST')
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const real = await realpath(current);
|
|
35
|
+
if (!isWithin(realBase, real) || real === realBase) {
|
|
36
|
+
throw new PathTraversalError(`Workspace "${target}" resolves outside "${baseDir}" via symlink`, {
|
|
37
|
+
base: baseDir,
|
|
38
|
+
path: target,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const st = await stat(resolved);
|
|
43
|
+
if (!st.isDirectory())
|
|
44
|
+
throw new OrchestratorError('ENOTDIR', `working directory is not a directory: ${resolved}`);
|
|
45
|
+
return resolved;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/executors/workspace.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE1G;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAe,EAAE,MAAc,EAAE,MAAe;IAC1F,MAAM,QAAQ,GAAG,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACxD,IAAI,MAAM;QAAE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChF,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtC,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,GAAG,CAAC;YAClE,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnD,MAAM,IAAI,kBAAkB,CAAC,cAAc,MAAM,uBAAuB,OAAO,eAAe,EAAE;gBAC9F,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,MAAM;aACb,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE;QAAE,MAAM,IAAI,iBAAiB,CAAC,SAAS,EAAE,yCAAyC,QAAQ,EAAE,CAAC,CAAC;IACnH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-valued, lazy evaluator for the declarative filter AST (Spec §6, §7 step 5).
|
|
3
|
+
*
|
|
4
|
+
* - Kleene logic: `all` is false if any child is false, else unknown if any
|
|
5
|
+
* child is unknown; `any` dually; `not unknown = unknown`.
|
|
6
|
+
* - Fields that cannot be resolved (`known: false`) evaluate to `unknown`,
|
|
7
|
+
* never to 0/empty (Spec §6, §20.7).
|
|
8
|
+
* - Inside `all`/`any`, children that only need cheap fields are evaluated
|
|
9
|
+
* first; evaluation short-circuits once the result is fixed, so expensive
|
|
10
|
+
* fields are only resolved when needed.
|
|
11
|
+
* - Every evaluation returns a diagnostic trace (per rule: true / false /
|
|
12
|
+
* unknown / not_evaluated) for dry-run and GUI.
|
|
13
|
+
*/
|
|
14
|
+
import { type FieldResolution, type FieldType, type FieldValue, type FilterNode, type FilterOperator, type FilterValue, type TriValue } from '../core/index.js';
|
|
15
|
+
/** Value of a rule in a diagnostic trace. `not_evaluated` = skipped (short-circuit / dry-run). */
|
|
16
|
+
export type TraceValue = TriValue | 'not_evaluated';
|
|
17
|
+
/** Diagnostic trace of one AST node; mirrors the AST structure (children in original order). */
|
|
18
|
+
export type FilterTrace = {
|
|
19
|
+
kind: 'all' | 'any';
|
|
20
|
+
/** Location in the AST, e.g. `$.all[1].any[0]` (root: {@link ROOT_PATH}). */
|
|
21
|
+
path: string;
|
|
22
|
+
value: TraceValue;
|
|
23
|
+
children: FilterTrace[];
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'not';
|
|
26
|
+
path: string;
|
|
27
|
+
value: TraceValue;
|
|
28
|
+
child: FilterTrace;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'predicate';
|
|
31
|
+
path: string;
|
|
32
|
+
value: TraceValue;
|
|
33
|
+
field: string;
|
|
34
|
+
operator: FilterOperator;
|
|
35
|
+
expected: FilterValue;
|
|
36
|
+
/** Resolved field value (only when known). */
|
|
37
|
+
actual?: FieldValue;
|
|
38
|
+
/** Why the value is `unknown` (unknown field, missing permission, incompatible types, ...). */
|
|
39
|
+
reason?: string;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'invalid';
|
|
42
|
+
path: string;
|
|
43
|
+
value: TraceValue;
|
|
44
|
+
reason: string;
|
|
45
|
+
};
|
|
46
|
+
/** Result of {@link evaluateFilter}. */
|
|
47
|
+
export interface FilterEvaluation {
|
|
48
|
+
value: TriValue;
|
|
49
|
+
trace: FilterTrace;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Lazy access to an item's fields. Implementations should memoize; the
|
|
53
|
+
* evaluator additionally memoizes per evaluation.
|
|
54
|
+
*/
|
|
55
|
+
export interface FieldSource {
|
|
56
|
+
/** Resolve a field. Must not throw; failures are `{ known: false }`. */
|
|
57
|
+
resolve(field: string): Promise<FieldResolution> | FieldResolution;
|
|
58
|
+
/** True if resolving `field` is expensive (evaluated last, only on demand). */
|
|
59
|
+
isExpensive?(field: string): boolean;
|
|
60
|
+
/** Declared type of `field` (enables date semantics). */
|
|
61
|
+
fieldType?(field: string): FieldType | undefined;
|
|
62
|
+
/** True if string values of `field` compare without regard to case (e.g. GitHub labels). */
|
|
63
|
+
isCaseInsensitive?(field: string): boolean;
|
|
64
|
+
}
|
|
65
|
+
/** Root path used in traces. */
|
|
66
|
+
export declare const ROOT_PATH = "$";
|
|
67
|
+
/** All field names referenced by `node` (deduplicated, in first-occurrence order). */
|
|
68
|
+
export declare function collectFields(node: FilterNode | undefined | null): string[];
|
|
69
|
+
/** True if `node` is structurally a filter node (children are not checked). */
|
|
70
|
+
export declare function isValidNode(node: unknown): node is FilterNode;
|
|
71
|
+
/** Trace of `node` with every rule marked `not_evaluated` (short-circuit, dry-run). */
|
|
72
|
+
export declare function notEvaluatedTrace(node: FilterNode, path?: string): FilterTrace;
|
|
73
|
+
/**
|
|
74
|
+
* Evaluate `filter` against an item's fields. A missing filter (`undefined`/`null`)
|
|
75
|
+
* matches everything. Never throws: malformed nodes and resolver errors yield `unknown`.
|
|
76
|
+
*/
|
|
77
|
+
export declare function evaluateFilter(filter: FilterNode | undefined | null, source: FieldSource): Promise<FilterEvaluation>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-valued, lazy evaluator for the declarative filter AST (Spec §6, §7 step 5).
|
|
3
|
+
*
|
|
4
|
+
* - Kleene logic: `all` is false if any child is false, else unknown if any
|
|
5
|
+
* child is unknown; `any` dually; `not unknown = unknown`.
|
|
6
|
+
* - Fields that cannot be resolved (`known: false`) evaluate to `unknown`,
|
|
7
|
+
* never to 0/empty (Spec §6, §20.7).
|
|
8
|
+
* - Inside `all`/`any`, children that only need cheap fields are evaluated
|
|
9
|
+
* first; evaluation short-circuits once the result is fixed, so expensive
|
|
10
|
+
* fields are only resolved when needed.
|
|
11
|
+
* - Every evaluation returns a diagnostic trace (per rule: true / false /
|
|
12
|
+
* unknown / not_evaluated) for dry-run and GUI.
|
|
13
|
+
*/
|
|
14
|
+
import { isFilterAll, isFilterAny, isFilterNot, isFilterPredicate, } from '../core/index.js';
|
|
15
|
+
import { applyOperator } from './predicate.js';
|
|
16
|
+
/** Root path used in traces. */
|
|
17
|
+
export const ROOT_PATH = '$';
|
|
18
|
+
function childPath(parent, kind, index) {
|
|
19
|
+
return index === undefined ? `${parent}.${kind}` : `${parent}.${kind}[${index}]`;
|
|
20
|
+
}
|
|
21
|
+
/** All field names referenced by `node` (deduplicated, in first-occurrence order). */
|
|
22
|
+
export function collectFields(node) {
|
|
23
|
+
const out = new Set();
|
|
24
|
+
const walk = (n) => {
|
|
25
|
+
if (!isValidNode(n))
|
|
26
|
+
return; // malformed subtrees are skipped (they evaluate to unknown)
|
|
27
|
+
if (isFilterAll(n))
|
|
28
|
+
n.all.forEach(walk);
|
|
29
|
+
else if (isFilterAny(n))
|
|
30
|
+
n.any.forEach(walk);
|
|
31
|
+
else if (isFilterNot(n))
|
|
32
|
+
walk(n.not);
|
|
33
|
+
else if (isFilterPredicate(n))
|
|
34
|
+
out.add(n.field);
|
|
35
|
+
};
|
|
36
|
+
if (node)
|
|
37
|
+
walk(node);
|
|
38
|
+
return [...out];
|
|
39
|
+
}
|
|
40
|
+
/** True if `node` is structurally a filter node (children are not checked). */
|
|
41
|
+
export function isValidNode(node) {
|
|
42
|
+
if (node === null || typeof node !== 'object')
|
|
43
|
+
return false;
|
|
44
|
+
const n = node;
|
|
45
|
+
if ('all' in n)
|
|
46
|
+
return Array.isArray(n.all);
|
|
47
|
+
if ('any' in n)
|
|
48
|
+
return Array.isArray(n.any);
|
|
49
|
+
if ('not' in n)
|
|
50
|
+
return n.not !== null && typeof n.not === 'object';
|
|
51
|
+
return typeof n.field === 'string' && typeof n.operator === 'string';
|
|
52
|
+
}
|
|
53
|
+
/** Trace of `node` with every rule marked `not_evaluated` (short-circuit, dry-run). */
|
|
54
|
+
export function notEvaluatedTrace(node, path = ROOT_PATH) {
|
|
55
|
+
if (!isValidNode(node))
|
|
56
|
+
return { kind: 'invalid', path, value: 'not_evaluated', reason: 'malformed filter node' };
|
|
57
|
+
if (isFilterAll(node) || isFilterAny(node)) {
|
|
58
|
+
const kind = isFilterAll(node) ? 'all' : 'any';
|
|
59
|
+
const children = isFilterAll(node) ? node.all : node.any;
|
|
60
|
+
return {
|
|
61
|
+
kind,
|
|
62
|
+
path,
|
|
63
|
+
value: 'not_evaluated',
|
|
64
|
+
children: children.map((c, i) => notEvaluatedTrace(c, childPath(path, kind, i))),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (isFilterNot(node)) {
|
|
68
|
+
return { kind: 'not', path, value: 'not_evaluated', child: notEvaluatedTrace(node.not, childPath(path, 'not')) };
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
kind: 'predicate',
|
|
72
|
+
path,
|
|
73
|
+
value: 'not_evaluated',
|
|
74
|
+
field: node.field,
|
|
75
|
+
operator: node.operator,
|
|
76
|
+
expected: node.value,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
class Evaluation {
|
|
80
|
+
source;
|
|
81
|
+
cache = new Map();
|
|
82
|
+
costs = new WeakMap();
|
|
83
|
+
constructor(source) {
|
|
84
|
+
this.source = source;
|
|
85
|
+
}
|
|
86
|
+
resolve(field) {
|
|
87
|
+
let p = this.cache.get(field);
|
|
88
|
+
if (!p) {
|
|
89
|
+
p = Promise.resolve()
|
|
90
|
+
.then(() => this.source.resolve(field))
|
|
91
|
+
.catch((err) => ({
|
|
92
|
+
known: false,
|
|
93
|
+
reason: `field resolution failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
94
|
+
}));
|
|
95
|
+
this.cache.set(field, p);
|
|
96
|
+
}
|
|
97
|
+
return p;
|
|
98
|
+
}
|
|
99
|
+
/** 0 = only cheap fields, 1 = needs at least one expensive field. Memoized per subtree. */
|
|
100
|
+
cost(node) {
|
|
101
|
+
if (!this.source.isExpensive || !isValidNode(node))
|
|
102
|
+
return 0;
|
|
103
|
+
const known = this.costs.get(node);
|
|
104
|
+
if (known !== undefined)
|
|
105
|
+
return known;
|
|
106
|
+
let cost;
|
|
107
|
+
if (isFilterAll(node))
|
|
108
|
+
cost = node.all.some((c) => this.cost(c) > 0) ? 1 : 0;
|
|
109
|
+
else if (isFilterAny(node))
|
|
110
|
+
cost = node.any.some((c) => this.cost(c) > 0) ? 1 : 0;
|
|
111
|
+
else if (isFilterNot(node))
|
|
112
|
+
cost = this.cost(node.not);
|
|
113
|
+
else
|
|
114
|
+
cost = this.source.isExpensive(node.field) ? 1 : 0;
|
|
115
|
+
this.costs.set(node, cost);
|
|
116
|
+
return cost;
|
|
117
|
+
}
|
|
118
|
+
async node(node, path) {
|
|
119
|
+
if (!isValidNode(node))
|
|
120
|
+
return { kind: 'invalid', path, value: 'unknown', reason: 'malformed filter node' };
|
|
121
|
+
if (isFilterAll(node))
|
|
122
|
+
return this.junction('all', node.all, path);
|
|
123
|
+
if (isFilterAny(node))
|
|
124
|
+
return this.junction('any', node.any, path);
|
|
125
|
+
if (isFilterNot(node)) {
|
|
126
|
+
const child = await this.node(node.not, childPath(path, 'not'));
|
|
127
|
+
const v = child.value;
|
|
128
|
+
const value = v === 'unknown' || v === 'not_evaluated' ? 'unknown' : !v;
|
|
129
|
+
return { kind: 'not', path, value, child };
|
|
130
|
+
}
|
|
131
|
+
return this.predicate(node, path);
|
|
132
|
+
}
|
|
133
|
+
async junction(kind, children, path) {
|
|
134
|
+
// `all`: false dominates; `any`: true dominates.
|
|
135
|
+
const dominant = kind === 'any';
|
|
136
|
+
const traces = new Array(children.length);
|
|
137
|
+
const order = children
|
|
138
|
+
.map((child, index) => ({ index, cost: this.cost(child) }))
|
|
139
|
+
.sort((a, b) => a.cost - b.cost || a.index - b.index);
|
|
140
|
+
let sawUnknown = false;
|
|
141
|
+
let decided = false;
|
|
142
|
+
for (const { index } of order) {
|
|
143
|
+
const child = children[index];
|
|
144
|
+
const p = childPath(path, kind, index);
|
|
145
|
+
if (decided) {
|
|
146
|
+
traces[index] = notEvaluatedTrace(child, p);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const t = await this.node(child, p);
|
|
150
|
+
traces[index] = t;
|
|
151
|
+
if (t.value === dominant)
|
|
152
|
+
decided = true;
|
|
153
|
+
else if (t.value !== !dominant)
|
|
154
|
+
sawUnknown = true;
|
|
155
|
+
}
|
|
156
|
+
const value = decided ? dominant : sawUnknown ? 'unknown' : !dominant;
|
|
157
|
+
return { kind, path, value, children: traces };
|
|
158
|
+
}
|
|
159
|
+
async predicate(node, path) {
|
|
160
|
+
const base = { kind: 'predicate', path, field: node.field, operator: node.operator, expected: node.value };
|
|
161
|
+
const resolution = await this.resolve(node.field);
|
|
162
|
+
if (!resolution.known)
|
|
163
|
+
return { ...base, value: 'unknown', reason: resolution.reason };
|
|
164
|
+
const fieldType = this.source.fieldType?.(node.field);
|
|
165
|
+
const caseInsensitive = this.source.isCaseInsensitive?.(node.field) ?? false;
|
|
166
|
+
const outcome = applyOperator(node.operator, resolution.value, node.value, fieldType, { caseInsensitive });
|
|
167
|
+
return {
|
|
168
|
+
...base,
|
|
169
|
+
value: outcome.value,
|
|
170
|
+
actual: resolution.value,
|
|
171
|
+
...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Evaluate `filter` against an item's fields. A missing filter (`undefined`/`null`)
|
|
177
|
+
* matches everything. Never throws: malformed nodes and resolver errors yield `unknown`.
|
|
178
|
+
*/
|
|
179
|
+
export async function evaluateFilter(filter, source) {
|
|
180
|
+
if (filter === undefined || filter === null) {
|
|
181
|
+
return { value: true, trace: { kind: 'all', path: ROOT_PATH, value: true, children: [] } };
|
|
182
|
+
}
|
|
183
|
+
const trace = await new Evaluation(source).node(filter, ROOT_PATH);
|
|
184
|
+
const value = trace.value === 'not_evaluated' ? 'unknown' : trace.value;
|
|
185
|
+
return { value, trace };
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=evaluate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../../src/filters/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,iBAAiB,GAQlB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAkD/C,gCAAgC;AAChC,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAE7B,SAAS,SAAS,CAAC,MAAc,EAAE,IAAY,EAAE,KAAc;IAC7D,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,IAAI,IAAI,KAAK,GAAG,CAAC;AACnF,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,IAAmC;IAC/D,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,MAAM,IAAI,GAAG,CAAC,CAAa,EAAQ,EAAE;QACnC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,4DAA4D;QACzF,IAAI,WAAW,CAAC,CAAC,CAAC;YAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;aACnC,IAAI,WAAW,CAAC,CAAC,CAAC;YAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;aACxC,IAAI,WAAW,CAAC,CAAC,CAAC;YAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;aAChC,IAAI,iBAAiB,CAAC,CAAC,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC;IACF,IAAI,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,CAAC,GAAG,IAA+B,CAAC;IAC1C,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,GAAG,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC;IACnE,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;AACvE,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,iBAAiB,CAAC,IAAgB,EAAE,IAAI,GAAW,SAAS;IAC1E,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;IAClH,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/C,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAE,IAA8B,CAAC,GAAG,CAAC;QACpF,OAAO;YACL,IAAI;YACJ,IAAI;YACJ,KAAK,EAAE,eAAe;YACtB,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;SACjF,CAAC;IACJ,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;IACnH,CAAC;IACD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,IAAI;QACJ,KAAK,EAAE,eAAe;QACtB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,IAAI,CAAC,KAAK;KACrB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU;IAIe,MAAM;IAHlB,KAAK,GAAG,IAAI,GAAG,EAAoC,CAAC;IACpD,KAAK,GAAG,IAAI,OAAO,EAAkB,CAAC;IAEvD,YAA6B,MAAmB;sBAAnB,MAAM;IAAgB,CAAC;IAE5C,OAAO,CAAC,KAAa;QAC3B,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE;iBAClB,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;iBACtC,KAAK,CAAC,CAAC,GAAY,EAAmB,EAAE,CAAC,CAAC;gBACzC,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;aACvF,CAAC,CAAC,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,2FAA2F;IACnF,IAAI,CAAC,IAAgB;QAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACtC,IAAI,IAAY,CAAC;QACjB,IAAI,WAAW,CAAC,IAAI,CAAC;YAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aACxE,IAAI,WAAW,CAAC,IAAI,CAAC;YAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAC7E,IAAI,WAAW,CAAC,IAAI,CAAC;YAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;YAClD,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAE,IAA0B,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAgB,EAAE,IAAY;QACvC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;QAC5G,IAAI,WAAW,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACnE,IAAI,WAAW,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACnE,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YAChE,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;YACtB,MAAM,KAAK,GAAe,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC7C,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,IAAmB,EAAE,QAAsB,EAAE,IAAY;QAC9E,iDAAiD;QACjD,MAAM,QAAQ,GAAG,IAAI,KAAK,KAAK,CAAC;QAChC,MAAM,MAAM,GAAgC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACvE,MAAM,KAAK,GAAG,QAAQ;aACnB,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;aAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACxD,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,KAAK,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAe,CAAC;YAC5C,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YACvC,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,CAAC,KAAK,CAAC,GAAG,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBAC5C,SAAS;YACX,CAAC;YACD,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAClB,IAAI,CAAC,CAAC,KAAK,KAAK,QAAQ;gBAAE,OAAO,GAAG,IAAI,CAAC;iBACpC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,QAAQ;gBAAE,UAAU,GAAG,IAAI,CAAC;QACpD,CAAC;QACD,MAAM,KAAK,GAAa,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAChF,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAuB,EAAE,CAAC;IAClE,CAAC;IAEO,KAAK,CAAC,SAAS,CACrB,IAAqE,EACrE,IAAY;QAEZ,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,WAAoB,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QACpH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,CAAC,UAAU,CAAC,KAAK;YAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;QACvF,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtD,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;QAC7E,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC;QAC3G,OAAO;YACL,GAAG,IAAI;YACP,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,UAAU,CAAC,KAAK;YACxB,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpE,CAAC;IACJ,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAqC,EACrC,MAAmB;IAEnB,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC;IAC7F,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnE,MAAM,KAAK,GAAa,KAAK,CAAC,KAAK,KAAK,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IAClF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {@link FieldSource} for a {@link SourceItem}, implementing the batch contract
|
|
3
|
+
* of `SourceAdapter.resolveFields` (see docs/handoff/WI-01.md):
|
|
4
|
+
*
|
|
5
|
+
* - cheap fields come from `item.fields`;
|
|
6
|
+
* - on the first expensive field that is actually needed, ALL wanted expensive
|
|
7
|
+
* fields of the item are requested in one `resolveFields` call;
|
|
8
|
+
* - results live only as long as this field source (one planning step / view
|
|
9
|
+
* request) and are never written back into `item.fields`;
|
|
10
|
+
* - unknown fields, missing batch entries and resolver errors are `known: false`.
|
|
11
|
+
*/
|
|
12
|
+
import { type FieldDefinition, type FieldResolution, type FieldValue, type SourceItem } from '../core/index.js';
|
|
13
|
+
import type { FieldSource } from './evaluate.js';
|
|
14
|
+
/** Batch resolver with the signature of `SourceAdapter.resolveFields`. */
|
|
15
|
+
export type ResolveFieldsFn = (item: SourceItem, fields: readonly string[]) => Promise<Record<string, FieldResolution>>;
|
|
16
|
+
/** Options for {@link createItemFieldSource}. */
|
|
17
|
+
export interface ItemFieldSourceOptions {
|
|
18
|
+
/** Field definitions of the item's adapter (`adapter.fieldDefinitions()`). */
|
|
19
|
+
definitions: readonly FieldDefinition[];
|
|
20
|
+
/** Usually `(item, fields) => adapter.resolveFields(item, fields)`. Without it expensive fields are unknown. */
|
|
21
|
+
resolveFields?: ResolveFieldsFn;
|
|
22
|
+
/**
|
|
23
|
+
* Fields the caller will possibly need (e.g. `collectFields(filter)` plus
|
|
24
|
+
* item-priority rule fields). All expensive ones among them are fetched in
|
|
25
|
+
* the first batch. Expensive fields not listed here are fetched on demand.
|
|
26
|
+
*/
|
|
27
|
+
wantedFields?: readonly string[];
|
|
28
|
+
/**
|
|
29
|
+
* Additional values not provided by the adapter, e.g. `run.last_workflow`
|
|
30
|
+
* from the database for views. Take precedence over `item.fields`.
|
|
31
|
+
*/
|
|
32
|
+
extraFields?: Record<string, FieldValue | FieldResolution>;
|
|
33
|
+
}
|
|
34
|
+
/** Create a memoizing {@link FieldSource} for one item. */
|
|
35
|
+
export declare function createItemFieldSource(item: SourceItem, options: ItemFieldSourceOptions): FieldSource;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {@link FieldSource} for a {@link SourceItem}, implementing the batch contract
|
|
3
|
+
* of `SourceAdapter.resolveFields` (see docs/handoff/WI-01.md):
|
|
4
|
+
*
|
|
5
|
+
* - cheap fields come from `item.fields`;
|
|
6
|
+
* - on the first expensive field that is actually needed, ALL wanted expensive
|
|
7
|
+
* fields of the item are requested in one `resolveFields` call;
|
|
8
|
+
* - results live only as long as this field source (one planning step / view
|
|
9
|
+
* request) and are never written back into `item.fields`;
|
|
10
|
+
* - unknown fields, missing batch entries and resolver errors are `known: false`.
|
|
11
|
+
*/
|
|
12
|
+
import { errorMessage, } from '../core/index.js';
|
|
13
|
+
function isResolution(v) {
|
|
14
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v) && 'known' in v;
|
|
15
|
+
}
|
|
16
|
+
/** Create a memoizing {@link FieldSource} for one item. */
|
|
17
|
+
export function createItemFieldSource(item, options) {
|
|
18
|
+
const defs = new Map(options.definitions.map((d) => [d.name, d]));
|
|
19
|
+
const extra = options.extraFields ?? {};
|
|
20
|
+
const fetched = new Map();
|
|
21
|
+
const isExpensive = (field) => !Object.hasOwn(extra, field) && defs.get(field)?.expensive === true;
|
|
22
|
+
const fetchBatch = (field) => {
|
|
23
|
+
const batch = new Set([field]);
|
|
24
|
+
for (const f of options.wantedFields ?? [])
|
|
25
|
+
if (isExpensive(f) && !fetched.has(f))
|
|
26
|
+
batch.add(f);
|
|
27
|
+
const names = [...batch];
|
|
28
|
+
const resolveFields = options.resolveFields;
|
|
29
|
+
const request = resolveFields
|
|
30
|
+
? Promise.resolve()
|
|
31
|
+
.then(() => resolveFields(item, names))
|
|
32
|
+
.catch((err) => {
|
|
33
|
+
const reason = `field resolution failed: ${errorMessage(err)}`;
|
|
34
|
+
return Object.fromEntries(names.map((n) => [n, { known: false, reason }]));
|
|
35
|
+
})
|
|
36
|
+
: Promise.resolve({});
|
|
37
|
+
for (const name of names) {
|
|
38
|
+
fetched.set(name, request.then((res) => {
|
|
39
|
+
const r = Object.hasOwn(res, name) ? res[name] : undefined;
|
|
40
|
+
if (r && isResolution(r))
|
|
41
|
+
return r;
|
|
42
|
+
return {
|
|
43
|
+
known: false,
|
|
44
|
+
reason: resolveFields ? `adapter returned no value for '${name}'` : `no resolver for expensive field '${name}'`,
|
|
45
|
+
};
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
isExpensive,
|
|
51
|
+
fieldType(field) {
|
|
52
|
+
return defs.get(field)?.type;
|
|
53
|
+
},
|
|
54
|
+
isCaseInsensitive(field) {
|
|
55
|
+
return !Object.hasOwn(extra, field) && defs.get(field)?.caseInsensitive === true;
|
|
56
|
+
},
|
|
57
|
+
resolve(field) {
|
|
58
|
+
if (Object.hasOwn(extra, field)) {
|
|
59
|
+
const v = extra[field];
|
|
60
|
+
return isResolution(v) ? v : { known: true, value: v };
|
|
61
|
+
}
|
|
62
|
+
const def = defs.get(field);
|
|
63
|
+
if (def?.expensive) {
|
|
64
|
+
if (!fetched.has(field))
|
|
65
|
+
fetchBatch(field);
|
|
66
|
+
return fetched.get(field);
|
|
67
|
+
}
|
|
68
|
+
if (Object.hasOwn(item.fields, field)) {
|
|
69
|
+
return { known: true, value: item.fields[field] };
|
|
70
|
+
}
|
|
71
|
+
return { known: false, reason: def ? `field '${field}' not provided by source` : `unknown field '${field}'` };
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.js","sourceRoot":"","sources":["../../src/filters/fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EACL,YAAY,GAMb,MAAM,kBAAkB,CAAC;AA4B1B,SAAS,YAAY,CAAC,CAAU;IAC9B,OAAO,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC;AAClF,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,qBAAqB,CAAC,IAAgB,EAAE,OAA+B;IACrF,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoC,CAAC;IAE5D,MAAM,WAAW,GAAG,CAAC,KAAa,EAAW,EAAE,CAC7C,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IAEtE,MAAM,UAAU,GAAG,CAAC,KAAa,EAAQ,EAAE;QACzC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACvC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,YAAY,IAAI,EAAE;YAAE,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChG,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;QACzB,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC5C,MAAM,OAAO,GAA6C,aAAa;YACrE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE;iBACd,IAAI,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;iBACtC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACtB,MAAM,MAAM,GAAG,4BAA4B,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/D,OAAO,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAqB,CAAC,CAAC,CAAC,CAAC;YAChG,CAAC,CAAC;YACN,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACxB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CACT,IAAI,EACJ,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;gBACnB,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC3D,IAAI,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC;oBAAE,OAAO,CAAC,CAAC;gBACnC,OAAO;oBACL,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,kCAAkC,IAAI,GAAG,CAAC,CAAC,CAAC,oCAAoC,IAAI,GAAG;iBAChH,CAAC;YACJ,CAAC,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,WAAW;QACX,SAAS,CAAC,KAAa;YACrB,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC;QAC/B,CAAC;QACD,iBAAiB,CAAC,KAAa;YAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,eAAe,KAAK,IAAI,CAAC;QACnF,CAAC;QACD,OAAO,CAAC,KAAa;YACnB,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;gBAChC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAiC,CAAC;gBACvD,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YACzD,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC5B,IAAI,GAAG,EAAE,SAAS,EAAE,CAAC;gBACnB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,UAAU,CAAC,KAAK,CAAC,CAAC;gBAC3C,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAA6B,CAAC;YACxD,CAAC;YACD,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;gBACtC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAe,EAAE,CAAC;YAClE,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,KAAK,0BAA0B,CAAC,CAAC,CAAC,kBAAkB,KAAK,GAAG,EAAE,CAAC;QAChH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component `filters` – public API (WI-04, Spec §6, §7).
|
|
3
|
+
* Other components import from this module only.
|
|
4
|
+
*/
|
|
5
|
+
export { applyOperator, type OperatorOptions, type PredicateOutcome } from './predicate.js';
|
|
6
|
+
export { parseIsoDate } from './iso-date.js';
|
|
7
|
+
export { collectFields, evaluateFilter, notEvaluatedTrace, ROOT_PATH, type FieldSource, type FilterEvaluation, type FilterTrace, type TraceValue, } from './evaluate.js';
|
|
8
|
+
export { createItemFieldSource, type ItemFieldSourceOptions, type ResolveFieldsFn } from './fields.js';
|
|
9
|
+
export { canPushDown, splitForPushdown, type PushdownSplit } from './pushdown.js';
|
|
10
|
+
export { compareCandidates, naturalCompare, resolveItemPriority, sortCandidates, type ItemPriorityConfig, type ItemPriorityResult, type ItemPriorityRule, type PrioritizedCandidate, } from './priority.js';
|
|
11
|
+
export { buildCheckContext, CHECK_CONTEXT_ENV, DEFAULT_CHECK_KILL_GRACE_MS, DEFAULT_MAX_CHECK_OUTPUT_BYTES, MINIMAL_ENV_KEYS, ShellCheckRunner, type ShellCheckContext, type ShellCheckKind, type ShellCheckResult, type ShellCheckRunnerOptions, type ShellCheckSpec, } from './shell-check.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component `filters` – public API (WI-04, Spec §6, §7).
|
|
3
|
+
* Other components import from this module only.
|
|
4
|
+
*/
|
|
5
|
+
export { applyOperator } from './predicate.js';
|
|
6
|
+
export { parseIsoDate } from './iso-date.js';
|
|
7
|
+
export { collectFields, evaluateFilter, notEvaluatedTrace, ROOT_PATH, } from './evaluate.js';
|
|
8
|
+
export { createItemFieldSource } from './fields.js';
|
|
9
|
+
export { canPushDown, splitForPushdown } from './pushdown.js';
|
|
10
|
+
export { compareCandidates, naturalCompare, resolveItemPriority, sortCandidates, } from './priority.js';
|
|
11
|
+
export { buildCheckContext, CHECK_CONTEXT_ENV, DEFAULT_CHECK_KILL_GRACE_MS, DEFAULT_MAX_CHECK_OUTPUT_BYTES, MINIMAL_ENV_KEYS, ShellCheckRunner, } from './shell-check.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/filters/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,aAAa,EAA+C,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EACL,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,SAAS,GAKV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAqD,MAAM,aAAa,CAAC;AACvG,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAsB,MAAM,eAAe,CAAC;AAClF,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,cAAc,GAKf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,2BAA2B,EAC3B,8BAA8B,EAC9B,gBAAgB,EAChB,gBAAgB,GAMjB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict ISO-8601 parsing for filter comparisons and candidate ordering.
|
|
3
|
+
* Unlike `Date.parse`, this is independent of the host time zone and rejects
|
|
4
|
+
* non-ISO formats. Values without an offset are interpreted as UTC.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Parse `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS[.fff]][Z|±HH:MM]` to epoch ms.
|
|
8
|
+
* Returns `undefined` for anything else or for out-of-range components.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseIsoDate(value: unknown): number | undefined;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict ISO-8601 parsing for filter comparisons and candidate ordering.
|
|
3
|
+
* Unlike `Date.parse`, this is independent of the host time zone and rejects
|
|
4
|
+
* non-ISO formats. Values without an offset are interpreted as UTC.
|
|
5
|
+
*/
|
|
6
|
+
const ISO_RE = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?(Z|[+-]\d{2}:\d{2})?)?$/;
|
|
7
|
+
/**
|
|
8
|
+
* Parse `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS[.fff]][Z|±HH:MM]` to epoch ms.
|
|
9
|
+
* Returns `undefined` for anything else or for out-of-range components.
|
|
10
|
+
*/
|
|
11
|
+
export function parseIsoDate(value) {
|
|
12
|
+
if (typeof value !== 'string')
|
|
13
|
+
return undefined;
|
|
14
|
+
const m = ISO_RE.exec(value);
|
|
15
|
+
if (!m)
|
|
16
|
+
return undefined;
|
|
17
|
+
const [, y, mo, d, h = '0', mi = '0', s = '0', frac = '', offset = 'Z'] = m;
|
|
18
|
+
const year = Number(y);
|
|
19
|
+
const month = Number(mo);
|
|
20
|
+
const day = Number(d);
|
|
21
|
+
const hour = Number(h);
|
|
22
|
+
const minute = Number(mi);
|
|
23
|
+
const second = Number(s);
|
|
24
|
+
if (month < 1 || month > 12 || day < 1 || hour > 23 || minute > 59 || second > 59)
|
|
25
|
+
return undefined;
|
|
26
|
+
const ms = Number(frac.padEnd(3, '0').slice(0, 3));
|
|
27
|
+
const utc = Date.UTC(year, month - 1, day, hour, minute, second, ms);
|
|
28
|
+
// Reject day overflow (e.g. 2026-02-30).
|
|
29
|
+
const check = new Date(utc);
|
|
30
|
+
if (check.getUTCFullYear() !== year || check.getUTCMonth() !== month - 1 || check.getUTCDate() !== day) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
if (offset === 'Z')
|
|
34
|
+
return utc;
|
|
35
|
+
const sign = offset.startsWith('-') ? -1 : 1;
|
|
36
|
+
const oh = Number(offset.slice(1, 3));
|
|
37
|
+
const om = Number(offset.slice(4, 6));
|
|
38
|
+
if (oh > 23 || om > 59)
|
|
39
|
+
return undefined;
|
|
40
|
+
return utc - sign * (oh * 60 + om) * 60_000;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=iso-date.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iso-date.js","sourceRoot":"","sources":["../../src/filters/iso-date.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,GACV,iGAAiG,CAAC;AAEpG;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,IAAI,GAAG,EAAE,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACvB,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,EAAE,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM,GAAG,EAAE;QAAE,OAAO,SAAS,CAAC;IACpG,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IACrE,yCAAyC;IACzC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,cAAc,EAAE,KAAK,IAAI,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,EAAE,KAAK,GAAG,EAAE,CAAC;QACvG,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACtC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator semantics for single filter predicates (Spec §6).
|
|
3
|
+
*
|
|
4
|
+
* Every comparison is three-valued: incompatible types, unparsable dates or
|
|
5
|
+
* malformed operands yield `unknown` (with a reason) instead of guessing.
|
|
6
|
+
*/
|
|
7
|
+
import type { FieldType, FieldValue, FilterOperator, FilterValue, TriValue } from '../core/index.js';
|
|
8
|
+
/** Outcome of applying one operator to a known field value. */
|
|
9
|
+
export interface PredicateOutcome {
|
|
10
|
+
value: TriValue;
|
|
11
|
+
/** Why the outcome is `unknown`. */
|
|
12
|
+
reason?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Per-field comparison options (from the adapter's field definition). */
|
|
15
|
+
export interface OperatorOptions {
|
|
16
|
+
/** Compare string values without regard to case (e.g. GitHub labels). */
|
|
17
|
+
caseInsensitive?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Apply `operator` to a known field value and the literal `expected`.
|
|
21
|
+
* `fieldType` (from the adapter's field definition) enables date semantics;
|
|
22
|
+
* `options.caseInsensitive` folds the case of all string values first.
|
|
23
|
+
*/
|
|
24
|
+
export declare function applyOperator(operator: FilterOperator, actualValue: FieldValue, expectedValue: FilterValue, fieldType?: FieldType, options?: OperatorOptions): PredicateOutcome;
|