1tube 0.1.22 → 0.1.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "1tube",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "private": false,
5
5
  "description": "Self-hosted Supabase Edge Functions gateway for Deno, plus an ESLint plugin for consumers",
6
6
  "license": "MIT",
@@ -20,7 +20,11 @@
20
20
  * tracks it correctly.
21
21
  */
22
22
 
23
- import { createGraph } from "@deno/graph";
23
+ // Direct `jsr:` specifier (not the `@deno/graph` import-map alias from
24
+ // our own deno.json) so this module loads cleanly when 1tube is
25
+ // consumed from a host project's node_modules — host configs do not
26
+ // mirror our import map. Same convention as every other src/ file.
27
+ import { createGraph } from "jsr:@deno/graph@^0.108";
24
28
  import { fileURLToPath, pathToFileURL } from "node:url";
25
29
 
26
30
  // Local re-export for callers that prefer std/path-style names. Underlying
@@ -147,42 +147,47 @@ export function createDenoReloadDebouncer(
147
147
  * precise case — exactly the functions that transitively import the
148
148
  * changed file.
149
149
  * 2. Files matching `<functionsDir>/<name>/index.ts`: include `<name>`.
150
- * Catches newly-added function dirs whose entry hasn't been graphed
151
- * yet, plus the entry's own edits.
152
- * 3. Path is the directory `<functionsDir>/<name>` itself or anything
153
- * under it (subtree change, including whole-dir removal). This is
154
- * the case `Deno.watchFs` emits on Windows when a function dir is
155
- * deleted — events come back as paths to the dir, not to the
156
- * nonexistent files inside. Only matched against `knownNames` so
157
- * random sibling-dir activity doesn't trigger spurious reloads.
150
+ * Catches the entry's own edits.
151
+ * 3. Path is the directory `<functionsDir>/<name>` itself or any path
152
+ * under it (subtree change). This is necessary for two cases the
153
+ * other rules miss:
154
+ * - **Linux**: `Deno.watchFs` is recursive only over the dirs
155
+ * that existed at start. A *new* function dir reports a single
156
+ * `<functionsDir>/<name>` create event — child file events
157
+ * never fire because inotify doesn't auto-watch the new subdir.
158
+ * - **Windows**: deleting a function dir tree emits dir-path
159
+ * events instead of `index.ts` events.
160
+ * Either way the affected name is the first path component after
161
+ * `functionsDir`. We let `discoverCandidates` filter out junk
162
+ * (e.g. `_shared`) on the reload pass so this rule stays simple
163
+ * and doesn't need the host's knownNames set.
158
164
  */
159
165
  export function computeAffected(
160
166
  paths: readonly string[],
161
167
  depGraph: DepGraph,
162
- options: { functionsDir?: string; knownNames?: Iterable<string> } = {},
168
+ options: { functionsDir?: string } = {},
163
169
  ): Set<string> {
164
170
  const out = depGraph.affected(paths);
165
171
  for (const p of paths) {
166
172
  const m = /[\\/]([^\\/]+)[\\/]index\.ts$/.exec(p);
167
173
  if (m && m[1]) out.add(m[1]);
168
174
  }
169
- if (options.functionsDir && options.knownNames) {
175
+ if (options.functionsDir) {
170
176
  const fnDir = options.functionsDir.replace(/[\\/]$/, "");
171
- for (const name of options.knownNames) {
172
- // Cheap substring test — avoids escaping the name for a regex
173
- // and works for both forward- and back-slashed paths.
174
- const needleA = `${fnDir}\\${name}`;
175
- const needleB = `${fnDir}/${name}`;
176
- for (const p of paths) {
177
- if (p === needleA || p === needleB) {
178
- out.add(name);
179
- break;
180
- }
181
- if (p.startsWith(needleA + "\\") || p.startsWith(needleB + "/")) {
182
- out.add(name);
183
- break;
184
- }
185
- }
177
+ for (const p of paths) {
178
+ // Strip the prefix; anything left should look like
179
+ // `/<name>` or `/<name>/<rest>`. Cross-platform: try both
180
+ // separators and accept whichever the OS produced.
181
+ let rest: string | null = null;
182
+ if (p.startsWith(fnDir + "/")) rest = p.slice(fnDir.length + 1);
183
+ else if (p.startsWith(fnDir + "\\")) rest = p.slice(fnDir.length + 1);
184
+ else if (p === fnDir) continue;
185
+ if (rest === null) continue;
186
+ const seg = rest.split(/[\\/]/, 1)[0];
187
+ // Skip private/shared dirs the host already filters out — keeps
188
+ // the affected set tight even before `reload()` runs.
189
+ if (!seg || seg.startsWith("_") || seg.endsWith("_shared")) continue;
190
+ out.add(seg);
186
191
  }
187
192
  }
188
193
  return out;
@@ -205,10 +210,29 @@ export function createDenoHotReloader(
205
210
  setTimer: opts.setTimer,
206
211
  clearTimer: opts.clearTimer,
207
212
  flushFn: async (paths) => {
208
- const affected = computeAffected(paths, opts.host.depGraph, {
213
+ const raw = computeAffected(paths, opts.host.depGraph, {
209
214
  functionsDir: opts.functionsDir,
210
- knownNames: opts.host.list(),
211
215
  });
216
+ // Filter to names that are either currently loaded or have an
217
+ // `index.ts` on disk right now. This drops spurious entries
218
+ // synthesized from non-function files at the root of
219
+ // `functionsDir` (README.md, .gitignore, …) so we don't spend
220
+ // a reload pass producing an empty summary.
221
+ const known = new Set(opts.host.list());
222
+ const affected = new Set<string>();
223
+ for (const name of raw) {
224
+ if (known.has(name)) {
225
+ affected.add(name);
226
+ continue;
227
+ }
228
+ try {
229
+ const indexPath = `${opts.functionsDir}/${name}/index.ts`;
230
+ const stat = await Deno.stat(indexPath);
231
+ if (stat.isFile) affected.add(name);
232
+ } catch {
233
+ // No index.ts → not a function; drop.
234
+ }
235
+ }
212
236
  if (affected.size === 0) return;
213
237
  const reason = `${affected.size} function(s) changed: ${
214
238
  [...affected].sort().join(", ")