@smthrs/plan 1.0.0-rc.8 → 1.0.0-rc.9

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.
Files changed (59) hide show
  1. package/dist/cjs/Effects.d.ts +29 -11
  2. package/dist/cjs/Effects.d.ts.map +1 -1
  3. package/dist/cjs/Effects.js.map +2 -2
  4. package/dist/cjs/FileSet.d.ts +121 -51
  5. package/dist/cjs/FileSet.d.ts.map +1 -1
  6. package/dist/cjs/FileSet.js.map +2 -2
  7. package/dist/cjs/GraphBuildError.d.ts +2 -4
  8. package/dist/cjs/GraphBuildError.d.ts.map +1 -1
  9. package/dist/cjs/GraphBuildError.js.map +2 -2
  10. package/dist/cjs/KeyMaterial.d.ts +13 -8
  11. package/dist/cjs/KeyMaterial.d.ts.map +1 -1
  12. package/dist/cjs/KeyMaterial.js.map +2 -2
  13. package/dist/cjs/Plan.d.ts +72 -23
  14. package/dist/cjs/Plan.d.ts.map +1 -1
  15. package/dist/cjs/Plan.js +3 -0
  16. package/dist/cjs/Plan.js.map +2 -2
  17. package/dist/cjs/PlanDiff.d.ts +7 -3
  18. package/dist/cjs/PlanDiff.d.ts.map +1 -1
  19. package/dist/cjs/PlanDiff.js.map +2 -2
  20. package/dist/cjs/StepKey.d.ts +38 -17
  21. package/dist/cjs/StepKey.d.ts.map +1 -1
  22. package/dist/cjs/StepKey.js.map +2 -2
  23. package/dist/esm/Effects.d.ts +29 -11
  24. package/dist/esm/Effects.d.ts.map +1 -1
  25. package/dist/esm/Effects.js +18 -7
  26. package/dist/esm/Effects.js.map +1 -1
  27. package/dist/esm/FileSet.d.ts +121 -51
  28. package/dist/esm/FileSet.d.ts.map +1 -1
  29. package/dist/esm/FileSet.js +97 -35
  30. package/dist/esm/FileSet.js.map +1 -1
  31. package/dist/esm/GraphBuildError.d.ts +2 -4
  32. package/dist/esm/GraphBuildError.d.ts.map +1 -1
  33. package/dist/esm/GraphBuildError.js +2 -3
  34. package/dist/esm/GraphBuildError.js.map +1 -1
  35. package/dist/esm/KeyMaterial.d.ts +13 -8
  36. package/dist/esm/KeyMaterial.d.ts.map +1 -1
  37. package/dist/esm/KeyMaterial.js +11 -4
  38. package/dist/esm/KeyMaterial.js.map +1 -1
  39. package/dist/esm/Plan.d.ts +72 -23
  40. package/dist/esm/Plan.d.ts.map +1 -1
  41. package/dist/esm/Plan.js +61 -16
  42. package/dist/esm/Plan.js.map +1 -1
  43. package/dist/esm/PlanDiff.d.ts +7 -3
  44. package/dist/esm/PlanDiff.d.ts.map +1 -1
  45. package/dist/esm/PlanDiff.js +0 -1
  46. package/dist/esm/PlanDiff.js.map +1 -1
  47. package/dist/esm/StepKey.d.ts +38 -17
  48. package/dist/esm/StepKey.d.ts.map +1 -1
  49. package/dist/esm/StepKey.js +33 -11
  50. package/dist/esm/StepKey.js.map +1 -1
  51. package/docs/guides/diff-two-plans.md +78 -1
  52. package/package.json +4 -4
  53. package/src/Effects.ts +29 -11
  54. package/src/FileSet.ts +121 -51
  55. package/src/GraphBuildError.ts +2 -4
  56. package/src/KeyMaterial.ts +13 -8
  57. package/src/Plan.ts +66 -23
  58. package/src/PlanDiff.ts +7 -3
  59. package/src/StepKey.ts +38 -17
