@velajs/storage 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -52,5 +52,26 @@ class AppModule {}
52
52
  | R2 (HTTP + hybrid) | `@velajs/storage/drivers/r2-http` | ✅ | ✅ |
53
53
  | storagesdk bridge | `@velajs/storage/storagesdk` | Node/Bun only | depends on adapter |
54
54
 
55
+ ## Testing
56
+
57
+ There's no separate storage fake — the **memory driver IS the fake**. Build a disk in one line and
58
+ assert against it with the helpers from `@velajs/storage/testing`:
59
+
60
+ ```ts
61
+ import { createStorage } from '@velajs/storage';
62
+ import { memoryDriver } from '@velajs/storage/drivers/memory';
63
+ import { assertExists, assertMissing, assertCount } from '@velajs/storage/testing';
64
+
65
+ const storage = createStorage({ driver: memoryDriver() });
66
+ await storage.upload('avatars/1.png', bytes);
67
+
68
+ await assertExists(storage, 'avatars/1.png');
69
+ await assertMissing(storage, 'avatars/2.png');
70
+ await assertCount(storage, 'avatars/', 1); // or assertCount(storage, 1) for the whole disk
71
+ ```
72
+
73
+ The helpers accept anything memory-backed — a `Storage` facade or an injected `StorageService`.
74
+ For DI/integration tests, register the same driver instead: `StorageModule.forRoot({ driver: memoryDriver() })`.
75
+
55
76
  See the docs site for presigned uploads, the HTTP upload controller + browser client, middleware,
56
77
  multi-bucket, and the full capability matrix.
