@graftland/hmr 0.3.1 → 0.3.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.
@@ -90,8 +90,9 @@ export const changes = (
90
90
  Effect.gen(function* () {
91
91
  if (paths.length === 0) return Stream.empty
92
92
  const fs = yield* FileSystem.FileSystem
93
- const roots = yield* Effect.forEach(paths, (path) =>
94
- Effect.gen(function* () {
93
+ const roots = yield* Effect.forEach(
94
+ paths,
95
+ Effect.fnUntraced(function* (path: string) {
95
96
  const canonical = yield* fs.realPath(path)
96
97
  const info = yield* fs.stat(canonical)
97
98
  return {
@@ -137,10 +138,10 @@ export const changes = (
137
138
  )
138
139
  // Watch only graph directories, never recursively traverse an application root.
139
140
  // Missing descendants use their nearest existing ancestor until they appear.
140
- const existingDirectory = (
141
+ const existingDirectory: (
141
142
  path: string,
142
- ): Effect.Effect<string, import("effect").PlatformError.PlatformError> =>
143
- Effect.gen(function* () {
143
+ ) => Effect.Effect<string, import("effect").PlatformError.PlatformError> =
144
+ Effect.fnUntraced(function* (path: string) {
144
145
  const info = yield* missing(fs.stat(path))
145
146
  return info?.type === "Directory" || dirname(path) === path
146
147
  ? path
@@ -188,105 +189,106 @@ export const changes = (
188
189
  readonly checked: number
189
190
  }
190
191
  const observed = yield* Ref.make(HashMap.empty<string, Observation>())
191
- const scan = (events: HashSet.HashSet<string> | undefined) =>
192
- Effect.gen(function* () {
193
- const loaded = yield* files
194
- const before = yield* Ref.get(observed)
195
- const now = yield* Clock.currentTimeMillis
196
- const selected = Array.filter(
197
- Array.fromIterable(loaded),
198
- ([url]) =>
199
- events === undefined ||
200
- Array.some(Array.fromIterable(events), (path) => {
201
- const file = fileURLToPath(url)
202
- return file === path || String.startsWith(`${path}/`)(file)
203
- }),
204
- )
205
- const checked = yield* Effect.forEach(
206
- selected,
207
- ([url, baseline]) =>
208
- Effect.gen(function* () {
209
- const path = fileURLToPath(url)
210
- const previous = Option.getOrUndefined(HashMap.get(before, url))
211
- const info = yield* missing(fs.stat(path))
212
- const stat =
213
- info === undefined
214
- ? "missing"
215
- : `${info.size}:${Option.getOrUndefined(info.mtime)?.getTime()}:${Option.getOrUndefined(info.ino)}`
216
- const resolution =
217
- baseline.resolution === undefined
218
- ? undefined
219
- : yield* missing(fs.realPath(path)).pipe(
220
- Effect.map((path) =>
221
- path === undefined
222
- ? undefined
223
- : pathToFileURL(path).href,
224
- ),
225
- )
226
- const audit =
227
- events !== undefined ||
228
- previous === undefined ||
229
- !sameVersion(previous.baseline, baseline) ||
230
- previous.stat !== stat ||
231
- now - previous.checked >= 10000
232
- const content = audit
233
- ? yield* missing(fs.readFile(path)).pipe(
234
- Effect.map((bytes) =>
235
- bytes === undefined ? undefined : digest(bytes),
236
- ),
237
- )
238
- : previous.content
239
- const expected =
240
- previous === undefined
241
- ? baseline
242
- : {
243
- content:
244
- baseline.content === undefined ||
245
- sameVersion(previous.baseline, baseline)
246
- ? previous.content
247
- : baseline.content,
248
- resolution: sameVersion(previous.baseline, baseline)
249
- ? previous.resolution
250
- : baseline.resolution,
251
- }
252
- return {
253
- url,
254
- observation: {
255
- baseline,
256
- content,
257
- resolution,
258
- stat,
259
- checked: audit ? now : previous.checked,
260
- },
261
- changed:
262
- expected.content !== content ||
263
- expected.resolution !== resolution,
264
- }
265
- }),
266
- { concurrency: 8 },
267
- )
268
- // The immutable snapshot keeps its bytes alive while owners can be released.
269
- // A stale result cannot acknowledge a newer import, even when bytes coincide.
270
- return yield* withFiles((current) =>
271
- Ref.modify(observed, (seen) => {
272
- let next = HashMap.filter(seen, (_, url) =>
273
- HashMap.has(current, url),
274
- )
275
- let changed = HashSet.empty<string>()
276
- for (const { url, observation, changed: differs } of checked) {
277
- if (
278
- !Option.exists(HashMap.get(current, url), (version) =>
279
- sameVersion(version, observation.baseline),
192
+ const scan = Effect.fnUntraced(function* (
193
+ events: HashSet.HashSet<string> | undefined,
194
+ ) {
195
+ const loaded = yield* files
196
+ const before = yield* Ref.get(observed)
197
+ const now = yield* Clock.currentTimeMillis
198
+ const selected = Array.filter(
199
+ Array.fromIterable(loaded),
200
+ ([url]) =>
201
+ events === undefined ||
202
+ Array.some(Array.fromIterable(events), (path) => {
203
+ const file = fileURLToPath(url)
204
+ return file === path || String.startsWith(`${path}/`)(file)
205
+ }),
206
+ )
207
+ const checked = yield* Effect.forEach(
208
+ selected,
209
+ Effect.fnUntraced(function* ([
210
+ url,
211
+ baseline,
212
+ ]: (typeof selected)[number]) {
213
+ const path = fileURLToPath(url)
214
+ const previous = Option.getOrUndefined(HashMap.get(before, url))
215
+ const info = yield* missing(fs.stat(path))
216
+ const stat =
217
+ info === undefined
218
+ ? "missing"
219
+ : `${info.size}:${Option.getOrUndefined(info.mtime)?.getTime()}:${Option.getOrUndefined(info.ino)}`
220
+ const resolution =
221
+ baseline.resolution === undefined
222
+ ? undefined
223
+ : yield* missing(fs.realPath(path)).pipe(
224
+ Effect.map((path) =>
225
+ path === undefined ? undefined : pathToFileURL(path).href,
226
+ ),
280
227
  )
228
+ const audit =
229
+ events !== undefined ||
230
+ previous === undefined ||
231
+ !sameVersion(previous.baseline, baseline) ||
232
+ previous.stat !== stat ||
233
+ now - previous.checked >= 10000
234
+ const content = audit
235
+ ? yield* missing(fs.readFile(path)).pipe(
236
+ Effect.map((bytes) =>
237
+ bytes === undefined ? undefined : digest(bytes),
238
+ ),
281
239
  )
282
- continue
283
- next = HashMap.set(next, url, observation)
284
- if (differs) changed = HashSet.add(changed, url)
285
- }
286
- return [changed, next]
287
- }),
288
- )
289
- })
240
+ : previous.content
241
+ const expected =
242
+ previous === undefined
243
+ ? baseline
244
+ : {
245
+ content:
246
+ baseline.content === undefined ||
247
+ sameVersion(previous.baseline, baseline)
248
+ ? previous.content
249
+ : baseline.content,
250
+ resolution: sameVersion(previous.baseline, baseline)
251
+ ? previous.resolution
252
+ : baseline.resolution,
253
+ }
254
+ return {
255
+ url,
256
+ observation: {
257
+ baseline,
258
+ content,
259
+ resolution,
260
+ stat,
261
+ checked: audit ? now : previous.checked,
262
+ },
263
+ changed:
264
+ expected.content !== content ||
265
+ expected.resolution !== resolution,
266
+ }
267
+ }),
268
+ { concurrency: 8 },
269
+ )
270
+ // The immutable snapshot keeps its bytes alive while owners can be released.
271
+ // A stale result cannot acknowledge a newer import, even when bytes coincide.
272
+ return yield* withFiles((current) =>
273
+ Ref.modify(observed, (seen) => {
274
+ let next = HashMap.filter(seen, (_, url) =>
275
+ HashMap.has(current, url),
276
+ )
277
+ let changed = HashSet.empty<string>()
278
+ for (const { url, observation, changed: differs } of checked) {
279
+ if (
280
+ !Option.exists(HashMap.get(current, url), (version) =>
281
+ sameVersion(version, observation.baseline),
282
+ )
283
+ )
284
+ continue
285
+ next = HashMap.set(next, url, observation)
286
+ if (differs) changed = HashSet.add(changed, url)
287
+ }
288
+ return [changed, next]
289
+ }),
290
+ )
291
+ })
290
292
  return Stream.merge(
291
293
  native,
292
294
  Stream.tick("1 second").pipe(