anchordb-sync-server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/cjs/adapters/express.d.ts +33 -0
- package/dist/cjs/adapters/express.d.ts.map +1 -0
- package/dist/cjs/adapters/express.js +40 -0
- package/dist/cjs/adapters/express.js.map +1 -0
- package/dist/cjs/adapters/nestjs.d.ts +34 -0
- package/dist/cjs/adapters/nestjs.d.ts.map +1 -0
- package/dist/cjs/adapters/nestjs.js +98 -0
- package/dist/cjs/adapters/nestjs.js.map +1 -0
- package/dist/cjs/adapters/nextjs.d.ts +20 -0
- package/dist/cjs/adapters/nextjs.d.ts.map +1 -0
- package/dist/cjs/adapters/nextjs.js +43 -0
- package/dist/cjs/adapters/nextjs.js.map +1 -0
- package/dist/cjs/handlers.d.ts +17 -0
- package/dist/cjs/handlers.d.ts.map +1 -0
- package/dist/cjs/handlers.js +168 -0
- package/dist/cjs/handlers.js.map +1 -0
- package/dist/cjs/index.d.ts +16 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/store/memory.d.ts +25 -0
- package/dist/cjs/store/memory.d.ts.map +1 -0
- package/dist/cjs/store/memory.js +65 -0
- package/dist/cjs/store/memory.js.map +1 -0
- package/dist/cjs/store/mongoose.d.ts +90 -0
- package/dist/cjs/store/mongoose.d.ts.map +1 -0
- package/dist/cjs/store/mongoose.js +129 -0
- package/dist/cjs/store/mongoose.js.map +1 -0
- package/dist/cjs/store/types.d.ts +62 -0
- package/dist/cjs/store/types.d.ts.map +1 -0
- package/dist/cjs/store/types.js +3 -0
- package/dist/cjs/store/types.js.map +1 -0
- package/dist/esm/adapters/express.d.ts +33 -0
- package/dist/esm/adapters/express.d.ts.map +1 -0
- package/dist/esm/adapters/express.js +37 -0
- package/dist/esm/adapters/express.js.map +1 -0
- package/dist/esm/adapters/nestjs.d.ts +34 -0
- package/dist/esm/adapters/nestjs.d.ts.map +1 -0
- package/dist/esm/adapters/nestjs.js +93 -0
- package/dist/esm/adapters/nestjs.js.map +1 -0
- package/dist/esm/adapters/nextjs.d.ts +20 -0
- package/dist/esm/adapters/nextjs.d.ts.map +1 -0
- package/dist/esm/adapters/nextjs.js +40 -0
- package/dist/esm/adapters/nextjs.js.map +1 -0
- package/dist/esm/handlers.d.ts +17 -0
- package/dist/esm/handlers.d.ts.map +1 -0
- package/dist/esm/handlers.js +162 -0
- package/dist/esm/handlers.js.map +1 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +13 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/store/memory.d.ts +25 -0
- package/dist/esm/store/memory.d.ts.map +1 -0
- package/dist/esm/store/memory.js +61 -0
- package/dist/esm/store/memory.js.map +1 -0
- package/dist/esm/store/mongoose.d.ts +90 -0
- package/dist/esm/store/mongoose.d.ts.map +1 -0
- package/dist/esm/store/mongoose.js +124 -0
- package/dist/esm/store/mongoose.js.map +1 -0
- package/dist/esm/store/types.d.ts +62 -0
- package/dist/esm/store/types.d.ts.map +1 -0
- package/dist/esm/store/types.js +2 -0
- package/dist/esm/store/types.js.map +1 -0
- package/package.json +109 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema definitions for the two collections the protocol needs.
|
|
3
|
+
*
|
|
4
|
+
* Timestamps are `Date`, which Mongoose casts from the ISO-8601 UTC strings the client sends — one
|
|
5
|
+
* of the reasons the wire format uses strings rather than a custom encoding.
|
|
6
|
+
*/
|
|
7
|
+
export function anchorSchemas() {
|
|
8
|
+
return {
|
|
9
|
+
document: {
|
|
10
|
+
documentId: { type: String, required: true, index: true },
|
|
11
|
+
collection: { type: String, required: true, index: true },
|
|
12
|
+
document: { type: Object, default: {} },
|
|
13
|
+
version: { type: Number, required: true, default: 0 },
|
|
14
|
+
deleted: { type: Boolean, default: false },
|
|
15
|
+
updatedAt: { type: Date, required: true },
|
|
16
|
+
hlc: { type: String, default: "" },
|
|
17
|
+
// Monotonic global sequence. Indexed because every pull is a range scan over it.
|
|
18
|
+
seq: { type: Number, required: true, index: true },
|
|
19
|
+
},
|
|
20
|
+
idempotency: {
|
|
21
|
+
clientId: { type: String, required: true },
|
|
22
|
+
mutationId: { type: String, required: true },
|
|
23
|
+
result: { type: Object, required: true },
|
|
24
|
+
createdAt: { type: Date, default: Date.now, expires: "30d" },
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export class MongooseStore {
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.name = "mongoose";
|
|
31
|
+
const { mongoose } = options;
|
|
32
|
+
const schemas = anchorSchemas();
|
|
33
|
+
const docName = options.documentModelName ?? "AnchorDocument";
|
|
34
|
+
const idemName = options.idempotencyModelName ?? "AnchorIdempotency";
|
|
35
|
+
const counterName = "AnchorCounter";
|
|
36
|
+
// Reuse an already-registered model rather than re-registering, which Mongoose treats as an
|
|
37
|
+
// error (OverwriteModelError) and which is easy to trigger under hot reload.
|
|
38
|
+
this.documents =
|
|
39
|
+
mongoose.models[docName] ??
|
|
40
|
+
mongoose.model(docName, new mongoose.Schema(schemas.document, { versionKey: false }), options.documentCollection);
|
|
41
|
+
this.idempotency =
|
|
42
|
+
mongoose.models[idemName] ??
|
|
43
|
+
mongoose.model(idemName, new mongoose.Schema(schemas.idempotency, { versionKey: false }), options.idempotencyCollection);
|
|
44
|
+
this.counters =
|
|
45
|
+
mongoose.models[counterName] ??
|
|
46
|
+
mongoose.model(counterName, new mongoose.Schema({ _id: String, value: { type: Number, default: 0 } }, { versionKey: false }));
|
|
47
|
+
}
|
|
48
|
+
async get(collection, documentId) {
|
|
49
|
+
const found = await this.documents.findOne({ collection, documentId }).lean().exec();
|
|
50
|
+
return found ? toStored(found) : null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Allocate the next sequence number atomically.
|
|
54
|
+
*
|
|
55
|
+
* `findOneAndUpdate` with `$inc` is a single atomic operation in MongoDB, so two concurrent
|
|
56
|
+
* pushes cannot receive the same seq. Reading-then-writing a counter would race, and duplicate
|
|
57
|
+
* sequence numbers would make pull cursors skip changes.
|
|
58
|
+
*/
|
|
59
|
+
async nextSeq() {
|
|
60
|
+
const doc = (await this.counters
|
|
61
|
+
.findOneAndUpdate({ _id: "seq" }, { $inc: { value: 1 } }, { upsert: true, new: true })
|
|
62
|
+
.lean()
|
|
63
|
+
.exec());
|
|
64
|
+
return doc?.value ?? 1;
|
|
65
|
+
}
|
|
66
|
+
async put(doc) {
|
|
67
|
+
const seq = await this.nextSeq();
|
|
68
|
+
const stored = { ...doc, seq };
|
|
69
|
+
await this.documents
|
|
70
|
+
.findOneAndUpdate({ collection: doc.collection, documentId: doc.documentId }, { $set: { ...stored, updatedAt: new Date(doc.updatedAt) } }, { upsert: true, new: true })
|
|
71
|
+
.lean()
|
|
72
|
+
.exec();
|
|
73
|
+
return stored;
|
|
74
|
+
}
|
|
75
|
+
async listChanges(afterSeq, limit, collections) {
|
|
76
|
+
const filter = { seq: { $gt: afterSeq } };
|
|
77
|
+
if (collections?.length)
|
|
78
|
+
filter.collection = { $in: collections };
|
|
79
|
+
const rows = await this.documents.find(filter).sort({ seq: 1 }).limit(limit).lean().exec();
|
|
80
|
+
return rows.map(toStored);
|
|
81
|
+
}
|
|
82
|
+
async hasMoreAfter(seq, collections) {
|
|
83
|
+
const filter = { seq: { $gt: seq } };
|
|
84
|
+
if (collections?.length)
|
|
85
|
+
filter.collection = { $in: collections };
|
|
86
|
+
return (await this.documents.countDocuments(filter).exec()) > 0;
|
|
87
|
+
}
|
|
88
|
+
async getIdempotent(clientId, mutationId) {
|
|
89
|
+
const found = (await this.idempotency.findOne({ clientId, mutationId }).lean().exec());
|
|
90
|
+
return found?.result ?? null;
|
|
91
|
+
}
|
|
92
|
+
async setIdempotent(clientId, mutationId, result) {
|
|
93
|
+
await this.idempotency
|
|
94
|
+
.findOneAndUpdate({ clientId, mutationId }, { $set: { result } }, { upsert: true, new: true })
|
|
95
|
+
.lean()
|
|
96
|
+
.exec();
|
|
97
|
+
}
|
|
98
|
+
async findByUniqueField(collection, field, value, exceptDocumentId) {
|
|
99
|
+
const found = await this.documents
|
|
100
|
+
.findOne({
|
|
101
|
+
collection,
|
|
102
|
+
deleted: false,
|
|
103
|
+
documentId: { $ne: exceptDocumentId },
|
|
104
|
+
[`document.${field}`]: value,
|
|
105
|
+
})
|
|
106
|
+
.lean()
|
|
107
|
+
.exec();
|
|
108
|
+
return found ? toStored(found) : null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function toStored(row) {
|
|
112
|
+
const updatedAt = row.updatedAt;
|
|
113
|
+
return {
|
|
114
|
+
documentId: String(row.documentId),
|
|
115
|
+
collection: String(row.collection),
|
|
116
|
+
document: (row.document ?? {}),
|
|
117
|
+
version: Number(row.version ?? 0),
|
|
118
|
+
deleted: Boolean(row.deleted),
|
|
119
|
+
updatedAt: updatedAt instanceof Date ? updatedAt.toISOString() : String(updatedAt),
|
|
120
|
+
hlc: String(row.hlc ?? ""),
|
|
121
|
+
seq: Number(row.seq ?? 0),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=mongoose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongoose.js","sourceRoot":"","sources":["../../../src/store/mongoose.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ChangeResult } from "anchordb";
|
|
2
|
+
/**
|
|
3
|
+
* The storage contract the sync protocol needs from a backend.
|
|
4
|
+
*
|
|
5
|
+
* Kept deliberately small — five reads and two writes — so implementing it against a new database
|
|
6
|
+
* is a short afternoon rather than a port. The protocol semantics (version checks, conflict
|
|
7
|
+
* verdicts, idempotency) live in `handlers.ts` and are identical for every store, which is what
|
|
8
|
+
* makes the conformance suite meaningful: MemoryStore and MongooseStore are held to the same tests.
|
|
9
|
+
*/
|
|
10
|
+
export interface StoredServerDocument {
|
|
11
|
+
documentId: string;
|
|
12
|
+
collection: string;
|
|
13
|
+
document: Record<string, unknown>;
|
|
14
|
+
/** Monotonic per document. The basis of optimistic concurrency. */
|
|
15
|
+
version: number;
|
|
16
|
+
deleted: boolean;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
hlc: string;
|
|
19
|
+
/** Position in the global change sequence — what pull cursors point at. */
|
|
20
|
+
seq: number;
|
|
21
|
+
}
|
|
22
|
+
export interface UniqueIndexSpec {
|
|
23
|
+
collection: string;
|
|
24
|
+
field: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ServerStore {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
get(collection: string, documentId: string): Promise<StoredServerDocument | null>;
|
|
29
|
+
/** Persist a document and assign it the next global sequence number. */
|
|
30
|
+
put(doc: Omit<StoredServerDocument, "seq">): Promise<StoredServerDocument>;
|
|
31
|
+
/** Documents whose seq is greater than `afterSeq`, ascending, at most `limit`. */
|
|
32
|
+
listChanges(afterSeq: number, limit: number, collections?: string[]): Promise<StoredServerDocument[]>;
|
|
33
|
+
/** True when more changes remain beyond the returned page. */
|
|
34
|
+
hasMoreAfter(seq: number, collections?: string[]): Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Look up a memoised verdict.
|
|
37
|
+
*
|
|
38
|
+
* This is what makes retries safe: a client that times out and re-sends must receive the original
|
|
39
|
+
* decision, not have the write applied a second time.
|
|
40
|
+
*/
|
|
41
|
+
getIdempotent(clientId: string, mutationId: string): Promise<ChangeResult | null>;
|
|
42
|
+
setIdempotent(clientId: string, mutationId: string, result: ChangeResult): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Find a document that already holds `value` in `field`, excluding `exceptDocumentId`.
|
|
45
|
+
*
|
|
46
|
+
* Unique constraints are checked here rather than in the handler because a real database can do
|
|
47
|
+
* it with an index; scanning in the handler would be correct but O(n) per push.
|
|
48
|
+
*/
|
|
49
|
+
findByUniqueField(collection: string, field: string, value: unknown, exceptDocumentId: string): Promise<StoredServerDocument | null>;
|
|
50
|
+
}
|
|
51
|
+
export interface SyncServerOptions {
|
|
52
|
+
store: ServerStore;
|
|
53
|
+
/** Unique constraints to enforce, mirroring the indexes declared in the client schema. */
|
|
54
|
+
uniqueIndexes?: UniqueIndexSpec[];
|
|
55
|
+
/** Injectable for deterministic tests. */
|
|
56
|
+
now?: () => number;
|
|
57
|
+
/** Reject collections not in this list, if provided. */
|
|
58
|
+
allowedCollections?: string[];
|
|
59
|
+
/** Resolve the tenant/user this request belongs to. Return null to reject. */
|
|
60
|
+
authenticate?: (headers: Record<string, string | undefined>) => Promise<string | null> | string | null;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/store/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C;;;;;;;GAOG;AAEH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAElF,wEAAwE;IACxE,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAE3E,kFAAkF;IAClF,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAEtG,8DAA8D;IAC9D,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpE;;;;;OAKG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAClF,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzF;;;;;OAKG;IACH,iBAAiB,CACf,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,OAAO,EACd,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,WAAW,CAAC;IACnB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;IAClC,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,wDAAwD;IACxD,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;CACxG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/store/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "anchordb-sync-server",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Framework-agnostic server implementation of the Anchor sync protocol, with Express, NestJS and Next.js adapters.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/cjs/index.js",
|
|
12
|
+
"module": "./dist/esm/index.js",
|
|
13
|
+
"types": "./dist/esm/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": {
|
|
17
|
+
"types": "./dist/esm/index.d.ts",
|
|
18
|
+
"default": "./dist/esm/index.js"
|
|
19
|
+
},
|
|
20
|
+
"require": {
|
|
21
|
+
"types": "./dist/cjs/index.d.ts",
|
|
22
|
+
"default": "./dist/cjs/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"./express": {
|
|
26
|
+
"import": {
|
|
27
|
+
"types": "./dist/esm/adapters/express.d.ts",
|
|
28
|
+
"default": "./dist/esm/adapters/express.js"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./dist/cjs/adapters/express.d.ts",
|
|
32
|
+
"default": "./dist/cjs/adapters/express.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"./nestjs": {
|
|
36
|
+
"import": {
|
|
37
|
+
"types": "./dist/esm/adapters/nestjs.d.ts",
|
|
38
|
+
"default": "./dist/esm/adapters/nestjs.js"
|
|
39
|
+
},
|
|
40
|
+
"require": {
|
|
41
|
+
"types": "./dist/cjs/adapters/nestjs.d.ts",
|
|
42
|
+
"default": "./dist/cjs/adapters/nestjs.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"./nextjs": {
|
|
46
|
+
"import": {
|
|
47
|
+
"types": "./dist/esm/adapters/nextjs.d.ts",
|
|
48
|
+
"default": "./dist/esm/adapters/nextjs.js"
|
|
49
|
+
},
|
|
50
|
+
"require": {
|
|
51
|
+
"types": "./dist/cjs/adapters/nextjs.d.ts",
|
|
52
|
+
"default": "./dist/cjs/adapters/nextjs.js"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"./mongoose": {
|
|
56
|
+
"import": {
|
|
57
|
+
"types": "./dist/esm/store/mongoose.d.ts",
|
|
58
|
+
"default": "./dist/esm/store/mongoose.js"
|
|
59
|
+
},
|
|
60
|
+
"require": {
|
|
61
|
+
"types": "./dist/cjs/store/mongoose.d.ts",
|
|
62
|
+
"default": "./dist/cjs/store/mongoose.js"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"./package.json": "./package.json"
|
|
66
|
+
},
|
|
67
|
+
"files": [
|
|
68
|
+
"dist",
|
|
69
|
+
"README.md",
|
|
70
|
+
"LICENSE"
|
|
71
|
+
],
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build": "rimraf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.build.cjs.json && node ../core/scripts/finalize-dist.mjs",
|
|
74
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
75
|
+
"check:package": "publint && attw --pack . --profile node16",
|
|
76
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
77
|
+
},
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"anchordb": "^0.2.0"
|
|
80
|
+
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"mongoose": ">=7",
|
|
83
|
+
"express": ">=4"
|
|
84
|
+
},
|
|
85
|
+
"peerDependenciesMeta": {
|
|
86
|
+
"mongoose": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"express": {
|
|
90
|
+
"optional": true
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"devDependencies": {
|
|
94
|
+
"rimraf": "^6.0.1"
|
|
95
|
+
},
|
|
96
|
+
"publishConfig": {
|
|
97
|
+
"access": "public"
|
|
98
|
+
},
|
|
99
|
+
"repository": {
|
|
100
|
+
"type": "git",
|
|
101
|
+
"url": "git+https://github.com/knnadeera/anchordb.git",
|
|
102
|
+
"directory": "packages/sync-server"
|
|
103
|
+
},
|
|
104
|
+
"homepage": "https://github.com/knnadeera/anchordb#readme",
|
|
105
|
+
"bugs": {
|
|
106
|
+
"url": "https://github.com/knnadeera/anchordb/issues"
|
|
107
|
+
},
|
|
108
|
+
"author": "Nisala Nadeera <knnadeera@gmail.com>"
|
|
109
|
+
}
|