@@ -0,0 +1,17 @@
1
+ import type { ListOptions, ListResult, OperationOptions } from '../storage.types';
2
+ /**
3
+ * The read surface the assertions need. Satisfied by `StorageService`, the
4
+ * `Storage` facade, and a bare `StorageDriver` — anything memory-backed works.
5
+ */
6
+ export interface StorageTarget {
7
+ exists(key: string, opts?: OperationOptions): Promise<boolean>;
8
+ list(opts?: ListOptions): Promise<ListResult>;
9
+ }
10
+ /** Assert an object exists at `key`; throws a diagnostic `Error` otherwise. */
11
+ export declare function assertExists(storage: StorageTarget, key: string): Promise<void>;
12
+ /** Assert nothing is stored at `key`; throws a diagnostic `Error` otherwise. */
13
+ export declare function assertMissing(storage: StorageTarget, key: string): Promise<void>;
14
+ /** Assert the disk holds exactly `expected` objects. */
15
+ export declare function assertCount(storage: StorageTarget, expected: number): Promise<void>;
16
+ /** Assert exactly `expected` objects exist under `prefix`. */
17
+ export declare function assertCount(storage: StorageTarget, prefix: string, expected: number): Promise<void>;
@@ -0,0 +1,57 @@
1
+ // @velajs/storage/testing — assertion helpers for tests.
2
+ //
3
+ // Vela has no separate storage fake: the in-package **memory driver IS the
4
+ // fake**. Build a test disk in one obvious line and assert against it —
5
+ // no bootstrap, no subclass:
6
+ //
7
+ // import { createStorage } from '@velajs/storage';
8
+ // import { memoryDriver } from '@velajs/storage/drivers/memory';
9
+ // import { assertExists, assertCount } from '@velajs/storage/testing';
10
+ //
11
+ // const storage = createStorage({ driver: memoryDriver() });
12
+ // await storage.upload('avatar.png', bytes);
13
+ // await assertExists(storage, 'avatar.png');
14
+ // await assertCount(storage, 'avatars/', 1);
15
+ //
16
+ // For DI/integration tests, register the same driver on the module instead —
17
+ // `StorageModule.forRoot({ driver: memoryDriver() })` — then inject the
18
+ // `StorageService` and pass it to these helpers. Both a `Storage` facade and a
19
+ // `StorageService` satisfy the {@link StorageTarget} surface below.
20
+ //
21
+ // The helpers are thin and framework-free: they throw a plain `Error` with a
22
+ // diagnostic message on failure (no vitest/`expect` dependency), and drive the
23
+ // public `exists`/`list` API so prefix scoping and drivers stay honest.
24
+ /** Assert an object exists at `key`; throws a diagnostic `Error` otherwise. */ export async function assertExists(storage, key) {
25
+ if (!await storage.exists(key)) {
26
+ const keys = await collectKeys(storage);
27
+ throw new Error(`assertExists: expected an object at "${key}" to exist, but it is missing. ` + `Stored: ${keys.length ? keys.join(', ') : '(none)'}`);
28
+ }
29
+ }
30
+ /** Assert nothing is stored at `key`; throws a diagnostic `Error` otherwise. */ export async function assertMissing(storage, key) {
31
+ if (await storage.exists(key)) {
32
+ throw new Error(`assertMissing: expected no object at "${key}", but one exists`);
33
+ }
34
+ }
35
+ export async function assertCount(storage, prefixOrExpected, maybeExpected) {
36
+ const prefix = typeof prefixOrExpected === 'string' ? prefixOrExpected : undefined;
37
+ const expected = typeof prefixOrExpected === 'number' ? prefixOrExpected : maybeExpected;
38
+ const keys = await collectKeys(storage, prefix);
39
+ if (keys.length !== expected) {
40
+ const scope = prefix ? ` under prefix "${prefix}"` : '';
41
+ const found = keys.length ? `: ${keys.join(', ')}` : '';
42
+ throw new Error(`assertCount: expected ${expected} object(s)${scope}, but found ${keys.length}${found}`);
43
+ }
44
+ }
45
+ /** Walk every listing page (following the cursor) and collect the object keys. */ async function collectKeys(storage, prefix) {
46
+ const keys = [];
47
+ let cursor;
48
+ do {
49
+ const page = await storage.list({
50
+ prefix,
51
+ cursor
52
+ });
53
+ for (const item of page.items)keys.push(item.key);
54
+ cursor = page.cursor;
55
+ }while (cursor)
56
+ return keys;
57
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velajs/storage",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Edge-first, driver-based object/file storage for the Vela framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,6 +37,10 @@
37
37
  "./storagesdk": {
38
38
  "types": "./dist/storagesdk/index.d.ts",
39
39
  "import": "./dist/storagesdk/index.js"
40
+ },
41
+ "./testing": {
42
+ "types": "./dist/testing/index.d.ts",
43
+ "import": "./dist/testing/index.js"
40
44
  }
41
45
  },
42
46
  "files": [
@@ -45,14 +49,6 @@
45
49
  "LICENSE",
46
50
  "CHANGELOG.md"
47
51
  ],
48
- "scripts": {
49
- "build": "rm -rf dist && swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
50
- "test": "vitest run",
51
- "test:workers": "vitest run --config vitest.config.workers.ts",
52
- "typecheck": "tsc --noEmit",
53
- "prepare": "swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
54
- "prepublishOnly": "pnpm run typecheck && pnpm test"
55
- },
56
52
  "sideEffects": false,
57
53
  "keywords": [
58
54
  "vela",
@@ -105,12 +101,10 @@
105
101
  "unplugin-swc": "^1.5.9",
106
102
  "vitest": "^4.1.9"
107
103
  },
108
- "packageManager": "pnpm@10.20.0",
109
- "pnpm": {
110
- "onlyBuiltDependencies": [
111
- "@swc/core",
112
- "esbuild",
113
- "workerd"
114
- ]
104
+ "scripts": {
105
+ "build": "rm -rf dist && swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
106
+ "test": "vitest run",
107
+ "test:workers": "vitest run --config vitest.config.workers.ts",
108
+ "typecheck": "tsc --noEmit"
115
109
  }
116
- }
110
+ }