@puzzle-ui/storage 0.0.0-stage → 0.5.1
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 +55 -2
- package/dist/LICENSE +21 -0
- package/dist/errors/storage.errors.cjs +55 -0
- package/dist/errors/storage.errors.cjs.map +1 -0
- package/dist/errors/storage.errors.d.cts +27 -0
- package/dist/errors/storage.errors.d.ts +27 -0
- package/dist/errors/storage.errors.js +50 -0
- package/dist/errors/storage.errors.js.map +1 -0
- package/dist/index.cjs +13 -0
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +4 -0
- package/dist/types/adapter.types.d.cts +68 -0
- package/dist/types/adapter.types.d.ts +68 -0
- package/dist/types/capabilities.types.cjs +14 -0
- package/dist/types/capabilities.types.cjs.map +1 -0
- package/dist/types/capabilities.types.d.cts +16 -0
- package/dist/types/capabilities.types.d.ts +16 -0
- package/dist/types/capabilities.types.js +14 -0
- package/dist/types/capabilities.types.js.map +1 -0
- package/dist/types/collection.types.d.cts +25 -0
- package/dist/types/collection.types.d.ts +25 -0
- package/dist/types/migration.types.d.cts +53 -0
- package/dist/types/migration.types.d.ts +53 -0
- package/dist/types/query.types.d.cts +55 -0
- package/dist/types/query.types.d.ts +55 -0
- package/dist/types/schema.types.d.cts +16 -0
- package/dist/types/schema.types.d.ts +16 -0
- package/dist/utils/query.utils.cjs +44 -0
- package/dist/utils/query.utils.cjs.map +1 -0
- package/dist/utils/query.utils.d.cts +4 -0
- package/dist/utils/query.utils.d.ts +4 -0
- package/dist/utils/query.utils.js +43 -0
- package/dist/utils/query.utils.js.map +1 -0
- package/package.json +51 -4
package/README.md
CHANGED
|
@@ -1,3 +1,56 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @puzzle-ui/storage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Vendor-neutral storage contracts for Puzzle Auth and other packages. This package defines the typed adapter API, query language, migration protocol, and reusable adapter conformance suites; it does not bundle a database implementation.
|
|
4
|
+
|
|
5
|
+
**Documentation: [puzzle-ui.com/docs/storage](https://puzzle-ui.com/docs/storage)**
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @puzzle-ui/storage
|
|
11
|
+
# or: bun add @puzzle-ui/storage
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Add a database adapter when you need an implementation: [`@puzzle-ui/storage-d1`](https://puzzle-ui.com/docs/storage#cloudflare-d1-adapter) for Cloudflare D1 or [`@puzzle-ui/storage-mongodb`](https://puzzle-ui.com/docs/storage#mongodb-adapter) for MongoDB.
|
|
15
|
+
|
|
16
|
+
## Use
|
|
17
|
+
|
|
18
|
+
Define record types by extending `StorageRecord`, then use an adapter supplied by your application:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import type { StorageAdapter, StorageRecord } from "@puzzle-ui/storage";
|
|
22
|
+
|
|
23
|
+
type Session = StorageRecord & { userId: string; expiresAt: Date };
|
|
24
|
+
|
|
25
|
+
const storage: StorageAdapter = getConfiguredStorage();
|
|
26
|
+
await storage.registerCollection<Session>({
|
|
27
|
+
name: "sessions",
|
|
28
|
+
indexes: [{ name: "by-user", fields: ["userId"] }],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const session = await storage.insert<Session>("sessions", {
|
|
32
|
+
id: "session_123",
|
|
33
|
+
userId: "user_123",
|
|
34
|
+
expiresAt: new Date(Date.now() + 60 * 60 * 1000),
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`getConfiguredStorage` is your own adapter factory. Records have an ID, creation/update timestamps, and a revision. The query AST supports comparisons, nested boolean conditions, sorting, offset/limit, and cursor pagination. Conditional writes use expected revisions or update times; adapters advertise guarantees through capability reports. Migrations are explicit and versioned.
|
|
39
|
+
|
|
40
|
+
## Entry points
|
|
41
|
+
|
|
42
|
+
- `@puzzle-ui/storage`: storage types, adapter and migration contracts, query validators, and storage errors.
|
|
43
|
+
- `@puzzle-ui/storage/testing`: Bun conformance suites for adapter and migration implementations.
|
|
44
|
+
|
|
45
|
+
The conformance kit checks core CRUD/query semantics, serialization, revisions, constraints, pagination, batch atomicity, capabilities, and migration behavior. Adapters should run it against isolated test databases before release. `bun run typecheck` also checks the conformance sources with Bun test types.
|
|
46
|
+
|
|
47
|
+
`StorageSchema` provides an optional type-guard shape, but the shipped D1 and MongoDB adapters do not invoke it automatically; validate untrusted records at your application boundary.
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
bun run build
|
|
53
|
+
bun run typecheck
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
See the [storage guide](https://puzzle-ui.com/docs/storage) for contract details, migrations, adapter setup, and custom-adapter guidance.
|
package/dist/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sjoerd van Den Berg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/errors/storage.errors.ts
|
|
2
|
+
var StorageError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
cause;
|
|
5
|
+
details;
|
|
6
|
+
constructor(code, message, options) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "StorageError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.cause = options?.cause;
|
|
11
|
+
this.details = options?.details;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var StorageCapabilityError = class extends StorageError {
|
|
15
|
+
capability;
|
|
16
|
+
constructor(capability, message = `Storage adapter does not support required capability: ${capability}`) {
|
|
17
|
+
super("CAPABILITY", message, { details: { capability } });
|
|
18
|
+
this.name = "StorageCapabilityError";
|
|
19
|
+
this.capability = capability;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var StorageConflictError = class extends StorageError {
|
|
23
|
+
constructor(message = "The storage operation conflicted with the current record state.") {
|
|
24
|
+
super("CONFLICT", message);
|
|
25
|
+
this.name = "StorageConflictError";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var StorageDuplicateError = class extends StorageError {
|
|
29
|
+
constraint;
|
|
30
|
+
constructor(constraint) {
|
|
31
|
+
super("DUPLICATE", `A record violates the unique constraint "${constraint}".`, { details: { constraint } });
|
|
32
|
+
this.name = "StorageDuplicateError";
|
|
33
|
+
this.constraint = constraint;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var StorageValidationError = class extends StorageError {
|
|
37
|
+
constructor(message, details) {
|
|
38
|
+
super("VALIDATION", message, { details });
|
|
39
|
+
this.name = "StorageValidationError";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var StorageAtomicityError = class extends StorageError {
|
|
43
|
+
constructor(message = "The adapter cannot guarantee atomic execution and rollback.") {
|
|
44
|
+
super("ATOMICITY", message);
|
|
45
|
+
this.name = "StorageAtomicityError";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
exports.StorageAtomicityError = StorageAtomicityError;
|
|
49
|
+
exports.StorageCapabilityError = StorageCapabilityError;
|
|
50
|
+
exports.StorageConflictError = StorageConflictError;
|
|
51
|
+
exports.StorageDuplicateError = StorageDuplicateError;
|
|
52
|
+
exports.StorageError = StorageError;
|
|
53
|
+
exports.StorageValidationError = StorageValidationError;
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=storage.errors.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.errors.cjs","names":[],"sources":["../../src/errors/storage.errors.ts"],"sourcesContent":["export type StorageErrorCode =\n | \"NOT_FOUND\"\n | \"CONFLICT\"\n | \"DUPLICATE\"\n | \"VALIDATION\"\n | \"CAPABILITY\"\n | \"ATOMICITY\"\n | \"MIGRATION\"\n | \"QUERY\"\n | \"UNKNOWN\";\n\nexport class StorageError extends Error {\n readonly code: StorageErrorCode;\n override readonly cause?: unknown;\n readonly details?: Readonly<Record<string, unknown>>;\n\n constructor(\n code: StorageErrorCode,\n message: string,\n options?: {\n readonly cause?: unknown;\n readonly details?: Readonly<Record<string, unknown>>;\n },\n ) {\n super(message);\n this.name = \"StorageError\";\n this.code = code;\n this.cause = options?.cause;\n this.details = options?.details;\n }\n}\n\nexport class StorageCapabilityError extends StorageError {\n readonly capability: string;\n\n constructor(\n capability: string,\n message = `Storage adapter does not support required capability: ${capability}`,\n ) {\n super(\"CAPABILITY\", message, {\n details: { capability },\n });\n\n this.name = \"StorageCapabilityError\";\n this.capability = capability;\n }\n}\n\nexport class StorageConflictError extends StorageError {\n constructor(\n message = \"The storage operation conflicted with the current record state.\",\n ) {\n super(\"CONFLICT\", message);\n this.name = \"StorageConflictError\";\n }\n}\n\nexport class StorageDuplicateError extends StorageError {\n readonly constraint: string;\n\n constructor(constraint: string) {\n super(\n \"DUPLICATE\",\n `A record violates the unique constraint \"${constraint}\".`,\n {\n details: { constraint },\n },\n );\n\n this.name = \"StorageDuplicateError\";\n this.constraint = constraint;\n }\n}\n\nexport class StorageValidationError extends StorageError {\n constructor(message: string, details?: Readonly<Record<string, unknown>>) {\n super(\"VALIDATION\", message, { details });\n this.name = \"StorageValidationError\";\n }\n}\n\nexport class StorageAtomicityError extends StorageError {\n constructor(\n message = \"The adapter cannot guarantee atomic execution and rollback.\",\n ) {\n super(\"ATOMICITY\", message);\n this.name = \"StorageAtomicityError\";\n }\n}\n"],"mappings":";AAWA,IAAa,eAAb,cAAkC,MAAM;CACtC;CACA;CACA;CAEA,YACE,MACA,SACA,SAIA;EACA,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,QAAQ,SAAS;EACtB,KAAK,UAAU,SAAS;CAC1B;AACF;AAEA,IAAa,yBAAb,cAA4C,aAAa;CACvD;CAEA,YACE,YACA,UAAU,yDAAyD,cACnE;EACA,MAAM,cAAc,SAAS,EAC3B,SAAS,EAAE,WAAW,EACxB,CAAC;EAED,KAAK,OAAO;EACZ,KAAK,aAAa;CACpB;AACF;AAEA,IAAa,uBAAb,cAA0C,aAAa;CACrD,YACE,UAAU,mEACV;EACA,MAAM,YAAY,OAAO;EACzB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,wBAAb,cAA2C,aAAa;CACtD;CAEA,YAAY,YAAoB;EAC9B,MACE,aACA,4CAA4C,WAAW,KACvD,EACE,SAAS,EAAE,WAAW,EACxB,CACF;EAEA,KAAK,OAAO;EACZ,KAAK,aAAa;CACpB;AACF;AAEA,IAAa,yBAAb,cAA4C,aAAa;CACvD,YAAY,SAAiB,SAA6C;EACxE,MAAM,cAAc,SAAS,EAAE,QAAQ,CAAC;EACxC,KAAK,OAAO;CACd;AACF;AAEA,IAAa,wBAAb,cAA2C,aAAa;CACtD,YACE,UAAU,+DACV;EACA,MAAM,aAAa,OAAO;EAC1B,KAAK,OAAO;CACd;AACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type StorageErrorCode = "NOT_FOUND" | "CONFLICT" | "DUPLICATE" | "VALIDATION" | "CAPABILITY" | "ATOMICITY" | "MIGRATION" | "QUERY" | "UNKNOWN";
|
|
2
|
+
export declare class StorageError extends Error {
|
|
3
|
+
readonly code: StorageErrorCode;
|
|
4
|
+
readonly cause?: unknown;
|
|
5
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
6
|
+
constructor(code: StorageErrorCode, message: string, options?: {
|
|
7
|
+
readonly cause?: unknown;
|
|
8
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export declare class StorageCapabilityError extends StorageError {
|
|
12
|
+
readonly capability: string;
|
|
13
|
+
constructor(capability: string, message?: string);
|
|
14
|
+
}
|
|
15
|
+
export declare class StorageConflictError extends StorageError {
|
|
16
|
+
constructor(message?: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class StorageDuplicateError extends StorageError {
|
|
19
|
+
readonly constraint: string;
|
|
20
|
+
constructor(constraint: string);
|
|
21
|
+
}
|
|
22
|
+
export declare class StorageValidationError extends StorageError {
|
|
23
|
+
constructor(message: string, details?: Readonly<Record<string, unknown>>);
|
|
24
|
+
}
|
|
25
|
+
export declare class StorageAtomicityError extends StorageError {
|
|
26
|
+
constructor(message?: string);
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type StorageErrorCode = "NOT_FOUND" | "CONFLICT" | "DUPLICATE" | "VALIDATION" | "CAPABILITY" | "ATOMICITY" | "MIGRATION" | "QUERY" | "UNKNOWN";
|
|
2
|
+
export declare class StorageError extends Error {
|
|
3
|
+
readonly code: StorageErrorCode;
|
|
4
|
+
readonly cause?: unknown;
|
|
5
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
6
|
+
constructor(code: StorageErrorCode, message: string, options?: {
|
|
7
|
+
readonly cause?: unknown;
|
|
8
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export declare class StorageCapabilityError extends StorageError {
|
|
12
|
+
readonly capability: string;
|
|
13
|
+
constructor(capability: string, message?: string);
|
|
14
|
+
}
|
|
15
|
+
export declare class StorageConflictError extends StorageError {
|
|
16
|
+
constructor(message?: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class StorageDuplicateError extends StorageError {
|
|
19
|
+
readonly constraint: string;
|
|
20
|
+
constructor(constraint: string);
|
|
21
|
+
}
|
|
22
|
+
export declare class StorageValidationError extends StorageError {
|
|
23
|
+
constructor(message: string, details?: Readonly<Record<string, unknown>>);
|
|
24
|
+
}
|
|
25
|
+
export declare class StorageAtomicityError extends StorageError {
|
|
26
|
+
constructor(message?: string);
|
|
27
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/errors/storage.errors.ts
|
|
2
|
+
var StorageError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
cause;
|
|
5
|
+
details;
|
|
6
|
+
constructor(code, message, options) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "StorageError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.cause = options?.cause;
|
|
11
|
+
this.details = options?.details;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var StorageCapabilityError = class extends StorageError {
|
|
15
|
+
capability;
|
|
16
|
+
constructor(capability, message = `Storage adapter does not support required capability: ${capability}`) {
|
|
17
|
+
super("CAPABILITY", message, { details: { capability } });
|
|
18
|
+
this.name = "StorageCapabilityError";
|
|
19
|
+
this.capability = capability;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var StorageConflictError = class extends StorageError {
|
|
23
|
+
constructor(message = "The storage operation conflicted with the current record state.") {
|
|
24
|
+
super("CONFLICT", message);
|
|
25
|
+
this.name = "StorageConflictError";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var StorageDuplicateError = class extends StorageError {
|
|
29
|
+
constraint;
|
|
30
|
+
constructor(constraint) {
|
|
31
|
+
super("DUPLICATE", `A record violates the unique constraint "${constraint}".`, { details: { constraint } });
|
|
32
|
+
this.name = "StorageDuplicateError";
|
|
33
|
+
this.constraint = constraint;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var StorageValidationError = class extends StorageError {
|
|
37
|
+
constructor(message, details) {
|
|
38
|
+
super("VALIDATION", message, { details });
|
|
39
|
+
this.name = "StorageValidationError";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var StorageAtomicityError = class extends StorageError {
|
|
43
|
+
constructor(message = "The adapter cannot guarantee atomic execution and rollback.") {
|
|
44
|
+
super("ATOMICITY", message);
|
|
45
|
+
this.name = "StorageAtomicityError";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
export { StorageAtomicityError, StorageCapabilityError, StorageConflictError, StorageDuplicateError, StorageError, StorageValidationError };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=storage.errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.errors.js","names":[],"sources":["../../src/errors/storage.errors.ts"],"sourcesContent":["export type StorageErrorCode =\n | \"NOT_FOUND\"\n | \"CONFLICT\"\n | \"DUPLICATE\"\n | \"VALIDATION\"\n | \"CAPABILITY\"\n | \"ATOMICITY\"\n | \"MIGRATION\"\n | \"QUERY\"\n | \"UNKNOWN\";\n\nexport class StorageError extends Error {\n readonly code: StorageErrorCode;\n override readonly cause?: unknown;\n readonly details?: Readonly<Record<string, unknown>>;\n\n constructor(\n code: StorageErrorCode,\n message: string,\n options?: {\n readonly cause?: unknown;\n readonly details?: Readonly<Record<string, unknown>>;\n },\n ) {\n super(message);\n this.name = \"StorageError\";\n this.code = code;\n this.cause = options?.cause;\n this.details = options?.details;\n }\n}\n\nexport class StorageCapabilityError extends StorageError {\n readonly capability: string;\n\n constructor(\n capability: string,\n message = `Storage adapter does not support required capability: ${capability}`,\n ) {\n super(\"CAPABILITY\", message, {\n details: { capability },\n });\n\n this.name = \"StorageCapabilityError\";\n this.capability = capability;\n }\n}\n\nexport class StorageConflictError extends StorageError {\n constructor(\n message = \"The storage operation conflicted with the current record state.\",\n ) {\n super(\"CONFLICT\", message);\n this.name = \"StorageConflictError\";\n }\n}\n\nexport class StorageDuplicateError extends StorageError {\n readonly constraint: string;\n\n constructor(constraint: string) {\n super(\n \"DUPLICATE\",\n `A record violates the unique constraint \"${constraint}\".`,\n {\n details: { constraint },\n },\n );\n\n this.name = \"StorageDuplicateError\";\n this.constraint = constraint;\n }\n}\n\nexport class StorageValidationError extends StorageError {\n constructor(message: string, details?: Readonly<Record<string, unknown>>) {\n super(\"VALIDATION\", message, { details });\n this.name = \"StorageValidationError\";\n }\n}\n\nexport class StorageAtomicityError extends StorageError {\n constructor(\n message = \"The adapter cannot guarantee atomic execution and rollback.\",\n ) {\n super(\"ATOMICITY\", message);\n this.name = \"StorageAtomicityError\";\n }\n}\n"],"mappings":";AAWA,IAAa,eAAb,cAAkC,MAAM;CACtC;CACA;CACA;CAEA,YACE,MACA,SACA,SAIA;EACA,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,QAAQ,SAAS;EACtB,KAAK,UAAU,SAAS;CAC1B;AACF;AAEA,IAAa,yBAAb,cAA4C,aAAa;CACvD;CAEA,YACE,YACA,UAAU,yDAAyD,cACnE;EACA,MAAM,cAAc,SAAS,EAC3B,SAAS,EAAE,WAAW,EACxB,CAAC;EAED,KAAK,OAAO;EACZ,KAAK,aAAa;CACpB;AACF;AAEA,IAAa,uBAAb,cAA0C,aAAa;CACrD,YACE,UAAU,mEACV;EACA,MAAM,YAAY,OAAO;EACzB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,wBAAb,cAA2C,aAAa;CACtD;CAEA,YAAY,YAAoB;EAC9B,MACE,aACA,4CAA4C,WAAW,KACvD,EACE,SAAS,EAAE,WAAW,EACxB,CACF;EAEA,KAAK,OAAO;EACZ,KAAK,aAAa;CACpB;AACF;AAEA,IAAa,yBAAb,cAA4C,aAAa;CACvD,YAAY,SAAiB,SAA6C;EACxE,MAAM,cAAc,SAAS,EAAE,QAAQ,CAAC;EACxC,KAAK,OAAO;CACd;AACF;AAEA,IAAa,wBAAb,cAA2C,aAAa;CACtD,YACE,UAAU,+DACV;EACA,MAAM,aAAa,OAAO;EAC1B,KAAK,OAAO;CACd;AACF"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_capabilities_types = require("./types/capabilities.types.cjs");
|
|
3
|
+
const require_storage_errors = require("./errors/storage.errors.cjs");
|
|
4
|
+
const require_query_utils = require("./utils/query.utils.cjs");
|
|
5
|
+
exports.DEFAULT_STORAGE_CAPABILITIES = require_capabilities_types.DEFAULT_STORAGE_CAPABILITIES;
|
|
6
|
+
exports.StorageAtomicityError = require_storage_errors.StorageAtomicityError;
|
|
7
|
+
exports.StorageCapabilityError = require_storage_errors.StorageCapabilityError;
|
|
8
|
+
exports.StorageConflictError = require_storage_errors.StorageConflictError;
|
|
9
|
+
exports.StorageDuplicateError = require_storage_errors.StorageDuplicateError;
|
|
10
|
+
exports.StorageError = require_storage_errors.StorageError;
|
|
11
|
+
exports.StorageValidationError = require_storage_errors.StorageValidationError;
|
|
12
|
+
exports.validateQuery = require_query_utils.validateQuery;
|
|
13
|
+
exports.validateWhere = require_query_utils.validateWhere;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { StorageCollection, StorageCollectionName, StorageCollectionOptions, StorageField, StorageIndex, StorageRecord, StorageScalar, StorageSchema } from "./types/collection.types.cjs";
|
|
2
|
+
import { StorageComparison, StorageCursor, StorageCursorQuery, StorageOrder, StoragePage, StorageQuery, StorageWhere } from "./types/query.types.cjs";
|
|
3
|
+
import { DEFAULT_STORAGE_CAPABILITIES, StorageCapabilities, StorageCapability, StorageCapabilityReport } from "./types/capabilities.types.cjs";
|
|
4
|
+
import { StorageAdapter, StorageAdapterOptions, StorageBatchOperation, StorageBatchResult, StorageCondition, StorageInsert, StorageUpdate } from "./types/adapter.types.cjs";
|
|
5
|
+
import { StorageMigration, StorageMigrationAdapter, StorageMigrationContext, StorageMigrationOperation, StorageMigrationOptions, StorageMigrationRecord, StorageMigrationResult, StorageMigrationStatus } from "./types/migration.types.cjs";
|
|
6
|
+
import { StorageIndexDefinition, StorageSchemaAdapter, StorageSchemaRegistry } from "./types/schema.types.cjs";
|
|
7
|
+
import { StorageAtomicityError, StorageCapabilityError, StorageConflictError, StorageDuplicateError, StorageError, StorageValidationError } from "./errors/storage.errors.cjs";
|
|
8
|
+
import { validateQuery, validateWhere } from "./utils/query.utils.cjs";
|
|
9
|
+
export { DEFAULT_STORAGE_CAPABILITIES, type StorageAdapter, type StorageAdapterOptions, StorageAtomicityError, type StorageBatchOperation, type StorageBatchResult, type StorageCapabilities, type StorageCapability, StorageCapabilityError, type StorageCapabilityReport, type StorageCollection, type StorageCollectionName, type StorageCollectionOptions, type StorageComparison, type StorageCondition, StorageConflictError, type StorageCursor, type StorageCursorQuery, StorageDuplicateError, StorageError, type StorageField, type StorageIndex, type StorageIndexDefinition, type StorageInsert, type StorageMigration, type StorageMigrationAdapter, type StorageMigrationContext, type StorageMigrationOperation, type StorageMigrationOptions, type StorageMigrationRecord, type StorageMigrationResult, type StorageMigrationStatus, type StorageOrder, type StoragePage, type StorageQuery, type StorageRecord, type StorageScalar, type StorageSchema, type StorageSchemaAdapter, type StorageSchemaRegistry, type StorageUpdate, StorageValidationError, type StorageWhere, validateQuery, validateWhere };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { StorageCollection, StorageCollectionName, StorageCollectionOptions, StorageField, StorageIndex, StorageRecord, StorageScalar, StorageSchema } from "./types/collection.types.js";
|
|
2
|
+
import { StorageComparison, StorageCursor, StorageCursorQuery, StorageOrder, StoragePage, StorageQuery, StorageWhere } from "./types/query.types.js";
|
|
3
|
+
import { DEFAULT_STORAGE_CAPABILITIES, StorageCapabilities, StorageCapability, StorageCapabilityReport } from "./types/capabilities.types.js";
|
|
4
|
+
import { StorageAdapter, StorageAdapterOptions, StorageBatchOperation, StorageBatchResult, StorageCondition, StorageInsert, StorageUpdate } from "./types/adapter.types.js";
|
|
5
|
+
import { StorageMigration, StorageMigrationAdapter, StorageMigrationContext, StorageMigrationOperation, StorageMigrationOptions, StorageMigrationRecord, StorageMigrationResult, StorageMigrationStatus } from "./types/migration.types.js";
|
|
6
|
+
import { StorageIndexDefinition, StorageSchemaAdapter, StorageSchemaRegistry } from "./types/schema.types.js";
|
|
7
|
+
import { StorageAtomicityError, StorageCapabilityError, StorageConflictError, StorageDuplicateError, StorageError, StorageValidationError } from "./errors/storage.errors.js";
|
|
8
|
+
import { validateQuery, validateWhere } from "./utils/query.utils.js";
|
|
9
|
+
export { DEFAULT_STORAGE_CAPABILITIES, type StorageAdapter, type StorageAdapterOptions, StorageAtomicityError, type StorageBatchOperation, type StorageBatchResult, type StorageCapabilities, type StorageCapability, StorageCapabilityError, type StorageCapabilityReport, type StorageCollection, type StorageCollectionName, type StorageCollectionOptions, type StorageComparison, type StorageCondition, StorageConflictError, type StorageCursor, type StorageCursorQuery, StorageDuplicateError, StorageError, type StorageField, type StorageIndex, type StorageIndexDefinition, type StorageInsert, type StorageMigration, type StorageMigrationAdapter, type StorageMigrationContext, type StorageMigrationOperation, type StorageMigrationOptions, type StorageMigrationRecord, type StorageMigrationResult, type StorageMigrationStatus, type StorageOrder, type StoragePage, type StorageQuery, type StorageRecord, type StorageScalar, type StorageSchema, type StorageSchemaAdapter, type StorageSchemaRegistry, type StorageUpdate, StorageValidationError, type StorageWhere, validateQuery, validateWhere };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_STORAGE_CAPABILITIES } from "./types/capabilities.types.js";
|
|
2
|
+
import { StorageAtomicityError, StorageCapabilityError, StorageConflictError, StorageDuplicateError, StorageError, StorageValidationError } from "./errors/storage.errors.js";
|
|
3
|
+
import { validateQuery, validateWhere } from "./utils/query.utils.js";
|
|
4
|
+
export { DEFAULT_STORAGE_CAPABILITIES, StorageAtomicityError, StorageCapabilityError, StorageConflictError, StorageDuplicateError, StorageError, StorageValidationError, validateQuery, validateWhere };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { StorageCollection, StorageCollectionName, StorageRecord } from "./collection.types.cjs";
|
|
2
|
+
import { StorageCapabilities, StorageCapabilityReport } from "./capabilities.types.cjs";
|
|
3
|
+
import { StorageCursorQuery, StoragePage, StorageQuery } from "./query.types.cjs";
|
|
4
|
+
export type StorageInsert<T extends StorageRecord> = Omit<T, "createdAt" | "updatedAt" | "revision"> & {
|
|
5
|
+
readonly createdAt?: Date;
|
|
6
|
+
readonly updatedAt?: Date;
|
|
7
|
+
readonly revision?: number;
|
|
8
|
+
};
|
|
9
|
+
export type StorageUpdate<T extends StorageRecord> = Partial<Omit<T, "id" | "createdAt" | "revision">>;
|
|
10
|
+
export type StorageCondition = {
|
|
11
|
+
readonly expectedRevision?: number;
|
|
12
|
+
readonly expectedUpdatedAt?: Date;
|
|
13
|
+
};
|
|
14
|
+
type StorageBatchOperationItem<T extends StorageRecord> = {
|
|
15
|
+
readonly type: "insert";
|
|
16
|
+
readonly collection: StorageCollectionName;
|
|
17
|
+
readonly record: StorageInsert<T>;
|
|
18
|
+
} | {
|
|
19
|
+
readonly type: "update";
|
|
20
|
+
readonly collection: StorageCollectionName;
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly condition?: StorageCondition;
|
|
23
|
+
readonly changes: StorageUpdate<T>;
|
|
24
|
+
} | {
|
|
25
|
+
readonly type: "delete";
|
|
26
|
+
readonly collection: StorageCollectionName;
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly condition?: StorageCondition;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Distributes over unions so an atomic batch can contain records
|
|
32
|
+
* from different collections with different record types.
|
|
33
|
+
*/
|
|
34
|
+
export type StorageBatchOperation<T extends StorageRecord> = T extends StorageRecord ? StorageBatchOperationItem<T> : never;
|
|
35
|
+
export type StorageBatchResult<T extends StorageRecord> = {
|
|
36
|
+
readonly results: readonly ({
|
|
37
|
+
readonly type: "insert";
|
|
38
|
+
readonly record: T;
|
|
39
|
+
} | {
|
|
40
|
+
readonly type: "update";
|
|
41
|
+
readonly record: T | null;
|
|
42
|
+
} | {
|
|
43
|
+
readonly type: "delete";
|
|
44
|
+
readonly deleted: boolean;
|
|
45
|
+
})[];
|
|
46
|
+
};
|
|
47
|
+
export type StorageAdapterOptions = {
|
|
48
|
+
readonly requireAtomicBatch?: boolean;
|
|
49
|
+
};
|
|
50
|
+
export interface StorageAdapter {
|
|
51
|
+
readonly capabilities: StorageCapabilities;
|
|
52
|
+
getCapabilities(): StorageCapabilityReport;
|
|
53
|
+
registerCollection<T extends StorageRecord>(collection: StorageCollection<T>): Promise<void>;
|
|
54
|
+
findById<T extends StorageRecord>(collection: StorageCollectionName, id: string): Promise<T | null>;
|
|
55
|
+
findOne<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>): Promise<T | null>;
|
|
56
|
+
findMany<T extends StorageRecord>(collection: StorageCollectionName, query?: StorageQuery<T>): Promise<readonly T[]>;
|
|
57
|
+
findPage<T extends StorageRecord>(collection: StorageCollectionName, query: StorageCursorQuery<T>): Promise<StoragePage<T>>;
|
|
58
|
+
insert<T extends StorageRecord>(collection: StorageCollectionName, record: StorageInsert<T>): Promise<T>;
|
|
59
|
+
upsert<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>, record: StorageInsert<T>): Promise<T>;
|
|
60
|
+
update<T extends StorageRecord>(collection: StorageCollectionName, id: string, changes: StorageUpdate<T>, condition?: StorageCondition): Promise<T | null>;
|
|
61
|
+
updateMany<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>, changes: StorageUpdate<T>): Promise<number>;
|
|
62
|
+
delete<T extends StorageRecord>(collection: StorageCollectionName, id: string, condition?: StorageCondition): Promise<boolean>;
|
|
63
|
+
deleteMany<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>): Promise<number>;
|
|
64
|
+
exists<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>): Promise<boolean>;
|
|
65
|
+
count<T extends StorageRecord>(collection: StorageCollectionName, query?: StorageQuery<T>): Promise<number>;
|
|
66
|
+
atomicBatch<T extends StorageRecord>(operations: readonly StorageBatchOperation<T>[]): Promise<StorageBatchResult<T>>;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { StorageCollection, StorageCollectionName, StorageRecord } from "./collection.types.js";
|
|
2
|
+
import { StorageCapabilities, StorageCapabilityReport } from "./capabilities.types.js";
|
|
3
|
+
import { StorageCursorQuery, StoragePage, StorageQuery } from "./query.types.js";
|
|
4
|
+
export type StorageInsert<T extends StorageRecord> = Omit<T, "createdAt" | "updatedAt" | "revision"> & {
|
|
5
|
+
readonly createdAt?: Date;
|
|
6
|
+
readonly updatedAt?: Date;
|
|
7
|
+
readonly revision?: number;
|
|
8
|
+
};
|
|
9
|
+
export type StorageUpdate<T extends StorageRecord> = Partial<Omit<T, "id" | "createdAt" | "revision">>;
|
|
10
|
+
export type StorageCondition = {
|
|
11
|
+
readonly expectedRevision?: number;
|
|
12
|
+
readonly expectedUpdatedAt?: Date;
|
|
13
|
+
};
|
|
14
|
+
type StorageBatchOperationItem<T extends StorageRecord> = {
|
|
15
|
+
readonly type: "insert";
|
|
16
|
+
readonly collection: StorageCollectionName;
|
|
17
|
+
readonly record: StorageInsert<T>;
|
|
18
|
+
} | {
|
|
19
|
+
readonly type: "update";
|
|
20
|
+
readonly collection: StorageCollectionName;
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly condition?: StorageCondition;
|
|
23
|
+
readonly changes: StorageUpdate<T>;
|
|
24
|
+
} | {
|
|
25
|
+
readonly type: "delete";
|
|
26
|
+
readonly collection: StorageCollectionName;
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly condition?: StorageCondition;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Distributes over unions so an atomic batch can contain records
|
|
32
|
+
* from different collections with different record types.
|
|
33
|
+
*/
|
|
34
|
+
export type StorageBatchOperation<T extends StorageRecord> = T extends StorageRecord ? StorageBatchOperationItem<T> : never;
|
|
35
|
+
export type StorageBatchResult<T extends StorageRecord> = {
|
|
36
|
+
readonly results: readonly ({
|
|
37
|
+
readonly type: "insert";
|
|
38
|
+
readonly record: T;
|
|
39
|
+
} | {
|
|
40
|
+
readonly type: "update";
|
|
41
|
+
readonly record: T | null;
|
|
42
|
+
} | {
|
|
43
|
+
readonly type: "delete";
|
|
44
|
+
readonly deleted: boolean;
|
|
45
|
+
})[];
|
|
46
|
+
};
|
|
47
|
+
export type StorageAdapterOptions = {
|
|
48
|
+
readonly requireAtomicBatch?: boolean;
|
|
49
|
+
};
|
|
50
|
+
export interface StorageAdapter {
|
|
51
|
+
readonly capabilities: StorageCapabilities;
|
|
52
|
+
getCapabilities(): StorageCapabilityReport;
|
|
53
|
+
registerCollection<T extends StorageRecord>(collection: StorageCollection<T>): Promise<void>;
|
|
54
|
+
findById<T extends StorageRecord>(collection: StorageCollectionName, id: string): Promise<T | null>;
|
|
55
|
+
findOne<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>): Promise<T | null>;
|
|
56
|
+
findMany<T extends StorageRecord>(collection: StorageCollectionName, query?: StorageQuery<T>): Promise<readonly T[]>;
|
|
57
|
+
findPage<T extends StorageRecord>(collection: StorageCollectionName, query: StorageCursorQuery<T>): Promise<StoragePage<T>>;
|
|
58
|
+
insert<T extends StorageRecord>(collection: StorageCollectionName, record: StorageInsert<T>): Promise<T>;
|
|
59
|
+
upsert<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>, record: StorageInsert<T>): Promise<T>;
|
|
60
|
+
update<T extends StorageRecord>(collection: StorageCollectionName, id: string, changes: StorageUpdate<T>, condition?: StorageCondition): Promise<T | null>;
|
|
61
|
+
updateMany<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>, changes: StorageUpdate<T>): Promise<number>;
|
|
62
|
+
delete<T extends StorageRecord>(collection: StorageCollectionName, id: string, condition?: StorageCondition): Promise<boolean>;
|
|
63
|
+
deleteMany<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>): Promise<number>;
|
|
64
|
+
exists<T extends StorageRecord>(collection: StorageCollectionName, query: StorageQuery<T>): Promise<boolean>;
|
|
65
|
+
count<T extends StorageRecord>(collection: StorageCollectionName, query?: StorageQuery<T>): Promise<number>;
|
|
66
|
+
atomicBatch<T extends StorageRecord>(operations: readonly StorageBatchOperation<T>[]): Promise<StorageBatchResult<T>>;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/types/capabilities.types.ts
|
|
2
|
+
const DEFAULT_STORAGE_CAPABILITIES = {
|
|
3
|
+
transactions: false,
|
|
4
|
+
atomicBatch: false,
|
|
5
|
+
conditionalWrites: false,
|
|
6
|
+
uniqueConstraints: false,
|
|
7
|
+
upsert: false,
|
|
8
|
+
cursorPagination: false,
|
|
9
|
+
migrations: false,
|
|
10
|
+
ttlIndexes: false
|
|
11
|
+
};
|
|
12
|
+
exports.DEFAULT_STORAGE_CAPABILITIES = DEFAULT_STORAGE_CAPABILITIES;
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=capabilities.types.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.types.cjs","names":[],"sources":["../../src/types/capabilities.types.ts"],"sourcesContent":["export type StorageCapabilities = {\n readonly transactions: boolean;\n readonly atomicBatch: boolean;\n readonly conditionalWrites: boolean;\n readonly uniqueConstraints: boolean;\n readonly upsert: boolean;\n readonly cursorPagination: boolean;\n readonly migrations: boolean;\n readonly ttlIndexes: boolean;\n};\n\nexport type StorageCapability = keyof StorageCapabilities;\n\nexport type StorageCapabilityReport = {\n readonly capabilities: StorageCapabilities;\n readonly limitations?: readonly string[];\n};\n\nexport const DEFAULT_STORAGE_CAPABILITIES: StorageCapabilities = {\n transactions: false,\n atomicBatch: false,\n conditionalWrites: false,\n uniqueConstraints: false,\n upsert: false,\n cursorPagination: false,\n migrations: false,\n ttlIndexes: false,\n};\n"],"mappings":";AAkBA,MAAa,+BAAoD;CAC/D,cAAc;CACd,aAAa;CACb,mBAAmB;CACnB,mBAAmB;CACnB,QAAQ;CACR,kBAAkB;CAClB,YAAY;CACZ,YAAY;AACd"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type StorageCapabilities = {
|
|
2
|
+
readonly transactions: boolean;
|
|
3
|
+
readonly atomicBatch: boolean;
|
|
4
|
+
readonly conditionalWrites: boolean;
|
|
5
|
+
readonly uniqueConstraints: boolean;
|
|
6
|
+
readonly upsert: boolean;
|
|
7
|
+
readonly cursorPagination: boolean;
|
|
8
|
+
readonly migrations: boolean;
|
|
9
|
+
readonly ttlIndexes: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type StorageCapability = keyof StorageCapabilities;
|
|
12
|
+
export type StorageCapabilityReport = {
|
|
13
|
+
readonly capabilities: StorageCapabilities;
|
|
14
|
+
readonly limitations?: readonly string[];
|
|
15
|
+
};
|
|
16
|
+
export declare const DEFAULT_STORAGE_CAPABILITIES: StorageCapabilities;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type StorageCapabilities = {
|
|
2
|
+
readonly transactions: boolean;
|
|
3
|
+
readonly atomicBatch: boolean;
|
|
4
|
+
readonly conditionalWrites: boolean;
|
|
5
|
+
readonly uniqueConstraints: boolean;
|
|
6
|
+
readonly upsert: boolean;
|
|
7
|
+
readonly cursorPagination: boolean;
|
|
8
|
+
readonly migrations: boolean;
|
|
9
|
+
readonly ttlIndexes: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type StorageCapability = keyof StorageCapabilities;
|
|
12
|
+
export type StorageCapabilityReport = {
|
|
13
|
+
readonly capabilities: StorageCapabilities;
|
|
14
|
+
readonly limitations?: readonly string[];
|
|
15
|
+
};
|
|
16
|
+
export declare const DEFAULT_STORAGE_CAPABILITIES: StorageCapabilities;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/types/capabilities.types.ts
|
|
2
|
+
const DEFAULT_STORAGE_CAPABILITIES = {
|
|
3
|
+
transactions: false,
|
|
4
|
+
atomicBatch: false,
|
|
5
|
+
conditionalWrites: false,
|
|
6
|
+
uniqueConstraints: false,
|
|
7
|
+
upsert: false,
|
|
8
|
+
cursorPagination: false,
|
|
9
|
+
migrations: false,
|
|
10
|
+
ttlIndexes: false
|
|
11
|
+
};
|
|
12
|
+
export { DEFAULT_STORAGE_CAPABILITIES };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=capabilities.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.types.js","names":[],"sources":["../../src/types/capabilities.types.ts"],"sourcesContent":["export type StorageCapabilities = {\n readonly transactions: boolean;\n readonly atomicBatch: boolean;\n readonly conditionalWrites: boolean;\n readonly uniqueConstraints: boolean;\n readonly upsert: boolean;\n readonly cursorPagination: boolean;\n readonly migrations: boolean;\n readonly ttlIndexes: boolean;\n};\n\nexport type StorageCapability = keyof StorageCapabilities;\n\nexport type StorageCapabilityReport = {\n readonly capabilities: StorageCapabilities;\n readonly limitations?: readonly string[];\n};\n\nexport const DEFAULT_STORAGE_CAPABILITIES: StorageCapabilities = {\n transactions: false,\n atomicBatch: false,\n conditionalWrites: false,\n uniqueConstraints: false,\n upsert: false,\n cursorPagination: false,\n migrations: false,\n ttlIndexes: false,\n};\n"],"mappings":";AAkBA,MAAa,+BAAoD;CAC/D,cAAc;CACd,aAAa;CACb,mBAAmB;CACnB,mBAAmB;CACnB,QAAQ;CACR,kBAAkB;CAClB,YAAY;CACZ,YAAY;AACd"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type StorageScalar = string | number | boolean | Date | null;
|
|
2
|
+
export type StorageRecord = {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly createdAt: Date;
|
|
5
|
+
readonly updatedAt: Date;
|
|
6
|
+
readonly revokedAt?: Date | null;
|
|
7
|
+
readonly revision: number;
|
|
8
|
+
};
|
|
9
|
+
export type StorageCollectionName = string;
|
|
10
|
+
export type StorageField<T> = { [K in keyof T]-?: T[K] extends StorageScalar ? K : never; }[keyof T] & string;
|
|
11
|
+
export type StorageCollectionOptions<T extends StorageRecord> = {
|
|
12
|
+
readonly name: StorageCollectionName;
|
|
13
|
+
readonly schema?: StorageSchema<T>;
|
|
14
|
+
readonly indexes?: readonly StorageIndex<T>[];
|
|
15
|
+
};
|
|
16
|
+
export type StorageCollection<T extends StorageRecord> = StorageCollectionOptions<T>;
|
|
17
|
+
export type StorageSchema<T extends StorageRecord> = {
|
|
18
|
+
readonly validate: (record: unknown) => record is T;
|
|
19
|
+
};
|
|
20
|
+
export type StorageIndex<T extends StorageRecord> = {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly fields: readonly StorageField<T>[];
|
|
23
|
+
readonly unique?: boolean;
|
|
24
|
+
readonly expireAfterSeconds?: number;
|
|
25
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type StorageScalar = string | number | boolean | Date | null;
|
|
2
|
+
export type StorageRecord = {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly createdAt: Date;
|
|
5
|
+
readonly updatedAt: Date;
|
|
6
|
+
readonly revokedAt?: Date | null;
|
|
7
|
+
readonly revision: number;
|
|
8
|
+
};
|
|
9
|
+
export type StorageCollectionName = string;
|
|
10
|
+
export type StorageField<T> = { [K in keyof T]-?: T[K] extends StorageScalar ? K : never; }[keyof T] & string;
|
|
11
|
+
export type StorageCollectionOptions<T extends StorageRecord> = {
|
|
12
|
+
readonly name: StorageCollectionName;
|
|
13
|
+
readonly schema?: StorageSchema<T>;
|
|
14
|
+
readonly indexes?: readonly StorageIndex<T>[];
|
|
15
|
+
};
|
|
16
|
+
export type StorageCollection<T extends StorageRecord> = StorageCollectionOptions<T>;
|
|
17
|
+
export type StorageSchema<T extends StorageRecord> = {
|
|
18
|
+
readonly validate: (record: unknown) => record is T;
|
|
19
|
+
};
|
|
20
|
+
export type StorageIndex<T extends StorageRecord> = {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly fields: readonly StorageField<T>[];
|
|
23
|
+
readonly unique?: boolean;
|
|
24
|
+
readonly expireAfterSeconds?: number;
|
|
25
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type StorageMigrationContext = {
|
|
2
|
+
readonly execute: (operation: StorageMigrationOperation) => Promise<void>;
|
|
3
|
+
};
|
|
4
|
+
export type StorageMigrationOperation = {
|
|
5
|
+
readonly type: "createCollection";
|
|
6
|
+
readonly collection: string;
|
|
7
|
+
} | {
|
|
8
|
+
readonly type: "dropCollection";
|
|
9
|
+
readonly collection: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly type: "createIndex";
|
|
12
|
+
readonly collection: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly fields: readonly string[];
|
|
15
|
+
readonly unique?: boolean;
|
|
16
|
+
readonly expireAfterSeconds?: number;
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: "dropIndex";
|
|
19
|
+
readonly collection: string;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
};
|
|
22
|
+
export type StorageMigration = {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly version: number;
|
|
25
|
+
readonly description: string;
|
|
26
|
+
readonly up: (context: StorageMigrationContext) => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
export type StorageMigrationStatus = "pending" | "running" | "completed" | "failed";
|
|
29
|
+
export type StorageMigrationRecord = {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly version: number;
|
|
32
|
+
readonly description: string;
|
|
33
|
+
readonly status: StorageMigrationStatus;
|
|
34
|
+
readonly startedAt?: Date;
|
|
35
|
+
readonly completedAt?: Date;
|
|
36
|
+
readonly error?: string;
|
|
37
|
+
};
|
|
38
|
+
export type StorageMigrationResult = {
|
|
39
|
+
readonly applied: readonly number[];
|
|
40
|
+
readonly skipped: readonly number[];
|
|
41
|
+
readonly currentVersion: number;
|
|
42
|
+
};
|
|
43
|
+
export type StorageMigrationOptions = {
|
|
44
|
+
readonly targetVersion?: number;
|
|
45
|
+
readonly dryRun?: boolean;
|
|
46
|
+
};
|
|
47
|
+
export interface StorageMigrationAdapter {
|
|
48
|
+
getMigrationStatus(): Promise<{
|
|
49
|
+
readonly currentVersion: number;
|
|
50
|
+
readonly migrations: readonly StorageMigrationRecord[];
|
|
51
|
+
}>;
|
|
52
|
+
runMigrations(migrations: readonly StorageMigration[], options?: StorageMigrationOptions): Promise<StorageMigrationResult>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type StorageMigrationContext = {
|
|
2
|
+
readonly execute: (operation: StorageMigrationOperation) => Promise<void>;
|
|
3
|
+
};
|
|
4
|
+
export type StorageMigrationOperation = {
|
|
5
|
+
readonly type: "createCollection";
|
|
6
|
+
readonly collection: string;
|
|
7
|
+
} | {
|
|
8
|
+
readonly type: "dropCollection";
|
|
9
|
+
readonly collection: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly type: "createIndex";
|
|
12
|
+
readonly collection: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly fields: readonly string[];
|
|
15
|
+
readonly unique?: boolean;
|
|
16
|
+
readonly expireAfterSeconds?: number;
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: "dropIndex";
|
|
19
|
+
readonly collection: string;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
};
|
|
22
|
+
export type StorageMigration = {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly version: number;
|
|
25
|
+
readonly description: string;
|
|
26
|
+
readonly up: (context: StorageMigrationContext) => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
export type StorageMigrationStatus = "pending" | "running" | "completed" | "failed";
|
|
29
|
+
export type StorageMigrationRecord = {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly version: number;
|
|
32
|
+
readonly description: string;
|
|
33
|
+
readonly status: StorageMigrationStatus;
|
|
34
|
+
readonly startedAt?: Date;
|
|
35
|
+
readonly completedAt?: Date;
|
|
36
|
+
readonly error?: string;
|
|
37
|
+
};
|
|
38
|
+
export type StorageMigrationResult = {
|
|
39
|
+
readonly applied: readonly number[];
|
|
40
|
+
readonly skipped: readonly number[];
|
|
41
|
+
readonly currentVersion: number;
|
|
42
|
+
};
|
|
43
|
+
export type StorageMigrationOptions = {
|
|
44
|
+
readonly targetVersion?: number;
|
|
45
|
+
readonly dryRun?: boolean;
|
|
46
|
+
};
|
|
47
|
+
export interface StorageMigrationAdapter {
|
|
48
|
+
getMigrationStatus(): Promise<{
|
|
49
|
+
readonly currentVersion: number;
|
|
50
|
+
readonly migrations: readonly StorageMigrationRecord[];
|
|
51
|
+
}>;
|
|
52
|
+
runMigrations(migrations: readonly StorageMigration[], options?: StorageMigrationOptions): Promise<StorageMigrationResult>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { StorageRecord, StorageScalar } from "./collection.types.cjs";
|
|
2
|
+
export type StorageComparison<T = StorageScalar> = {
|
|
3
|
+
readonly operator: "eq";
|
|
4
|
+
readonly value: T;
|
|
5
|
+
} | {
|
|
6
|
+
readonly operator: "ne";
|
|
7
|
+
readonly value: T;
|
|
8
|
+
} | {
|
|
9
|
+
readonly operator: "in";
|
|
10
|
+
readonly value: readonly T[];
|
|
11
|
+
} | {
|
|
12
|
+
readonly operator: "notIn";
|
|
13
|
+
readonly value: readonly T[];
|
|
14
|
+
} | {
|
|
15
|
+
readonly operator: "gt";
|
|
16
|
+
readonly value: T;
|
|
17
|
+
} | {
|
|
18
|
+
readonly operator: "gte";
|
|
19
|
+
readonly value: T;
|
|
20
|
+
} | {
|
|
21
|
+
readonly operator: "lt";
|
|
22
|
+
readonly value: T;
|
|
23
|
+
} | {
|
|
24
|
+
readonly operator: "lte";
|
|
25
|
+
readonly value: T;
|
|
26
|
+
} | {
|
|
27
|
+
readonly operator: "exists";
|
|
28
|
+
readonly value: boolean;
|
|
29
|
+
};
|
|
30
|
+
export type StorageWhere<T extends object> = { readonly [K in keyof T]?: T[K] extends StorageScalar ? StorageComparison<T[K]> : StorageComparison<StorageScalar>; } & {
|
|
31
|
+
readonly and?: readonly StorageWhere<T>[];
|
|
32
|
+
readonly or?: readonly StorageWhere<T>[];
|
|
33
|
+
};
|
|
34
|
+
export type StorageOrder<T extends object> = {
|
|
35
|
+
readonly field: keyof T & string;
|
|
36
|
+
readonly direction: "asc" | "desc";
|
|
37
|
+
};
|
|
38
|
+
export type StorageQuery<T extends StorageRecord> = {
|
|
39
|
+
readonly where?: StorageWhere<T>;
|
|
40
|
+
readonly orderBy?: readonly StorageOrder<T>[];
|
|
41
|
+
readonly limit?: number;
|
|
42
|
+
readonly offset?: number;
|
|
43
|
+
};
|
|
44
|
+
export type StorageCursor = {
|
|
45
|
+
readonly value: string;
|
|
46
|
+
};
|
|
47
|
+
export type StorageCursorQuery<T extends StorageRecord> = Omit<StorageQuery<T>, "offset"> & {
|
|
48
|
+
readonly first: number;
|
|
49
|
+
readonly after?: StorageCursor;
|
|
50
|
+
};
|
|
51
|
+
export type StoragePage<T extends StorageRecord> = {
|
|
52
|
+
readonly items: readonly T[];
|
|
53
|
+
readonly nextCursor: StorageCursor | null;
|
|
54
|
+
readonly hasNextPage: boolean;
|
|
55
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { StorageRecord, StorageScalar } from "./collection.types.js";
|
|
2
|
+
export type StorageComparison<T = StorageScalar> = {
|
|
3
|
+
readonly operator: "eq";
|
|
4
|
+
readonly value: T;
|
|
5
|
+
} | {
|
|
6
|
+
readonly operator: "ne";
|
|
7
|
+
readonly value: T;
|
|
8
|
+
} | {
|
|
9
|
+
readonly operator: "in";
|
|
10
|
+
readonly value: readonly T[];
|
|
11
|
+
} | {
|
|
12
|
+
readonly operator: "notIn";
|
|
13
|
+
readonly value: readonly T[];
|
|
14
|
+
} | {
|
|
15
|
+
readonly operator: "gt";
|
|
16
|
+
readonly value: T;
|
|
17
|
+
} | {
|
|
18
|
+
readonly operator: "gte";
|
|
19
|
+
readonly value: T;
|
|
20
|
+
} | {
|
|
21
|
+
readonly operator: "lt";
|
|
22
|
+
readonly value: T;
|
|
23
|
+
} | {
|
|
24
|
+
readonly operator: "lte";
|
|
25
|
+
readonly value: T;
|
|
26
|
+
} | {
|
|
27
|
+
readonly operator: "exists";
|
|
28
|
+
readonly value: boolean;
|
|
29
|
+
};
|
|
30
|
+
export type StorageWhere<T extends object> = { readonly [K in keyof T]?: T[K] extends StorageScalar ? StorageComparison<T[K]> : StorageComparison<StorageScalar>; } & {
|
|
31
|
+
readonly and?: readonly StorageWhere<T>[];
|
|
32
|
+
readonly or?: readonly StorageWhere<T>[];
|
|
33
|
+
};
|
|
34
|
+
export type StorageOrder<T extends object> = {
|
|
35
|
+
readonly field: keyof T & string;
|
|
36
|
+
readonly direction: "asc" | "desc";
|
|
37
|
+
};
|
|
38
|
+
export type StorageQuery<T extends StorageRecord> = {
|
|
39
|
+
readonly where?: StorageWhere<T>;
|
|
40
|
+
readonly orderBy?: readonly StorageOrder<T>[];
|
|
41
|
+
readonly limit?: number;
|
|
42
|
+
readonly offset?: number;
|
|
43
|
+
};
|
|
44
|
+
export type StorageCursor = {
|
|
45
|
+
readonly value: string;
|
|
46
|
+
};
|
|
47
|
+
export type StorageCursorQuery<T extends StorageRecord> = Omit<StorageQuery<T>, "offset"> & {
|
|
48
|
+
readonly first: number;
|
|
49
|
+
readonly after?: StorageCursor;
|
|
50
|
+
};
|
|
51
|
+
export type StoragePage<T extends StorageRecord> = {
|
|
52
|
+
readonly items: readonly T[];
|
|
53
|
+
readonly nextCursor: StorageCursor | null;
|
|
54
|
+
readonly hasNextPage: boolean;
|
|
55
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { StorageCollection, StorageRecord } from "./collection.types.cjs";
|
|
2
|
+
export type StorageSchemaRegistry = {
|
|
3
|
+
readonly collections: readonly StorageCollection<StorageRecord>[];
|
|
4
|
+
};
|
|
5
|
+
export type StorageIndexDefinition = {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly collection: string;
|
|
8
|
+
readonly fields: readonly string[];
|
|
9
|
+
readonly unique?: boolean;
|
|
10
|
+
readonly expireAfterSeconds?: number;
|
|
11
|
+
};
|
|
12
|
+
export type StorageSchemaAdapter = {
|
|
13
|
+
ensureCollection<T extends StorageRecord>(collection: StorageCollection<T>): Promise<void>;
|
|
14
|
+
ensureIndex(index: StorageIndexDefinition): Promise<void>;
|
|
15
|
+
listIndexes(collection: string): Promise<readonly StorageIndexDefinition[]>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { StorageCollection, StorageRecord } from "./collection.types.js";
|
|
2
|
+
export type StorageSchemaRegistry = {
|
|
3
|
+
readonly collections: readonly StorageCollection<StorageRecord>[];
|
|
4
|
+
};
|
|
5
|
+
export type StorageIndexDefinition = {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly collection: string;
|
|
8
|
+
readonly fields: readonly string[];
|
|
9
|
+
readonly unique?: boolean;
|
|
10
|
+
readonly expireAfterSeconds?: number;
|
|
11
|
+
};
|
|
12
|
+
export type StorageSchemaAdapter = {
|
|
13
|
+
ensureCollection<T extends StorageRecord>(collection: StorageCollection<T>): Promise<void>;
|
|
14
|
+
ensureIndex(index: StorageIndexDefinition): Promise<void>;
|
|
15
|
+
listIndexes(collection: string): Promise<readonly StorageIndexDefinition[]>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const require_storage_errors = require("../errors/storage.errors.cjs");
|
|
2
|
+
// src/utils/query.utils.ts
|
|
3
|
+
const isScalar = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date;
|
|
4
|
+
const isComparison = (value) => {
|
|
5
|
+
if (!value || typeof value !== "object") return false;
|
|
6
|
+
const comparison = value;
|
|
7
|
+
if (![
|
|
8
|
+
"eq",
|
|
9
|
+
"ne",
|
|
10
|
+
"in",
|
|
11
|
+
"notIn",
|
|
12
|
+
"gt",
|
|
13
|
+
"gte",
|
|
14
|
+
"lt",
|
|
15
|
+
"lte",
|
|
16
|
+
"exists"
|
|
17
|
+
].includes(String(comparison.operator))) return false;
|
|
18
|
+
if (comparison.operator === "in" || comparison.operator === "notIn") return Array.isArray(comparison.value) && comparison.value.every(isScalar);
|
|
19
|
+
if (comparison.operator === "exists") return typeof comparison.value === "boolean";
|
|
20
|
+
return isScalar(comparison.value);
|
|
21
|
+
};
|
|
22
|
+
const validateWhere = (where) => {
|
|
23
|
+
for (const [field, value] of Object.entries(where)) {
|
|
24
|
+
if (field === "and" || field === "or") {
|
|
25
|
+
if (!Array.isArray(value)) throw new require_storage_errors.StorageValidationError(`Query operator "${field}" must be an array.`);
|
|
26
|
+
for (const nested of value) validateWhere(nested);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!isComparison(value)) throw new require_storage_errors.StorageValidationError(`Invalid comparison for field "${field}".`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const validateQuery = (query) => {
|
|
33
|
+
if (query.limit !== void 0) {
|
|
34
|
+
if (!Number.isInteger(query.limit) || query.limit < 0) throw new require_storage_errors.StorageValidationError("Query limit must be a non-negative integer.");
|
|
35
|
+
}
|
|
36
|
+
if (query.offset !== void 0) {
|
|
37
|
+
if (!Number.isInteger(query.offset) || query.offset < 0) throw new require_storage_errors.StorageValidationError("Query offset must be a non-negative integer.");
|
|
38
|
+
}
|
|
39
|
+
if (query.where) validateWhere(query.where);
|
|
40
|
+
};
|
|
41
|
+
exports.validateQuery = validateQuery;
|
|
42
|
+
exports.validateWhere = validateWhere;
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=query.utils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.utils.cjs","names":["StorageValidationError"],"sources":["../../src/utils/query.utils.ts"],"sourcesContent":["import type { StorageRecord, StorageScalar } from \"../types/collection.types\";\nimport type {\n StorageComparison,\n StorageQuery,\n StorageWhere,\n} from \"../types/query.types\";\nimport { StorageValidationError } from \"../errors/storage.errors\";\n\nconst isScalar = (value: unknown): value is StorageScalar =>\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\" ||\n value instanceof Date;\n\nconst isComparison = (value: unknown): value is StorageComparison => {\n if (!value || typeof value !== \"object\") return false;\n\n const comparison = value as Record<string, unknown>;\n const operators = [\n \"eq\",\n \"ne\",\n \"in\",\n \"notIn\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"exists\",\n ];\n\n if (!operators.includes(String(comparison.operator))) return false;\n\n if (comparison.operator === \"in\" || comparison.operator === \"notIn\") {\n return Array.isArray(comparison.value) && comparison.value.every(isScalar);\n }\n\n if (comparison.operator === \"exists\") {\n return typeof comparison.value === \"boolean\";\n }\n\n return isScalar(comparison.value);\n};\n\nexport const validateWhere = <T extends object>(\n where: StorageWhere<T>,\n): void => {\n for (const [field, value] of Object.entries(where)) {\n if (field === \"and\" || field === \"or\") {\n if (!Array.isArray(value)) {\n throw new StorageValidationError(\n `Query operator \"${field}\" must be an array.`,\n );\n }\n\n for (const nested of value) {\n validateWhere(nested as StorageWhere<T>);\n }\n\n continue;\n }\n\n if (!isComparison(value)) {\n throw new StorageValidationError(\n `Invalid comparison for field \"${field}\".`,\n );\n }\n }\n};\n\nexport const validateQuery = <T extends StorageRecord>(\n query: StorageQuery<T>,\n): void => {\n if (query.limit !== undefined) {\n if (!Number.isInteger(query.limit) || query.limit < 0) {\n throw new StorageValidationError(\n \"Query limit must be a non-negative integer.\",\n );\n }\n }\n\n if (query.offset !== undefined) {\n if (!Number.isInteger(query.offset) || query.offset < 0) {\n throw new StorageValidationError(\n \"Query offset must be a non-negative integer.\",\n );\n }\n }\n\n if (query.where) validateWhere(query.where);\n};\n"],"mappings":";;AAQA,MAAM,YAAY,UAChB,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,aACjB,iBAAiB;AAEnB,MAAM,gBAAgB,UAA+C;CACnE,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAEhD,MAAM,aAAa;CAanB,IAAI,CAAC;EAXH;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAGW,CAAC,CAAC,SAAS,OAAO,WAAW,QAAQ,CAAC,GAAG,OAAO;CAE7D,IAAI,WAAW,aAAa,QAAQ,WAAW,aAAa,SAC1D,OAAO,MAAM,QAAQ,WAAW,KAAK,KAAK,WAAW,MAAM,MAAM,QAAQ;CAG3E,IAAI,WAAW,aAAa,UAC1B,OAAO,OAAO,WAAW,UAAU;CAGrC,OAAO,SAAS,WAAW,KAAK;AAClC;AAEA,MAAa,iBACX,UACS;CACT,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,KAAK,GAAG;EAClD,IAAI,UAAU,SAAS,UAAU,MAAM;GACrC,IAAI,CAAC,MAAM,QAAQ,KAAK,GACtB,MAAM,IAAIA,uBAAAA,uBACR,mBAAmB,MAAM,oBAC3B;GAGF,KAAK,MAAM,UAAU,OACnB,cAAc,MAAyB;GAGzC;EACF;EAEA,IAAI,CAAC,aAAa,KAAK,GACrB,MAAM,IAAIA,uBAAAA,uBACR,iCAAiC,MAAM,GACzC;CAEJ;AACF;AAEA,MAAa,iBACX,UACS;CACT,IAAI,MAAM,UAAU,KAAA,GACd;MAAA,CAAC,OAAO,UAAU,MAAM,KAAK,KAAK,MAAM,QAAQ,GAClD,MAAM,IAAIA,uBAAAA,uBACR,6CACF;CAAA;CAIJ,IAAI,MAAM,WAAW,KAAA,GACf;MAAA,CAAC,OAAO,UAAU,MAAM,MAAM,KAAK,MAAM,SAAS,GACpD,MAAM,IAAIA,uBAAAA,uBACR,8CACF;CAAA;CAIJ,IAAI,MAAM,OAAO,cAAc,MAAM,KAAK;AAC5C"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { StorageRecord } from "../types/collection.types.cjs";
|
|
2
|
+
import { StorageQuery, StorageWhere } from "../types/query.types.cjs";
|
|
3
|
+
export declare const validateWhere: <T extends object>(where: StorageWhere<T>) => void;
|
|
4
|
+
export declare const validateQuery: <T extends StorageRecord>(query: StorageQuery<T>) => void;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { StorageRecord } from "../types/collection.types.js";
|
|
2
|
+
import { StorageQuery, StorageWhere } from "../types/query.types.js";
|
|
3
|
+
export declare const validateWhere: <T extends object>(where: StorageWhere<T>) => void;
|
|
4
|
+
export declare const validateQuery: <T extends StorageRecord>(query: StorageQuery<T>) => void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { StorageValidationError } from "../errors/storage.errors.js";
|
|
2
|
+
// src/utils/query.utils.ts
|
|
3
|
+
const isScalar = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date;
|
|
4
|
+
const isComparison = (value) => {
|
|
5
|
+
if (!value || typeof value !== "object") return false;
|
|
6
|
+
const comparison = value;
|
|
7
|
+
if (![
|
|
8
|
+
"eq",
|
|
9
|
+
"ne",
|
|
10
|
+
"in",
|
|
11
|
+
"notIn",
|
|
12
|
+
"gt",
|
|
13
|
+
"gte",
|
|
14
|
+
"lt",
|
|
15
|
+
"lte",
|
|
16
|
+
"exists"
|
|
17
|
+
].includes(String(comparison.operator))) return false;
|
|
18
|
+
if (comparison.operator === "in" || comparison.operator === "notIn") return Array.isArray(comparison.value) && comparison.value.every(isScalar);
|
|
19
|
+
if (comparison.operator === "exists") return typeof comparison.value === "boolean";
|
|
20
|
+
return isScalar(comparison.value);
|
|
21
|
+
};
|
|
22
|
+
const validateWhere = (where) => {
|
|
23
|
+
for (const [field, value] of Object.entries(where)) {
|
|
24
|
+
if (field === "and" || field === "or") {
|
|
25
|
+
if (!Array.isArray(value)) throw new StorageValidationError(`Query operator "${field}" must be an array.`);
|
|
26
|
+
for (const nested of value) validateWhere(nested);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!isComparison(value)) throw new StorageValidationError(`Invalid comparison for field "${field}".`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const validateQuery = (query) => {
|
|
33
|
+
if (query.limit !== void 0) {
|
|
34
|
+
if (!Number.isInteger(query.limit) || query.limit < 0) throw new StorageValidationError("Query limit must be a non-negative integer.");
|
|
35
|
+
}
|
|
36
|
+
if (query.offset !== void 0) {
|
|
37
|
+
if (!Number.isInteger(query.offset) || query.offset < 0) throw new StorageValidationError("Query offset must be a non-negative integer.");
|
|
38
|
+
}
|
|
39
|
+
if (query.where) validateWhere(query.where);
|
|
40
|
+
};
|
|
41
|
+
export { validateQuery, validateWhere };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=query.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.utils.js","names":[],"sources":["../../src/utils/query.utils.ts"],"sourcesContent":["import type { StorageRecord, StorageScalar } from \"../types/collection.types\";\nimport type {\n StorageComparison,\n StorageQuery,\n StorageWhere,\n} from \"../types/query.types\";\nimport { StorageValidationError } from \"../errors/storage.errors\";\n\nconst isScalar = (value: unknown): value is StorageScalar =>\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\" ||\n value instanceof Date;\n\nconst isComparison = (value: unknown): value is StorageComparison => {\n if (!value || typeof value !== \"object\") return false;\n\n const comparison = value as Record<string, unknown>;\n const operators = [\n \"eq\",\n \"ne\",\n \"in\",\n \"notIn\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"exists\",\n ];\n\n if (!operators.includes(String(comparison.operator))) return false;\n\n if (comparison.operator === \"in\" || comparison.operator === \"notIn\") {\n return Array.isArray(comparison.value) && comparison.value.every(isScalar);\n }\n\n if (comparison.operator === \"exists\") {\n return typeof comparison.value === \"boolean\";\n }\n\n return isScalar(comparison.value);\n};\n\nexport const validateWhere = <T extends object>(\n where: StorageWhere<T>,\n): void => {\n for (const [field, value] of Object.entries(where)) {\n if (field === \"and\" || field === \"or\") {\n if (!Array.isArray(value)) {\n throw new StorageValidationError(\n `Query operator \"${field}\" must be an array.`,\n );\n }\n\n for (const nested of value) {\n validateWhere(nested as StorageWhere<T>);\n }\n\n continue;\n }\n\n if (!isComparison(value)) {\n throw new StorageValidationError(\n `Invalid comparison for field \"${field}\".`,\n );\n }\n }\n};\n\nexport const validateQuery = <T extends StorageRecord>(\n query: StorageQuery<T>,\n): void => {\n if (query.limit !== undefined) {\n if (!Number.isInteger(query.limit) || query.limit < 0) {\n throw new StorageValidationError(\n \"Query limit must be a non-negative integer.\",\n );\n }\n }\n\n if (query.offset !== undefined) {\n if (!Number.isInteger(query.offset) || query.offset < 0) {\n throw new StorageValidationError(\n \"Query offset must be a non-negative integer.\",\n );\n }\n }\n\n if (query.where) validateWhere(query.where);\n};\n"],"mappings":";;AAQA,MAAM,YAAY,UAChB,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,aACjB,iBAAiB;AAEnB,MAAM,gBAAgB,UAA+C;CACnE,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAEhD,MAAM,aAAa;CAanB,IAAI,CAAC;EAXH;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAGW,CAAC,CAAC,SAAS,OAAO,WAAW,QAAQ,CAAC,GAAG,OAAO;CAE7D,IAAI,WAAW,aAAa,QAAQ,WAAW,aAAa,SAC1D,OAAO,MAAM,QAAQ,WAAW,KAAK,KAAK,WAAW,MAAM,MAAM,QAAQ;CAG3E,IAAI,WAAW,aAAa,UAC1B,OAAO,OAAO,WAAW,UAAU;CAGrC,OAAO,SAAS,WAAW,KAAK;AAClC;AAEA,MAAa,iBACX,UACS;CACT,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,KAAK,GAAG;EAClD,IAAI,UAAU,SAAS,UAAU,MAAM;GACrC,IAAI,CAAC,MAAM,QAAQ,KAAK,GACtB,MAAM,IAAI,uBACR,mBAAmB,MAAM,oBAC3B;GAGF,KAAK,MAAM,UAAU,OACnB,cAAc,MAAyB;GAGzC;EACF;EAEA,IAAI,CAAC,aAAa,KAAK,GACrB,MAAM,IAAI,uBACR,iCAAiC,MAAM,GACzC;CAEJ;AACF;AAEA,MAAa,iBACX,UACS;CACT,IAAI,MAAM,UAAU,KAAA,GACd;MAAA,CAAC,OAAO,UAAU,MAAM,KAAK,KAAK,MAAM,QAAQ,GAClD,MAAM,IAAI,uBACR,6CACF;CAAA;CAIJ,IAAI,MAAM,WAAW,KAAA,GACf;MAAA,CAAC,OAAO,UAAU,MAAM,MAAM,KAAK,MAAM,SAAS,GACpD,MAAM,IAAI,uBACR,8CACF;CAAA;CAIJ,IAAI,MAAM,OAAO,cAAc,MAAM,KAAK;AAC5C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@puzzle-ui/storage",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "Vendor-neutral storage contracts for Puzzle Auth.",
|
|
5
|
+
"homepage": "https://puzzle-ui.com",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/senorprogramador/puzzle.git"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public",
|
|
13
|
+
"registry": "https://registry.npmjs.org/"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/index.d.cts",
|
|
27
|
+
"default": "./dist/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"./testing": {
|
|
31
|
+
"types": "./dist/testing/index.d.ts",
|
|
32
|
+
"import": "./dist/testing/index.js",
|
|
33
|
+
"require": "./dist/testing/index.cjs"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"sideEffects": false,
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsdown",
|
|
42
|
+
"typecheck": "tsc --noEmit -p .",
|
|
43
|
+
"vitest": "node --test",
|
|
44
|
+
"clean": "rm -rf dist",
|
|
45
|
+
"prepack": "tsdown"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/bun": "^1.4.4",
|
|
49
|
+
"bun": "^1.4.4",
|
|
50
|
+
"tsdown": "^0.23.1",
|
|
51
|
+
"typescript": "^7.0.2"
|
|
52
|
+
}
|
|
53
|
+
}
|