js-idb 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/README.md +93 -0
- package/dist/collection.d.ts +24 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +163 -0
- package/dist/collection.js.map +1 -0
- package/dist/database.d.ts +14 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +27 -0
- package/dist/database.js.map +1 -0
- package/dist/id.d.ts +2 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +7 -0
- package/dist/id.js.map +1 -0
- package/dist/index-store.d.ts +33 -0
- package/dist/index-store.d.ts.map +1 -0
- package/dist/index-store.js +177 -0
- package/dist/index-store.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/storage.d.ts +20 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +52 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +7 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +62 -0
- package/dist/validation.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# js-idb
|
|
2
|
+
|
|
3
|
+
A lightweight JSON database for TypeScript with schema validation and indexed search. Works in-memory or persisted to disk.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install js-idb
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createDB } from "js-idb";
|
|
15
|
+
|
|
16
|
+
const db = createDB({
|
|
17
|
+
collections: {
|
|
18
|
+
users: {
|
|
19
|
+
schema: {
|
|
20
|
+
name: { type: "string", index: true, indexSetting: { ignoreCase: true } },
|
|
21
|
+
age: { type: "number", index: true },
|
|
22
|
+
active: { type: "boolean" },
|
|
23
|
+
meta: { type: "object" },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Add
|
|
30
|
+
const doc = db.users.add({ name: "Josef", age: 30, active: true, meta: { role: "admin" } });
|
|
31
|
+
// doc._id is auto-generated
|
|
32
|
+
|
|
33
|
+
// Bulk insert
|
|
34
|
+
db.users.addMany([
|
|
35
|
+
{ name: "Karel", age: 25, active: true, meta: {} },
|
|
36
|
+
{ name: "Anna", age: 35, active: false, meta: {} },
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
// Get by ID
|
|
40
|
+
db.users.get(doc._id);
|
|
41
|
+
|
|
42
|
+
// Get all
|
|
43
|
+
db.users.all();
|
|
44
|
+
|
|
45
|
+
// Search (indexed fields only)
|
|
46
|
+
db.users.find({ name: "josef" }); // exact (case-insensitive)
|
|
47
|
+
db.users.find({ name: "jos%" }); // prefix
|
|
48
|
+
db.users.find({ name: "%sef" }); // suffix
|
|
49
|
+
db.users.find({ name: "%ose%" }); // contains
|
|
50
|
+
db.users.find({ age: ">20" }); // range: >, >=, <, <=
|
|
51
|
+
db.users.find({ name: "jos%", age: "<=30" }); // compound (intersection)
|
|
52
|
+
|
|
53
|
+
// Update
|
|
54
|
+
db.users.update(doc._id, { name: "Josef II" });
|
|
55
|
+
|
|
56
|
+
// Remove
|
|
57
|
+
db.users.remove(doc._id);
|
|
58
|
+
|
|
59
|
+
// Clear collection
|
|
60
|
+
db.users.clear();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## File persistence
|
|
64
|
+
|
|
65
|
+
Pass a `path` to persist data as JSON files on disk. Omit it for in-memory only.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const db = createDB({
|
|
69
|
+
path: "./data",
|
|
70
|
+
collections: {
|
|
71
|
+
users: {
|
|
72
|
+
schema: {
|
|
73
|
+
name: { type: "string", index: true },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Each collection is stored as `<name>.data.json` and `<name>.meta.json`. Indexes are rebuilt from data on load.
|
|
81
|
+
|
|
82
|
+
## Schema
|
|
83
|
+
|
|
84
|
+
Fields support four types: `string`, `number`, `boolean`, `object`.
|
|
85
|
+
|
|
86
|
+
- `index: true` enables search on the field via `find()` (not supported for `object`)
|
|
87
|
+
- `indexSetting: { ignoreCase: true }` for case-insensitive string indexes
|
|
88
|
+
- `object` fields must be flat (no nesting)
|
|
89
|
+
- All fields are required on `add`, partial updates are allowed on `update`
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Schema, Document, InferDocument, StorageAdapter } from './types.js';
|
|
2
|
+
export declare class Collection<S extends Schema> {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
private schema;
|
|
5
|
+
private data;
|
|
6
|
+
private indexes;
|
|
7
|
+
private storage;
|
|
8
|
+
constructor(name: string, schema: S, storage: StorageAdapter);
|
|
9
|
+
private load;
|
|
10
|
+
private save;
|
|
11
|
+
private toDocument;
|
|
12
|
+
add(record: InferDocument<S>): Document<S>;
|
|
13
|
+
/** Single save at the end instead of per-record */
|
|
14
|
+
addMany(records: InferDocument<S>[]): Document<S>[];
|
|
15
|
+
get(id: string): Document<S> | undefined;
|
|
16
|
+
all(): Document<S>[];
|
|
17
|
+
/** All queried fields must be indexed. Compound queries intersect results. */
|
|
18
|
+
find(query: Record<string, string>): Document<S>[];
|
|
19
|
+
update(id: string, partial: Partial<InferDocument<S>>): Document<S>;
|
|
20
|
+
remove(id: string): void;
|
|
21
|
+
clear(): void;
|
|
22
|
+
get count(): number;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKlF,qBAAa,UAAU,CAAC,CAAC,SAAS,MAAM;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,IAAI,CAAuC;IACnD,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,OAAO,CAAiB;gBAEpB,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc;IAgB5D,OAAO,CAAC,IAAI;IAeZ,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,UAAU;IAIlB,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAkB1C,mDAAmD;IACnD,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE;IAwBnD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS;IAMxC,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE;IAIpB,8EAA8E;IAC9E,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE;IAgClD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAwBnE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAiBxB,KAAK,IAAI,IAAI;IAQb,IAAI,KAAK,IAAI,MAAM,CAElB;CACF"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Index } from './index-store.js';
|
|
2
|
+
import { generateId } from './id.js';
|
|
3
|
+
import { validateRecord } from './validation.js';
|
|
4
|
+
export class Collection {
|
|
5
|
+
name;
|
|
6
|
+
schema;
|
|
7
|
+
data;
|
|
8
|
+
indexes;
|
|
9
|
+
storage;
|
|
10
|
+
constructor(name, schema, storage) {
|
|
11
|
+
this.name = name;
|
|
12
|
+
this.schema = schema;
|
|
13
|
+
this.storage = storage;
|
|
14
|
+
this.data = new Map();
|
|
15
|
+
this.indexes = new Map();
|
|
16
|
+
for (const [field, def] of Object.entries(schema)) {
|
|
17
|
+
if (def.index) {
|
|
18
|
+
this.indexes.set(field, new Index(field, def.type, def.indexSetting?.ignoreCase));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
this.load();
|
|
22
|
+
}
|
|
23
|
+
load() {
|
|
24
|
+
const rawData = this.storage.readData(this.name);
|
|
25
|
+
if (!rawData)
|
|
26
|
+
return;
|
|
27
|
+
for (const [id, record] of Object.entries(rawData)) {
|
|
28
|
+
this.data.set(id, record);
|
|
29
|
+
for (const [field, index] of this.indexes) {
|
|
30
|
+
const val = record[field];
|
|
31
|
+
if (val !== undefined) {
|
|
32
|
+
index.add(id, val);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
save() {
|
|
38
|
+
const obj = {};
|
|
39
|
+
for (const [id, record] of this.data) {
|
|
40
|
+
obj[id] = record;
|
|
41
|
+
}
|
|
42
|
+
this.storage.writeData(this.name, obj);
|
|
43
|
+
this.storage.writeMeta(this.name, { schema: this.schema });
|
|
44
|
+
}
|
|
45
|
+
toDocument(id, record) {
|
|
46
|
+
return { _id: id, ...record };
|
|
47
|
+
}
|
|
48
|
+
add(record) {
|
|
49
|
+
validateRecord(record, this.schema, false);
|
|
50
|
+
const id = generateId();
|
|
51
|
+
const raw = { ...record };
|
|
52
|
+
this.data.set(id, raw);
|
|
53
|
+
for (const [field, index] of this.indexes) {
|
|
54
|
+
const val = raw[field];
|
|
55
|
+
if (val !== undefined) {
|
|
56
|
+
index.add(id, val);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.save();
|
|
60
|
+
return this.toDocument(id, raw);
|
|
61
|
+
}
|
|
62
|
+
/** Single save at the end instead of per-record */
|
|
63
|
+
addMany(records) {
|
|
64
|
+
const docs = [];
|
|
65
|
+
for (const record of records) {
|
|
66
|
+
validateRecord(record, this.schema, false);
|
|
67
|
+
const id = generateId();
|
|
68
|
+
const raw = { ...record };
|
|
69
|
+
this.data.set(id, raw);
|
|
70
|
+
for (const [field, index] of this.indexes) {
|
|
71
|
+
const val = raw[field];
|
|
72
|
+
if (val !== undefined) {
|
|
73
|
+
index.add(id, val);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
docs.push(this.toDocument(id, raw));
|
|
77
|
+
}
|
|
78
|
+
this.save();
|
|
79
|
+
return docs;
|
|
80
|
+
}
|
|
81
|
+
get(id) {
|
|
82
|
+
const record = this.data.get(id);
|
|
83
|
+
if (!record)
|
|
84
|
+
return undefined;
|
|
85
|
+
return this.toDocument(id, record);
|
|
86
|
+
}
|
|
87
|
+
all() {
|
|
88
|
+
return [...this.data.entries()].map(([id, record]) => this.toDocument(id, record));
|
|
89
|
+
}
|
|
90
|
+
/** All queried fields must be indexed. Compound queries intersect results. */
|
|
91
|
+
find(query) {
|
|
92
|
+
const queryEntries = Object.entries(query);
|
|
93
|
+
if (queryEntries.length === 0) {
|
|
94
|
+
return this.all();
|
|
95
|
+
}
|
|
96
|
+
let resultIds = null;
|
|
97
|
+
for (const [field, queryStr] of queryEntries) {
|
|
98
|
+
const index = this.indexes.get(field);
|
|
99
|
+
if (!index) {
|
|
100
|
+
throw new Error(`Field "${field}" is not indexed. Add "index: true" to the schema to enable search.`);
|
|
101
|
+
}
|
|
102
|
+
const ids = new Set(index.find(queryStr));
|
|
103
|
+
if (resultIds === null) {
|
|
104
|
+
resultIds = ids;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
for (const id of resultIds) {
|
|
108
|
+
if (!ids.has(id)) {
|
|
109
|
+
resultIds.delete(id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (resultIds.size === 0)
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
return [...resultIds].map(id => this.get(id));
|
|
117
|
+
}
|
|
118
|
+
update(id, partial) {
|
|
119
|
+
const existing = this.data.get(id);
|
|
120
|
+
if (!existing) {
|
|
121
|
+
throw new Error(`Record "${id}" not found`);
|
|
122
|
+
}
|
|
123
|
+
validateRecord(partial, this.schema, true);
|
|
124
|
+
for (const [field, index] of this.indexes) {
|
|
125
|
+
const newVal = partial[field];
|
|
126
|
+
if (newVal === undefined)
|
|
127
|
+
continue;
|
|
128
|
+
const oldVal = existing[field];
|
|
129
|
+
if (oldVal !== undefined) {
|
|
130
|
+
index.remove(id, oldVal);
|
|
131
|
+
}
|
|
132
|
+
index.add(id, newVal);
|
|
133
|
+
}
|
|
134
|
+
Object.assign(existing, partial);
|
|
135
|
+
this.save();
|
|
136
|
+
return this.toDocument(id, existing);
|
|
137
|
+
}
|
|
138
|
+
remove(id) {
|
|
139
|
+
const existing = this.data.get(id);
|
|
140
|
+
if (!existing) {
|
|
141
|
+
throw new Error(`Record "${id}" not found`);
|
|
142
|
+
}
|
|
143
|
+
for (const [field, index] of this.indexes) {
|
|
144
|
+
const val = existing[field];
|
|
145
|
+
if (val !== undefined) {
|
|
146
|
+
index.remove(id, val);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
this.data.delete(id);
|
|
150
|
+
this.save();
|
|
151
|
+
}
|
|
152
|
+
clear() {
|
|
153
|
+
this.data.clear();
|
|
154
|
+
for (const index of this.indexes.values()) {
|
|
155
|
+
index.clear();
|
|
156
|
+
}
|
|
157
|
+
this.save();
|
|
158
|
+
}
|
|
159
|
+
get count() {
|
|
160
|
+
return this.data.size;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CollectionConfig } from './types.js';
|
|
2
|
+
import { Collection } from './collection.js';
|
|
3
|
+
type DBCollections<T extends Record<string, CollectionConfig>> = {
|
|
4
|
+
[K in keyof T]: Collection<T[K]['schema']>;
|
|
5
|
+
};
|
|
6
|
+
type DBInstance<T extends Record<string, CollectionConfig>> = DBCollections<T> & {
|
|
7
|
+
collection<K extends keyof T & string>(name: K): Collection<T[K]['schema']>;
|
|
8
|
+
};
|
|
9
|
+
export declare function createDB<T extends Record<string, CollectionConfig>>(options: {
|
|
10
|
+
path?: string;
|
|
11
|
+
collections: T;
|
|
12
|
+
}): DBInstance<T>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,gBAAgB,EAAkB,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,KAAK,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI;KAC9D,CAAC,IAAI,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;CAC3C,CAAC;AAEF,KAAK,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IAC/E,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;CAC7E,CAAC;AAEF,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACjE,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,CAAC,CAAA;CAAE,GACzC,UAAU,CAAC,CAAC,CAAC,CA4Bf"}
|
package/dist/database.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Collection } from './collection.js';
|
|
2
|
+
import { MemoryAdapter, FileAdapter } from './storage.js';
|
|
3
|
+
import { validateSchema } from './validation.js';
|
|
4
|
+
export function createDB(options) {
|
|
5
|
+
const storage = options.path
|
|
6
|
+
? new FileAdapter(options.path)
|
|
7
|
+
: new MemoryAdapter();
|
|
8
|
+
const collections = new Map();
|
|
9
|
+
for (const [name, config] of Object.entries(options.collections)) {
|
|
10
|
+
validateSchema(config.schema);
|
|
11
|
+
const col = new Collection(name, config.schema, storage);
|
|
12
|
+
collections.set(name, col);
|
|
13
|
+
}
|
|
14
|
+
const db = {};
|
|
15
|
+
for (const [name, col] of collections) {
|
|
16
|
+
db[name] = col;
|
|
17
|
+
}
|
|
18
|
+
db['collection'] = (name) => {
|
|
19
|
+
const col = collections.get(name);
|
|
20
|
+
if (!col) {
|
|
21
|
+
throw new Error(`Collection "${name}" does not exist`);
|
|
22
|
+
}
|
|
23
|
+
return col;
|
|
24
|
+
};
|
|
25
|
+
return db;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAUjD,MAAM,UAAU,QAAQ,CACtB,OAA0C;IAE1C,MAAM,OAAO,GAAmB,OAAO,CAAC,IAAI;QAC1C,CAAC,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;QAC/B,CAAC,CAAC,IAAI,aAAa,EAAE,CAAC;IAExB,MAAM,WAAW,GAAG,IAAI,GAAG,EAA8B,CAAC;IAE1D,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACjE,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACzD,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,EAAE,GAAG,EAA6B,CAAC;IAEzC,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,WAAW,EAAE,CAAC;QACtC,EAAE,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE;QAClC,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,kBAAkB,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,OAAO,EAAmB,CAAC;AAC7B,CAAC"}
|
package/dist/id.d.ts
ADDED
package/dist/id.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,IAAI,MAAM,CAOnC"}
|
package/dist/id.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function generateId() {
|
|
2
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
3
|
+
return crypto.randomUUID();
|
|
4
|
+
}
|
|
5
|
+
return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=id.js.map
|
package/dist/id.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU;IACxB,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAC7E,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CACrC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAC5C,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FieldType } from './types.js';
|
|
2
|
+
/** Sorted array + binary search index over a single field */
|
|
3
|
+
export declare class Index {
|
|
4
|
+
readonly field: string;
|
|
5
|
+
readonly fieldType: FieldType;
|
|
6
|
+
readonly ignoreCase: boolean;
|
|
7
|
+
private entries;
|
|
8
|
+
constructor(field: string, fieldType: FieldType, ignoreCase?: boolean);
|
|
9
|
+
private normalize;
|
|
10
|
+
/** Returns index of first entry with value >= target */
|
|
11
|
+
private lowerBound;
|
|
12
|
+
private compare;
|
|
13
|
+
add(id: string, value: string | number | boolean): void;
|
|
14
|
+
remove(id: string, value: string | number | boolean): void;
|
|
15
|
+
/**
|
|
16
|
+
* Query patterns:
|
|
17
|
+
* Strings: 'josef' (exact), 'josef%' (prefix), '%josef' (suffix), '%josef%' (contains)
|
|
18
|
+
* Numbers: '30' (exact), '>10', '>=20', '<50', '<=30'
|
|
19
|
+
* Booleans: 'true', 'false'
|
|
20
|
+
*/
|
|
21
|
+
find(query: string): string[];
|
|
22
|
+
private exactQuery;
|
|
23
|
+
/** O(log n + k) — binary search to prefix start, scan forward */
|
|
24
|
+
private prefixQuery;
|
|
25
|
+
/** O(n) — must scan all entries */
|
|
26
|
+
private suffixQuery;
|
|
27
|
+
/** O(n) — must scan all entries */
|
|
28
|
+
private containsQuery;
|
|
29
|
+
private rangeQuery;
|
|
30
|
+
clear(): void;
|
|
31
|
+
get size(): number;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=index-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-store.d.ts","sourceRoot":"","sources":["../src/index-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAO5C,6DAA6D;AAC7D,qBAAa,KAAK;IAChB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,OAAO,CAAoB;gBAEvB,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,UAAQ;IAMnE,OAAO,CAAC,SAAS;IAOjB,wDAAwD;IACxD,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,OAAO;IAMf,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI;IAOvD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI;IAa1D;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAyC7B,OAAO,CAAC,UAAU;IAWlB,iEAAiE;IACjE,OAAO,CAAC,WAAW;IAanB,mCAAmC;IACnC,OAAO,CAAC,WAAW;IAOnB,mCAAmC;IACnC,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,UAAU;IA4BlB,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/** Sorted array + binary search index over a single field */
|
|
2
|
+
export class Index {
|
|
3
|
+
field;
|
|
4
|
+
fieldType;
|
|
5
|
+
ignoreCase;
|
|
6
|
+
entries = [];
|
|
7
|
+
constructor(field, fieldType, ignoreCase = false) {
|
|
8
|
+
this.field = field;
|
|
9
|
+
this.fieldType = fieldType;
|
|
10
|
+
this.ignoreCase = ignoreCase;
|
|
11
|
+
}
|
|
12
|
+
normalize(value) {
|
|
13
|
+
if (this.ignoreCase && typeof value === 'string') {
|
|
14
|
+
return value.toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
/** Returns index of first entry with value >= target */
|
|
19
|
+
lowerBound(target) {
|
|
20
|
+
let lo = 0;
|
|
21
|
+
let hi = this.entries.length;
|
|
22
|
+
while (lo < hi) {
|
|
23
|
+
const mid = (lo + hi) >>> 1;
|
|
24
|
+
if (this.compare(this.entries[mid].value, target) < 0) {
|
|
25
|
+
lo = mid + 1;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
hi = mid;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return lo;
|
|
32
|
+
}
|
|
33
|
+
compare(a, b) {
|
|
34
|
+
if (a < b)
|
|
35
|
+
return -1;
|
|
36
|
+
if (a > b)
|
|
37
|
+
return 1;
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
add(id, value) {
|
|
41
|
+
const normalized = this.normalize(value);
|
|
42
|
+
const entry = { value: normalized, id };
|
|
43
|
+
const idx = this.lowerBound(normalized);
|
|
44
|
+
this.entries.splice(idx, 0, entry);
|
|
45
|
+
}
|
|
46
|
+
remove(id, value) {
|
|
47
|
+
const normalized = this.normalize(value);
|
|
48
|
+
const idx = this.lowerBound(normalized);
|
|
49
|
+
for (let i = idx; i < this.entries.length; i++) {
|
|
50
|
+
const entry = this.entries[i];
|
|
51
|
+
if (entry.value !== normalized)
|
|
52
|
+
break;
|
|
53
|
+
if (entry.id === id) {
|
|
54
|
+
this.entries.splice(i, 1);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Query patterns:
|
|
61
|
+
* Strings: 'josef' (exact), 'josef%' (prefix), '%josef' (suffix), '%josef%' (contains)
|
|
62
|
+
* Numbers: '30' (exact), '>10', '>=20', '<50', '<=30'
|
|
63
|
+
* Booleans: 'true', 'false'
|
|
64
|
+
*/
|
|
65
|
+
find(query) {
|
|
66
|
+
if (this.fieldType === 'number') {
|
|
67
|
+
const rangeMatch = query.match(/^(>=?|<=?)(.+)$/);
|
|
68
|
+
if (rangeMatch) {
|
|
69
|
+
const op = rangeMatch[1];
|
|
70
|
+
const num = Number(rangeMatch[2]);
|
|
71
|
+
if (Number.isNaN(num)) {
|
|
72
|
+
throw new Error(`Invalid numeric query: "${query}"`);
|
|
73
|
+
}
|
|
74
|
+
return this.rangeQuery(op, num);
|
|
75
|
+
}
|
|
76
|
+
const num = Number(query);
|
|
77
|
+
if (!Number.isNaN(num)) {
|
|
78
|
+
return this.exactQuery(num);
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`Invalid query for number field: "${query}"`);
|
|
81
|
+
}
|
|
82
|
+
if (this.fieldType === 'boolean') {
|
|
83
|
+
if (query === 'true')
|
|
84
|
+
return this.exactQuery(true);
|
|
85
|
+
if (query === 'false')
|
|
86
|
+
return this.exactQuery(false);
|
|
87
|
+
throw new Error(`Invalid query for boolean field: "${query}" (use "true" or "false")`);
|
|
88
|
+
}
|
|
89
|
+
const startsWithWild = query.startsWith('%');
|
|
90
|
+
const endsWithWild = query.endsWith('%');
|
|
91
|
+
if (startsWithWild && endsWithWild && query.length > 1) {
|
|
92
|
+
return this.containsQuery(query.slice(1, -1));
|
|
93
|
+
}
|
|
94
|
+
if (endsWithWild) {
|
|
95
|
+
return this.prefixQuery(query.slice(0, -1));
|
|
96
|
+
}
|
|
97
|
+
if (startsWithWild) {
|
|
98
|
+
return this.suffixQuery(query.slice(1));
|
|
99
|
+
}
|
|
100
|
+
const normalized = this.ignoreCase ? query.toLowerCase() : query;
|
|
101
|
+
return this.exactQuery(normalized);
|
|
102
|
+
}
|
|
103
|
+
exactQuery(target) {
|
|
104
|
+
const idx = this.lowerBound(target);
|
|
105
|
+
const results = [];
|
|
106
|
+
for (let i = idx; i < this.entries.length; i++) {
|
|
107
|
+
const entry = this.entries[i];
|
|
108
|
+
if (entry.value !== target)
|
|
109
|
+
break;
|
|
110
|
+
results.push(entry.id);
|
|
111
|
+
}
|
|
112
|
+
return results;
|
|
113
|
+
}
|
|
114
|
+
/** O(log n + k) — binary search to prefix start, scan forward */
|
|
115
|
+
prefixQuery(prefix) {
|
|
116
|
+
const normalized = this.ignoreCase ? prefix.toLowerCase() : prefix;
|
|
117
|
+
const idx = this.lowerBound(normalized);
|
|
118
|
+
const results = [];
|
|
119
|
+
for (let i = idx; i < this.entries.length; i++) {
|
|
120
|
+
const entry = this.entries[i];
|
|
121
|
+
const val = String(entry.value);
|
|
122
|
+
if (!val.startsWith(normalized))
|
|
123
|
+
break;
|
|
124
|
+
results.push(entry.id);
|
|
125
|
+
}
|
|
126
|
+
return results;
|
|
127
|
+
}
|
|
128
|
+
/** O(n) — must scan all entries */
|
|
129
|
+
suffixQuery(suffix) {
|
|
130
|
+
const normalized = this.ignoreCase ? suffix.toLowerCase() : suffix;
|
|
131
|
+
return this.entries
|
|
132
|
+
.filter(e => String(e.value).endsWith(normalized))
|
|
133
|
+
.map(e => e.id);
|
|
134
|
+
}
|
|
135
|
+
/** O(n) — must scan all entries */
|
|
136
|
+
containsQuery(sub) {
|
|
137
|
+
const normalized = this.ignoreCase ? sub.toLowerCase() : sub;
|
|
138
|
+
return this.entries
|
|
139
|
+
.filter(e => String(e.value).includes(normalized))
|
|
140
|
+
.map(e => e.id);
|
|
141
|
+
}
|
|
142
|
+
rangeQuery(op, target) {
|
|
143
|
+
const results = [];
|
|
144
|
+
if (op === '>' || op === '>=') {
|
|
145
|
+
const idx = this.lowerBound(target);
|
|
146
|
+
for (let i = idx; i < this.entries.length; i++) {
|
|
147
|
+
const entry = this.entries[i];
|
|
148
|
+
const val = entry.value;
|
|
149
|
+
if (op === '>' && val === target)
|
|
150
|
+
continue;
|
|
151
|
+
results.push(entry.id);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const idx = this.lowerBound(target);
|
|
156
|
+
for (let i = 0; i < idx; i++) {
|
|
157
|
+
results.push(this.entries[i].id);
|
|
158
|
+
}
|
|
159
|
+
if (op === '<=') {
|
|
160
|
+
for (let i = idx; i < this.entries.length; i++) {
|
|
161
|
+
const entry = this.entries[i];
|
|
162
|
+
if (entry.value !== target)
|
|
163
|
+
break;
|
|
164
|
+
results.push(entry.id);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return results;
|
|
169
|
+
}
|
|
170
|
+
clear() {
|
|
171
|
+
this.entries = [];
|
|
172
|
+
}
|
|
173
|
+
get size() {
|
|
174
|
+
return this.entries.length;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=index-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-store.js","sourceRoot":"","sources":["../src/index-store.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC1D,mBAAmB,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StorageAdapter, CollectionMeta } from './types.js';
|
|
2
|
+
export declare class MemoryAdapter implements StorageAdapter {
|
|
3
|
+
private dataStore;
|
|
4
|
+
private metaStore;
|
|
5
|
+
readData(collection: string): Record<string, Record<string, unknown>> | null;
|
|
6
|
+
writeData(collection: string, data: Record<string, Record<string, unknown>>): void;
|
|
7
|
+
readMeta(collection: string): CollectionMeta | null;
|
|
8
|
+
writeMeta(collection: string, meta: CollectionMeta): void;
|
|
9
|
+
}
|
|
10
|
+
export declare class FileAdapter implements StorageAdapter {
|
|
11
|
+
private basePath;
|
|
12
|
+
constructor(basePath: string);
|
|
13
|
+
private dataPath;
|
|
14
|
+
private metaPath;
|
|
15
|
+
readData(collection: string): Record<string, Record<string, unknown>> | null;
|
|
16
|
+
writeData(collection: string, data: Record<string, Record<string, unknown>>): void;
|
|
17
|
+
readMeta(collection: string): CollectionMeta | null;
|
|
18
|
+
writeMeta(collection: string, meta: CollectionMeta): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,qBAAa,aAAc,YAAW,cAAc;IAClD,OAAO,CAAC,SAAS,CAA8D;IAC/E,OAAO,CAAC,SAAS,CAAqC;IAEtD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAI5E,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAIlF,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAInD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI;CAG1D;AAED,qBAAa,WAAY,YAAW,cAAc;IAChD,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,EAAE,MAAM;IAO5B,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,QAAQ;IAIhB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAM5E,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAIlF,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAMnD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI;CAG1D"}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export class MemoryAdapter {
|
|
4
|
+
dataStore = new Map();
|
|
5
|
+
metaStore = new Map();
|
|
6
|
+
readData(collection) {
|
|
7
|
+
return this.dataStore.get(collection) ?? null;
|
|
8
|
+
}
|
|
9
|
+
writeData(collection, data) {
|
|
10
|
+
this.dataStore.set(collection, data);
|
|
11
|
+
}
|
|
12
|
+
readMeta(collection) {
|
|
13
|
+
return this.metaStore.get(collection) ?? null;
|
|
14
|
+
}
|
|
15
|
+
writeMeta(collection, meta) {
|
|
16
|
+
this.metaStore.set(collection, meta);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class FileAdapter {
|
|
20
|
+
basePath;
|
|
21
|
+
constructor(basePath) {
|
|
22
|
+
this.basePath = basePath;
|
|
23
|
+
if (!existsSync(basePath)) {
|
|
24
|
+
mkdirSync(basePath, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
dataPath(collection) {
|
|
28
|
+
return join(this.basePath, `${collection}.data.json`);
|
|
29
|
+
}
|
|
30
|
+
metaPath(collection) {
|
|
31
|
+
return join(this.basePath, `${collection}.meta.json`);
|
|
32
|
+
}
|
|
33
|
+
readData(collection) {
|
|
34
|
+
const p = this.dataPath(collection);
|
|
35
|
+
if (!existsSync(p))
|
|
36
|
+
return null;
|
|
37
|
+
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
38
|
+
}
|
|
39
|
+
writeData(collection, data) {
|
|
40
|
+
writeFileSync(this.dataPath(collection), JSON.stringify(data, null, 2));
|
|
41
|
+
}
|
|
42
|
+
readMeta(collection) {
|
|
43
|
+
const p = this.metaPath(collection);
|
|
44
|
+
if (!existsSync(p))
|
|
45
|
+
return null;
|
|
46
|
+
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
47
|
+
}
|
|
48
|
+
writeMeta(collection, meta) {
|
|
49
|
+
writeFileSync(this.metaPath(collection), JSON.stringify(meta, null, 2));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,OAAO,aAAa;IAChB,SAAS,GAAG,IAAI,GAAG,EAAmD,CAAC;IACvE,SAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEtD,QAAQ,CAAC,UAAkB;QACzB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;IAChD,CAAC;IAED,SAAS,CAAC,UAAkB,EAAE,IAA6C;QACzE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,QAAQ,CAAC,UAAkB;QACzB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;IAChD,CAAC;IAED,SAAS,CAAC,UAAkB,EAAE,IAAoB;QAChD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;CACF;AAED,MAAM,OAAO,WAAW;IACd,QAAQ,CAAS;IAEzB,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,UAAkB;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,UAAU,YAAY,CAAC,CAAC;IACxD,CAAC;IAEO,QAAQ,CAAC,UAAkB;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,UAAU,YAAY,CAAC,CAAC;IACxD,CAAC;IAED,QAAQ,CAAC,UAAkB;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAChC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAA4C,CAAC;IACzF,CAAC;IAED,SAAS,CAAC,UAAkB,EAAE,IAA6C;QACzE,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,QAAQ,CAAC,UAAkB;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAChC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAmB,CAAC;IAChE,CAAC;IAED,SAAS,CAAC,UAAkB,EAAE,IAAoB;QAChD,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC;CACF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type FieldType = 'string' | 'number' | 'boolean' | 'object';
|
|
2
|
+
export interface IndexSetting {
|
|
3
|
+
ignoreCase?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface FieldDefinition {
|
|
6
|
+
type: FieldType;
|
|
7
|
+
index?: boolean;
|
|
8
|
+
/** Only valid for string fields with index: true */
|
|
9
|
+
indexSetting?: IndexSetting;
|
|
10
|
+
}
|
|
11
|
+
export type Schema = Record<string, FieldDefinition>;
|
|
12
|
+
export type InferFieldType<T extends FieldType> = T extends 'string' ? string : T extends 'number' ? number : T extends 'boolean' ? boolean : T extends 'object' ? Record<string, unknown> : never;
|
|
13
|
+
export type InferDocument<S extends Schema> = {
|
|
14
|
+
[K in keyof S]: InferFieldType<S[K]['type']>;
|
|
15
|
+
};
|
|
16
|
+
export type Document<S extends Schema> = InferDocument<S> & {
|
|
17
|
+
_id: string;
|
|
18
|
+
};
|
|
19
|
+
export interface CollectionConfig {
|
|
20
|
+
schema: Schema;
|
|
21
|
+
}
|
|
22
|
+
export interface DatabaseOptions<T extends Record<string, CollectionConfig> = Record<string, CollectionConfig>> {
|
|
23
|
+
/** Omit for in-memory (browser). Provide for file persistence (Node.js). */
|
|
24
|
+
path?: string;
|
|
25
|
+
collections: T;
|
|
26
|
+
}
|
|
27
|
+
export interface StorageAdapter {
|
|
28
|
+
readData(collection: string): Record<string, Record<string, unknown>> | null;
|
|
29
|
+
writeData(collection: string, data: Record<string, Record<string, unknown>>): void;
|
|
30
|
+
readMeta(collection: string): CollectionMeta | null;
|
|
31
|
+
writeMeta(collection: string, meta: CollectionMeta): void;
|
|
32
|
+
}
|
|
33
|
+
/** Indexes are rebuilt from data on load, so meta only stores the schema */
|
|
34
|
+
export interface CollectionMeta {
|
|
35
|
+
schema: Schema;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oDAAoD;IACpD,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAErD,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,SAAS,IAC5C,CAAC,SAAS,QAAQ,GAAG,MAAM,GAC3B,CAAC,SAAS,QAAQ,GAAG,MAAM,GAC3B,CAAC,SAAS,SAAS,GAAG,OAAO,GAC7B,CAAC,SAAS,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5C,KAAK,CAAC;AAER,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,IAAI;KAC3C,CAAC,IAAI,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAC5G,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,CAAC,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IACnF,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAAC;IACpD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC3D;AAED,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Schema } from './types.js';
|
|
2
|
+
export declare function validateSchema(schema: Schema): void;
|
|
3
|
+
/**
|
|
4
|
+
* @param isPartial - true for update (missing fields allowed), false for add (all required)
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateRecord(record: Record<string, unknown>, schema: Schema, isPartial: boolean): void;
|
|
7
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAYnD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,GACjB,IAAI,CAgDN"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function validateSchema(schema) {
|
|
2
|
+
for (const [field, def] of Object.entries(schema)) {
|
|
3
|
+
if (def.index && def.type === 'object') {
|
|
4
|
+
throw new Error(`Field "${field}": cannot index 'object' type fields`);
|
|
5
|
+
}
|
|
6
|
+
if (def.indexSetting && !def.index) {
|
|
7
|
+
throw new Error(`Field "${field}": indexSetting requires index: true`);
|
|
8
|
+
}
|
|
9
|
+
if (def.indexSetting && def.type !== 'string') {
|
|
10
|
+
throw new Error(`Field "${field}": indexSetting is only valid for 'string' type fields`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @param isPartial - true for update (missing fields allowed), false for add (all required)
|
|
16
|
+
*/
|
|
17
|
+
export function validateRecord(record, schema, isPartial) {
|
|
18
|
+
const schemaKeys = new Set(Object.keys(schema));
|
|
19
|
+
for (const key of Object.keys(record)) {
|
|
20
|
+
if (!schemaKeys.has(key)) {
|
|
21
|
+
throw new Error(`Unknown field "${key}" is not defined in the schema`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
for (const [field, def] of Object.entries(schema)) {
|
|
25
|
+
const value = record[field];
|
|
26
|
+
if (value === undefined) {
|
|
27
|
+
if (!isPartial) {
|
|
28
|
+
throw new Error(`Missing required field "${field}"`);
|
|
29
|
+
}
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
switch (def.type) {
|
|
33
|
+
case 'string':
|
|
34
|
+
if (typeof value !== 'string') {
|
|
35
|
+
throw new Error(`Field "${field}" must be of type string, got ${typeof value}`);
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case 'number':
|
|
39
|
+
if (typeof value !== 'number' || Number.isNaN(value)) {
|
|
40
|
+
throw new Error(`Field "${field}" must be of type number`);
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
case 'boolean':
|
|
44
|
+
if (typeof value !== 'boolean') {
|
|
45
|
+
throw new Error(`Field "${field}" must be of type boolean, got ${typeof value}`);
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
case 'object':
|
|
49
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
50
|
+
throw new Error(`Field "${field}" must be a plain object`);
|
|
51
|
+
}
|
|
52
|
+
// Flat only — no nested objects allowed
|
|
53
|
+
for (const [k, v] of Object.entries(value)) {
|
|
54
|
+
if (typeof v === 'object' && v !== null) {
|
|
55
|
+
throw new Error(`Field "${field}.${k}" contains a nested object — only flat objects are allowed`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "js-idb",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Simple JSON database with fast search over data via indexes and type checking",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
15
|
+
"preversion": "npm test && npm run build",
|
|
16
|
+
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"database",
|
|
20
|
+
"json",
|
|
21
|
+
"index",
|
|
22
|
+
"typescript"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/jest": "^30.0.0",
|
|
29
|
+
"@types/node": "^24.10.13",
|
|
30
|
+
"conventional-changelog-cli": "^5.0.0",
|
|
31
|
+
"jest": "^30.2.0",
|
|
32
|
+
"ts-jest": "^29.4.6",
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
}
|
|
35
|
+
}
|