Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 28aa638

Browse files
committedMay 15, 2023
Remove legacy storage system
Now that all gateways have been migrated to the new storage system, we aren't using the legacy one. Also removes remote storage, which will need rethinking with the new storage system.

24 files changed

+50
-1516
lines changed
 

‎packages/tre/README.md

-19
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ await mf.dispose();
3434

3535
## API
3636

37-
> :warning: Features marked **(Experimental)** may change at any point and are
38-
> not subject to semantic versioning guarantees.
39-
4037
### `type Awaitable<T>`
4138

4239
`T | Promise<T>`
@@ -121,14 +118,6 @@ Represents where data should be persisted, if anywhere.
121118
- If the protocol is `file:`, data will be stored on the file-system, in the
122119
specified directory (e.g. `file:///path/to/directory`). If the `unsanitise`
123120
search parameter is `true`, path sanitisation will be disabled.
124-
- If the protocol is `sqlite:`, data will be stored in an SQLite database, at
125-
the specified path (e.g. `sqlite:///path/to/db.sqlite`).
126-
- **(Experimental)** If the protocol is `remote:`, data will be read/written
127-
from/to real data stores on the Cloudflare network. By default, this will
128-
cache data in-memory, but the `cache` search parameter can be set to a
129-
URL-encoded persistence string to customise this. Note, this feature is only
130-
supported for KV namespaces at the moment, and requires the
131-
`cloudflareFetch` option to be set.
132121
- Otherwise, if this is just a regular `string`, data will be stored on the
133122
file-system, using the value as the directory path.
134123

@@ -433,14 +422,6 @@ Options shared between all Workers/"nanoservices".
433422
automatically reloads the page in-browser whenever the Miniflare instance's
434423
options are updated.
435424

436-
- **(Experimental)**
437-
`cloudflareFetch?: (resource: string, searchParams?: URLSearchParams, init?: RequestInit) => Awaitable<Response>`
438-
439-
Authenticated `fetch` used by `remote:` storage to communicate with the
440-
Cloudflare API. `https://api.cloudflare.com/client/v4/accounts/<account_id>/`
441-
should be prepended to `resource` to form the request URL. Appropriate
442-
authorization headers should also be added.
443-
444425
#### Cache, Durable Objects, KV, R2 and D1
445426

446427
- `cachePersist?: Persistence`

