dl-once 0.0.1 → 0.0.2

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +128 -28
  3. package/dist/_bytes-builder.d.ts +34 -0
  4. package/dist/_bytes-builder.d.ts.map +1 -0
  5. package/dist/_bytes-builder.js +48 -0
  6. package/dist/cache-storage.d.ts +74 -0
  7. package/dist/cache-storage.d.ts.map +1 -0
  8. package/dist/cache-storage.js +1 -0
  9. package/dist/dl-once.d.ts +242 -0
  10. package/dist/dl-once.d.ts.map +1 -0
  11. package/dist/dl-once.js +245 -0
  12. package/dist/hash-verification-hook.d.ts +46 -0
  13. package/dist/hash-verification-hook.d.ts.map +1 -0
  14. package/dist/hash-verification-hook.js +97 -0
  15. package/dist/index.d.ts +16 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +8 -0
  18. package/dist/indexeddb-cache-storage.d.ts +51 -0
  19. package/dist/indexeddb-cache-storage.d.ts.map +1 -0
  20. package/dist/indexeddb-cache-storage.js +573 -0
  21. package/dist/md5-verification-hook.d.ts +15 -0
  22. package/dist/md5-verification-hook.d.ts.map +1 -0
  23. package/dist/md5-verification-hook.js +17 -0
  24. package/dist/memory-cache-storage.d.ts +41 -0
  25. package/dist/memory-cache-storage.d.ts.map +1 -0
  26. package/dist/memory-cache-storage.js +290 -0
  27. package/dist/node-fs-cache-storage.d.ts +63 -0
  28. package/dist/node-fs-cache-storage.d.ts.map +1 -0
  29. package/dist/node-fs-cache-storage.js +565 -0
  30. package/dist/sha256-verification-hook.d.ts +15 -0
  31. package/dist/sha256-verification-hook.d.ts.map +1 -0
  32. package/dist/sha256-verification-hook.js +17 -0
  33. package/package.json +46 -7
  34. package/src/_bytes-builder.ts +66 -0
  35. package/src/cache-storage.ts +86 -0
  36. package/src/dl-once.ts +554 -0
  37. package/src/hash-verification-hook.ts +98 -0
  38. package/src/index.ts +28 -0
  39. package/src/indexeddb-cache-storage.ts +535 -0
  40. package/src/md5-verification-hook.ts +18 -0
  41. package/src/memory-cache-storage.ts +361 -0
  42. package/src/node-fs-cache-storage.ts +511 -0
  43. package/src/sha256-verification-hook.ts +18 -0