package/src/FileSet.ts CHANGED
@@ -47,7 +47,6 @@ export type BoundaryMode = typeof BoundaryMode.Type
47
47
  *
48
48
  * @category accessors
49
49
  * @since 0.1.0
50
- * @slop
51
50
  */
52
51
  export const canonical = (path: string): string => path.replaceAll("\\", "/").normalize("NFC")
53
52
 
@@ -65,10 +64,15 @@ export const canonical = (path: string): string => path.replaceAll("\\", "/").no
65
64
  */
66
65
  export const folded = (path: string): string => canonical(path).toUpperCase().normalize("NFC")
67
66
 
68
- /** Workspace-relative glob pattern.
67
+ /**
68
+ * A non-empty workspace-relative path or pattern.
69
+ *
70
+ * Decoding fails with a `SchemaError` when {@link workspaceRelative} refuses
71
+ * the string. A bare `Pattern` in a declaration names one literal path; only a
72
+ * {@link Glob} gives `*` and `**` wildcard meaning.
73
+ *
69
74
  * @category schemas
70
75
  * @since 0.1.0
71
- * @slop
72
76
  */
73
77
  export const Pattern = Schema.NonEmptyString.check(
74
78
  Schema.makeFilter(
@@ -83,9 +87,10 @@ export const Pattern = Schema.NonEmptyString.check(
83
87
  * traversal, and the aliasing forms — `.` segments, empty segments — that
84
88
  * would let two spellings of one file defeat exact-string overlap detection.
85
89
  *
90
+ * C0 controls and DEL are refused as well.
91
+ *
86
92
  * @category predicates
87
93
  * @since 0.1.0
88
- * @slop
89
94
  */
90
95
  export const workspaceRelative = (pattern: string): boolean => {
91
96
  for (let index = 0; index < pattern.length; index++) {
@@ -99,133 +104,171 @@ export const workspaceRelative = (pattern: string): boolean => {
99
104
  return segments.every((segment) => segment !== ".." && segment !== "." && segment !== "")
100
105
  }
101
106
 
102
- /** Bazel-style file glob.
107
+ /**
108
+ * A Bazel-style file glob: the paths matching any `include` pattern and no
109
+ * `exclude` pattern.
110
+ *
111
+ * In a pattern, `*` matches within one path segment and a `**` segment
112
+ * matches zero or more whole segments. A trailing `**` needs its separator,
113
+ * so `src/**` does not match `src` itself.
114
+ *
103
115
  * @category schemas
104
116
  * @since 0.1.0
105
- * @slop
106
117
  */
107
118
  export const Glob = Schema.TaggedStruct("Glob", {
108
119
  include: Schema.NonEmptyArray(Pattern),
109
120
  exclude: Schema.optional(Schema.Array(Pattern))
110
121
  })
111
122
 
112
- /** Bazel-style file glob.
123
+ /**
124
+ * The value form of {@link Glob}.
125
+ *
113
126
  * @category models
114
127
  * @since 0.1.0
115
- * @slop
116
128
  */
117
129
  export type Glob = typeof Glob.Type
118
130
 
119
- /** A directory output captured and replayed as one tree artifact.
131
+ /**
132
+ * A directory output captured and replayed as one tree artifact.
133
+ *
134
+ * It covers `path` and every path beneath it, compared in {@link folded}
135
+ * form.
136
+ *
120
137
  * @category schemas
121
138
  * @since 0.1.0
122
- * @slop
123
139
  */
124
140
  export const TreeArtifact = Schema.TaggedStruct("TreeArtifact", {
125
141
  path: Pattern
126
142
  })
127
143
 
128
- /** A directory output captured and replayed as one tree artifact.
144
+ /**
145
+ * The value form of {@link TreeArtifact}.
146
+ *
129
147
  * @category models
130
148
  * @since 0.1.0
131
- * @slop
132
149
  */
133
150
  export type TreeArtifact = typeof TreeArtifact.Type
134
151
 
135
- /** One leaf of a named filegroup.
152
+ /**
153
+ * One leaf declaration: a literal path, a {@link Glob}, or a
154
+ * {@link TreeArtifact}. Write sets and filegroups hold entries.
155
+ *
136
156
  * @category schemas
137
157
  * @since 0.1.0
138
- * @slop
139
158
  */
140
159
  export const Entry = Schema.Union([Pattern, Glob, TreeArtifact])
141
160
 
142
- /** One leaf of a named filegroup.
161
+ /**
162
+ * The value form of {@link Entry}.
163
+ *
143
164
  * @category models
144
165
  * @since 0.1.0
145
- * @slop
146
166
  */
147
167
  export type Entry = typeof Entry.Type
148
168
 
149
- /** A declaration valid in a read set.
169
+ /**
170
+ * One leaf declaration valid in a read set: a literal path or a {@link Glob}.
171
+ * A read set cannot name a {@link TreeArtifact}, which is an output form.
172
+ *
150
173
  * @category schemas
151
174
  * @since 0.1.0
152
- * @slop
153
175
  */
154
176
  export const ReadEntry = Schema.Union([Pattern, Glob])
155
177
 
156
- /** A declaration valid in a read set.
178
+ /**
179
+ * The value form of {@link ReadEntry}.
180
+ *
157
181
  * @category models
158
182
  * @since 0.1.0
159
- * @slop
160
183
  */
161
184
  export type ReadEntry = typeof ReadEntry.Type
162
185
 
163
- /** A named reusable collection expanded before measurement.
186
+ /**
187
+ * A named, reusable list of {@link Entry} declarations.
188
+ *
189
+ * {@link expand} replaces a filegroup with its entries in place before any
190
+ * overlap check or measurement, so the name never reaches the boundary.
191
+ * Filegroups do not nest.
192
+ *
164
193
  * @category schemas
165
194
  * @since 0.1.0
166
- * @slop
167
195
  */
168
196
  export const Filegroup = Schema.TaggedStruct("Filegroup", {
169
197
  name: Schema.NonEmptyString,
170
198
  entries: Schema.Array(Entry)
171
199
  })
172
200
 
173
- /** A named reusable collection expanded before measurement.
201
+ /**
202
+ * The value form of {@link Filegroup}.
203
+ *
174
204
  * @category models
175
205
  * @since 0.1.0
176
- * @slop
177
206
  */
178
207
  export type Filegroup = typeof Filegroup.Type
179
208
 
180
- /** A named read-only collection.
209
+ /**
210
+ * A {@link Filegroup} restricted to {@link ReadEntry} declarations, for use
211
+ * in a read set. It shares the `"Filegroup"` tag.
212
+ *
181
213
  * @category schemas
182
214
  * @since 0.1.0
183
- * @slop
184
215
  */
185
216
  export const ReadFilegroup = Schema.TaggedStruct("Filegroup", {
186
217
  name: Schema.NonEmptyString,
187
218
  entries: Schema.Array(ReadEntry)
188
219
  })
189
220
 
190
- /** A named read-only collection.
221
+ /**
222
+ * The value form of {@link ReadFilegroup}.
223
+ *
191
224
  * @category models
192
225
  * @since 0.1.0
193
- * @slop
194
226
  */
195
227
  export type ReadFilegroup = typeof ReadFilegroup.Type
196
228
 
197
- /** Any declaration accepted by a plan effect set.
229
+ /**
230
+ * Any declaration accepted in a write set: an {@link Entry} or a
231
+ * {@link Filegroup}.
232
+ *
198
233
  * @category schemas
199
234
  * @since 0.1.0
200
- * @slop
201
235
  */
202
236
  export const Declaration = Schema.Union([Entry, Filegroup])
203
237
 
204
- /** Any declaration accepted by a plan effect set.
238
+ /**
239
+ * The value form of {@link Declaration}.
240
+ *
205
241
  * @category models
206
242
  * @since 0.1.0
207
- * @slop
208
243
  */
209
244
  export type Declaration = typeof Declaration.Type
210
245
 
211
- /** Any declaration accepted by a plan read set.
246
+ /**
247
+ * Any declaration accepted in a read set: a {@link ReadEntry} or a
248
+ * {@link ReadFilegroup}.
249
+ *
212
250
  * @category schemas
213
251
  * @since 0.1.0
214
- * @slop
215
252
  */
216
253
  export const ReadDeclaration = Schema.Union([ReadEntry, ReadFilegroup])
217
254
 
218
- /** Any declaration accepted by a plan read set.
255
+ /**
256
+ * The value form of {@link ReadDeclaration}.
257
+ *
219
258
  * @category models
220
259
  * @since 0.1.0
221
- * @slop
222
260
  */
223
261
  export type ReadDeclaration = typeof ReadDeclaration.Type
224
262
 
225
- /** Creates a named filegroup.
263
+ /**
264
+ * Creates a {@link Filegroup}. It does not validate `name` or `entries`;
265
+ * decode through {@link Filegroup} when the input is untrusted.
266
+ *
267
+ * @param name - The filegroup name.
268
+ * @param entries - The entries the filegroup expands to, in order.
269
+ * @returns The filegroup value.
226
270
  * @category constructors
227
271
  * @since 0.1.0
228
- * @slop
229
272
  */
230
273
  export const makeFilegroup = (name: string, entries: ReadonlyArray<Entry>): Filegroup => ({
231
274
  _tag: "Filegroup",
@@ -233,38 +276,54 @@ export const makeFilegroup = (name: string, entries: ReadonlyArray<Entry>): File
233
276
  entries
234
277
  })
235
278
 
236
- /** Expands filegroups deterministically, preserving declaration order.
279
+ /**
280
+ * Replaces each {@link Filegroup} with its entries, in place, and keeps every
281
+ * other declaration as it is. Order is preserved and duplicates are kept.
282
+ *
283
+ * @param declarations - Write-set declarations, possibly holding filegroups.
284
+ * @returns The flat list of entries.
237
285
  * @category accessors
238
286
  * @since 0.1.0
239
- * @slop
240
287
  */
241
288
  export const expand = (declarations: ReadonlyArray<Declaration>): ReadonlyArray<Entry> =>
242
289
  declarations.flatMap((declaration) =>
243
290
  typeof declaration !== "string" && declaration._tag === "Filegroup" ? declaration.entries : [declaration]
244
291
  )
245
292
 
246
- /** Expands read filegroups deterministically, preserving declaration order.
293
+ /**
294
+ * {@link expand} for read sets: replaces each {@link ReadFilegroup} with its
295
+ * entries, in place, preserving order and duplicates.
296
+ *
297
+ * @param declarations - Read-set declarations, possibly holding filegroups.
298
+ * @returns The flat list of read entries.
247
299
  * @category accessors
248
300
  * @since 0.1.0
249
- * @slop
250
301
  */
251
302
  export const expandReads = (declarations: ReadonlyArray<ReadDeclaration>): ReadonlyArray<ReadEntry> =>
252
303
  declarations.flatMap((declaration) =>
253
304
  typeof declaration !== "string" && declaration._tag === "Filegroup" ? declaration.entries : [declaration]
254
305
  )
255
306
 
256
- /** Whether a value is a glob declaration.
307
+ /**
308
+ * Whether a value carries the `"Glob"` tag. It checks only the tag, not the
309
+ * patterns.
310
+ *
311
+ * @param value - Any value.
312
+ * @returns `true` for an object whose `_tag` is `"Glob"`.
257
313
  * @category guards
258
314
  * @since 0.1.0
259
- * @slop
260
315
  */
261
316
  export const isGlob = (value: unknown): value is Glob =>
262
317
  typeof value === "object" && value !== null && "_tag" in value && value._tag === "Glob"
263
318
 
264
- /** Whether a value is a tree artifact declaration.
319
+ /**
320
+ * Whether a value carries the `"TreeArtifact"` tag. It checks only the tag,
321
+ * not the path.
322
+ *
323
+ * @param value - Any value.
324
+ * @returns `true` for an object whose `_tag` is `"TreeArtifact"`.
265
325
  * @category guards
266
326
  * @since 0.1.0
267
- * @slop
268
327
  */
269
328
  export const isTreeArtifact = (value: unknown): value is TreeArtifact =>
270
329
  typeof value === "object" && value !== null && "_tag" in value && value._tag === "TreeArtifact"
@@ -343,17 +402,29 @@ const patternMatcher = (pattern: string): (path: string) => boolean => {
343
402
  return matcher
344
403
  }
345
404
 
346
- /** Tests one workspace-relative path against one Bazel-style pattern.
405
+ /**
406
+ * Whether `path` matches `pattern` under {@link Glob} wildcard rules.
407
+ *
408
+ * The pattern is read in its {@link canonical} spelling; the path is matched
409
+ * as given, case-sensitively, with `/` as its only separator.
410
+ *
411
+ * @param pattern - A Bazel-style pattern such as `src/*.ts` or `docs/**`.
412
+ * @param path - A workspace-relative path.
413
+ * @returns `true` when the path matches.
347
414
  * @category predicates
348
415
  * @since 0.1.0
349
- * @slop
350
416
  */
351
417
  export const matchesPattern = (pattern: string, path: string): boolean => patternMatcher(pattern)(path)
352
418
 
353
- /** Tests one file path against a glob, including exclusions.
419
+ /**
420
+ * Whether `path` matches some `include` pattern of `glob` and no `exclude`
421
+ * pattern, each tested with {@link matchesPattern}. Case-sensitive.
422
+ *
423
+ * @param glob - The glob to test.
424
+ * @param path - A workspace-relative path.
425
+ * @returns `true` when the glob selects the path.
354
426
  * @category predicates
355
427
  * @since 0.1.0
356
- * @slop
357
428
  */
358
429
  export const matchesGlob = (glob: Glob, path: string): boolean =>
359
430
  glob.include.some((pattern) => matchesPattern(pattern, path)) &&
@@ -391,7 +462,6 @@ const matchesGlobFolded = (glob: Glob, path: string): boolean => {
391
462
  *
392
463
  * @category predicates
393
464
  * @since 0.1.0
394
- * @slop
395
465
  */
396
466
  export const overlaps = (left: Entry, right: Entry): boolean => {
397
467
  if (typeof left === "string" && typeof right === "string") return folded(left) === folded(right)
@@ -70,7 +70,6 @@ import * as Schema from "effect/Schema"
70
70
  *
71
71
  * @since 0.1.0
72
72
  * @category schemas
73
- * @slop
74
73
  */
75
74
  export const GraphBuildErrorCode = Schema.Literals([
76
75
  "planned_value_computed",
@@ -103,7 +102,6 @@ export const GraphBuildErrorCode = Schema.Literals([
103
102
  *
104
103
  * @since 0.1.0
105
104
  * @category models
106
- * @slop
107
105
  */
108
106
  export type GraphBuildErrorCode = typeof GraphBuildErrorCode.Type
109
107
 
@@ -124,7 +122,6 @@ export type GraphBuildErrorCode = typeof GraphBuildErrorCode.Type
124
122
  *
125
123
  * @since 0.1.0
126
124
  * @category errors
127
- * @slop
128
125
  */
129
126
  export class GraphBuildError extends Schema.TaggedError<GraphBuildError>()("@smthrs/plan/GraphBuildError", {
130
127
  code: GraphBuildErrorCode,
@@ -151,8 +148,9 @@ const advisoryCodes: ReadonlySet<GraphBuildErrorCode> = new Set<GraphBuildErrorC
151
148
  * written, so the drafts are withheld. An advisory one reports a narrowing the
152
149
  * author should see while the graph still compiles.
153
150
  *
151
+ * @param diagnostic - A recorded refusal.
152
+ * @returns `false` only for an advisory code (`capability_outside_grant`).
154
153
  * @since 0.1.0
155
154
  * @category predicates
156
- * @slop
157
155
  */
158
156
  export const isFatalDiagnostic = (diagnostic: GraphBuildError): boolean => !advisoryCodes.has(diagnostic.code)
@@ -25,9 +25,12 @@ import * as Effects from "./Effects.ts"
25
25
  * `Ref{from, path: []}` — which resolve to the same dependency digest — can
26
26
  * never collide.
27
27
  *
28
+ * - `Literal` hashes `value` inline.
29
+ * - `Ref` consumes the value at `path` inside node `from`'s result.
30
+ * - `Pending` consumes node `from`'s whole result.
31
+ *
28
32
  * @since 0.1.0
29
33
  * @category schemas
30
- * @slop
31
34
  */
32
35
  export const InputRef = Schema.Union([
33
36
  Schema.TaggedStruct("Literal", { value: Schema.Unknown }),
@@ -36,11 +39,10 @@ export const InputRef = Schema.Union([
36
39
  ])
37
40
 
38
41
  /**
39
- * A reference to one declared input of a node.
42
+ * The value form of {@link InputRef}.
40
43
  *
41
44
  * @since 0.1.0
42
45
  * @category models
43
- * @slop
44
46
  */
45
47
  export type InputRef = typeof InputRef.Type
46
48
 
@@ -50,7 +52,6 @@ export type InputRef = typeof InputRef.Type
50
52
  *
51
53
  * @since 0.1.0
52
54
  * @category constants
53
- * @slop
54
55
  */
55
56
  export const version = "flows/key-material/v2"
56
57
 
@@ -61,9 +62,12 @@ export const version = "flows/key-material/v2"
61
62
  * interpreted, which is what keeps the compiler independent of whatever the
62
63
  * flow builder decides an effect declaration looks like.
63
64
  *
65
+ * `version` must equal {@link version}. `kind` is the effect tier. `inputs`
66
+ * are hashed in order; `layers` and `capabilities` name what the node runs
67
+ * with.
68
+ *
64
69
  * @since 0.1.0
65
70
  * @category schemas
66
- * @slop
67
71
  */
68
72
  export const KeyMaterial = Schema.Struct({
69
73
  version: Schema.Literal(version),
@@ -79,11 +83,10 @@ export const KeyMaterial = Schema.Struct({
79
83
  })
80
84
 
81
85
  /**
82
- * Everything that can change a node's result, handed to the digest.
86
+ * The value form of {@link KeyMaterial}.
83
87
  *
84
88
  * @since 0.1.0
85
89
  * @category models
86
- * @slop
87
90
  */
88
91
  export type KeyMaterial = typeof KeyMaterial.Type
89
92
 
@@ -93,9 +96,11 @@ export type KeyMaterial = typeof KeyMaterial.Type
93
96
  * {@link module:Plan} uses them as the edge set — one derivation, so an edge
94
97
  * and a hashed reference can never disagree.
95
98
  *
99
+ * @param material - The node's key material.
100
+ * @returns The `from` of every `Ref` and `Pending` input, first occurrence
101
+ * order, deduplicated.
96
102
  * @since 0.1.0
97
103
  * @category accessors
98
- * @slop
99
104
  */
100
105
  export const dependencies = (material: KeyMaterial): ReadonlyArray<string> => {
101
106
  const seen = new Set<string>()