@cloudhal/storage 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +26 -2
- package/dist/migrations/0001_init.d.ts +12 -0
- package/dist/migrations/0001_init.js +46 -0
- package/dist/migrations/0002_auth.d.ts +7 -0
- package/dist/migrations/0002_auth.js +26 -0
- package/dist/migrations/0003_agent.d.ts +6 -0
- package/dist/migrations/0003_agent.js +32 -0
- package/dist/migrations/0004_audit_actor_name.d.ts +3 -0
- package/dist/migrations/0004_audit_actor_name.js +7 -0
- package/dist/migrations/0005_auth_rate_limit.d.ts +7 -0
- package/dist/migrations/0005_auth_rate_limit.js +11 -0
- package/dist/migrations/0006_approvals.d.ts +7 -0
- package/dist/migrations/0006_approvals.js +31 -0
- package/dist/migrations/0007_audit_mode.d.ts +3 -0
- package/dist/migrations/0007_audit_mode.js +7 -0
- package/dist/migrations/0008_policy_detail.d.ts +7 -0
- package/dist/migrations/0008_policy_detail.js +17 -0
- package/dist/migrations/0009_audit_elevation.d.ts +3 -0
- package/dist/migrations/0009_audit_elevation.js +7 -0
- package/dist/migrations/0010_approval_result.d.ts +6 -0
- package/dist/migrations/0010_approval_result.js +10 -0
- package/dist/migrations/0011_op_guards.d.ts +17 -0
- package/dist/migrations/0011_op_guards.js +30 -0
- package/dist/migrations/0012_roles.d.ts +23 -0
- package/dist/migrations/0012_roles.js +78 -0
- package/dist/migrations/0013_media.d.ts +16 -0
- package/dist/migrations/0013_media.js +69 -0
- package/dist/migrations/0014_analytics.d.ts +19 -0
- package/dist/migrations/0014_analytics.js +105 -0
- package/dist/migrations/0015_analytics_privacy_signal.d.ts +8 -0
- package/dist/migrations/0015_analytics_privacy_signal.js +14 -0
- package/dist/migrations/0016_comments.d.ts +27 -0
- package/dist/migrations/0016_comments.js +173 -0
- package/dist/migrations/0017_schedules.d.ts +21 -0
- package/dist/migrations/0017_schedules.js +108 -0
- package/dist/migrations/0018_query_indexes.d.ts +12 -0
- package/dist/migrations/0018_query_indexes.js +22 -0
- package/dist/migrations/0019_body_proposals.d.ts +8 -0
- package/dist/migrations/0019_body_proposals.js +24 -0
- package/dist/migrations/0020_ai.d.ts +8 -0
- package/dist/migrations/0020_ai.js +31 -0
- package/dist/migrations/0021_search.d.ts +12 -0
- package/dist/migrations/0021_search.js +29 -0
- package/dist/migrations/0022_search_kinds.d.ts +12 -0
- package/dist/migrations/0022_search_kinds.js +35 -0
- package/dist/migrations/0023_search_usage.d.ts +7 -0
- package/dist/migrations/0023_search_usage.js +18 -0
- package/dist/migrations/0024_search_quota.d.ts +8 -0
- package/dist/migrations/0024_search_quota.js +18 -0
- package/dist/migrations/0025_translation_groups.d.ts +7 -0
- package/dist/migrations/0025_translation_groups.js +15 -0
- package/dist/migrations/0026_search_cjk.d.ts +8 -0
- package/dist/migrations/0026_search_cjk.js +20 -0
- package/dist/migrations/0027_multi_site.d.ts +14 -0
- package/dist/migrations/0027_multi_site.js +44 -0
- package/dist/migrations/0028_site_members.d.ts +8 -0
- package/dist/migrations/0028_site_members.js +22 -0
- package/dist/migrations/index.d.ts +3 -0
- package/dist/migrations/index.js +61 -0
- package/dist/src/blob/fs.d.ts +19 -0
- package/dist/src/blob/fs.js +172 -0
- package/dist/src/blob/memory.d.ts +18 -0
- package/dist/src/blob/memory.js +79 -0
- package/dist/src/blob/r2.d.ts +19 -0
- package/dist/src/blob/r2.js +146 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.js +12 -0
- package/dist/src/migrate.d.ts +17 -0
- package/dist/src/migrate.js +67 -0
- package/dist/src/sql/better-sqlite3.d.ts +23 -0
- package/dist/src/sql/better-sqlite3.js +104 -0
- package/dist/src/sql/d1.d.ts +13 -0
- package/dist/src/sql/d1.js +70 -0
- package/dist/src/types.d.ts +131 -0
- package/dist/src/types.js +235 -0
- package/package.json +76 -3
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage interfaces. Deliberately tiny and shaped by the most constrained backend
|
|
3
|
+
* (Cloudflare D1): no interactive transactions, atomic `batch()` only, ≤100 bound
|
|
4
|
+
* parameters per statement, ≤100 KB per statement, ≤1000 statements per batch.
|
|
5
|
+
* Every adapter, including the local SQLite one, enforces the same limits so nothing
|
|
6
|
+
* "works locally but not on Cloudflare" (ADR-0002).
|
|
7
|
+
*/
|
|
8
|
+
/** Values that can be bound to SQL parameters on every backend. Booleans are stored as 0/1 by callers. */
|
|
9
|
+
export type SqlValue = string | number | null | Uint8Array;
|
|
10
|
+
export interface SqlStatement {
|
|
11
|
+
readonly sql: string;
|
|
12
|
+
readonly params?: readonly SqlValue[];
|
|
13
|
+
}
|
|
14
|
+
export interface RunResult {
|
|
15
|
+
/** Rows changed by an INSERT/UPDATE/DELETE (0 for reads). */
|
|
16
|
+
readonly changes: number;
|
|
17
|
+
/** ROWID of the last inserted row, when the backend reports it. */
|
|
18
|
+
readonly lastRowId: number | null;
|
|
19
|
+
}
|
|
20
|
+
export interface SqlDriver {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
/** First row of a query, or `undefined`. */
|
|
23
|
+
get<T extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: readonly SqlValue[]): Promise<T | undefined>;
|
|
24
|
+
/** All rows of a query. */
|
|
25
|
+
all<T extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: readonly SqlValue[]): Promise<T[]>;
|
|
26
|
+
/** Execute one write statement. */
|
|
27
|
+
run(sql: string, params?: readonly SqlValue[]): Promise<RunResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Execute statements atomically: either all succeed or none are applied.
|
|
30
|
+
* This is the only multi-statement write primitive; there is no `transaction()`.
|
|
31
|
+
*/
|
|
32
|
+
batch(statements: readonly SqlStatement[]): Promise<RunResult[]>;
|
|
33
|
+
/** Run raw multi-statement SQL (DDL in migrations). Not atomic on every backend; use `batch` for data. */
|
|
34
|
+
exec(sql: string): Promise<void>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
export interface BlobMeta {
|
|
38
|
+
readonly size: number;
|
|
39
|
+
readonly contentType?: string;
|
|
40
|
+
/** Lower-case hex SHA-256, when the backend recorded one (R2 checksums, RFC-0004 §4.1). */
|
|
41
|
+
readonly sha256?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface BlobPutOptions {
|
|
44
|
+
readonly contentType?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Lower-case hex SHA-256 the data must have. On a mismatch the adapter throws
|
|
47
|
+
* `BlobIntegrityError` and stores nothing (RFC-0004 §4.1).
|
|
48
|
+
*/
|
|
49
|
+
readonly sha256?: string;
|
|
50
|
+
}
|
|
51
|
+
/** A byte range: `offset` (+ `length`), or the last `suffix` bytes. */
|
|
52
|
+
export type BlobRange = {
|
|
53
|
+
offset: number;
|
|
54
|
+
length?: number;
|
|
55
|
+
} | {
|
|
56
|
+
suffix: number;
|
|
57
|
+
};
|
|
58
|
+
export interface BlobStream {
|
|
59
|
+
readonly body: ReadableStream<Uint8Array>;
|
|
60
|
+
/** Size of the whole object. */
|
|
61
|
+
readonly size: number;
|
|
62
|
+
/** The part `body` carries, when a range was asked for. */
|
|
63
|
+
readonly range?: {
|
|
64
|
+
offset: number;
|
|
65
|
+
length: number;
|
|
66
|
+
};
|
|
67
|
+
readonly contentType?: string;
|
|
68
|
+
}
|
|
69
|
+
/** Content store keyed by path-like strings ("nodes/<hash>.json"). Mirrors the R2 surface. */
|
|
70
|
+
export interface BlobStore {
|
|
71
|
+
readonly name: string;
|
|
72
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
73
|
+
head(key: string): Promise<BlobMeta | null>;
|
|
74
|
+
put(key: string, data: Uint8Array, options?: BlobPutOptions): Promise<void>;
|
|
75
|
+
delete(key: string): Promise<void>;
|
|
76
|
+
/** Keys under `prefix`, sorted ascending. */
|
|
77
|
+
list(prefix?: string): Promise<string[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Streams exactly `size` bytes to `key` and verifies `sha256` before the object becomes visible;
|
|
80
|
+
* a short, long or mismatching stream stores nothing. Optional: callers fall back to buffered
|
|
81
|
+
* `put` (RFC-0004 §4.1).
|
|
82
|
+
*/
|
|
83
|
+
putStream?(key: string, data: ReadableStream<Uint8Array>, options: BlobPutOptions & {
|
|
84
|
+
size: number;
|
|
85
|
+
sha256: string;
|
|
86
|
+
}): Promise<void>;
|
|
87
|
+
/** Streams an object, or a range of it. Optional: callers fall back to `get`. */
|
|
88
|
+
getStream?(key: string, options?: {
|
|
89
|
+
range?: BlobRange;
|
|
90
|
+
}): Promise<BlobStream | null>;
|
|
91
|
+
}
|
|
92
|
+
/** The data did not have the SHA-256 (or size) the caller declared; nothing was stored. */
|
|
93
|
+
export declare class BlobIntegrityError extends Error {
|
|
94
|
+
readonly name = "BlobIntegrityError";
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Blob keys are path-like and built from validated parts only: `[A-Za-z0-9._-/]`, no empty, `.`
|
|
98
|
+
* or `..` segment, at most 512 characters. Every adapter calls this (RFC-0004 §4.1), so a key can
|
|
99
|
+
* never escape its prefix on the filesystem or address another site's objects by traversal.
|
|
100
|
+
*/
|
|
101
|
+
export declare function assertBlobKey(key: string): void;
|
|
102
|
+
/** Resolves a range against an object size; undefined when it cannot be satisfied (416). */
|
|
103
|
+
export declare function resolveBlobRange(range: BlobRange, size: number): {
|
|
104
|
+
offset: number;
|
|
105
|
+
length: number;
|
|
106
|
+
} | undefined;
|
|
107
|
+
/** Lower-case hex SHA-256 of bytes (WebCrypto: Node and Workers). */
|
|
108
|
+
export declare function sha256OfBytes(data: Uint8Array): Promise<string>;
|
|
109
|
+
/** Reads a stream into memory, failing once it carries more than `max` bytes. */
|
|
110
|
+
export declare function readAllBytes(stream: ReadableStream<Uint8Array>, max: number): Promise<Uint8Array>;
|
|
111
|
+
/** A stream over bytes (the buffered fallback of `getStream`). */
|
|
112
|
+
export declare function bytesStream(data: Uint8Array): ReadableStream<Uint8Array>;
|
|
113
|
+
export declare const SHA256_HEX: RegExp;
|
|
114
|
+
/** Throws BlobIntegrityError unless `data` is `size` bytes (when given) with SHA-256 `sha256`. */
|
|
115
|
+
export declare function verifyBlob(data: Uint8Array, sha256: string, size?: number): Promise<void>;
|
|
116
|
+
/** D1 limits, enforced by every SQL adapter. */
|
|
117
|
+
export declare const SQL_LIMITS: {
|
|
118
|
+
readonly maxBoundParams: 100;
|
|
119
|
+
readonly maxStatementBytes: 100000;
|
|
120
|
+
readonly maxBatchStatements: 1000;
|
|
121
|
+
};
|
|
122
|
+
export declare class StorageLimitError extends Error {
|
|
123
|
+
readonly name = "StorageLimitError";
|
|
124
|
+
}
|
|
125
|
+
export declare class StorageError extends Error {
|
|
126
|
+
readonly name = "StorageError";
|
|
127
|
+
constructor(message: string, cause?: unknown);
|
|
128
|
+
}
|
|
129
|
+
/** Throws `StorageLimitError` when a statement would be rejected by D1. */
|
|
130
|
+
export declare function assertStatementLimits(sql: string, params?: readonly SqlValue[]): void;
|
|
131
|
+
export declare function assertBatchLimits(statements: readonly SqlStatement[]): void;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage interfaces. Deliberately tiny and shaped by the most constrained backend
|
|
3
|
+
* (Cloudflare D1): no interactive transactions, atomic `batch()` only, ≤100 bound
|
|
4
|
+
* parameters per statement, ≤100 KB per statement, ≤1000 statements per batch.
|
|
5
|
+
* Every adapter, including the local SQLite one, enforces the same limits so nothing
|
|
6
|
+
* "works locally but not on Cloudflare" (ADR-0002).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Values that can be bound to SQL parameters on every backend. Booleans are stored as 0/1 by callers. */
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/** A byte range: `offset` (+ `length`), or the last `suffix` bytes. */
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
/** Content store keyed by path-like strings ("nodes/<hash>.json"). Mirrors the R2 surface. */
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/** The data did not have the SHA-256 (or size) the caller declared; nothing was stored. */
|
|
100
|
+
export class BlobIntegrityError extends Error {
|
|
101
|
+
name = "BlobIntegrityError";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const KEY_RE = /^[A-Za-z0-9._\-/]+$/;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Blob keys are path-like and built from validated parts only: `[A-Za-z0-9._-/]`, no empty, `.`
|
|
108
|
+
* or `..` segment, at most 512 characters. Every adapter calls this (RFC-0004 §4.1), so a key can
|
|
109
|
+
* never escape its prefix on the filesystem or address another site's objects by traversal.
|
|
110
|
+
*/
|
|
111
|
+
export function assertBlobKey(key ) {
|
|
112
|
+
if (
|
|
113
|
+
typeof key !== "string" ||
|
|
114
|
+
key.length === 0 ||
|
|
115
|
+
key.length > 512 ||
|
|
116
|
+
!KEY_RE.test(key) ||
|
|
117
|
+
key.split("/").some((seg) => seg === "" || seg === "." || seg === "..")
|
|
118
|
+
) {
|
|
119
|
+
throw new StorageError(`invalid blob key "${String(key).slice(0, 80)}"`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Resolves a range against an object size; undefined when it cannot be satisfied (416). */
|
|
124
|
+
export function resolveBlobRange(
|
|
125
|
+
range ,
|
|
126
|
+
size ,
|
|
127
|
+
) {
|
|
128
|
+
if ("suffix" in range) {
|
|
129
|
+
if (!Number.isInteger(range.suffix) || range.suffix <= 0 || size === 0) return undefined;
|
|
130
|
+
const length = Math.min(range.suffix, size);
|
|
131
|
+
return { offset: size - length, length };
|
|
132
|
+
}
|
|
133
|
+
const { offset } = range;
|
|
134
|
+
if (!Number.isInteger(offset) || offset < 0 || offset >= size) return undefined;
|
|
135
|
+
const max = size - offset;
|
|
136
|
+
const length = range.length === undefined ? max : Math.min(range.length, max);
|
|
137
|
+
if (!Number.isInteger(length) || length <= 0) return undefined;
|
|
138
|
+
return { offset, length };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Lower-case hex SHA-256 of bytes (WebCrypto: Node and Workers). */
|
|
142
|
+
export async function sha256OfBytes(data ) {
|
|
143
|
+
const digest = await crypto.subtle.digest("SHA-256", data );
|
|
144
|
+
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Reads a stream into memory, failing once it carries more than `max` bytes. */
|
|
148
|
+
export async function readAllBytes(
|
|
149
|
+
stream ,
|
|
150
|
+
max ,
|
|
151
|
+
) {
|
|
152
|
+
const reader = stream.getReader();
|
|
153
|
+
const chunks = [];
|
|
154
|
+
let total = 0;
|
|
155
|
+
for (;;) {
|
|
156
|
+
const { done, value } = await reader.read();
|
|
157
|
+
if (done) break;
|
|
158
|
+
total += value.byteLength;
|
|
159
|
+
if (total > max) {
|
|
160
|
+
await reader.cancel().catch(() => {});
|
|
161
|
+
throw new BlobIntegrityError(`stream is longer than ${max} bytes`);
|
|
162
|
+
}
|
|
163
|
+
chunks.push(value);
|
|
164
|
+
}
|
|
165
|
+
const out = new Uint8Array(total);
|
|
166
|
+
let at = 0;
|
|
167
|
+
for (const c of chunks) {
|
|
168
|
+
out.set(c, at);
|
|
169
|
+
at += c.byteLength;
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** A stream over bytes (the buffered fallback of `getStream`). */
|
|
175
|
+
export function bytesStream(data ) {
|
|
176
|
+
return new ReadableStream ({
|
|
177
|
+
start(controller) {
|
|
178
|
+
if (data.byteLength > 0) controller.enqueue(data);
|
|
179
|
+
controller.close();
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
185
|
+
|
|
186
|
+
/** Throws BlobIntegrityError unless `data` is `size` bytes (when given) with SHA-256 `sha256`. */
|
|
187
|
+
export async function verifyBlob(data , sha256 , size ) {
|
|
188
|
+
if (!SHA256_HEX.test(sha256)) throw new BlobIntegrityError("sha256 must be 64 lower-case hex");
|
|
189
|
+
if (size !== undefined && data.byteLength !== size)
|
|
190
|
+
throw new BlobIntegrityError(`expected ${size} bytes, got ${data.byteLength}`);
|
|
191
|
+
if ((await sha256OfBytes(data)) !== sha256)
|
|
192
|
+
throw new BlobIntegrityError("data does not match the declared sha256");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** D1 limits, enforced by every SQL adapter. */
|
|
196
|
+
export const SQL_LIMITS = {
|
|
197
|
+
maxBoundParams: 100,
|
|
198
|
+
maxStatementBytes: 100_000,
|
|
199
|
+
maxBatchStatements: 1000,
|
|
200
|
+
} ;
|
|
201
|
+
|
|
202
|
+
export class StorageLimitError extends Error {
|
|
203
|
+
name = "StorageLimitError";
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export class StorageError extends Error {
|
|
207
|
+
name = "StorageError";
|
|
208
|
+
constructor(message , cause ) {
|
|
209
|
+
super(message, cause === undefined ? undefined : { cause });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const encoder = new TextEncoder();
|
|
214
|
+
|
|
215
|
+
/** Throws `StorageLimitError` when a statement would be rejected by D1. */
|
|
216
|
+
export function assertStatementLimits(sql , params = []) {
|
|
217
|
+
if (params.length > SQL_LIMITS.maxBoundParams) {
|
|
218
|
+
throw new StorageLimitError(
|
|
219
|
+
`statement binds ${params.length} parameters; the limit is ${SQL_LIMITS.maxBoundParams} (chunk the write)`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
if (encoder.encode(sql).byteLength > SQL_LIMITS.maxStatementBytes) {
|
|
223
|
+
throw new StorageLimitError(`statement exceeds ${SQL_LIMITS.maxStatementBytes} bytes`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function assertBatchLimits(statements ) {
|
|
228
|
+
if (statements.length === 0) throw new StorageError("batch requires at least one statement");
|
|
229
|
+
if (statements.length > SQL_LIMITS.maxBatchStatements) {
|
|
230
|
+
throw new StorageLimitError(
|
|
231
|
+
`batch has ${statements.length} statements; the limit is ${SQL_LIMITS.maxBatchStatements}`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
for (const s of statements) assertStatementLimits(s.sql, s.params);
|
|
235
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,79 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudhal/storage",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cloudhal storage interfaces (SQL driver, blob store) with Cloudflare D1/R2 and local SQLite/filesystem adapters",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"homepage": "https://cloudhal.dev",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/cloudhal-dev/cloudhal.git",
|
|
10
|
+
"directory": "packages/storage"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/cloudhal-dev/cloudhal/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/src/index.d.ts",
|
|
19
|
+
"default": "./dist/src/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./sqlite": {
|
|
22
|
+
"types": "./dist/src/sql/better-sqlite3.d.ts",
|
|
23
|
+
"default": "./dist/src/sql/better-sqlite3.js"
|
|
24
|
+
},
|
|
25
|
+
"./d1": {
|
|
26
|
+
"types": "./dist/src/sql/d1.d.ts",
|
|
27
|
+
"default": "./dist/src/sql/d1.js"
|
|
28
|
+
},
|
|
29
|
+
"./r2": {
|
|
30
|
+
"types": "./dist/src/blob/r2.d.ts",
|
|
31
|
+
"default": "./dist/src/blob/r2.js"
|
|
32
|
+
},
|
|
33
|
+
"./fs": {
|
|
34
|
+
"types": "./dist/src/blob/fs.d.ts",
|
|
35
|
+
"default": "./dist/src/blob/fs.js"
|
|
36
|
+
},
|
|
37
|
+
"./testing": {
|
|
38
|
+
"types": "./dist/src/testing.d.ts",
|
|
39
|
+
"default": "./dist/src/testing.js"
|
|
40
|
+
},
|
|
41
|
+
"./migrations/*": "./dist/migrations/*"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"README.md"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@cloudhal/core-contracts": "0.1.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"better-sqlite3": "^13.0.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"better-sqlite3": {
|
|
55
|
+
"optional": true
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@cloudflare/workers-types": "^5.20261007.1",
|
|
60
|
+
"@types/better-sqlite3": "^9.6.0",
|
|
61
|
+
"@types/node": "^26.6.5",
|
|
62
|
+
"better-sqlite3": "13.0.3",
|
|
63
|
+
"miniflare": "4.20260730.0",
|
|
64
|
+
"tsx": "^4.23.15",
|
|
65
|
+
"typescript": "7.0.2",
|
|
66
|
+
"vitest": "5.0.3"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=24"
|
|
70
|
+
},
|
|
71
|
+
"publishConfig": {
|
|
72
|
+
"access": "public"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
76
|
+
"test": "vitest run",
|
|
77
|
+
"check": "pnpm typecheck && pnpm test"
|
|
78
|
+
}
|
|
6
79
|
}
|