@@ -0,0 +1,565 @@
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
34
+ };
35
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
36
+ if (value !== null && value !== void 0) {
37
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
38
+ var dispose, inner;
39
+ if (async) {
40
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
41
+ dispose = value[Symbol.asyncDispose];
42
+ }
43
+ if (dispose === void 0) {
44
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
45
+ dispose = value[Symbol.dispose];
46
+ if (async) inner = dispose;
47
+ }
48
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
49
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
50
+ env.stack.push({ value: value, dispose: dispose, async: async });
51
+ }
52
+ else if (async) {
53
+ env.stack.push({ async: true });
54
+ }
55
+ return value;
56
+ };
57
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
58
+ return function (env) {
59
+ function fail(e) {
60
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
61
+ env.hasError = true;
62
+ }
63
+ var r, s = 0;
64
+ function next() {
65
+ while (r = env.stack.pop()) {
66
+ try {
67
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
68
+ if (r.dispose) {
69
+ var result = r.dispose.call(r.value);
70
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
71
+ }
72
+ else s |= 1;
73
+ }
74
+ catch (e) {
75
+ fail(e);
76
+ }
77
+ }
78
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
79
+ if (env.hasError) throw env.error;
80
+ }
81
+ return next();
82
+ };
83
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
84
+ var e = new Error(message);
85
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
86
+ });
87
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
88
+ if (kind === "m") throw new TypeError("Private method is not writable");
89
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
90
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
91
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
92
+ };
93
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
94
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
95
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
96
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
97
+ };
98
+ var _NodeFsCacheHandle_instances, _NodeFsCacheHandle_storage, _NodeFsCacheHandle_mux, _NodeFsCacheHandle_cacheDir, _NodeFsCacheHandle_assertOk, _NodeFsCacheStorage_instances, _NodeFsCacheStorage_baseDirInput, _NodeFsCacheStorage_baseDir, _NodeFsCacheStorage_mux, _NodeFsCacheStorage_isOpen, _NodeFsCacheStorage_assertOk;
99
+ import { Asyncmux, asyncmux } from "asyncmux";
100
+ import { tryCaptureStackTrace } from "try-capture-stack-trace";
101
+ /**
102
+ * 動的にインポートされる Node.js 標準モジュールを保持するオブジェクトです。
103
+ */
104
+ const modules = {};
105
+ /**
106
+ * 中断理由が設定されていないことを示すための初期値シンボルです。
107
+ */
108
+ const NONE = Symbol("NONE");
109
+ /**
110
+ * ベースディレクトリーとキャッシュキーから、安全なキャッシュディレクトリーのパスを計算します。
111
+ *
112
+ * @param baseDir ベースとなるディレクトリーのパスです。
113
+ * @param cacheKey キャッシュを一意に識別するキーです。
114
+ * @returns 計算されたフルパスです。
115
+ */
116
+ function getCacheDir(baseDir, cacheKey) {
117
+ const { path } = modules;
118
+ // キャッシュキー自体にディレクトリー区切り文字が含まれるのを防ぎます。
119
+ if (cacheKey.includes(path.sep)) {
120
+ throw new Error(`Invalid cacheKey: "${cacheKey}". Path separators are not allowed.`);
121
+ }
122
+ // フルパスを解決し、ベースディレクトリー配下に収まっているかを検証します(パストラバーサル対策)。
123
+ const fullPath = path.resolve(baseDir, cacheKey);
124
+ const relative = path.relative(baseDir, fullPath);
125
+ const isOutside = relative.startsWith("..");
126
+ if (isOutside) {
127
+ throw new Error("Security Error: The generated path is outside of the base directory.");
128
+ }
129
+ return fullPath;
130
+ }
131
+ /**
132
+ * Node.js のファイルシステムを使用してキャッシュを書き込むクラスです。
133
+ */
134
+ let NodeFsCacheWriter = (() => {
135
+ var _NodeFsCacheWriter_instances, _a, _NodeFsCacheWriter_storage, _NodeFsCacheWriter_cacheDir, _NodeFsCacheWriter_reason, _NodeFsCacheWriter_isClosed, _NodeFsCacheWriter_chunkCounter, _NodeFsCacheWriter_assertOk;
136
+ let _instanceExtraInitializers = [];
137
+ let _write_decorators;
138
+ let _close_decorators;
139
+ let _abort_decorators;
140
+ return _a = class NodeFsCacheWriter {
141
+ /**
142
+ * インスタンスを初期化します。
143
+ *
144
+ * @param storage 所属するストレージインスタンスです。
145
+ * @param cacheDir 書き込み対象のディレクトリーパスです。
146
+ */
147
+ constructor(storage, cacheDir) {
148
+ _NodeFsCacheWriter_instances.add(this);
149
+ /**
150
+ * ストレージの本体です。
151
+ */
152
+ _NodeFsCacheWriter_storage.set(this, __runInitializers(this, _instanceExtraInitializers));
153
+ /**
154
+ * 書き込み先のキャッシュディレクトリーです。
155
+ */
156
+ _NodeFsCacheWriter_cacheDir.set(this, void 0);
157
+ /**
158
+ * 中断された際の原因です。
159
+ */
160
+ _NodeFsCacheWriter_reason.set(this, void 0);
161
+ /**
162
+ * ライターが閉じられたかどうかを管理するフラグです。
163
+ */
164
+ _NodeFsCacheWriter_isClosed.set(this, void 0);
165
+ /**
166
+ * 書き込まれたチャンクの数をカウントします。
167
+ */
168
+ _NodeFsCacheWriter_chunkCounter.set(this, void 0);
169
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_storage, storage, "f");
170
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_cacheDir, cacheDir, "f");
171
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_reason, NONE, "f");
172
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_isClosed, false, "f");
173
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_chunkCounter, 0, "f");
174
+ }
175
+ /**
176
+ * チャンクデータをファイルとして書き込みます。
177
+ *
178
+ * @param chunkData 書き込むデータです。
179
+ * @returns 書き込み完了を示すプロミスです。
180
+ */
181
+ async write(chunkData) {
182
+ const { fs, path } = modules;
183
+ __classPrivateFieldGet(this, _NodeFsCacheWriter_instances, "m", _NodeFsCacheWriter_assertOk).call(this);
184
+ // 現在のカウンター値をインデックスとしてファイル名(0.bin, 1.bin...)を決定します。
185
+ const index = __classPrivateFieldGet(this, _NodeFsCacheWriter_chunkCounter, "f");
186
+ const chunkPath = path.join(__classPrivateFieldGet(this, _NodeFsCacheWriter_cacheDir, "f"), `${index}.bin`);
187
+ // データをファイルに永続化し、カウンターをインクリメントします。
188
+ await fs.writeFile(chunkPath, chunkData);
189
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_chunkCounter, __classPrivateFieldGet(this, _NodeFsCacheWriter_chunkCounter, "f") + 1, "f");
190
+ }
191
+ /**
192
+ * 書き込みを完了し、メタデータファイルを生成してライターを閉じます。
193
+ *
194
+ * @returns 完了を示すプロミスです。
195
+ */
196
+ async close() {
197
+ const { fs, path } = modules;
198
+ __classPrivateFieldGet(this, _NodeFsCacheWriter_instances, "m", _NodeFsCacheWriter_assertOk).call(this);
199
+ // 書き込まれた総チャンク数をメタデータとして保存します。
200
+ const chunkCount = __classPrivateFieldGet(this, _NodeFsCacheWriter_chunkCounter, "f");
201
+ const meta = {
202
+ chunkCount,
203
+ };
204
+ const metaPath = path.join(__classPrivateFieldGet(this, _NodeFsCacheWriter_cacheDir, "f"), "meta.json");
205
+ await fs.writeFile(metaPath, JSON.stringify(meta), "utf-8");
206
+ // 内部状態を更新して、以降の操作を受け付けないようにします。
207
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_isClosed, true, "f");
208
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_chunkCounter, 0, "f");
209
+ }
210
+ /**
211
+ * 書き込みを中断し、これまでに書き込んだ不完全なチャンクファイルを削除します。
212
+ *
213
+ * @param reason 中断した理由(エラーオブジェクトなど)です。
214
+ * @returns 中断処理完了を示すプロミスです。
215
+ */
216
+ async abort(reason) {
217
+ const { fs, path } = modules;
218
+ __classPrivateFieldGet(this, _NodeFsCacheWriter_instances, "m", _NodeFsCacheWriter_assertOk).call(this);
219
+ const chunkCount = __classPrivateFieldGet(this, _NodeFsCacheWriter_chunkCounter, "f");
220
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_reason, reason, "f");
221
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_isClosed, true, "f");
222
+ __classPrivateFieldSet(this, _NodeFsCacheWriter_chunkCounter, 0, "f");
223
+ // まだ何も書き込まれていない場合は削除処理をスキップします。
224
+ if (chunkCount === 0) {
225
+ return;
226
+ }
227
+ // 全てのチャンクファイルを走査して削除します。
228
+ for (let index = 0; index < chunkCount; index++) {
229
+ const chunkPath = path.join(__classPrivateFieldGet(this, _NodeFsCacheWriter_cacheDir, "f"), `${index}.bin`);
230
+ await fs.rm(chunkPath, { force: true });
231
+ }
232
+ }
233
+ },
234
+ _NodeFsCacheWriter_storage = new WeakMap(),
235
+ _NodeFsCacheWriter_cacheDir = new WeakMap(),
236
+ _NodeFsCacheWriter_reason = new WeakMap(),
237
+ _NodeFsCacheWriter_isClosed = new WeakMap(),
238
+ _NodeFsCacheWriter_chunkCounter = new WeakMap(),
239
+ _NodeFsCacheWriter_instances = new WeakSet(),
240
+ _NodeFsCacheWriter_assertOk = function _NodeFsCacheWriter_assertOk() {
241
+ if (!__classPrivateFieldGet(this, _NodeFsCacheWriter_storage, "f").isOpen) {
242
+ const error = new Error("NodeFsCacheStorage is closed");
243
+ tryCaptureStackTrace(error, __classPrivateFieldGet(this, _NodeFsCacheWriter_instances, "m", _NodeFsCacheWriter_assertOk));
244
+ throw error;
245
+ }
246
+ if (__classPrivateFieldGet(this, _NodeFsCacheWriter_isClosed, "f")) {
247
+ if (__classPrivateFieldGet(this, _NodeFsCacheWriter_reason, "f") === NONE) {
248
+ const error = new Error("NodeFsCacheWriter is closed");
249
+ tryCaptureStackTrace(error, __classPrivateFieldGet(this, _NodeFsCacheWriter_instances, "m", _NodeFsCacheWriter_assertOk));
250
+ throw error;
251
+ }
252
+ // 中断理由がある場合は、その理由をそのまま投げます。
253
+ throw __classPrivateFieldGet(this, _NodeFsCacheWriter_reason, "f");
254
+ }
255
+ },
256
+ (() => {
257
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
258
+ _write_decorators = [asyncmux];
259
+ _close_decorators = [asyncmux];
260
+ _abort_decorators = [asyncmux];
261
+ __esDecorate(_a, null, _write_decorators, { kind: "method", name: "write", static: false, private: false, access: { has: obj => "write" in obj, get: obj => obj.write }, metadata: _metadata }, null, _instanceExtraInitializers);
262
+ __esDecorate(_a, null, _close_decorators, { kind: "method", name: "close", static: false, private: false, access: { has: obj => "close" in obj, get: obj => obj.close }, metadata: _metadata }, null, _instanceExtraInitializers);
263
+ __esDecorate(_a, null, _abort_decorators, { kind: "method", name: "abort", static: false, private: false, access: { has: obj => "abort" in obj, get: obj => obj.abort }, metadata: _metadata }, null, _instanceExtraInitializers);
264
+ if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
265
+ })(),
266
+ _a;
267
+ })();
268
+ /**
269
+ * 特定のキャッシュキーに対する読み書きを管理するハンドルクラスです。
270
+ */
271
+ class NodeFsCacheHandle {
272
+ /**
273
+ * ハンドルを初期化します。
274
+ *
275
+ * @param storage 親ストレージです。
276
+ * @param mux 同期用ミューテックスです。
277
+ * @param cacheDir キャッシュパスです。
278
+ */
279
+ constructor(storage, mux, cacheDir) {
280
+ _NodeFsCacheHandle_instances.add(this);
281
+ /**
282
+ * 親ストレージインスタンスです。
283
+ */
284
+ _NodeFsCacheHandle_storage.set(this, void 0);
285
+ /**
286
+ * 排他制御用のミューテックスです。
287
+ */
288
+ _NodeFsCacheHandle_mux.set(this, void 0);
289
+ /**
290
+ * 対象のキャッシュディレクトリーパスです。
291
+ */
292
+ _NodeFsCacheHandle_cacheDir.set(this, void 0);
293
+ __classPrivateFieldSet(this, _NodeFsCacheHandle_storage, storage, "f");
294
+ __classPrivateFieldSet(this, _NodeFsCacheHandle_mux, mux, "f");
295
+ __classPrivateFieldSet(this, _NodeFsCacheHandle_cacheDir, cacheDir, "f");
296
+ }
297
+ /**
298
+ * キャッシュデータを読み取るための非同期ジェネレーターを取得します。
299
+ *
300
+ * @param args 読み取りオプション(キャンセル信号など)です。
301
+ * @returns チャンクを生成するジェネレーター、またはキャッシュが存在しない場合は null です。
302
+ */
303
+ async getReader(args) {
304
+ const env_1 = { stack: [], error: void 0, hasError: false };
305
+ try {
306
+ const { fs, path } = modules;
307
+ const { signal } = args;
308
+ // 読み取り用の共有ロックを取得します。
309
+ const _1 = __addDisposableResource(env_1, await __classPrivateFieldGet(this, _NodeFsCacheHandle_mux, "f").rLock({ signal }), false);
310
+ __classPrivateFieldGet(this, _NodeFsCacheHandle_instances, "m", _NodeFsCacheHandle_assertOk).call(this);
311
+ const metaPath = path.join(__classPrivateFieldGet(this, _NodeFsCacheHandle_cacheDir, "f"), "meta.json");
312
+ let metaJson;
313
+ try {
314
+ // メタデータファイルを読み込み、キャッシュの整合性を確認します。
315
+ metaJson = await fs.readFile(metaPath, { encoding: "utf-8", signal });
316
+ }
317
+ catch (ex) {
318
+ // ファイルが存在しない場合はキャッシュ未作成と判断し、null を返します。
319
+ if (typeof ex === "object" && ex && "code" in ex && ex.code === "ENOENT") {
320
+ return null;
321
+ }
322
+ throw ex;
323
+ }
324
+ const meta = JSON.parse(metaJson);
325
+ const chunkCount = meta.chunkCount;
326
+ const storage = __classPrivateFieldGet(this, _NodeFsCacheHandle_storage, "f");
327
+ const cacheDir = __classPrivateFieldGet(this, _NodeFsCacheHandle_cacheDir, "f");
328
+ /**
329
+ * 各チャンクファイルを順番に読み込むジェネレーター関数です。
330
+ */
331
+ async function* createReader() {
332
+ for (let index = 0; index < chunkCount; index++) {
333
+ // 各ループでストレージの状態を再確認します。
334
+ if (!storage.isOpen) {
335
+ throw new Error("NodeFsCacheStorage is closed");
336
+ }
337
+ const chunkPath = path.join(cacheDir, `${index}.bin`);
338
+ try {
339
+ // チャンクファイルをバッファーとして読み込み、Uint8Array に変換して yield します。
340
+ const chunkBuffer = await fs.readFile(chunkPath, { signal });
341
+ const chunk = new Uint8Array(chunkBuffer.buffer, chunkBuffer.byteOffset, chunkBuffer.byteLength);
342
+ yield chunk;
343
+ }
344
+ catch (ex) {
345
+ if (typeof ex === "object" && ex && "code" in ex && ex.code === "ENOENT") {
346
+ throw new Error(`Missing chunk at index ${index}`);
347
+ }
348
+ throw ex;
349
+ }
350
+ }
351
+ }
352
+ return createReader();
353
+ }
354
+ catch (e_1) {
355
+ env_1.error = e_1;
356
+ env_1.hasError = true;
357
+ }
358
+ finally {
359
+ __disposeResources(env_1);
360
+ }
361
+ }
362
+ /**
363
+ * キャッシュデータを書き込むためのライターを取得します。
364
+ *
365
+ * @param args 書き込みオプションです。
366
+ * @returns 書き込み用のライターインスタンスです。
367
+ */
368
+ async getWriter(args) {
369
+ const env_2 = { stack: [], error: void 0, hasError: false };
370
+ try {
371
+ const { fs } = modules;
372
+ const { signal } = args;
373
+ // 読み取り用ロックを取得(書き込み中でも構造的な競合を避けるため)します。
374
+ const _1 = __addDisposableResource(env_2, await __classPrivateFieldGet(this, _NodeFsCacheHandle_mux, "f").rLock({ signal }), false);
375
+ __classPrivateFieldGet(this, _NodeFsCacheHandle_instances, "m", _NodeFsCacheHandle_assertOk).call(this);
376
+ // キャッシュを格納するためのディレクトリーを作成します。
377
+ await fs.mkdir(__classPrivateFieldGet(this, _NodeFsCacheHandle_cacheDir, "f"), { recursive: true });
378
+ return new NodeFsCacheWriter(__classPrivateFieldGet(this, _NodeFsCacheHandle_storage, "f"), __classPrivateFieldGet(this, _NodeFsCacheHandle_cacheDir, "f"));
379
+ }
380
+ catch (e_2) {
381
+ env_2.error = e_2;
382
+ env_2.hasError = true;
383
+ }
384
+ finally {
385
+ __disposeResources(env_2);
386
+ }
387
+ }
388
+ }
389
+ _NodeFsCacheHandle_storage = new WeakMap(), _NodeFsCacheHandle_mux = new WeakMap(), _NodeFsCacheHandle_cacheDir = new WeakMap(), _NodeFsCacheHandle_instances = new WeakSet(), _NodeFsCacheHandle_assertOk = function _NodeFsCacheHandle_assertOk() {
390
+ if (!__classPrivateFieldGet(this, _NodeFsCacheHandle_storage, "f").isOpen) {
391
+ const error = new Error("NodeFsCacheStorage is closed");
392
+ tryCaptureStackTrace(error, __classPrivateFieldGet(this, _NodeFsCacheHandle_instances, "m", _NodeFsCacheHandle_assertOk));
393
+ throw error;
394
+ }
395
+ };
396
+ /**
397
+ * Node.js のファイルシステムを基盤としたキャッシュストレージの実装です。
398
+ */
399
+ export default class NodeFsCacheStorage {
400
+ /**
401
+ * インスタンスを生成します。
402
+ *
403
+ * @param baseDir キャッシュを保存するベースディレクトリーです。デフォルトは ".dl-once" です。
404
+ */
405
+ constructor(baseDir = ".dl-once") {
406
+ _NodeFsCacheStorage_instances.add(this);
407
+ /**
408
+ * ユーザーから渡されたベースディレクトリーの入力値です。
409
+ */
410
+ _NodeFsCacheStorage_baseDirInput.set(this, void 0);
411
+ /**
412
+ * 解決された絶対パス形式のベースディレクトリーです。
413
+ */
414
+ _NodeFsCacheStorage_baseDir.set(this, void 0);
415
+ /**
416
+ * 全体的な排他制御を司るミューテックスです。
417
+ */
418
+ _NodeFsCacheStorage_mux.set(this, void 0);
419
+ /**
420
+ * ストレージが開いているかどうかを保持します。
421
+ */
422
+ _NodeFsCacheStorage_isOpen.set(this, void 0);
423
+ __classPrivateFieldSet(this, _NodeFsCacheStorage_baseDirInput, baseDir, "f");
424
+ __classPrivateFieldSet(this, _NodeFsCacheStorage_baseDir, "", "f");
425
+ __classPrivateFieldSet(this, _NodeFsCacheStorage_mux, new Asyncmux(), "f");
426
+ __classPrivateFieldSet(this, _NodeFsCacheStorage_isOpen, false, "f");
427
+ }
428
+ /**
429
+ * ストレージが現在利用可能(オープン状態)かどうかを取得します。
430
+ */
431
+ get isOpen() {
432
+ return __classPrivateFieldGet(this, _NodeFsCacheStorage_isOpen, "f");
433
+ }
434
+ /**
435
+ * ストレージを初期化し、利用可能な状態にします。
436
+ *
437
+ * 必要な Node.js モジュールのインポートとベースディレクトリーの作成を行います。
438
+ *
439
+ * @param options オープン時のオプション(タイムアウト用シグナルなど)です。
440
+ * @returns 完了を示すプロミスです。
441
+ */
442
+ async open(options = {}) {
443
+ const env_3 = { stack: [], error: void 0, hasError: false };
444
+ try {
445
+ const { signal } = options;
446
+ // 書き込みロックを取得して排他的に初期化を行います。
447
+ const _ = __addDisposableResource(env_3, await __classPrivateFieldGet(this, _NodeFsCacheStorage_mux, "f").lock({ signal }), false);
448
+ if (__classPrivateFieldGet(this, _NodeFsCacheStorage_isOpen, "f")) {
449
+ throw new Error("Storage is already open");
450
+ }
451
+ // 実行時に Node.js 標準モジュールをインポートします。
452
+ const [fs, path] = await Promise.all([import("node:fs/promises"), import("node:path")]);
453
+ modules.fs = fs;
454
+ modules.path = path;
455
+ // ベースディレクトリーのパスを絶対パスとして確定させます。
456
+ __classPrivateFieldSet(this, _NodeFsCacheStorage_baseDir, path.resolve(__classPrivateFieldGet(this, _NodeFsCacheStorage_baseDirInput, "f")), "f");
457
+ // ストレージのルートディレクトリーを作成します。
458
+ await fs.mkdir(__classPrivateFieldGet(this, _NodeFsCacheStorage_baseDir, "f"), { recursive: true });
459
+ __classPrivateFieldSet(this, _NodeFsCacheStorage_isOpen, true, "f");
460
+ }
461
+ catch (e_3) {
462
+ env_3.error = e_3;
463
+ env_3.hasError = true;
464
+ }
465
+ finally {
466
+ __disposeResources(env_3);
467
+ }
468
+ }
469
+ /**
470
+ * ストレージを閉じ、以降の操作を無効化します。
471
+ *
472
+ * @param options クローズ時のオプションです。
473
+ * @returns 完了を示すプロミスです。
474
+ */
475
+ async close(options = {}) {
476
+ const env_4 = { stack: [], error: void 0, hasError: false };
477
+ try {
478
+ const { signal } = options;
479
+ const _ = __addDisposableResource(env_4, await __classPrivateFieldGet(this, _NodeFsCacheStorage_mux, "f").lock({ signal }), false);
480
+ __classPrivateFieldGet(this, _NodeFsCacheStorage_instances, "m", _NodeFsCacheStorage_assertOk).call(this);
481
+ __classPrivateFieldSet(this, _NodeFsCacheStorage_isOpen, false, "f");
482
+ }
483
+ catch (e_4) {
484
+ env_4.error = e_4;
485
+ env_4.hasError = true;
486
+ }
487
+ finally {
488
+ __disposeResources(env_4);
489
+ }
490
+ }
491
+ /**
492
+ * `using` 構文などで自動的にリソースを解放するためのメソッドです。
493
+ *
494
+ * @returns 完了を示すプロミスです。
495
+ */
496
+ async [(_NodeFsCacheStorage_baseDirInput = new WeakMap(), _NodeFsCacheStorage_baseDir = new WeakMap(), _NodeFsCacheStorage_mux = new WeakMap(), _NodeFsCacheStorage_isOpen = new WeakMap(), _NodeFsCacheStorage_instances = new WeakSet(), _NodeFsCacheStorage_assertOk = function _NodeFsCacheStorage_assertOk() {
497
+ if (!this.isOpen) {
498
+ const error = new Error("NodeFsCacheStorage is closed");
499
+ tryCaptureStackTrace(error, __classPrivateFieldGet(this, _NodeFsCacheStorage_instances, "m", _NodeFsCacheStorage_assertOk));
500
+ throw error;
501
+ }
502
+ }, Symbol.asyncDispose)]() {
503
+ const env_5 = { stack: [], error: void 0, hasError: false };
504
+ try {
505
+ const _ = __addDisposableResource(env_5, await __classPrivateFieldGet(this, _NodeFsCacheStorage_mux, "f").lock(), false);
506
+ __classPrivateFieldSet(this, _NodeFsCacheStorage_isOpen, false, "f");
507
+ }
508
+ catch (e_5) {
509
+ env_5.error = e_5;
510
+ env_5.hasError = true;
511
+ }
512
+ finally {
513
+ __disposeResources(env_5);
514
+ }
515
+ }
516
+ /**
517
+ * キャッシュの削除処理を実際に実行します。
518
+ *
519
+ * @param cacheKeyOrOptions キー文字列またはオプションオブジェクトです。
520
+ * @param options 追加のオプションです。
521
+ */
522
+ async clear(cacheKeyOrOptions, options = {}) {
523
+ const env_6 = { stack: [], error: void 0, hasError: false };
524
+ try {
525
+ const { fs } = modules;
526
+ // 引数の型に応じて正規化し、シグナルとキーを抽出します。
527
+ const { signal, cacheKey, } = typeof cacheKeyOrOptions === "object"
528
+ ? cacheKeyOrOptions
529
+ : {
530
+ ...options,
531
+ cacheKey: cacheKeyOrOptions,
532
+ };
533
+ const _1 = __addDisposableResource(env_6, await __classPrivateFieldGet(this, _NodeFsCacheStorage_mux, "f").lock({ signal }), false);
534
+ __classPrivateFieldGet(this, _NodeFsCacheStorage_instances, "m", _NodeFsCacheStorage_assertOk).call(this);
535
+ if (cacheKey !== undefined) {
536
+ // 特定のキャッシュディレクトリーのみを再帰的に削除します。
537
+ const cacheDir = getCacheDir(__classPrivateFieldGet(this, _NodeFsCacheStorage_baseDir, "f"), cacheKey);
538
+ await fs.rm(cacheDir, { recursive: true, force: true });
539
+ }
540
+ else {
541
+ // ストレージ全体を削除し、空のベースディレクトリーを再作成します。
542
+ await fs.rm(__classPrivateFieldGet(this, _NodeFsCacheStorage_baseDir, "f"), { recursive: true, force: true });
543
+ await fs.mkdir(__classPrivateFieldGet(this, _NodeFsCacheStorage_baseDir, "f"), { recursive: true });
544
+ }
545
+ }
546
+ catch (e_6) {
547
+ env_6.error = e_6;
548
+ env_6.hasError = true;
549
+ }
550
+ finally {
551
+ __disposeResources(env_6);
552
+ }
553
+ }
554
+ /**
555
+ * 指定されたキーに対応するキャッシュ操作ハンドルを作成します。
556
+ *
557
+ * @param key キャッシュを一意に識別するキーです。
558
+ * @returns キャッシュハンドルインスタンスです。
559
+ */
560
+ createCacheHandle(key) {
561
+ __classPrivateFieldGet(this, _NodeFsCacheStorage_instances, "m", _NodeFsCacheStorage_assertOk).call(this);
562
+ const cacheDir = getCacheDir(__classPrivateFieldGet(this, _NodeFsCacheStorage_baseDir, "f"), key);
563
+ return new NodeFsCacheHandle(this, __classPrivateFieldGet(this, _NodeFsCacheStorage_mux, "f"), cacheDir);
564
+ }
565
+ }
@@ -0,0 +1,15 @@
1
+ import HashVerificationHook from "./hash-verification-hook.js";
2
+ /**
3
+ * ダウンロードされたデータの SHA-256 ハッシュ値を検証するためのフッククラスです。
4
+ *
5
+ * `hash-wasm` ライブラリーを使用して、ストリーム読み込みに合わせて逐次的にハッシュ計算を行い、完了時に期待されるハッシュ値と一致するかを判定します。
6
+ */
7
+ export default class Sha256VerificationHook extends HashVerificationHook {
8
+ /**
9
+ * Sha256VerificationHook の新しいインスタンスを生成します。
10
+ *
11
+ * @param expectedHash 比較対象となる 16 進数形式の期待されるハッシュ値です。
12
+ */
13
+ constructor(expectedHash: string);
14
+ }
15
+ //# sourceMappingURL=sha256-verification-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256-verification-hook.d.ts","sourceRoot":"","sources":["../src/sha256-verification-hook.ts"],"names":[],"mappings":"AACA,OAAO,oBAAoB,MAAM,6BAA6B,CAAC;AAE/D;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAuB,SAAQ,oBAAoB;IACtE;;;;OAIG;gBACgB,YAAY,EAAE,MAAM;CAGxC"}
@@ -0,0 +1,17 @@
1
+ import { createSHA256 } from "hash-wasm";
2
+ import HashVerificationHook from "./hash-verification-hook.js";
3
+ /**
4
+ * ダウンロードされたデータの SHA-256 ハッシュ値を検証するためのフッククラスです。
5
+ *
6
+ * `hash-wasm` ライブラリーを使用して、ストリーム読み込みに合わせて逐次的にハッシュ計算を行い、完了時に期待されるハッシュ値と一致するかを判定します。
7
+ */
8
+ export default class Sha256VerificationHook extends HashVerificationHook {
9
+ /**
10
+ * Sha256VerificationHook の新しいインスタンスを生成します。
11
+ *
12
+ * @param expectedHash 比較対象となる 16 進数形式の期待されるハッシュ値です。
13
+ */
14
+ constructor(expectedHash) {
15
+ super(createSHA256, expectedHash);
16
+ }
17
+ }
package/package.json CHANGED
@@ -1,10 +1,49 @@
1
1
  {
2
2
  "name": "dl-once",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for dl-once",
5
- "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
3
+ "version": "0.0.2",
4
+ "license": "MIT",
5
+ "description": "Downloads only once",
6
+ "sideEffects": false,
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src",
17
+ "package.json",
18
+ "LICENSE"
19
+ ],
20
+ "homepage": "https://github.com/tai-kun/dl-once",
21
+ "repository": {
22
+ "url": "https://github.com/tai-kun/dl-once"
23
+ },
24
+ "dependencies": {
25
+ "@logtape/logtape": "^2.0.5",
26
+ "asyncmux": "^0.0.4",
27
+ "call-fn-once": "^0.0.4",
28
+ "hash-wasm": "^4.12.0",
29
+ "idb": "^8.0.3",
30
+ "try-capture-stack-trace": "^0.0.2"
31
+ },
32
+ "devDependencies": {
33
+ "@babel/plugin-proposal-decorators": "^7.29.0",
34
+ "@rolldown/plugin-babel": "^0.2.2",
35
+ "@tsconfig/node22": "^22.0.5",
36
+ "@tsconfig/strictest": "^2.0.8",
37
+ "@types/node": "^22.19.15",
38
+ "@vitest/browser": "^4.1.2",
39
+ "@vitest/browser-playwright": "^4.1.2",
40
+ "dprint": "^0.53.2",
41
+ "npm-check-updates": "^19.6.6",
42
+ "playwright": "^1.59.1",
43
+ "typescript": "^5.9.3",
44
+ "vitest": "^4.1.2"
45
+ },
46
+ "scripts": {
47
+ "build": "tsc --project ./.config/tsconfig.build.json"
48
+ }
10
49
  }