@smthrs/plan 0.0.0-stage → 1.0.0-rc.3
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/CHANGELOG.md +183 -0
- package/LICENSE +21 -0
- package/README.md +228 -2
- package/dist/cjs/CachePolicy.d.ts +131 -0
- package/dist/cjs/CachePolicy.d.ts.map +1 -0
- package/dist/cjs/CachePolicy.js +50 -0
- package/dist/cjs/CachePolicy.js.map +7 -0
- package/dist/cjs/Effects.d.ts +240 -0
- package/dist/cjs/Effects.d.ts.map +1 -0
- package/dist/cjs/Effects.js +76 -0
- package/dist/cjs/Effects.js.map +7 -0
- package/dist/cjs/FileSet.d.ts +288 -0
- package/dist/cjs/FileSet.d.ts.map +1 -0
- package/dist/cjs/FileSet.js +191 -0
- package/dist/cjs/FileSet.js.map +7 -0
- package/dist/cjs/GraphBuildError.d.ts +122 -0
- package/dist/cjs/GraphBuildError.d.ts.map +1 -0
- package/dist/cjs/GraphBuildError.js +71 -0
- package/dist/cjs/GraphBuildError.js.map +7 -0
- package/dist/cjs/KeyMaterial.d.ts +102 -0
- package/dist/cjs/KeyMaterial.d.ts.map +1 -0
- package/dist/cjs/KeyMaterial.js +64 -0
- package/dist/cjs/KeyMaterial.js.map +7 -0
- package/dist/cjs/Node.d.ts +606 -0
- package/dist/cjs/Node.d.ts.map +1 -0
- package/dist/cjs/Node.js +207 -0
- package/dist/cjs/Node.js.map +7 -0
- package/dist/cjs/Placement.d.ts +117 -0
- package/dist/cjs/Placement.d.ts.map +1 -0
- package/dist/cjs/Placement.js +46 -0
- package/dist/cjs/Placement.js.map +7 -0
- package/dist/cjs/Plan.d.ts +440 -0
- package/dist/cjs/Plan.d.ts.map +1 -0
- package/dist/cjs/Plan.js +508 -0
- package/dist/cjs/Plan.js.map +7 -0
- package/dist/cjs/PlanDiff.d.ts +58 -0
- package/dist/cjs/PlanDiff.d.ts.map +1 -0
- package/dist/cjs/PlanDiff.js +124 -0
- package/dist/cjs/PlanDiff.js.map +7 -0
- package/dist/cjs/Planned.d.ts +121 -0
- package/dist/cjs/Planned.d.ts.map +1 -0
- package/dist/cjs/Planned.js +72 -0
- package/dist/cjs/Planned.js.map +7 -0
- package/dist/cjs/Repetition.d.ts +31 -0
- package/dist/cjs/Repetition.d.ts.map +1 -0
- package/dist/cjs/Repetition.js +36 -0
- package/dist/cjs/Repetition.js.map +7 -0
- package/dist/cjs/Scheduling.d.ts +89 -0
- package/dist/cjs/Scheduling.d.ts.map +1 -0
- package/dist/cjs/Scheduling.js +76 -0
- package/dist/cjs/Scheduling.js.map +7 -0
- package/dist/cjs/StepKey.d.ts +333 -0
- package/dist/cjs/StepKey.d.ts.map +1 -0
- package/dist/cjs/StepKey.js +311 -0
- package/dist/cjs/StepKey.js.map +7 -0
- package/dist/cjs/index.d.ts +91 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +59 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/ConflictAnnotation.d.ts +59 -0
- package/dist/cjs/internal/ConflictAnnotation.d.ts.map +1 -0
- package/dist/cjs/internal/ConflictAnnotation.js +247 -0
- package/dist/cjs/internal/ConflictAnnotation.js.map +7 -0
- package/dist/cjs/internal/EffectCandidates.d.ts +19 -0
- package/dist/cjs/internal/EffectCandidates.d.ts.map +1 -0
- package/dist/cjs/internal/EffectCandidates.js +92 -0
- package/dist/cjs/internal/EffectCandidates.js.map +7 -0
- package/dist/cjs/internal/JsonMirror.d.ts +34 -0
- package/dist/cjs/internal/JsonMirror.d.ts.map +1 -0
- package/dist/cjs/internal/JsonMirror.js +195 -0
- package/dist/cjs/internal/JsonMirror.js.map +7 -0
- package/dist/cjs/internal/effects.d.ts +245 -0
- package/dist/cjs/internal/effects.d.ts.map +1 -0
- package/dist/cjs/internal/effects.js +290 -0
- package/dist/cjs/internal/effects.js.map +7 -0
- package/dist/cjs/internal/node.d.ts +526 -0
- package/dist/cjs/internal/node.d.ts.map +1 -0
- package/dist/cjs/internal/node.js +312 -0
- package/dist/cjs/internal/node.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/PlanFixtures.d.ts +127 -0
- package/dist/cjs/test/PlanFixtures.d.ts.map +1 -0
- package/dist/cjs/test/PlanFixtures.js +59 -0
- package/dist/cjs/test/PlanFixtures.js.map +7 -0
- package/dist/esm/CachePolicy.d.ts +131 -0
- package/dist/esm/CachePolicy.d.ts.map +1 -0
- package/dist/esm/CachePolicy.js +117 -0
- package/dist/esm/CachePolicy.js.map +1 -0
- package/dist/esm/Effects.d.ts +240 -0
- package/dist/esm/Effects.d.ts.map +1 -0
- package/dist/esm/Effects.js +167 -0
- package/dist/esm/Effects.js.map +1 -0
- package/dist/esm/FileSet.d.ts +288 -0
- package/dist/esm/FileSet.d.ts.map +1 -0
- package/dist/esm/FileSet.js +324 -0
- package/dist/esm/FileSet.js.map +1 -0
- package/dist/esm/GraphBuildError.d.ts +122 -0
- package/dist/esm/GraphBuildError.d.ts.map +1 -0
- package/dist/esm/GraphBuildError.js +146 -0
- package/dist/esm/GraphBuildError.js.map +1 -0
- package/dist/esm/KeyMaterial.d.ts +102 -0
- package/dist/esm/KeyMaterial.d.ts.map +1 -0
- package/dist/esm/KeyMaterial.js +85 -0
- package/dist/esm/KeyMaterial.js.map +1 -0
- package/dist/esm/Node.d.ts +606 -0
- package/dist/esm/Node.d.ts.map +1 -0
- package/dist/esm/Node.js +521 -0
- package/dist/esm/Node.js.map +1 -0
- package/dist/esm/Placement.d.ts +117 -0
- package/dist/esm/Placement.d.ts.map +1 -0
- package/dist/esm/Placement.js +65 -0
- package/dist/esm/Placement.js.map +1 -0
- package/dist/esm/Plan.d.ts +440 -0
- package/dist/esm/Plan.d.ts.map +1 -0
- package/dist/esm/Plan.js +673 -0
- package/dist/esm/Plan.js.map +1 -0
- package/dist/esm/PlanDiff.d.ts +58 -0
- package/dist/esm/PlanDiff.d.ts.map +1 -0
- package/dist/esm/PlanDiff.js +159 -0
- package/dist/esm/PlanDiff.js.map +1 -0
- package/dist/esm/Planned.d.ts +121 -0
- package/dist/esm/Planned.d.ts.map +1 -0
- package/dist/esm/Planned.js +147 -0
- package/dist/esm/Planned.js.map +1 -0
- package/dist/esm/Repetition.d.ts +31 -0
- package/dist/esm/Repetition.d.ts.map +1 -0
- package/dist/esm/Repetition.js +24 -0
- package/dist/esm/Repetition.js.map +1 -0
- package/dist/esm/Scheduling.d.ts +89 -0
- package/dist/esm/Scheduling.d.ts.map +1 -0
- package/dist/esm/Scheduling.js +82 -0
- package/dist/esm/Scheduling.js.map +1 -0
- package/dist/esm/StepKey.d.ts +333 -0
- package/dist/esm/StepKey.d.ts.map +1 -0
- package/dist/esm/StepKey.js +478 -0
- package/dist/esm/StepKey.js.map +1 -0
- package/dist/esm/index.d.ts +91 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +91 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/ConflictAnnotation.d.ts +59 -0
- package/dist/esm/internal/ConflictAnnotation.d.ts.map +1 -0
- package/dist/esm/internal/ConflictAnnotation.js +332 -0
- package/dist/esm/internal/ConflictAnnotation.js.map +1 -0
- package/dist/esm/internal/EffectCandidates.d.ts +19 -0
- package/dist/esm/internal/EffectCandidates.d.ts.map +1 -0
- package/dist/esm/internal/EffectCandidates.js +86 -0
- package/dist/esm/internal/EffectCandidates.js.map +1 -0
- package/dist/esm/internal/JsonMirror.d.ts +34 -0
- package/dist/esm/internal/JsonMirror.d.ts.map +1 -0
- package/dist/esm/internal/JsonMirror.js +204 -0
- package/dist/esm/internal/JsonMirror.js.map +1 -0
- package/dist/esm/internal/effects.d.ts +245 -0
- package/dist/esm/internal/effects.d.ts.map +1 -0
- package/dist/esm/internal/effects.js +459 -0
- package/dist/esm/internal/effects.js.map +1 -0
- package/dist/esm/internal/node.d.ts +526 -0
- package/dist/esm/internal/node.d.ts.map +1 -0
- package/dist/esm/internal/node.js +564 -0
- package/dist/esm/internal/node.js.map +1 -0
- package/dist/esm/test/PlanFixtures.d.ts +127 -0
- package/dist/esm/test/PlanFixtures.d.ts.map +1 -0
- package/dist/esm/test/PlanFixtures.js +60 -0
- package/dist/esm/test/PlanFixtures.js.map +1 -0
- package/docs/README.md +165 -0
- package/docs/api.md +1475 -0
- package/docs/concepts/authoring-ast.md +203 -0
- package/docs/concepts/effects-and-conflicts.md +135 -0
- package/docs/concepts/plan-value.md +168 -0
- package/docs/concepts/step-keys.md +179 -0
- package/docs/guides/author-a-node-graph.md +182 -0
- package/docs/guides/compile-a-plan.md +146 -0
- package/docs/guides/declare-file-effects.md +143 -0
- package/docs/guides/diff-two-plans.md +101 -0
- package/docs/installation.md +71 -0
- package/docs/quickstart.md +128 -0
- package/docs/testing.md +73 -0
- package/docs/troubleshooting.md +314 -0
- package/package.json +254 -3
- package/src/CachePolicy.ts +143 -0
- package/src/Effects.ts +284 -0
- package/src/FileSet.ts +411 -0
- package/src/GraphBuildError.ts +158 -0
- package/src/KeyMaterial.ts +106 -0
- package/src/Node.ts +871 -0
- package/src/Placement.ts +99 -0
- package/src/Plan.ts +868 -0
- package/src/PlanDiff.ts +199 -0
- package/src/Planned.ts +208 -0
- package/src/Repetition.ts +33 -0
- package/src/Scheduling.ts +154 -0
- package/src/StepKey.ts +710 -0
- package/src/index.ts +103 -0
- package/src/internal/ConflictAnnotation.ts +374 -0
- package/src/internal/EffectCandidates.ts +89 -0
- package/src/internal/JsonMirror.ts +255 -0
- package/src/internal/effects.ts +543 -0
- package/src/internal/node.ts +858 -0
- package/src/test/PlanFixtures.ts +76 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Effects.js","sourceRoot":"","sources":["../../src/Effects.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure effect declarations used to describe flow read and write envelopes.\n *\n * This is the ONE effect-envelope model. `@smthrs/flow` enforces it while it\n * builds a graph, `@smthrs/core` re-exports it, and `@smthrs/patterns` reads it\n * to intersect a decorator's declaration with the flow it wraps. It lives here\n * because `@smthrs/plan` is the lowest package all three depend on, beside the\n * `tier` and `onConflict` vocabularies a plan already speaks.\n *\n * Two layers: the declaration API, which a flow author and a catalog use, and\n * the prepared matching API below it, which a graph builder uses to check one\n * envelope against many steps without re-reading the envelope each time.\n *\n * Governing contract: `packages/smithers/flows/plan/docs/api.md`, published as\n * https://smithers.sh/docs/reference/api/plan.\n *\n * @since 0.0.0\n */\n\nimport * as Context from \"effect/Context\"\nimport * as Schema from \"effect/Schema\"\nimport * as Index from \"./internal/effects.ts\"\n\n/**\n * How reversible the work beneath a declaration is.\n *\n * This is the ONE tier vocabulary. `KeyMaterial.kind` is this schema, and so\n * is `@smthrs/flow`'s `Action.Tier`: an action's durability and a declared\n * envelope's ceiling are the same three words, and a fourth spelling of them\n * in one of the three places would fork identity silently.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const Tier = Schema.Literals([\"sealed\", \"compensable\", \"irreversible\"])\n\n/**\n * The value form of {@link Tier}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type Tier = typeof Tier.Type\n\n/**\n * How hermetic the work beneath a declaration is.\n *\n * This is the ONE mode vocabulary: the two words were written out in\n * `Declaration`, in `MakeOptions`, and twice more inside the matcher.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const Mode = Schema.Literals([\"hermetic\", \"expected\"])\n\n/**\n * The value form of {@link Mode}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type Mode = typeof Mode.Type\n\n/**\n * What a plan does when two nodes write the same path.\n *\n * This is the ONE conflict vocabulary: `Declaration.onConflict` declares it and\n * `Plan.PairStrategy` is this same schema, resolved per pair by\n * `Plan.compile`.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const ConflictStrategy = Schema.Literals([\"serialize\", \"lane\", \"fail\"])\n\n/**\n * The value form of {@link ConflictStrategy}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ConflictStrategy = typeof ConflictStrategy.Type\n\n/**\n * The annotation key a flow's declared effect ENVELOPE is carried under.\n *\n * One key, here, because two graph builders read it: `@smthrs/flow`'s\n * `Graph.build` enforces the envelope it names, and `@smthrs/core` re-exports\n * this key as `Annotations.Effects`. While they were separate keys a flow\n * annotated for one builder was invisible to the other.\n *\n * It is a `Context` key, not hashed material: annotating a flow with an\n * envelope changes what a build ADMITS, and the declaration reaches identity\n * only through `KeyMaterial.kind`.\n *\n * @category annotations\n * @since 1.0.0-rc.0\n */\nexport const Envelope = Context.Service<Declaration>(\"@smthrs/plan/Effects/Envelope\")\n\n/**\n * The prepared matching API: what a graph builder needs to check one envelope\n * against every step it encloses, and to find the write overlaps between two\n * declarations, at the cost the public functions above pay once rather than\n * once per step.\n *\n * {@link prepareEnvelope} reads an envelope once into the form\n * {@link narrowPrepared} answers from, which is what makes a wide envelope cost\n * its size per build rather than per node. {@link indexPaths},\n * {@link rankPaths} and {@link overlapRanks} are the same decomposition for\n * write overlap, which {@link overlaps} wraps for the pairwise case.\n * {@link boundedEffects} snapshots a declaration into builder-owned data,\n * refusing through the caller's own error before the copy grows past its limit,\n * and {@link maximumPathLength} and {@link maximumGlobs} are the per-path bounds\n * that refusal enforces.\n *\n * @category matching\n * @since 1.0.0-rc.0\n */\nexport type { PathIndex, PreparedEnvelope, Ranked } from \"./internal/effects.ts\"\n\n/**\n * The prepared matching API. See the type re-exports above for what each member\n * is for.\n *\n * @category matching\n * @since 1.0.0-rc.0\n */\nexport {\n boundedEffects,\n indexPaths,\n maximumGlobs,\n maximumPathLength,\n narrowPrepared,\n overlapRanks,\n prepareEnvelope,\n rankPaths\n} from \"./internal/effects.ts\"\n\n/**\n * A normalized description of the resources a flow or step may read and\n * write.\n *\n * @category models\n * @since 0.0.0\n * @slop\n */\nexport interface Declaration {\n readonly reads: ReadonlyArray<string>\n readonly writes: ReadonlyArray<string>\n readonly mode: Mode\n readonly onConflict: ConflictStrategy\n readonly tier?: Tier | undefined\n}\n\n/**\n * Input accepted by {@link make}. Iterables are normalized into sorted,\n * duplicate-free arrays so declarations are deterministic key material.\n * Envelope entries and declared paths must already be path-normalized: no\n * separator or dot-segment rewriting is performed. A declared path containing\n * a whole `.` or `..` segment is never covered and therefore surfaces from\n * {@link narrow} as an `effect_outside_envelope` diagnostic.\n *\n * @category models\n * @since 0.0.0\n * @slop\n */\nexport interface MakeOptions {\n readonly reads: Iterable<string>\n readonly writes: Iterable<string>\n readonly mode: Mode\n readonly onConflict: ConflictStrategy\n readonly tier?: Tier | undefined\n}\n\n/**\n * A result of checking that a step declaration narrows a flow envelope.\n *\n * @category models\n * @since 0.0.0\n * @slop\n */\nexport type NarrowResult =\n | { readonly ok: true }\n | {\n readonly ok: false\n readonly code: \"effect_outside_envelope\" | \"effect_mode_widening\" | \"effect_tier_widening\"\n readonly paths: ReadonlyArray<string>\n }\n\nconst normalize = (paths: Iterable<string>): ReadonlyArray<string> => [...new Set(paths)].sort()\n\n/**\n * Constructs a deterministic effect declaration.\n *\n * @category constructors\n * @since 0.0.0\n * @slop\n */\nexport const make = (input: MakeOptions): Declaration => ({\n reads: normalize(input.reads),\n writes: normalize(input.writes),\n mode: input.mode,\n onConflict: input.onConflict,\n ...(input.tier === undefined ? {} : { tier: input.tier })\n})\n\n/**\n * Checks whether `path` is covered by an envelope entry.\n *\n * The grammar is exhaustive: an exact path matches itself; `*` and `**` match\n * everything; `prefix*` matches by string prefix; and `prefix/**` matches\n * `prefix/` and everything below it, but not bare `prefix`. This is\n * intentionally not full minimatch syntax. Envelope entries and declared paths\n * must be normalized. A path containing a whole `.` or `..` segment is never\n * covered, so {@link narrow} reports it as `effect_outside_envelope` rather\n * than silently accepting an escape.\n *\n * @category predicates\n * @since 0.0.0\n * @slop\n */\nexport const covers = (envelope: string, path: string): boolean => {\n if (Index.hasDotSegment(path)) return false\n return envelope === path || (Index.isGlob(envelope) && path.startsWith(Index.globPrefix(envelope)))\n}\n\n/**\n * Verifies that a step declaration stays within an enclosing flow envelope.\n *\n * Read and write paths must be covered independently. A step may tighten\n * `expected` to `hermetic`, but cannot widen `hermetic` to `expected`.\n * Effect tiers narrow from irreversible to compensable to sealed.\n *\n * The envelope's lists are prepared once: exact entries go into a set and\n * covering patterns collapse to their outermost prefixes, sorted, so each step\n * path costs one dot-segment scan, one lookup, and one binary search ending in\n * one prefix comparison, whatever the envelope's width or how many of its\n * patterns nest. A graph build prepares each envelope once for every node it\n * encloses.\n *\n * @category validation\n * @since 0.0.0\n * @slop\n */\nexport const narrow = (envelope: Declaration, step: Declaration): NarrowResult =>\n Index.narrowPrepared(Index.prepareEnvelope(envelope), step)\n\n/**\n * Returns the concrete or narrower write declarations shared by two effect\n * declarations. The result is sorted and duplicate-free.\n *\n * Two declarations of the same literal path always overlap, including a path\n * {@link covers} refuses to match because it carries a `.` or `..` segment.\n * Glob coverage stays strict for those paths, so an unnormalized declaration\n * still escapes no envelope, but two writers naming the same unnormalized path\n * are still detected as writing the same resource.\n *\n * The union of both declarations is indexed once: the distinct paths are\n * sorted, each is scanned once for a dot segment, and each pattern's prefix\n * is located by binary search. Exact paths then match through a merge of two\n * rank lists and each covering pattern enumerates the other declaration's\n * ranks inside its interval, so the cost is linear in the two declarations\n * plus the matches, whatever the paths' lengths or how many patterns nest.\n *\n * @category analysis\n * @since 0.0.0\n * @slop\n */\nexport const overlaps = (a: Declaration, b: Declaration): ReadonlyArray<string> => {\n const indexed = Index.indexPaths([a.writes, b.writes])\n return Index.overlapRanks(indexed, Index.rankPaths(indexed, a.writes), Index.rankPaths(indexed, b.writes))\n .map((rank) => indexed.paths[rank]!)\n}\n\n/**\n * Returns a sealed, hermetic copy of an effect declaration.\n *\n * @category constructors\n * @since 0.0.0\n * @slop\n */\nexport const sealed = (declaration: Declaration): Declaration =>\n make({ ...declaration, mode: \"hermetic\", tier: \"sealed\" })\n"]}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static filesystem declaration vocabulary shared by planning and execution.
|
|
3
|
+
*
|
|
4
|
+
* Declared paths use `/` as their canonical separator and normalize Unicode
|
|
5
|
+
* to NFC. Workspace-relative paths reject C0 controls (U+0000 through U+001F)
|
|
6
|
+
* and DEL (U+007F), which name nothing a supported filesystem can open. C1
|
|
7
|
+
* controls (U+0080 through U+009F) remain accepted because those bytes are
|
|
8
|
+
* legal in a POSIX file name.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
import * as Schema from "effect/Schema";
|
|
13
|
+
/**
|
|
14
|
+
* How strictly a declared file set is enforced at the boundary.
|
|
15
|
+
*
|
|
16
|
+
* `hard` rejects undeclared access. `expected` records access for validation
|
|
17
|
+
* after execution without requiring the sandbox to reject it immediately.
|
|
18
|
+
*
|
|
19
|
+
* This is the ONE spelling of the pair. `Plan.NodeEffects.boundaryMode` is this
|
|
20
|
+
* schema and so is `@smthrs/flow`'s `Action.BoundaryMode`; the literal used to
|
|
21
|
+
* be written out in all three places.
|
|
22
|
+
*
|
|
23
|
+
* @category schemas
|
|
24
|
+
* @since 1.0.0-rc.0
|
|
25
|
+
*/
|
|
26
|
+
export declare const BoundaryMode: Schema.Literals<readonly ["hard", "expected"]>;
|
|
27
|
+
/**
|
|
28
|
+
* The value form of {@link BoundaryMode}.
|
|
29
|
+
*
|
|
30
|
+
* @category models
|
|
31
|
+
* @since 1.0.0-rc.0
|
|
32
|
+
*/
|
|
33
|
+
export type BoundaryMode = typeof BoundaryMode.Type;
|
|
34
|
+
/**
|
|
35
|
+
* The canonical spelling of a declared path or pattern: every separator is
|
|
36
|
+
* `/`, and Unicode is normalized to NFC.
|
|
37
|
+
*
|
|
38
|
+
* {@link workspaceRelative} accepts a backslash as a separator, so
|
|
39
|
+
* `dist\same.js` and `dist/same.js` are two spellings of ONE workspace path.
|
|
40
|
+
* Every exact-path comparison in this module goes through this form, because
|
|
41
|
+
* comparing the raw spellings would let the separator alias defeat overlap
|
|
42
|
+
* detection exactly as `.` segments and empty segments would.
|
|
43
|
+
*
|
|
44
|
+
* @category accessors
|
|
45
|
+
* @since 0.1.0
|
|
46
|
+
* @slop
|
|
47
|
+
*/
|
|
48
|
+
export declare const canonical: (path: string) => string;
|
|
49
|
+
/**
|
|
50
|
+
* The {@link canonical} spelling with letter case folded away.
|
|
51
|
+
*
|
|
52
|
+
* A case-insensitive filesystem (default macOS APFS, Windows NTFS) opens
|
|
53
|
+
* `src/A.ts` and `src/a.ts` as one file, so static overlap compares this form:
|
|
54
|
+
* two declarations that differ only in case must serialize. Folding goes
|
|
55
|
+
* through upper case because that mapping is context-free, so it folds each
|
|
56
|
+
* character alone and commutes with splitting a path into segments.
|
|
57
|
+
*
|
|
58
|
+
* @category accessors
|
|
59
|
+
* @since 1.0.0-rc.0
|
|
60
|
+
*/
|
|
61
|
+
export declare const folded: (path: string) => string;
|
|
62
|
+
/** Workspace-relative glob pattern.
|
|
63
|
+
* @category schemas
|
|
64
|
+
* @since 0.1.0
|
|
65
|
+
* @slop
|
|
66
|
+
*/
|
|
67
|
+
export declare const Pattern: Schema.NonEmptyString;
|
|
68
|
+
/**
|
|
69
|
+
* Whether a declared path or pattern stays inside the workspace and names it
|
|
70
|
+
* one way only. Refuses absolute paths (POSIX and drive-letter), upward
|
|
71
|
+
* traversal, and the aliasing forms — `.` segments, empty segments — that
|
|
72
|
+
* would let two spellings of one file defeat exact-string overlap detection.
|
|
73
|
+
*
|
|
74
|
+
* @category predicates
|
|
75
|
+
* @since 0.1.0
|
|
76
|
+
* @slop
|
|
77
|
+
*/
|
|
78
|
+
export declare const workspaceRelative: (pattern: string) => boolean;
|
|
79
|
+
/** Bazel-style file glob.
|
|
80
|
+
* @category schemas
|
|
81
|
+
* @since 0.1.0
|
|
82
|
+
* @slop
|
|
83
|
+
*/
|
|
84
|
+
export declare const Glob: Schema.TaggedStruct<"Glob", {
|
|
85
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
86
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
87
|
+
}>;
|
|
88
|
+
/** Bazel-style file glob.
|
|
89
|
+
* @category models
|
|
90
|
+
* @since 0.1.0
|
|
91
|
+
* @slop
|
|
92
|
+
*/
|
|
93
|
+
export type Glob = typeof Glob.Type;
|
|
94
|
+
/** A directory output captured and replayed as one tree artifact.
|
|
95
|
+
* @category schemas
|
|
96
|
+
* @since 0.1.0
|
|
97
|
+
* @slop
|
|
98
|
+
*/
|
|
99
|
+
export declare const TreeArtifact: Schema.TaggedStruct<"TreeArtifact", {
|
|
100
|
+
readonly path: Schema.NonEmptyString;
|
|
101
|
+
}>;
|
|
102
|
+
/** A directory output captured and replayed as one tree artifact.
|
|
103
|
+
* @category models
|
|
104
|
+
* @since 0.1.0
|
|
105
|
+
* @slop
|
|
106
|
+
*/
|
|
107
|
+
export type TreeArtifact = typeof TreeArtifact.Type;
|
|
108
|
+
/** One leaf of a named filegroup.
|
|
109
|
+
* @category schemas
|
|
110
|
+
* @since 0.1.0
|
|
111
|
+
* @slop
|
|
112
|
+
*/
|
|
113
|
+
export declare const Entry: Schema.Union<readonly [Schema.NonEmptyString, Schema.TaggedStruct<"Glob", {
|
|
114
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
115
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
116
|
+
}>, Schema.TaggedStruct<"TreeArtifact", {
|
|
117
|
+
readonly path: Schema.NonEmptyString;
|
|
118
|
+
}>]>;
|
|
119
|
+
/** One leaf of a named filegroup.
|
|
120
|
+
* @category models
|
|
121
|
+
* @since 0.1.0
|
|
122
|
+
* @slop
|
|
123
|
+
*/
|
|
124
|
+
export type Entry = typeof Entry.Type;
|
|
125
|
+
/** A declaration valid in a read set.
|
|
126
|
+
* @category schemas
|
|
127
|
+
* @since 0.1.0
|
|
128
|
+
* @slop
|
|
129
|
+
*/
|
|
130
|
+
export declare const ReadEntry: Schema.Union<readonly [Schema.NonEmptyString, Schema.TaggedStruct<"Glob", {
|
|
131
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
132
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
133
|
+
}>]>;
|
|
134
|
+
/** A declaration valid in a read set.
|
|
135
|
+
* @category models
|
|
136
|
+
* @since 0.1.0
|
|
137
|
+
* @slop
|
|
138
|
+
*/
|
|
139
|
+
export type ReadEntry = typeof ReadEntry.Type;
|
|
140
|
+
/** A named reusable collection expanded before measurement.
|
|
141
|
+
* @category schemas
|
|
142
|
+
* @since 0.1.0
|
|
143
|
+
* @slop
|
|
144
|
+
*/
|
|
145
|
+
export declare const Filegroup: Schema.TaggedStruct<"Filegroup", {
|
|
146
|
+
readonly name: Schema.NonEmptyString;
|
|
147
|
+
readonly entries: Schema.$Array<Schema.Union<readonly [Schema.NonEmptyString, Schema.TaggedStruct<"Glob", {
|
|
148
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
149
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
150
|
+
}>, Schema.TaggedStruct<"TreeArtifact", {
|
|
151
|
+
readonly path: Schema.NonEmptyString;
|
|
152
|
+
}>]>>;
|
|
153
|
+
}>;
|
|
154
|
+
/** A named reusable collection expanded before measurement.
|
|
155
|
+
* @category models
|
|
156
|
+
* @since 0.1.0
|
|
157
|
+
* @slop
|
|
158
|
+
*/
|
|
159
|
+
export type Filegroup = typeof Filegroup.Type;
|
|
160
|
+
/** A named read-only collection.
|
|
161
|
+
* @category schemas
|
|
162
|
+
* @since 0.1.0
|
|
163
|
+
* @slop
|
|
164
|
+
*/
|
|
165
|
+
export declare const ReadFilegroup: Schema.TaggedStruct<"Filegroup", {
|
|
166
|
+
readonly name: Schema.NonEmptyString;
|
|
167
|
+
readonly entries: Schema.$Array<Schema.Union<readonly [Schema.NonEmptyString, Schema.TaggedStruct<"Glob", {
|
|
168
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
169
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
170
|
+
}>]>>;
|
|
171
|
+
}>;
|
|
172
|
+
/** A named read-only collection.
|
|
173
|
+
* @category models
|
|
174
|
+
* @since 0.1.0
|
|
175
|
+
* @slop
|
|
176
|
+
*/
|
|
177
|
+
export type ReadFilegroup = typeof ReadFilegroup.Type;
|
|
178
|
+
/** Any declaration accepted by a plan effect set.
|
|
179
|
+
* @category schemas
|
|
180
|
+
* @since 0.1.0
|
|
181
|
+
* @slop
|
|
182
|
+
*/
|
|
183
|
+
export declare const Declaration: Schema.Union<readonly [Schema.Union<readonly [Schema.NonEmptyString, Schema.TaggedStruct<"Glob", {
|
|
184
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
185
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
186
|
+
}>, Schema.TaggedStruct<"TreeArtifact", {
|
|
187
|
+
readonly path: Schema.NonEmptyString;
|
|
188
|
+
}>]>, Schema.TaggedStruct<"Filegroup", {
|
|
189
|
+
readonly name: Schema.NonEmptyString;
|
|
190
|
+
readonly entries: Schema.$Array<Schema.Union<readonly [Schema.NonEmptyString, Schema.TaggedStruct<"Glob", {
|
|
191
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
192
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
193
|
+
}>, Schema.TaggedStruct<"TreeArtifact", {
|
|
194
|
+
readonly path: Schema.NonEmptyString;
|
|
195
|
+
}>]>>;
|
|
196
|
+
}>]>;
|
|
197
|
+
/** Any declaration accepted by a plan effect set.
|
|
198
|
+
* @category models
|
|
199
|
+
* @since 0.1.0
|
|
200
|
+
* @slop
|
|
201
|
+
*/
|
|
202
|
+
export type Declaration = typeof Declaration.Type;
|
|
203
|
+
/** Any declaration accepted by a plan read set.
|
|
204
|
+
* @category schemas
|
|
205
|
+
* @since 0.1.0
|
|
206
|
+
* @slop
|
|
207
|
+
*/
|
|
208
|
+
export declare const ReadDeclaration: Schema.Union<readonly [Schema.Union<readonly [Schema.NonEmptyString, Schema.TaggedStruct<"Glob", {
|
|
209
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
210
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
211
|
+
}>]>, Schema.TaggedStruct<"Filegroup", {
|
|
212
|
+
readonly name: Schema.NonEmptyString;
|
|
213
|
+
readonly entries: Schema.$Array<Schema.Union<readonly [Schema.NonEmptyString, Schema.TaggedStruct<"Glob", {
|
|
214
|
+
readonly include: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
215
|
+
readonly exclude: Schema.optional<Schema.$Array<Schema.NonEmptyString>>;
|
|
216
|
+
}>]>>;
|
|
217
|
+
}>]>;
|
|
218
|
+
/** Any declaration accepted by a plan read set.
|
|
219
|
+
* @category models
|
|
220
|
+
* @since 0.1.0
|
|
221
|
+
* @slop
|
|
222
|
+
*/
|
|
223
|
+
export type ReadDeclaration = typeof ReadDeclaration.Type;
|
|
224
|
+
/** Creates a named filegroup.
|
|
225
|
+
* @category constructors
|
|
226
|
+
* @since 0.1.0
|
|
227
|
+
* @slop
|
|
228
|
+
*/
|
|
229
|
+
export declare const makeFilegroup: (name: string, entries: ReadonlyArray<Entry>) => Filegroup;
|
|
230
|
+
/** Expands filegroups deterministically, preserving declaration order.
|
|
231
|
+
* @category accessors
|
|
232
|
+
* @since 0.1.0
|
|
233
|
+
* @slop
|
|
234
|
+
*/
|
|
235
|
+
export declare const expand: (declarations: ReadonlyArray<Declaration>) => ReadonlyArray<Entry>;
|
|
236
|
+
/** Expands read filegroups deterministically, preserving declaration order.
|
|
237
|
+
* @category accessors
|
|
238
|
+
* @since 0.1.0
|
|
239
|
+
* @slop
|
|
240
|
+
*/
|
|
241
|
+
export declare const expandReads: (declarations: ReadonlyArray<ReadDeclaration>) => ReadonlyArray<ReadEntry>;
|
|
242
|
+
/** Whether a value is a glob declaration.
|
|
243
|
+
* @category guards
|
|
244
|
+
* @since 0.1.0
|
|
245
|
+
* @slop
|
|
246
|
+
*/
|
|
247
|
+
export declare const isGlob: (value: unknown) => value is Glob;
|
|
248
|
+
/** Whether a value is a tree artifact declaration.
|
|
249
|
+
* @category guards
|
|
250
|
+
* @since 0.1.0
|
|
251
|
+
* @slop
|
|
252
|
+
*/
|
|
253
|
+
export declare const isTreeArtifact: (value: unknown) => value is TreeArtifact;
|
|
254
|
+
/** Tests one workspace-relative path against one Bazel-style pattern.
|
|
255
|
+
* @category predicates
|
|
256
|
+
* @since 0.1.0
|
|
257
|
+
* @slop
|
|
258
|
+
*/
|
|
259
|
+
export declare const matchesPattern: (pattern: string, path: string) => boolean;
|
|
260
|
+
/** Tests one file path against a glob, including exclusions.
|
|
261
|
+
* @category predicates
|
|
262
|
+
* @since 0.1.0
|
|
263
|
+
* @slop
|
|
264
|
+
*/
|
|
265
|
+
export declare const matchesGlob: (glob: Glob, path: string) => boolean;
|
|
266
|
+
/**
|
|
267
|
+
* Conservative static overlap. `true` may over-serialize; `false` proves that
|
|
268
|
+
* no path can belong to both declarations.
|
|
269
|
+
*
|
|
270
|
+
* A plain string is a literal path, even when it contains `*` or `**`, as it
|
|
271
|
+
* is at execution (sandbox admission, capture, removal, and replay); wildcard
|
|
272
|
+
* matching belongs to a Glob.
|
|
273
|
+
*
|
|
274
|
+
* Exact paths and tree roots compare in their
|
|
275
|
+
* {@link folded} form, so separator aliases, canonically equivalent Unicode
|
|
276
|
+
* spellings, and spellings that differ only in letter case overlap. A Glob
|
|
277
|
+
* against an exact path matches both its `include` and `exclude` patterns in
|
|
278
|
+
* folded form. {@link matchesGlob} itself stays case-sensitive and tests the path bytes it
|
|
279
|
+
* is handed; its property suite pins a backslash inside a path segment as
|
|
280
|
+
* literal text, so canonicalizing a measured path before matching is its
|
|
281
|
+
* caller's decision, not this module's.
|
|
282
|
+
*
|
|
283
|
+
* @category predicates
|
|
284
|
+
* @since 0.1.0
|
|
285
|
+
* @slop
|
|
286
|
+
*/
|
|
287
|
+
export declare const overlaps: (left: Entry, right: Entry) => boolean;
|
|
288
|
+
//# sourceMappingURL=FileSet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSet.d.ts","sourceRoot":"","sources":["../../src/FileSet.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static filesystem declaration vocabulary shared by planning and execution.
|
|
3
|
+
*
|
|
4
|
+
* Declared paths use `/` as their canonical separator and normalize Unicode
|
|
5
|
+
* to NFC. Workspace-relative paths reject C0 controls (U+0000 through U+001F)
|
|
6
|
+
* and DEL (U+007F), which name nothing a supported filesystem can open. C1
|
|
7
|
+
* controls (U+0080 through U+009F) remain accepted because those bytes are
|
|
8
|
+
* legal in a POSIX file name.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
import * as Schema from "effect/Schema";
|
|
13
|
+
/**
|
|
14
|
+
* How strictly a declared file set is enforced at the boundary.
|
|
15
|
+
*
|
|
16
|
+
* `hard` rejects undeclared access. `expected` records access for validation
|
|
17
|
+
* after execution without requiring the sandbox to reject it immediately.
|
|
18
|
+
*
|
|
19
|
+
* This is the ONE spelling of the pair. `Plan.NodeEffects.boundaryMode` is this
|
|
20
|
+
* schema and so is `@smthrs/flow`'s `Action.BoundaryMode`; the literal used to
|
|
21
|
+
* be written out in all three places.
|
|
22
|
+
*
|
|
23
|
+
* @category schemas
|
|
24
|
+
* @since 1.0.0-rc.0
|
|
25
|
+
*/
|
|
26
|
+
export const BoundaryMode = Schema.Literals(["hard", "expected"]);
|
|
27
|
+
/**
|
|
28
|
+
* The canonical spelling of a declared path or pattern: every separator is
|
|
29
|
+
* `/`, and Unicode is normalized to NFC.
|
|
30
|
+
*
|
|
31
|
+
* {@link workspaceRelative} accepts a backslash as a separator, so
|
|
32
|
+
* `dist\same.js` and `dist/same.js` are two spellings of ONE workspace path.
|
|
33
|
+
* Every exact-path comparison in this module goes through this form, because
|
|
34
|
+
* comparing the raw spellings would let the separator alias defeat overlap
|
|
35
|
+
* detection exactly as `.` segments and empty segments would.
|
|
36
|
+
*
|
|
37
|
+
* @category accessors
|
|
38
|
+
* @since 0.1.0
|
|
39
|
+
* @slop
|
|
40
|
+
*/
|
|
41
|
+
export const canonical = (path) => path.replaceAll("\\", "/").normalize("NFC");
|
|
42
|
+
/**
|
|
43
|
+
* The {@link canonical} spelling with letter case folded away.
|
|
44
|
+
*
|
|
45
|
+
* A case-insensitive filesystem (default macOS APFS, Windows NTFS) opens
|
|
46
|
+
* `src/A.ts` and `src/a.ts` as one file, so static overlap compares this form:
|
|
47
|
+
* two declarations that differ only in case must serialize. Folding goes
|
|
48
|
+
* through upper case because that mapping is context-free, so it folds each
|
|
49
|
+
* character alone and commutes with splitting a path into segments.
|
|
50
|
+
*
|
|
51
|
+
* @category accessors
|
|
52
|
+
* @since 1.0.0-rc.0
|
|
53
|
+
*/
|
|
54
|
+
export const folded = (path) => canonical(path).toUpperCase().normalize("NFC");
|
|
55
|
+
/** Workspace-relative glob pattern.
|
|
56
|
+
* @category schemas
|
|
57
|
+
* @since 0.1.0
|
|
58
|
+
* @slop
|
|
59
|
+
*/
|
|
60
|
+
export const Pattern = Schema.NonEmptyString.check(Schema.makeFilter((pattern) => workspaceRelative(pattern) || "file patterns must be workspace-relative and cannot traverse upward", { title: "workspaceRelativePattern" }));
|
|
61
|
+
/**
|
|
62
|
+
* Whether a declared path or pattern stays inside the workspace and names it
|
|
63
|
+
* one way only. Refuses absolute paths (POSIX and drive-letter), upward
|
|
64
|
+
* traversal, and the aliasing forms — `.` segments, empty segments — that
|
|
65
|
+
* would let two spellings of one file defeat exact-string overlap detection.
|
|
66
|
+
*
|
|
67
|
+
* @category predicates
|
|
68
|
+
* @since 0.1.0
|
|
69
|
+
* @slop
|
|
70
|
+
*/
|
|
71
|
+
export const workspaceRelative = (pattern) => {
|
|
72
|
+
for (let index = 0; index < pattern.length; index++) {
|
|
73
|
+
const code = pattern.charCodeAt(index);
|
|
74
|
+
if (code <= 0x1f || code === 0x7f)
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (pattern.startsWith("/"))
|
|
78
|
+
return false;
|
|
79
|
+
const segments = canonical(pattern).split("/");
|
|
80
|
+
// `C:foo` is drive-relative on Windows, so any drive-letter prefix escapes.
|
|
81
|
+
if (/^[A-Za-z]:/.test(segments[0]) || segments[0].endsWith(":"))
|
|
82
|
+
return false;
|
|
83
|
+
return segments.every((segment) => segment !== ".." && segment !== "." && segment !== "");
|
|
84
|
+
};
|
|
85
|
+
/** Bazel-style file glob.
|
|
86
|
+
* @category schemas
|
|
87
|
+
* @since 0.1.0
|
|
88
|
+
* @slop
|
|
89
|
+
*/
|
|
90
|
+
export const Glob = Schema.TaggedStruct("Glob", {
|
|
91
|
+
include: Schema.NonEmptyArray(Pattern),
|
|
92
|
+
exclude: Schema.optional(Schema.Array(Pattern))
|
|
93
|
+
});
|
|
94
|
+
/** A directory output captured and replayed as one tree artifact.
|
|
95
|
+
* @category schemas
|
|
96
|
+
* @since 0.1.0
|
|
97
|
+
* @slop
|
|
98
|
+
*/
|
|
99
|
+
export const TreeArtifact = Schema.TaggedStruct("TreeArtifact", {
|
|
100
|
+
path: Pattern
|
|
101
|
+
});
|
|
102
|
+
/** One leaf of a named filegroup.
|
|
103
|
+
* @category schemas
|
|
104
|
+
* @since 0.1.0
|
|
105
|
+
* @slop
|
|
106
|
+
*/
|
|
107
|
+
export const Entry = Schema.Union([Pattern, Glob, TreeArtifact]);
|
|
108
|
+
/** A declaration valid in a read set.
|
|
109
|
+
* @category schemas
|
|
110
|
+
* @since 0.1.0
|
|
111
|
+
* @slop
|
|
112
|
+
*/
|
|
113
|
+
export const ReadEntry = Schema.Union([Pattern, Glob]);
|
|
114
|
+
/** A named reusable collection expanded before measurement.
|
|
115
|
+
* @category schemas
|
|
116
|
+
* @since 0.1.0
|
|
117
|
+
* @slop
|
|
118
|
+
*/
|
|
119
|
+
export const Filegroup = Schema.TaggedStruct("Filegroup", {
|
|
120
|
+
name: Schema.NonEmptyString,
|
|
121
|
+
entries: Schema.Array(Entry)
|
|
122
|
+
});
|
|
123
|
+
/** A named read-only collection.
|
|
124
|
+
* @category schemas
|
|
125
|
+
* @since 0.1.0
|
|
126
|
+
* @slop
|
|
127
|
+
*/
|
|
128
|
+
export const ReadFilegroup = Schema.TaggedStruct("Filegroup", {
|
|
129
|
+
name: Schema.NonEmptyString,
|
|
130
|
+
entries: Schema.Array(ReadEntry)
|
|
131
|
+
});
|
|
132
|
+
/** Any declaration accepted by a plan effect set.
|
|
133
|
+
* @category schemas
|
|
134
|
+
* @since 0.1.0
|
|
135
|
+
* @slop
|
|
136
|
+
*/
|
|
137
|
+
export const Declaration = Schema.Union([Entry, Filegroup]);
|
|
138
|
+
/** Any declaration accepted by a plan read set.
|
|
139
|
+
* @category schemas
|
|
140
|
+
* @since 0.1.0
|
|
141
|
+
* @slop
|
|
142
|
+
*/
|
|
143
|
+
export const ReadDeclaration = Schema.Union([ReadEntry, ReadFilegroup]);
|
|
144
|
+
/** Creates a named filegroup.
|
|
145
|
+
* @category constructors
|
|
146
|
+
* @since 0.1.0
|
|
147
|
+
* @slop
|
|
148
|
+
*/
|
|
149
|
+
export const makeFilegroup = (name, entries) => ({
|
|
150
|
+
_tag: "Filegroup",
|
|
151
|
+
name,
|
|
152
|
+
entries
|
|
153
|
+
});
|
|
154
|
+
/** Expands filegroups deterministically, preserving declaration order.
|
|
155
|
+
* @category accessors
|
|
156
|
+
* @since 0.1.0
|
|
157
|
+
* @slop
|
|
158
|
+
*/
|
|
159
|
+
export const expand = (declarations) => declarations.flatMap((declaration) => typeof declaration !== "string" && declaration._tag === "Filegroup" ? declaration.entries : [declaration]);
|
|
160
|
+
/** Expands read filegroups deterministically, preserving declaration order.
|
|
161
|
+
* @category accessors
|
|
162
|
+
* @since 0.1.0
|
|
163
|
+
* @slop
|
|
164
|
+
*/
|
|
165
|
+
export const expandReads = (declarations) => declarations.flatMap((declaration) => typeof declaration !== "string" && declaration._tag === "Filegroup" ? declaration.entries : [declaration]);
|
|
166
|
+
/** Whether a value is a glob declaration.
|
|
167
|
+
* @category guards
|
|
168
|
+
* @since 0.1.0
|
|
169
|
+
* @slop
|
|
170
|
+
*/
|
|
171
|
+
export const isGlob = (value) => typeof value === "object" && value !== null && "_tag" in value && value._tag === "Glob";
|
|
172
|
+
/** Whether a value is a tree artifact declaration.
|
|
173
|
+
* @category guards
|
|
174
|
+
* @since 0.1.0
|
|
175
|
+
* @slop
|
|
176
|
+
*/
|
|
177
|
+
export const isTreeArtifact = (value) => typeof value === "object" && value !== null && "_tag" in value && value._tag === "TreeArtifact";
|
|
178
|
+
/** KMP searches a literal without rescanning a long near-matching prefix. */
|
|
179
|
+
const literalSearch = (literal) => {
|
|
180
|
+
const fallback = new Uint32Array(literal.length);
|
|
181
|
+
let matched = 0;
|
|
182
|
+
for (let index = 1; index < literal.length; index++) {
|
|
183
|
+
while (matched > 0 && literal[index] !== literal[matched])
|
|
184
|
+
matched = fallback[matched - 1];
|
|
185
|
+
if (literal[index] === literal[matched])
|
|
186
|
+
matched++;
|
|
187
|
+
fallback[index] = matched;
|
|
188
|
+
}
|
|
189
|
+
return (path, start, end) => {
|
|
190
|
+
let matched = 0;
|
|
191
|
+
for (let index = start; index < end; index++) {
|
|
192
|
+
while (matched > 0 && path[index] !== literal[matched])
|
|
193
|
+
matched = fallback[matched - 1];
|
|
194
|
+
if (path[index] === literal[matched])
|
|
195
|
+
matched++;
|
|
196
|
+
if (matched === literal.length)
|
|
197
|
+
return index + 1;
|
|
198
|
+
}
|
|
199
|
+
return -1;
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
/** Anchored ends and disjoint literal searches make each segment match linear. */
|
|
203
|
+
const segmentMatcher = (segment) => {
|
|
204
|
+
const chunks = segment.split(/\*+/);
|
|
205
|
+
const prefix = chunks.shift();
|
|
206
|
+
if (chunks.length === 0)
|
|
207
|
+
return (path) => path === prefix;
|
|
208
|
+
const suffix = chunks.pop();
|
|
209
|
+
const searches = chunks.map(literalSearch);
|
|
210
|
+
return (path) => {
|
|
211
|
+
const end = path.length - suffix.length;
|
|
212
|
+
if (prefix.length > end || !path.startsWith(prefix) || !path.endsWith(suffix))
|
|
213
|
+
return false;
|
|
214
|
+
let position = prefix.length;
|
|
215
|
+
for (const search of searches) {
|
|
216
|
+
position = search(path, position, end);
|
|
217
|
+
if (position === -1)
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return true;
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
const patternMatchers = new Map();
|
|
224
|
+
/** Compile once per canonical spelling; recursive segments use no backtracking. */
|
|
225
|
+
const patternMatcher = (pattern) => {
|
|
226
|
+
const key = canonical(pattern);
|
|
227
|
+
const cached = patternMatchers.get(key);
|
|
228
|
+
if (cached !== undefined)
|
|
229
|
+
return cached;
|
|
230
|
+
const segments = key.split("/").map((segment) => segment === "**" ? undefined : segmentMatcher(segment));
|
|
231
|
+
const matcher = (path) => {
|
|
232
|
+
const parts = path.split("/");
|
|
233
|
+
// Suffix membership: next[j] says the remaining pattern matches parts[j..].
|
|
234
|
+
// Each pattern/path segment pair is visited once, even with repeated **.
|
|
235
|
+
let next = new Uint8Array(parts.length + 1);
|
|
236
|
+
next[parts.length] = 1;
|
|
237
|
+
for (let index = segments.length - 1; index >= 0; index--) {
|
|
238
|
+
const segment = segments[index];
|
|
239
|
+
const current = new Uint8Array(parts.length + 1);
|
|
240
|
+
for (let part = parts.length - 1; part >= 0; part--) {
|
|
241
|
+
current[part] = segment === undefined
|
|
242
|
+
// A trailing ** accepts every remaining code unit. It still needs
|
|
243
|
+
// the separator preceding it: src/** does not match bare src.
|
|
244
|
+
? Number(index === segments.length - 1 || next[part] === 1 ||
|
|
245
|
+
(parts[part] !== "" && current[part + 1] === 1))
|
|
246
|
+
: Number(next[part + 1] === 1 && segment(parts[part]));
|
|
247
|
+
}
|
|
248
|
+
next = current;
|
|
249
|
+
}
|
|
250
|
+
return next[0] === 1;
|
|
251
|
+
};
|
|
252
|
+
patternMatchers.set(key, matcher);
|
|
253
|
+
return matcher;
|
|
254
|
+
};
|
|
255
|
+
/** Tests one workspace-relative path against one Bazel-style pattern.
|
|
256
|
+
* @category predicates
|
|
257
|
+
* @since 0.1.0
|
|
258
|
+
* @slop
|
|
259
|
+
*/
|
|
260
|
+
export const matchesPattern = (pattern, path) => patternMatcher(pattern)(path);
|
|
261
|
+
/** Tests one file path against a glob, including exclusions.
|
|
262
|
+
* @category predicates
|
|
263
|
+
* @since 0.1.0
|
|
264
|
+
* @slop
|
|
265
|
+
*/
|
|
266
|
+
export const matchesGlob = (glob, path) => glob.include.some((pattern) => matchesPattern(pattern, path)) &&
|
|
267
|
+
!(glob.exclude ?? []).some((pattern) => matchesPattern(pattern, path));
|
|
268
|
+
const beneath = (tree, path) => {
|
|
269
|
+
const root = folded(tree);
|
|
270
|
+
const inside = folded(path);
|
|
271
|
+
return inside === root || inside.startsWith(`${root}/`);
|
|
272
|
+
};
|
|
273
|
+
/** {@link matchesGlob} over {@link folded} spellings of the glob and the path. */
|
|
274
|
+
const matchesGlobFolded = (glob, path) => {
|
|
275
|
+
const inside = folded(path);
|
|
276
|
+
return glob.include.some((pattern) => matchesPattern(folded(pattern), inside)) &&
|
|
277
|
+
!(glob.exclude ?? []).some((pattern) => matchesPattern(folded(pattern), inside));
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Conservative static overlap. `true` may over-serialize; `false` proves that
|
|
281
|
+
* no path can belong to both declarations.
|
|
282
|
+
*
|
|
283
|
+
* A plain string is a literal path, even when it contains `*` or `**`, as it
|
|
284
|
+
* is at execution (sandbox admission, capture, removal, and replay); wildcard
|
|
285
|
+
* matching belongs to a Glob.
|
|
286
|
+
*
|
|
287
|
+
* Exact paths and tree roots compare in their
|
|
288
|
+
* {@link folded} form, so separator aliases, canonically equivalent Unicode
|
|
289
|
+
* spellings, and spellings that differ only in letter case overlap. A Glob
|
|
290
|
+
* against an exact path matches both its `include` and `exclude` patterns in
|
|
291
|
+
* folded form. {@link matchesGlob} itself stays case-sensitive and tests the path bytes it
|
|
292
|
+
* is handed; its property suite pins a backslash inside a path segment as
|
|
293
|
+
* literal text, so canonicalizing a measured path before matching is its
|
|
294
|
+
* caller's decision, not this module's.
|
|
295
|
+
*
|
|
296
|
+
* @category predicates
|
|
297
|
+
* @since 0.1.0
|
|
298
|
+
* @slop
|
|
299
|
+
*/
|
|
300
|
+
export const overlaps = (left, right) => {
|
|
301
|
+
if (typeof left === "string" && typeof right === "string")
|
|
302
|
+
return folded(left) === folded(right);
|
|
303
|
+
if (typeof left === "string" && isGlob(right))
|
|
304
|
+
return matchesGlobFolded(right, left);
|
|
305
|
+
if (isGlob(left) && typeof right === "string")
|
|
306
|
+
return matchesGlobFolded(left, right);
|
|
307
|
+
if (isGlob(left) && isGlob(right))
|
|
308
|
+
return true;
|
|
309
|
+
if (isTreeArtifact(left) && typeof right === "string")
|
|
310
|
+
return beneath(left.path, right);
|
|
311
|
+
if (typeof left === "string" && isTreeArtifact(right))
|
|
312
|
+
return beneath(right.path, left);
|
|
313
|
+
if (isTreeArtifact(left) && isTreeArtifact(right)) {
|
|
314
|
+
return beneath(left.path, right.path) || beneath(right.path, left.path);
|
|
315
|
+
}
|
|
316
|
+
if (isTreeArtifact(left) && isGlob(right))
|
|
317
|
+
return true;
|
|
318
|
+
/* v8 ignore else -- the closed Entry union leaves only Glob/TreeArtifact here */
|
|
319
|
+
if (isGlob(left) && isTreeArtifact(right))
|
|
320
|
+
return overlaps(right, left);
|
|
321
|
+
/* v8 ignore next -- Entry's closed union makes every pair return above */
|
|
322
|
+
return true;
|
|
323
|
+
};
|
|
324
|
+
//# sourceMappingURL=FileSet.js.map
|