‎packages/tre/src/index.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,8 @@ export class Miniflare {
418418
this.#log,
419419
this.#timers,
420420
this.dispatchFetch,
421-
this.#sharedOpts.core.cloudflareFetch,
422421
key,
423-
plugin.gateway,
424-
plugin.remoteStorage
422+
plugin.gateway
425423
);
426424
const router = new plugin.router(this.#log, gatewayFactory);
427425
// @ts-expect-error this.#gatewayFactories[key] could be any plugin's
@@ -889,7 +887,7 @@ export class Miniflare {
889887
): NewStorage {
890888
const factory = this.#gatewayFactories[plugin];
891889
assert(factory !== undefined);
892-
return factory.getStorage(namespace, persist).getNewStorage();
890+
return factory.getStorage(namespace, persist);
893891
}
894892

895893
async dispose(): Promise<void> {
@@ -914,6 +912,5 @@ export * from "./http";
914912
export * from "./plugins";
915913
export * from "./runtime";
916914
export * from "./shared";
917-
export * from "./storage";
918915
export * from "./storage2";
919916
export * from "./workers";

‎packages/tre/src/plugins/cache/gateway.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { ReadableStream, TransformStream } from "stream/web";
55
import CachePolicy from "http-cache-semantics";
66
import { Headers, HeadersInit, Request, Response, fetch } from "../../http";
77
import { DeferredPromise, Log, Timers } from "../../shared";
8-
import { Storage } from "../../storage";
98
import {
109
InclusiveRange,
1110
KeyValueStorage,
1211
MultipartReadableStream,
12+
NewStorage,
1313
} from "../../storage2";
1414
import { isSitesRequest } from "../kv";
1515
import { _parseRanges } from "../shared";
@@ -232,10 +232,9 @@ export class CacheGateway {
232232

233233
constructor(
234234
private readonly log: Log,
235-
legacyStorage: Storage,
235+
storage: NewStorage,
236236
private readonly timers: Timers
237237
) {
238-
const storage = legacyStorage.getNewStorage();
239238
this.storage = new KeyValueStorage(storage, timers);
240239
}
241240

‎packages/tre/src/plugins/core/index.ts

-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import { CoreBindings, CoreHeaders } from "../../workers";
2323
import { getCacheServiceName } from "../cache";
2424
import { DURABLE_OBJECTS_STORAGE_SERVICE_NAME } from "../do";
2525
import {
26-
CloudflareFetchSchema,
2726
HEADER_CF_BLOB,
2827
Plugin,
2928
SERVICE_LOOPBACK,
@@ -71,7 +70,6 @@ export const CoreSharedOptionsSchema = z.object({
7170

7271
log: z.instanceof(Log).optional(),
7372
timers: z.custom<Timers>().optional(),
74-
cloudflareFetch: CloudflareFetchSchema.optional(),
7573

7674
// TODO: add back validation of cf object
7775
cf: z.union([z.boolean(), z.string(), z.record(z.any())]).optional(),

‎packages/tre/src/plugins/d1/gateway.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { Database as DatabaseType } from "better-sqlite3";
77
import { z } from "zod";
88
import { Response } from "../../http";
99
import { HttpError, Log } from "../../shared";
10-
import { Storage } from "../../storage";
10+
import { NewStorage } from "../../storage2";
1111
import splitSqlQuery from "./splitter";
1212

1313
export const D1ValueSchema = z.union([
@@ -153,8 +153,7 @@ interface ChangesLastRowResult {
153153
export class D1Gateway {
154154
private readonly db: DatabaseType;
155155

156-
constructor(private readonly log: Log, legacyStorage: Storage) {
157-
const storage = legacyStorage.getNewStorage();
156+
constructor(private readonly log: Log, storage: NewStorage) {
158157
this.db = storage.db;
159158
}
160159

‎packages/tre/src/plugins/kv/gateway.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import {
77
millisToSeconds,
88
secondsToMillis,
99
} from "../../shared";
10-
import { Storage } from "../../storage";
11-
import { KeyValueStorage } from "../../storage2";
10+
import { KeyValueStorage, NewStorage } from "../../storage2";
1211
import {
1312
MAX_KEY_SIZE,
1413
MAX_LIST_KEYS,
@@ -158,10 +157,9 @@ export class KVGateway {
158157

159158
constructor(
160159
private readonly log: Log,
161-
legacyStorage: Storage,
160+
storage: NewStorage,
162161
private readonly timers: Timers
163162
) {
164-
const storage = legacyStorage.getNewStorage();
165163
this.storage = new KeyValueStorage(storage, timers);
166164
}
167165

‎packages/tre/src/plugins/kv/index.ts

-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
} from "../shared";
99
import { KV_PLUGIN_NAME } from "./constants";
1010
import { KVGateway } from "./gateway";
11-
import { KVRemoteStorage } from "./remote";
1211
import { KVRouter } from "./router";
1312
import { SitesOptions, getSitesBindings, getSitesService } from "./sites";
1413

@@ -39,7 +38,6 @@ export const KV_PLUGIN: Plugin<
3938
> = {
4039
gateway: KVGateway,
4140
router: KVRouter,
42-
remoteStorage: KVRemoteStorage,
4341
options: KVOptionsSchema,
4442
sharedOptions: KVSharedOptionsSchema,
4543
async getBindings(options) {

‎packages/tre/src/plugins/kv/remote.ts

-276
This file was deleted.

‎packages/tre/src/plugins/queues/gateway.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { stringify } from "devalue";
77
import { Colorize, bold, green, grey, red, reset, yellow } from "kleur/colors";
88
import { z } from "zod";
99
import { Log, Timers } from "../../shared";
10-
import { Storage } from "../../storage";
10+
import { NewStorage } from "../../storage2";
1111
import { CoreHeaders, structuredSerializableReducers } from "../../workers";
1212
import { DispatchFetch, QueueConsumer } from "../shared";
1313

@@ -99,7 +99,7 @@ export class QueuesGateway {
9999

100100
constructor(
101101
private readonly log: Log,
102-
_storage: Storage,
102+
_storage: NewStorage,
103103
private readonly timers: Timers,
104104
private readonly queueName: string,
105105
private readonly dispatchFetch: DispatchFetch

‎packages/tre/src/plugins/r2/gateway.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
maybeApply,
1212
prefixError,
1313
} from "../../shared";
14-
import { Storage } from "../../storage";
1514
import {
1615
BlobId,
1716
InclusiveRange,
@@ -569,10 +568,10 @@ export class R2Gateway {
569568

570569
constructor(
571570
private readonly log: Log,
572-
legacyStorage: Storage,
571+
storage: NewStorage,
573572
private readonly timers: Timers
574573
) {
575-
this.#storage = legacyStorage.getNewStorage();
574+
this.#storage = storage;
576575
this.#storage.db.pragma("case_sensitive_like = TRUE");
577576
this.#storage.db.exec(SQL_SCHEMA);
578577
this.#stmts = sqlStmts(this.#storage.db);

‎packages/tre/src/plugins/shared/gateway.ts

+12-54
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@ import {
1111
sanitisePath,
1212
} from "../../shared";
1313
import {
14-
FileStorage,
15-
MemoryStorage,
16-
RemoteStorage,
17-
SqliteStorage,
18-
Storage,
19-
} from "../../storage";
14+
NewStorage,
15+
createFileStorage,
16+
createMemoryStorage,
17+
} from "../../storage2";
2018

2119
// TODO: explain why persist passed as header, want options set to be atomic,
2220
// if set gateway before script update, may be using new persist before new script
@@ -42,21 +40,13 @@ export type CloudflareFetch = z.infer<typeof CloudflareFetchSchema>;
4240
export interface GatewayConstructor<Gateway> {
4341
new (
4442
log: Log,
45-
storage: Storage,
43+
storage: NewStorage,
4644
timers: Timers,
4745
namespace: string,
4846
dispatchFetch: DispatchFetch
4947
): Gateway;
5048
}
5149

52-
export interface RemoteStorageConstructor {
53-
new (
54-
cache: Storage,
55-
cloudflareFetch: CloudflareFetch,
56-
namespace: string
57-
): RemoteStorage;
58-
}
59-
6050
export const DEFAULT_PERSIST_ROOT = ".mf";
6151

6252
export const PARAM_FILE_UNSANITISE = "unsanitise";
@@ -69,30 +59,25 @@ export function maybeParseURL(url: Persistence): URL | undefined {
6959
}
7060

7161
export class GatewayFactory<Gateway> {
72-
readonly #memoryStorages = new Map<string, MemoryStorage>();
62+
readonly #memoryStorages = new Map<string, NewStorage>();
7363
readonly #gateways = new Map<string, [Persistence, Gateway]>();
7464

7565
constructor(
7666
private readonly log: Log,
7767
private readonly timers: Timers,
7868
private readonly dispatchFetch: DispatchFetch,
79-
private readonly cloudflareFetch: CloudflareFetch | undefined,
8069
private readonly pluginName: string,
81-
private readonly gatewayClass: GatewayConstructor<Gateway>,
82-
private readonly remoteStorageClass?: RemoteStorageConstructor
70+
private readonly gatewayClass: GatewayConstructor<Gateway>
8371
) {}
8472

8573
#getMemoryStorage(namespace: string) {
8674
let storage = this.#memoryStorages.get(namespace);
8775
if (storage !== undefined) return storage;
88-
this.#memoryStorages.set(
89-
namespace,
90-
(storage = new MemoryStorage(undefined, this.timers.now))
91-
);
76+
this.#memoryStorages.set(namespace, (storage = createMemoryStorage()));
9277
return storage;
9378
}
9479

95-
getStorage(namespace: string, persist: Persistence): Storage {
80+
getStorage(namespace: string, persist: Persistence): NewStorage {
9681
// If persistence is disabled, use memory storage
9782
if (persist === undefined || persist === false) {
9883
return this.#getMemoryStorage(namespace);
@@ -109,47 +94,20 @@ export class GatewayFactory<Gateway> {
10994
}
11095
if (url.protocol === "file:") {
11196
const root = path.join(fileURLToPath(url), sanitisedNamespace);
112-
const unsanitise =
113-
url.searchParams.get(PARAM_FILE_UNSANITISE) === "true";
114-
return new FileStorage(root, !unsanitise, this.timers.now);
115-
} else if (url.protocol === "sqlite:") {
116-
return new SqliteStorage(
117-
url.pathname,
118-
sanitisedNamespace,
119-
this.timers.now
120-
);
121-
}
122-
// TODO: support Redis storage?
123-
if (url.protocol === "remote:") {
124-
const { cloudflareFetch, remoteStorageClass } = this;
125-
if (cloudflareFetch === undefined) {
126-
throw new MiniflareCoreError(
127-
"ERR_PERSIST_REMOTE_UNAUTHENTICATED",
128-
"Authenticated Cloudflare API `cloudflareFetch` option not provided but required for remote storage"
129-
);
130-
}
131-
if (remoteStorageClass === undefined) {
132-
throw new MiniflareCoreError(
133-
"ERR_PERSIST_REMOTE_UNSUPPORTED",
134-
`The "${this.pluginName}" plugin does not support remote storage`
135-
);
136-
}
137-
const cachePersist = url.searchParams.get("cache") ?? undefined;
138-
const cache = this.getStorage(namespace, cachePersist);
139-
return new remoteStorageClass(cache, cloudflareFetch, namespace);
97+
return createFileStorage(root);
14098
}
14199
throw new MiniflareCoreError(
142100
"ERR_PERSIST_UNSUPPORTED",
143101
`Unsupported "${url.protocol}" persistence protocol for storage: ${url.href}`
144102
);
145103
}
146104

147-
// Otherwise, fallback to sanitised file storage
105+
// Otherwise, fallback to file storage
148106
const root =
149107
persist === true
150108
? path.join(DEFAULT_PERSIST_ROOT, this.pluginName, sanitisedNamespace)
151109
: path.join(persist, sanitisedNamespace);
152-
return new FileStorage(root, undefined, this.timers.now);
110+
return createFileStorage(root);
153111
}
154112

155113
get(namespace: string, persist: Persistence): Gateway {

‎packages/tre/src/plugins/shared/index.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from "zod";
22
import { Service, Worker_Binding, Worker_Module } from "../../runtime";
33
import { Awaitable, Log, OptionalZodTypeOf } from "../../shared";
4-
import { GatewayConstructor, RemoteStorageConstructor } from "./gateway";
4+
import { GatewayConstructor } from "./gateway";
55
import { RouterConstructor } from "./router";
66

77
// Maps **service** names to the Durable Object class names exported by them
@@ -70,11 +70,10 @@ export type Plugin<
7070
? { sharedOptions?: undefined }
7171
: { sharedOptions: SharedOptions }) &
7272
(Gateway extends undefined
73-
? { gateway?: undefined; router?: undefined; remoteStorage?: undefined }
73+
? { gateway?: undefined; router?: undefined }
7474
: {
7575
gateway: GatewayConstructor<Gateway>;
7676
router: RouterConstructor<Gateway>;
77-
remoteStorage?: RemoteStorageConstructor;
7877
});
7978

8079
export function namespaceEntries(

‎packages/tre/src/storage/file.ts

-317
This file was deleted.

‎packages/tre/src/storage/index.ts

-5
This file was deleted.

‎packages/tre/src/storage/local.ts

-118
This file was deleted.

‎packages/tre/src/storage/memory.ts

-118
This file was deleted.

‎packages/tre/src/storage/sqlite.ts

-212
This file was deleted.

‎packages/tre/src/storage/storage.ts

-317
This file was deleted.

‎packages/tre/test/plugins/cache/index.spec.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import crypto from "crypto";
33
import path from "path";
44
import { text } from "stream/consumers";
55
import {
6-
FileStorage,
76
HeadersInit,
87
KeyValueStorage,
98
LogLevel,
9+
createFileStorage,
1010
} from "@miniflare/tre";
1111
import { miniflareTest, useTmp } from "../../test-shared";
1212

@@ -406,15 +406,8 @@ test.serial("operations log warning on workers.dev subdomain", async (t) => {
406406
test.serial("operations persist cached data", async (t) => {
407407
// Create new temporary file-system persistence directory
408408
const tmp = await useTmp(t);
409-
const clock = () => t.context.timers.timestamp;
410-
// TODO(soon): clean up this mess once we've migrated all gateways
411-
const legacyStorage = new FileStorage(
412-
path.join(tmp, "default"),
413-
undefined,
414-
clock
415-
);
416-
const newStorage = legacyStorage.getNewStorage();
417-
const kvStorage = new KeyValueStorage(newStorage, t.context.timers);
409+
const storage = createFileStorage(path.join(tmp, "default"));
410+
const kvStorage = new KeyValueStorage(storage, t.context.timers);
418411

419412
// Set option, then reset after test
420413
await t.context.setOptions({ cachePersist: tmp });

‎packages/tre/test/plugins/d1/index.spec.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
D1PreparedStatement,
77
D1Result,
88
} from "@cloudflare/workers-types/experimental";
9-
import { FileStorage, Miniflare, MiniflareOptions } from "@miniflare/tre";
9+
import { Miniflare, MiniflareOptions, createFileStorage } from "@miniflare/tre";
1010
import Database from "better-sqlite3";
1111
import {
1212
MiniflareTestContext,
@@ -468,10 +468,8 @@ test.serial("operations persist D1 data", async (t) => {
468468

469469
// Create new temporary file-system persistence directory
470470
const tmp = await useTmp(t);
471-
// TODO(soon): clean up this mess once we've migrated all gateways
472-
const legacyStorage = new FileStorage(path.join(tmp, "db"));
473-
const newStorage = legacyStorage.getNewStorage();
474-
const sqliteDb = newStorage.db;
471+
const storage = createFileStorage(path.join(tmp, "db"));
472+
const sqliteDb = storage.db;
475473

476474
// Set option, then reset after test
477475
await t.context.setOptions({ ...opts, d1Persist: tmp });

‎packages/tre/test/plugins/kv/gateway.spec.ts

+14-13
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ import {
77
KVGatewayListOptions,
88
KVGatewayListResult,
99
KeyValueStorage,
10-
MemoryStorage,
1110
NoOpLog,
11+
createMemoryStorage,
1212
} from "@miniflare/tre";
1313
import anyTest, { Macro, TestFn, ThrowsExpectation } from "ava";
14-
import {
15-
TIME_EXPIRED,
16-
TIME_EXPIRING,
17-
TIME_NOW,
18-
createJunkStream,
19-
testTimers,
20-
} from "../../test-shared";
14+
import { TestTimers, createJunkStream } from "../../test-shared";
15+
16+
// Stored expiration value to signal an expired key.
17+
export const TIME_EXPIRED = 500;
18+
// Time in seconds the testClock always returns:
19+
// TIME_EXPIRED < TIME_NOW < TIME_EXPIRING
20+
export const TIME_NOW = 1000;
21+
// Stored expiration value to signal a key that will expire in the future.
22+
export const TIME_EXPIRING = 1500;
2123

2224
interface Context {
2325
storage: KeyValueStorage;
@@ -27,11 +29,10 @@ interface Context {
2729
const test = anyTest as TestFn<Context>;
2830

2931
test.beforeEach((t) => {
30-
// TODO(soon): clean up this mess once we've migrated all gateways
31-
const legacyStorage = new MemoryStorage(undefined, testTimers.now);
32-
const newStorage = legacyStorage.getNewStorage();
33-
const gateway = new KVGateway(new NoOpLog(), legacyStorage, testTimers);
34-
const kvStorage = new KeyValueStorage(newStorage, testTimers);
32+
const storage = createMemoryStorage();
33+
const timers = new TestTimers();
34+
const gateway = new KVGateway(new NoOpLog(), storage, timers);
35+
const kvStorage = new KeyValueStorage(storage, timers);
3536
t.context = { storage: kvStorage, gateway };
3637
});
3738

‎packages/tre/test/plugins/r2/index.spec.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import type {
2828
} from "@cloudflare/workers-types/experimental";
2929
import {
3030
File,
31-
FileStorage,
3231
FormData,
3332
Headers,
3433
Miniflare,
@@ -39,6 +38,7 @@ import {
3938
R2Gateway,
4039
Response,
4140
TypedDatabase,
41+
createFileStorage,
4242
viewToArray,
4343
viewToBuffer,
4444
} from "@miniflare/tre";
@@ -1346,16 +1346,15 @@ test.serial("operations persist stored data", async (t) => {
13461346

13471347
// Create new temporary file-system persistence directory
13481348
const tmp = await useTmp(t);
1349-
const legacyStorage = new FileStorage(path.join(tmp, "bucket"));
1350-
const newStorage = legacyStorage.getNewStorage();
1349+
const storage = createFileStorage(path.join(tmp, "bucket"));
13511350

13521351
// Set option, then reset after test
13531352
await t.context.setOptions({ ...opts, r2Persist: tmp });
13541353
t.teardown(() => t.context.setOptions(opts));
13551354

13561355
// Check put respects persist
13571356
await r2.put("key", "value");
1358-
const stmtListByNs = newStorage.db.prepare<{ ns: string }, { key: string }>(
1357+
const stmtListByNs = storage.db.prepare<{ ns: string }, { key: string }>(
13591358
"SELECT key FROM _mf_objects WHERE key LIKE :ns || '%'"
13601359
);
13611360
let stored = stmtListByNs.all({ ns });

‎packages/tre/test/plugins/shared/router.spec.ts

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class TestRouter extends Router<TestGateway> {
2525
log,
2626
defaultTimers,
2727
() => assert.fail("dispatchFetch not implemented"),
28-
undefined,
2928
"test",
3029
TestGateway
3130
)

‎packages/tre/test/test-shared/storage.ts

+1-20
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import fs from "fs/promises";
33
import path from "path";
44
import { ReadableStream } from "stream/web";
55
import { TextDecoder, TextEncoder } from "util";
6-
import { Timers, sanitisePath, unwrapBYOBRequest } from "@miniflare/tre";
6+
import { sanitisePath, unwrapBYOBRequest } from "@miniflare/tre";
77
import { ExecutionContext } from "ava";
88

99
const encoder = new TextEncoder();
@@ -16,25 +16,6 @@ export function utf8Decode(encoded?: Uint8Array): string {
1616
return decoder.decode(encoded);
1717
}
1818

19-
// Stored expiration value to signal an expired key. Storages using actual
20-
// time should interpret this as the current time.
21-
export const TIME_EXPIRED = 500;
22-
// Time in seconds the testClock always returns:
23-
// TIME_EXPIRED < TIME_NOW < TIME_EXPIRING
24-
export const TIME_NOW = 750;
25-
// Stored expiration value to signal a key that will expire in the future.
26-
// Storages using actual time should interpret this as the current time + 1hr.
27-
// Tests will check the expiry is within 120s of this.
28-
export const TIME_EXPIRING = 1000;
29-
30-
// TODO(soon): remove once we remove the old storage system
31-
export const testTimers: Timers = {
32-
now: () => TIME_NOW * 1000,
33-
setTimeout,
34-
clearTimeout,
35-
queueMicrotask,
36-
};
37-
3819
const tmpRoot = path.resolve(".tmp");
3920
export async function useTmp(t: ExecutionContext): Promise<string> {
4021
const filePath = path.join(

0 commit comments

Comments
 (0)
This repository has been archived.