@contractspec/lib.context-storage 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 +42 -0
- package/dist/browser/in-memory-store.js +80 -0
- package/dist/browser/index.js +80 -0
- package/dist/browser/store.js +0 -0
- package/dist/browser/types.js +0 -0
- package/dist/in-memory-store.d.ts +18 -0
- package/dist/in-memory-store.js +81 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +81 -0
- package/dist/node/in-memory-store.js +80 -0
- package/dist/node/index.js +80 -0
- package/dist/node/store.js +0 -0
- package/dist/node/types.js +0 -0
- package/dist/store.d.ts +14 -0
- package/dist/store.js +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.js +1 -0
- package/package.json +109 -0
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @contractspec/lib.context-storage
|
|
2
|
+
|
|
3
|
+
Website: https://contractspec.io/
|
|
4
|
+
|
|
5
|
+
**Context pack and snapshot storage primitives.**
|
|
6
|
+
|
|
7
|
+
Provides the store interface, domain types, and an in-memory implementation for managing context packs and snapshots. Packs describe curated sets of sources (docblocks, contracts, schemas); snapshots capture point-in-time materializations of those packs.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun add @contractspec/lib.context-storage
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Exports
|
|
16
|
+
|
|
17
|
+
- `.` -- Re-exports types, store interface, and in-memory implementation
|
|
18
|
+
- `./store` -- `ContextSnapshotStore` interface (upsertPack, createSnapshot, listPacks, etc.)
|
|
19
|
+
- `./in-memory-store` -- `InMemoryContextSnapshotStore` class
|
|
20
|
+
- `./types` -- Domain types: `ContextPackRecord`, `ContextSnapshotRecord`, `ContextSnapshotItem`, queries, and list results
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { InMemoryContextSnapshotStore } from "@contractspec/lib.context-storage/in-memory-store";
|
|
26
|
+
import type { ContextPackRecord } from "@contractspec/lib.context-storage/types";
|
|
27
|
+
|
|
28
|
+
const store = new InMemoryContextSnapshotStore();
|
|
29
|
+
|
|
30
|
+
const pack: ContextPackRecord = {
|
|
31
|
+
packKey: "onboarding",
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
title: "Onboarding Context Pack",
|
|
34
|
+
tags: ["getting-started"],
|
|
35
|
+
createdAt: new Date().toISOString(),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
await store.upsertPack(pack);
|
|
39
|
+
|
|
40
|
+
const result = await store.listPacks({ tag: "getting-started" });
|
|
41
|
+
console.log(result.packs);
|
|
42
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/in-memory-store.ts
|
|
2
|
+
class InMemoryContextSnapshotStore {
|
|
3
|
+
packs = new Map;
|
|
4
|
+
snapshots = new Map;
|
|
5
|
+
items = new Map;
|
|
6
|
+
async upsertPack(record) {
|
|
7
|
+
const key = `${record.packKey}.v${record.version}`;
|
|
8
|
+
const updatedAt = record.updatedAt ?? new Date().toISOString();
|
|
9
|
+
const next = { ...record, updatedAt };
|
|
10
|
+
this.packs.set(key, next);
|
|
11
|
+
return next;
|
|
12
|
+
}
|
|
13
|
+
async getPack(packKey, version) {
|
|
14
|
+
if (version)
|
|
15
|
+
return this.packs.get(`${packKey}.v${version}`) ?? null;
|
|
16
|
+
const matches = [...this.packs.values()].filter((pack) => pack.packKey === packKey);
|
|
17
|
+
if (matches.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
return matches.sort((a, b) => b.version.localeCompare(a.version))[0] ?? null;
|
|
20
|
+
}
|
|
21
|
+
async listPacks(query = {}) {
|
|
22
|
+
const { query: q, tag, owner, limit = 50, offset = 0 } = query;
|
|
23
|
+
let results = [...this.packs.values()];
|
|
24
|
+
if (q) {
|
|
25
|
+
const needle = q.toLowerCase();
|
|
26
|
+
results = results.filter((pack) => pack.packKey.toLowerCase().includes(needle) || pack.title.toLowerCase().includes(needle));
|
|
27
|
+
}
|
|
28
|
+
if (tag) {
|
|
29
|
+
results = results.filter((pack) => pack.tags?.includes(tag));
|
|
30
|
+
}
|
|
31
|
+
if (owner) {
|
|
32
|
+
results = results.filter((pack) => pack.owners?.includes(owner));
|
|
33
|
+
}
|
|
34
|
+
const slice = results.slice(offset, offset + limit);
|
|
35
|
+
return {
|
|
36
|
+
packs: slice,
|
|
37
|
+
total: results.length,
|
|
38
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async createSnapshot(record) {
|
|
42
|
+
this.snapshots.set(record.snapshotId, record);
|
|
43
|
+
return record;
|
|
44
|
+
}
|
|
45
|
+
async getSnapshot(snapshotId) {
|
|
46
|
+
return this.snapshots.get(snapshotId) ?? null;
|
|
47
|
+
}
|
|
48
|
+
async listSnapshots(query = {}) {
|
|
49
|
+
const { packKey, snapshotId, limit = 50, offset = 0 } = query;
|
|
50
|
+
let results = [...this.snapshots.values()];
|
|
51
|
+
if (packKey)
|
|
52
|
+
results = results.filter((snap) => snap.packKey === packKey);
|
|
53
|
+
if (snapshotId)
|
|
54
|
+
results = results.filter((snap) => snap.snapshotId === snapshotId);
|
|
55
|
+
const slice = results.slice(offset, offset + limit);
|
|
56
|
+
return {
|
|
57
|
+
snapshots: slice,
|
|
58
|
+
total: results.length,
|
|
59
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async addSnapshotItems(snapshotId, items) {
|
|
63
|
+
const created = items.map((item) => ({
|
|
64
|
+
...item,
|
|
65
|
+
snapshotId,
|
|
66
|
+
createdAt: item.createdAt ?? new Date().toISOString()
|
|
67
|
+
}));
|
|
68
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
69
|
+
this.items.set(snapshotId, [...current, ...created]);
|
|
70
|
+
return created;
|
|
71
|
+
}
|
|
72
|
+
async listSnapshotItems(snapshotId, options = {}) {
|
|
73
|
+
const { limit = 100, offset = 0 } = options;
|
|
74
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
75
|
+
return current.slice(offset, offset + limit);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
InMemoryContextSnapshotStore
|
|
80
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/in-memory-store.ts
|
|
2
|
+
class InMemoryContextSnapshotStore {
|
|
3
|
+
packs = new Map;
|
|
4
|
+
snapshots = new Map;
|
|
5
|
+
items = new Map;
|
|
6
|
+
async upsertPack(record) {
|
|
7
|
+
const key = `${record.packKey}.v${record.version}`;
|
|
8
|
+
const updatedAt = record.updatedAt ?? new Date().toISOString();
|
|
9
|
+
const next = { ...record, updatedAt };
|
|
10
|
+
this.packs.set(key, next);
|
|
11
|
+
return next;
|
|
12
|
+
}
|
|
13
|
+
async getPack(packKey, version) {
|
|
14
|
+
if (version)
|
|
15
|
+
return this.packs.get(`${packKey}.v${version}`) ?? null;
|
|
16
|
+
const matches = [...this.packs.values()].filter((pack) => pack.packKey === packKey);
|
|
17
|
+
if (matches.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
return matches.sort((a, b) => b.version.localeCompare(a.version))[0] ?? null;
|
|
20
|
+
}
|
|
21
|
+
async listPacks(query = {}) {
|
|
22
|
+
const { query: q, tag, owner, limit = 50, offset = 0 } = query;
|
|
23
|
+
let results = [...this.packs.values()];
|
|
24
|
+
if (q) {
|
|
25
|
+
const needle = q.toLowerCase();
|
|
26
|
+
results = results.filter((pack) => pack.packKey.toLowerCase().includes(needle) || pack.title.toLowerCase().includes(needle));
|
|
27
|
+
}
|
|
28
|
+
if (tag) {
|
|
29
|
+
results = results.filter((pack) => pack.tags?.includes(tag));
|
|
30
|
+
}
|
|
31
|
+
if (owner) {
|
|
32
|
+
results = results.filter((pack) => pack.owners?.includes(owner));
|
|
33
|
+
}
|
|
34
|
+
const slice = results.slice(offset, offset + limit);
|
|
35
|
+
return {
|
|
36
|
+
packs: slice,
|
|
37
|
+
total: results.length,
|
|
38
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async createSnapshot(record) {
|
|
42
|
+
this.snapshots.set(record.snapshotId, record);
|
|
43
|
+
return record;
|
|
44
|
+
}
|
|
45
|
+
async getSnapshot(snapshotId) {
|
|
46
|
+
return this.snapshots.get(snapshotId) ?? null;
|
|
47
|
+
}
|
|
48
|
+
async listSnapshots(query = {}) {
|
|
49
|
+
const { packKey, snapshotId, limit = 50, offset = 0 } = query;
|
|
50
|
+
let results = [...this.snapshots.values()];
|
|
51
|
+
if (packKey)
|
|
52
|
+
results = results.filter((snap) => snap.packKey === packKey);
|
|
53
|
+
if (snapshotId)
|
|
54
|
+
results = results.filter((snap) => snap.snapshotId === snapshotId);
|
|
55
|
+
const slice = results.slice(offset, offset + limit);
|
|
56
|
+
return {
|
|
57
|
+
snapshots: slice,
|
|
58
|
+
total: results.length,
|
|
59
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async addSnapshotItems(snapshotId, items) {
|
|
63
|
+
const created = items.map((item) => ({
|
|
64
|
+
...item,
|
|
65
|
+
snapshotId,
|
|
66
|
+
createdAt: item.createdAt ?? new Date().toISOString()
|
|
67
|
+
}));
|
|
68
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
69
|
+
this.items.set(snapshotId, [...current, ...created]);
|
|
70
|
+
return created;
|
|
71
|
+
}
|
|
72
|
+
async listSnapshotItems(snapshotId, options = {}) {
|
|
73
|
+
const { limit = 100, offset = 0 } = options;
|
|
74
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
75
|
+
return current.slice(offset, offset + limit);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
InMemoryContextSnapshotStore
|
|
80
|
+
};
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ContextPackListResult, ContextPackQuery, ContextPackRecord, ContextSnapshotItem, ContextSnapshotItemInput, ContextSnapshotListResult, ContextSnapshotQuery, ContextSnapshotRecord } from './types';
|
|
2
|
+
import type { ContextSnapshotStore } from './store';
|
|
3
|
+
export declare class InMemoryContextSnapshotStore implements ContextSnapshotStore {
|
|
4
|
+
private readonly packs;
|
|
5
|
+
private readonly snapshots;
|
|
6
|
+
private readonly items;
|
|
7
|
+
upsertPack(record: ContextPackRecord): Promise<ContextPackRecord>;
|
|
8
|
+
getPack(packKey: string, version?: string): Promise<ContextPackRecord | null>;
|
|
9
|
+
listPacks(query?: ContextPackQuery): Promise<ContextPackListResult>;
|
|
10
|
+
createSnapshot(record: ContextSnapshotRecord): Promise<ContextSnapshotRecord>;
|
|
11
|
+
getSnapshot(snapshotId: string): Promise<ContextSnapshotRecord | null>;
|
|
12
|
+
listSnapshots(query?: ContextSnapshotQuery): Promise<ContextSnapshotListResult>;
|
|
13
|
+
addSnapshotItems(snapshotId: string, items: ContextSnapshotItemInput[]): Promise<ContextSnapshotItem[]>;
|
|
14
|
+
listSnapshotItems(snapshotId: string, options?: {
|
|
15
|
+
limit?: number;
|
|
16
|
+
offset?: number;
|
|
17
|
+
}): Promise<ContextSnapshotItem[]>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/in-memory-store.ts
|
|
3
|
+
class InMemoryContextSnapshotStore {
|
|
4
|
+
packs = new Map;
|
|
5
|
+
snapshots = new Map;
|
|
6
|
+
items = new Map;
|
|
7
|
+
async upsertPack(record) {
|
|
8
|
+
const key = `${record.packKey}.v${record.version}`;
|
|
9
|
+
const updatedAt = record.updatedAt ?? new Date().toISOString();
|
|
10
|
+
const next = { ...record, updatedAt };
|
|
11
|
+
this.packs.set(key, next);
|
|
12
|
+
return next;
|
|
13
|
+
}
|
|
14
|
+
async getPack(packKey, version) {
|
|
15
|
+
if (version)
|
|
16
|
+
return this.packs.get(`${packKey}.v${version}`) ?? null;
|
|
17
|
+
const matches = [...this.packs.values()].filter((pack) => pack.packKey === packKey);
|
|
18
|
+
if (matches.length === 0)
|
|
19
|
+
return null;
|
|
20
|
+
return matches.sort((a, b) => b.version.localeCompare(a.version))[0] ?? null;
|
|
21
|
+
}
|
|
22
|
+
async listPacks(query = {}) {
|
|
23
|
+
const { query: q, tag, owner, limit = 50, offset = 0 } = query;
|
|
24
|
+
let results = [...this.packs.values()];
|
|
25
|
+
if (q) {
|
|
26
|
+
const needle = q.toLowerCase();
|
|
27
|
+
results = results.filter((pack) => pack.packKey.toLowerCase().includes(needle) || pack.title.toLowerCase().includes(needle));
|
|
28
|
+
}
|
|
29
|
+
if (tag) {
|
|
30
|
+
results = results.filter((pack) => pack.tags?.includes(tag));
|
|
31
|
+
}
|
|
32
|
+
if (owner) {
|
|
33
|
+
results = results.filter((pack) => pack.owners?.includes(owner));
|
|
34
|
+
}
|
|
35
|
+
const slice = results.slice(offset, offset + limit);
|
|
36
|
+
return {
|
|
37
|
+
packs: slice,
|
|
38
|
+
total: results.length,
|
|
39
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async createSnapshot(record) {
|
|
43
|
+
this.snapshots.set(record.snapshotId, record);
|
|
44
|
+
return record;
|
|
45
|
+
}
|
|
46
|
+
async getSnapshot(snapshotId) {
|
|
47
|
+
return this.snapshots.get(snapshotId) ?? null;
|
|
48
|
+
}
|
|
49
|
+
async listSnapshots(query = {}) {
|
|
50
|
+
const { packKey, snapshotId, limit = 50, offset = 0 } = query;
|
|
51
|
+
let results = [...this.snapshots.values()];
|
|
52
|
+
if (packKey)
|
|
53
|
+
results = results.filter((snap) => snap.packKey === packKey);
|
|
54
|
+
if (snapshotId)
|
|
55
|
+
results = results.filter((snap) => snap.snapshotId === snapshotId);
|
|
56
|
+
const slice = results.slice(offset, offset + limit);
|
|
57
|
+
return {
|
|
58
|
+
snapshots: slice,
|
|
59
|
+
total: results.length,
|
|
60
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async addSnapshotItems(snapshotId, items) {
|
|
64
|
+
const created = items.map((item) => ({
|
|
65
|
+
...item,
|
|
66
|
+
snapshotId,
|
|
67
|
+
createdAt: item.createdAt ?? new Date().toISOString()
|
|
68
|
+
}));
|
|
69
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
70
|
+
this.items.set(snapshotId, [...current, ...created]);
|
|
71
|
+
return created;
|
|
72
|
+
}
|
|
73
|
+
async listSnapshotItems(snapshotId, options = {}) {
|
|
74
|
+
const { limit = 100, offset = 0 } = options;
|
|
75
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
76
|
+
return current.slice(offset, offset + limit);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
InMemoryContextSnapshotStore
|
|
81
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/in-memory-store.ts
|
|
3
|
+
class InMemoryContextSnapshotStore {
|
|
4
|
+
packs = new Map;
|
|
5
|
+
snapshots = new Map;
|
|
6
|
+
items = new Map;
|
|
7
|
+
async upsertPack(record) {
|
|
8
|
+
const key = `${record.packKey}.v${record.version}`;
|
|
9
|
+
const updatedAt = record.updatedAt ?? new Date().toISOString();
|
|
10
|
+
const next = { ...record, updatedAt };
|
|
11
|
+
this.packs.set(key, next);
|
|
12
|
+
return next;
|
|
13
|
+
}
|
|
14
|
+
async getPack(packKey, version) {
|
|
15
|
+
if (version)
|
|
16
|
+
return this.packs.get(`${packKey}.v${version}`) ?? null;
|
|
17
|
+
const matches = [...this.packs.values()].filter((pack) => pack.packKey === packKey);
|
|
18
|
+
if (matches.length === 0)
|
|
19
|
+
return null;
|
|
20
|
+
return matches.sort((a, b) => b.version.localeCompare(a.version))[0] ?? null;
|
|
21
|
+
}
|
|
22
|
+
async listPacks(query = {}) {
|
|
23
|
+
const { query: q, tag, owner, limit = 50, offset = 0 } = query;
|
|
24
|
+
let results = [...this.packs.values()];
|
|
25
|
+
if (q) {
|
|
26
|
+
const needle = q.toLowerCase();
|
|
27
|
+
results = results.filter((pack) => pack.packKey.toLowerCase().includes(needle) || pack.title.toLowerCase().includes(needle));
|
|
28
|
+
}
|
|
29
|
+
if (tag) {
|
|
30
|
+
results = results.filter((pack) => pack.tags?.includes(tag));
|
|
31
|
+
}
|
|
32
|
+
if (owner) {
|
|
33
|
+
results = results.filter((pack) => pack.owners?.includes(owner));
|
|
34
|
+
}
|
|
35
|
+
const slice = results.slice(offset, offset + limit);
|
|
36
|
+
return {
|
|
37
|
+
packs: slice,
|
|
38
|
+
total: results.length,
|
|
39
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async createSnapshot(record) {
|
|
43
|
+
this.snapshots.set(record.snapshotId, record);
|
|
44
|
+
return record;
|
|
45
|
+
}
|
|
46
|
+
async getSnapshot(snapshotId) {
|
|
47
|
+
return this.snapshots.get(snapshotId) ?? null;
|
|
48
|
+
}
|
|
49
|
+
async listSnapshots(query = {}) {
|
|
50
|
+
const { packKey, snapshotId, limit = 50, offset = 0 } = query;
|
|
51
|
+
let results = [...this.snapshots.values()];
|
|
52
|
+
if (packKey)
|
|
53
|
+
results = results.filter((snap) => snap.packKey === packKey);
|
|
54
|
+
if (snapshotId)
|
|
55
|
+
results = results.filter((snap) => snap.snapshotId === snapshotId);
|
|
56
|
+
const slice = results.slice(offset, offset + limit);
|
|
57
|
+
return {
|
|
58
|
+
snapshots: slice,
|
|
59
|
+
total: results.length,
|
|
60
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async addSnapshotItems(snapshotId, items) {
|
|
64
|
+
const created = items.map((item) => ({
|
|
65
|
+
...item,
|
|
66
|
+
snapshotId,
|
|
67
|
+
createdAt: item.createdAt ?? new Date().toISOString()
|
|
68
|
+
}));
|
|
69
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
70
|
+
this.items.set(snapshotId, [...current, ...created]);
|
|
71
|
+
return created;
|
|
72
|
+
}
|
|
73
|
+
async listSnapshotItems(snapshotId, options = {}) {
|
|
74
|
+
const { limit = 100, offset = 0 } = options;
|
|
75
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
76
|
+
return current.slice(offset, offset + limit);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
InMemoryContextSnapshotStore
|
|
81
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/in-memory-store.ts
|
|
2
|
+
class InMemoryContextSnapshotStore {
|
|
3
|
+
packs = new Map;
|
|
4
|
+
snapshots = new Map;
|
|
5
|
+
items = new Map;
|
|
6
|
+
async upsertPack(record) {
|
|
7
|
+
const key = `${record.packKey}.v${record.version}`;
|
|
8
|
+
const updatedAt = record.updatedAt ?? new Date().toISOString();
|
|
9
|
+
const next = { ...record, updatedAt };
|
|
10
|
+
this.packs.set(key, next);
|
|
11
|
+
return next;
|
|
12
|
+
}
|
|
13
|
+
async getPack(packKey, version) {
|
|
14
|
+
if (version)
|
|
15
|
+
return this.packs.get(`${packKey}.v${version}`) ?? null;
|
|
16
|
+
const matches = [...this.packs.values()].filter((pack) => pack.packKey === packKey);
|
|
17
|
+
if (matches.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
return matches.sort((a, b) => b.version.localeCompare(a.version))[0] ?? null;
|
|
20
|
+
}
|
|
21
|
+
async listPacks(query = {}) {
|
|
22
|
+
const { query: q, tag, owner, limit = 50, offset = 0 } = query;
|
|
23
|
+
let results = [...this.packs.values()];
|
|
24
|
+
if (q) {
|
|
25
|
+
const needle = q.toLowerCase();
|
|
26
|
+
results = results.filter((pack) => pack.packKey.toLowerCase().includes(needle) || pack.title.toLowerCase().includes(needle));
|
|
27
|
+
}
|
|
28
|
+
if (tag) {
|
|
29
|
+
results = results.filter((pack) => pack.tags?.includes(tag));
|
|
30
|
+
}
|
|
31
|
+
if (owner) {
|
|
32
|
+
results = results.filter((pack) => pack.owners?.includes(owner));
|
|
33
|
+
}
|
|
34
|
+
const slice = results.slice(offset, offset + limit);
|
|
35
|
+
return {
|
|
36
|
+
packs: slice,
|
|
37
|
+
total: results.length,
|
|
38
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async createSnapshot(record) {
|
|
42
|
+
this.snapshots.set(record.snapshotId, record);
|
|
43
|
+
return record;
|
|
44
|
+
}
|
|
45
|
+
async getSnapshot(snapshotId) {
|
|
46
|
+
return this.snapshots.get(snapshotId) ?? null;
|
|
47
|
+
}
|
|
48
|
+
async listSnapshots(query = {}) {
|
|
49
|
+
const { packKey, snapshotId, limit = 50, offset = 0 } = query;
|
|
50
|
+
let results = [...this.snapshots.values()];
|
|
51
|
+
if (packKey)
|
|
52
|
+
results = results.filter((snap) => snap.packKey === packKey);
|
|
53
|
+
if (snapshotId)
|
|
54
|
+
results = results.filter((snap) => snap.snapshotId === snapshotId);
|
|
55
|
+
const slice = results.slice(offset, offset + limit);
|
|
56
|
+
return {
|
|
57
|
+
snapshots: slice,
|
|
58
|
+
total: results.length,
|
|
59
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async addSnapshotItems(snapshotId, items) {
|
|
63
|
+
const created = items.map((item) => ({
|
|
64
|
+
...item,
|
|
65
|
+
snapshotId,
|
|
66
|
+
createdAt: item.createdAt ?? new Date().toISOString()
|
|
67
|
+
}));
|
|
68
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
69
|
+
this.items.set(snapshotId, [...current, ...created]);
|
|
70
|
+
return created;
|
|
71
|
+
}
|
|
72
|
+
async listSnapshotItems(snapshotId, options = {}) {
|
|
73
|
+
const { limit = 100, offset = 0 } = options;
|
|
74
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
75
|
+
return current.slice(offset, offset + limit);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
InMemoryContextSnapshotStore
|
|
80
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/in-memory-store.ts
|
|
2
|
+
class InMemoryContextSnapshotStore {
|
|
3
|
+
packs = new Map;
|
|
4
|
+
snapshots = new Map;
|
|
5
|
+
items = new Map;
|
|
6
|
+
async upsertPack(record) {
|
|
7
|
+
const key = `${record.packKey}.v${record.version}`;
|
|
8
|
+
const updatedAt = record.updatedAt ?? new Date().toISOString();
|
|
9
|
+
const next = { ...record, updatedAt };
|
|
10
|
+
this.packs.set(key, next);
|
|
11
|
+
return next;
|
|
12
|
+
}
|
|
13
|
+
async getPack(packKey, version) {
|
|
14
|
+
if (version)
|
|
15
|
+
return this.packs.get(`${packKey}.v${version}`) ?? null;
|
|
16
|
+
const matches = [...this.packs.values()].filter((pack) => pack.packKey === packKey);
|
|
17
|
+
if (matches.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
return matches.sort((a, b) => b.version.localeCompare(a.version))[0] ?? null;
|
|
20
|
+
}
|
|
21
|
+
async listPacks(query = {}) {
|
|
22
|
+
const { query: q, tag, owner, limit = 50, offset = 0 } = query;
|
|
23
|
+
let results = [...this.packs.values()];
|
|
24
|
+
if (q) {
|
|
25
|
+
const needle = q.toLowerCase();
|
|
26
|
+
results = results.filter((pack) => pack.packKey.toLowerCase().includes(needle) || pack.title.toLowerCase().includes(needle));
|
|
27
|
+
}
|
|
28
|
+
if (tag) {
|
|
29
|
+
results = results.filter((pack) => pack.tags?.includes(tag));
|
|
30
|
+
}
|
|
31
|
+
if (owner) {
|
|
32
|
+
results = results.filter((pack) => pack.owners?.includes(owner));
|
|
33
|
+
}
|
|
34
|
+
const slice = results.slice(offset, offset + limit);
|
|
35
|
+
return {
|
|
36
|
+
packs: slice,
|
|
37
|
+
total: results.length,
|
|
38
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async createSnapshot(record) {
|
|
42
|
+
this.snapshots.set(record.snapshotId, record);
|
|
43
|
+
return record;
|
|
44
|
+
}
|
|
45
|
+
async getSnapshot(snapshotId) {
|
|
46
|
+
return this.snapshots.get(snapshotId) ?? null;
|
|
47
|
+
}
|
|
48
|
+
async listSnapshots(query = {}) {
|
|
49
|
+
const { packKey, snapshotId, limit = 50, offset = 0 } = query;
|
|
50
|
+
let results = [...this.snapshots.values()];
|
|
51
|
+
if (packKey)
|
|
52
|
+
results = results.filter((snap) => snap.packKey === packKey);
|
|
53
|
+
if (snapshotId)
|
|
54
|
+
results = results.filter((snap) => snap.snapshotId === snapshotId);
|
|
55
|
+
const slice = results.slice(offset, offset + limit);
|
|
56
|
+
return {
|
|
57
|
+
snapshots: slice,
|
|
58
|
+
total: results.length,
|
|
59
|
+
nextOffset: offset + slice.length < results.length ? offset + slice.length : undefined
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async addSnapshotItems(snapshotId, items) {
|
|
63
|
+
const created = items.map((item) => ({
|
|
64
|
+
...item,
|
|
65
|
+
snapshotId,
|
|
66
|
+
createdAt: item.createdAt ?? new Date().toISOString()
|
|
67
|
+
}));
|
|
68
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
69
|
+
this.items.set(snapshotId, [...current, ...created]);
|
|
70
|
+
return created;
|
|
71
|
+
}
|
|
72
|
+
async listSnapshotItems(snapshotId, options = {}) {
|
|
73
|
+
const { limit = 100, offset = 0 } = options;
|
|
74
|
+
const current = this.items.get(snapshotId) ?? [];
|
|
75
|
+
return current.slice(offset, offset + limit);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
InMemoryContextSnapshotStore
|
|
80
|
+
};
|
|
File without changes
|
|
File without changes
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ContextPackListResult, ContextPackQuery, ContextPackRecord, ContextSnapshotItem, ContextSnapshotItemInput, ContextSnapshotListResult, ContextSnapshotQuery, ContextSnapshotRecord } from './types';
|
|
2
|
+
export interface ContextSnapshotStore {
|
|
3
|
+
upsertPack(record: ContextPackRecord): Promise<ContextPackRecord>;
|
|
4
|
+
getPack(packKey: string, version?: string): Promise<ContextPackRecord | null>;
|
|
5
|
+
listPacks(query?: ContextPackQuery): Promise<ContextPackListResult>;
|
|
6
|
+
createSnapshot(record: ContextSnapshotRecord): Promise<ContextSnapshotRecord>;
|
|
7
|
+
getSnapshot(snapshotId: string): Promise<ContextSnapshotRecord | null>;
|
|
8
|
+
listSnapshots(query?: ContextSnapshotQuery): Promise<ContextSnapshotListResult>;
|
|
9
|
+
addSnapshotItems(snapshotId: string, items: ContextSnapshotItemInput[]): Promise<ContextSnapshotItem[]>;
|
|
10
|
+
listSnapshotItems(snapshotId: string, options?: {
|
|
11
|
+
limit?: number;
|
|
12
|
+
offset?: number;
|
|
13
|
+
}): Promise<ContextSnapshotItem[]>;
|
|
14
|
+
}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export type ContextSourceKind = 'docblock' | 'contract' | 'knowledge' | 'data-view' | 'schema' | 'migration' | 'external';
|
|
2
|
+
export interface ContextPackSourceRef {
|
|
3
|
+
kind: ContextSourceKind;
|
|
4
|
+
key: string;
|
|
5
|
+
version?: string;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
description?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ContextPackRecord {
|
|
10
|
+
packKey: string;
|
|
11
|
+
version: string;
|
|
12
|
+
title: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
owners?: string[];
|
|
15
|
+
tags?: string[];
|
|
16
|
+
sources?: ContextPackSourceRef[];
|
|
17
|
+
createdAt: string;
|
|
18
|
+
updatedAt?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ContextSnapshotRecord {
|
|
21
|
+
snapshotId: string;
|
|
22
|
+
packKey: string;
|
|
23
|
+
packVersion: string;
|
|
24
|
+
hash: string;
|
|
25
|
+
itemCount?: number;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
createdBy?: string;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
export interface ContextSnapshotItemInput {
|
|
31
|
+
itemId: string;
|
|
32
|
+
kind: string;
|
|
33
|
+
sourceKey: string;
|
|
34
|
+
sourceVersion?: string;
|
|
35
|
+
content: Record<string, unknown> | string;
|
|
36
|
+
textContent?: string;
|
|
37
|
+
metadata?: Record<string, unknown>;
|
|
38
|
+
createdAt?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface ContextSnapshotItem extends ContextSnapshotItemInput {
|
|
41
|
+
snapshotId: string;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
}
|
|
44
|
+
export interface ContextPackQuery {
|
|
45
|
+
query?: string;
|
|
46
|
+
tag?: string;
|
|
47
|
+
owner?: string;
|
|
48
|
+
limit?: number;
|
|
49
|
+
offset?: number;
|
|
50
|
+
}
|
|
51
|
+
export interface ContextSnapshotQuery {
|
|
52
|
+
packKey?: string;
|
|
53
|
+
snapshotId?: string;
|
|
54
|
+
limit?: number;
|
|
55
|
+
offset?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface ContextPackListResult {
|
|
58
|
+
packs: ContextPackRecord[];
|
|
59
|
+
total?: number;
|
|
60
|
+
nextOffset?: number;
|
|
61
|
+
}
|
|
62
|
+
export interface ContextSnapshotListResult {
|
|
63
|
+
snapshots: ContextSnapshotRecord[];
|
|
64
|
+
total?: number;
|
|
65
|
+
nextOffset?: number;
|
|
66
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
package/package.json
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contractspec/lib.context-storage",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Context pack and snapshot storage primitives",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"contractspec",
|
|
7
|
+
"context",
|
|
8
|
+
"snapshot",
|
|
9
|
+
"storage"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
19
|
+
"publish:pkg:canary": "bun publish:pkg --tag canary",
|
|
20
|
+
"build": "bun run prebuild && bun run build:bundle && bun run build:types",
|
|
21
|
+
"build:bundle": "contractspec-bun-build transpile",
|
|
22
|
+
"build:types": "contractspec-bun-build types",
|
|
23
|
+
"dev": "contractspec-bun-build dev",
|
|
24
|
+
"clean": "rimraf dist .turbo",
|
|
25
|
+
"lint": "bun lint:fix",
|
|
26
|
+
"lint:fix": "eslint src --fix",
|
|
27
|
+
"lint:check": "eslint src",
|
|
28
|
+
"test": "bun test --pass-with-no-tests",
|
|
29
|
+
"prebuild": "contractspec-bun-build prebuild",
|
|
30
|
+
"typecheck": "tsc --noEmit"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@contractspec/tool.typescript": "3.1.0",
|
|
35
|
+
"@contractspec/tool.bun": "3.1.0",
|
|
36
|
+
"typescript": "^5.9.3"
|
|
37
|
+
},
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"bun": "./dist/index.js",
|
|
42
|
+
"node": "./dist/node/index.js",
|
|
43
|
+
"browser": "./dist/browser/index.js",
|
|
44
|
+
"default": "./dist/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./in-memory-store": {
|
|
47
|
+
"types": "./dist/in-memory-store.d.ts",
|
|
48
|
+
"bun": "./dist/in-memory-store.js",
|
|
49
|
+
"node": "./dist/node/in-memory-store.js",
|
|
50
|
+
"browser": "./dist/browser/in-memory-store.js",
|
|
51
|
+
"default": "./dist/in-memory-store.js"
|
|
52
|
+
},
|
|
53
|
+
"./store": {
|
|
54
|
+
"types": "./dist/store.d.ts",
|
|
55
|
+
"bun": "./dist/store.js",
|
|
56
|
+
"node": "./dist/node/store.js",
|
|
57
|
+
"browser": "./dist/browser/store.js",
|
|
58
|
+
"default": "./dist/store.js"
|
|
59
|
+
},
|
|
60
|
+
"./types": {
|
|
61
|
+
"types": "./dist/types.d.ts",
|
|
62
|
+
"bun": "./dist/types.js",
|
|
63
|
+
"node": "./dist/node/types.js",
|
|
64
|
+
"browser": "./dist/browser/types.js",
|
|
65
|
+
"default": "./dist/types.js"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public",
|
|
70
|
+
"exports": {
|
|
71
|
+
".": {
|
|
72
|
+
"types": "./dist/index.d.ts",
|
|
73
|
+
"bun": "./dist/index.js",
|
|
74
|
+
"node": "./dist/node/index.js",
|
|
75
|
+
"browser": "./dist/browser/index.js",
|
|
76
|
+
"default": "./dist/index.js"
|
|
77
|
+
},
|
|
78
|
+
"./in-memory-store": {
|
|
79
|
+
"types": "./dist/in-memory-store.d.ts",
|
|
80
|
+
"bun": "./dist/in-memory-store.js",
|
|
81
|
+
"node": "./dist/node/in-memory-store.js",
|
|
82
|
+
"browser": "./dist/browser/in-memory-store.js",
|
|
83
|
+
"default": "./dist/in-memory-store.js"
|
|
84
|
+
},
|
|
85
|
+
"./store": {
|
|
86
|
+
"types": "./dist/store.d.ts",
|
|
87
|
+
"bun": "./dist/store.js",
|
|
88
|
+
"node": "./dist/node/store.js",
|
|
89
|
+
"browser": "./dist/browser/store.js",
|
|
90
|
+
"default": "./dist/store.js"
|
|
91
|
+
},
|
|
92
|
+
"./types": {
|
|
93
|
+
"types": "./dist/types.d.ts",
|
|
94
|
+
"bun": "./dist/types.js",
|
|
95
|
+
"node": "./dist/node/types.js",
|
|
96
|
+
"browser": "./dist/browser/types.js",
|
|
97
|
+
"default": "./dist/types.js"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"registry": "https://registry.npmjs.org/"
|
|
101
|
+
},
|
|
102
|
+
"license": "MIT",
|
|
103
|
+
"repository": {
|
|
104
|
+
"type": "git",
|
|
105
|
+
"url": "https://github.com/lssm-tech/contractspec.git",
|
|
106
|
+
"directory": "packages/libs/context-storage"
|
|
107
|
+
},
|
|
108
|
+
"homepage": "https://contractspec.io"
|
|
109
|
+
}
|