jinaga-server 3.1.0 → 3.1.2
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 +2 -0
- package/dist/http/line-reader.d.ts +2 -0
- package/dist/http/line-reader.js +68 -0
- package/dist/http/line-reader.js.map +1 -0
- package/dist/http/router.d.ts +4 -1
- package/dist/http/router.js +122 -15
- package/dist/http/router.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/jinaga-server.d.ts +1 -0
- package/dist/jinaga-server.js +1 -1
- package/dist/jinaga-server.js.map +1 -1
- package/dist/memory/memory-keystore.js +5 -8
- package/dist/memory/memory-keystore.js.map +1 -1
- package/dist/postgres/postgres-keystore.js +5 -32
- package/dist/postgres/postgres-keystore.js.map +1 -1
- package/http/sans-auth/Feeds options.http +1 -0
- package/http/sans-auth/Save graph.http +6 -0
- package/http/sans-auth/Save json.http +13 -0
- package/http/with-auth/Create post with auth.http +24 -0
- package/http/with-auth/Define authorization rules.http +25 -0
- package/http/with-auth/Define distribution rules.http +30 -0
- package/http/with-auth/Feed sites.http +3 -0
- package/http/with-auth/Feeds options.http +1 -0
- package/http/with-auth/Feeds sites.http +32 -0
- package/http/with-auth/Get posts with auth.http +32 -0
- package/http/with-auth/Load titles.http +21 -0
- package/integration-test/jinaga-test/Dockerfile +1 -1
- package/integration-test/jinaga-test/package-lock.json +6 -5
- package/integration-test/jinaga-test/package.json +1 -1
- package/package.json +3 -5
- package/replicator/index.ts +0 -2
- package/replicator/package-lock.json +29 -75
- package/replicator/package.json +2 -4
- package/src/http/line-reader.ts +69 -0
- package/src/http/router.ts +152 -20
- package/src/index.ts +1 -0
- package/src/jinaga-server.ts +2 -1
- package/src/memory/memory-keystore.ts +7 -10
- package/src/postgres/postgres-keystore.ts +6 -38
- package/dist/http/serializer.d.ts +0 -11
- package/dist/http/serializer.js +0 -68
- package/dist/http/serializer.js.map +0 -1
- package/src/http/serializer.ts +0 -81
- package/test/http/serializerSpec.ts +0 -140
package/src/http/router.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Handler,
|
|
1
|
+
import { Handler, NextFunction, Request, Response, Router } from "express";
|
|
2
2
|
import {
|
|
3
3
|
Authorization,
|
|
4
4
|
Declaration,
|
|
@@ -11,12 +11,14 @@ import {
|
|
|
11
11
|
FeedResponse,
|
|
12
12
|
FeedsResponse,
|
|
13
13
|
Forbidden,
|
|
14
|
+
GraphDeserializer,
|
|
15
|
+
GraphSerializer,
|
|
16
|
+
GraphSource,
|
|
14
17
|
LoadMessage,
|
|
15
18
|
LoadResponse,
|
|
16
19
|
ProfileMessage,
|
|
17
20
|
ProjectedResult,
|
|
18
21
|
ReferencesByName,
|
|
19
|
-
SaveMessage,
|
|
20
22
|
Specification,
|
|
21
23
|
SpecificationParser,
|
|
22
24
|
Trace,
|
|
@@ -26,11 +28,11 @@ import {
|
|
|
26
28
|
computeTupleSubsetHash,
|
|
27
29
|
invertSpecification,
|
|
28
30
|
parseLoadMessage,
|
|
29
|
-
parseSaveMessage
|
|
31
|
+
parseSaveMessage,
|
|
32
|
+
verifyEnvelopes
|
|
30
33
|
} from "jinaga";
|
|
31
|
-
|
|
32
34
|
import { Stream } from "./stream";
|
|
33
|
-
import {
|
|
35
|
+
import { createLineReader } from "./line-reader";
|
|
34
36
|
|
|
35
37
|
interface ParsedQs { [key: string]: undefined | string | string[] | ParsedQs | ParsedQs[] }
|
|
36
38
|
|
|
@@ -203,15 +205,12 @@ function postString<U>(method: (user: RequestUser, message: string) => Promise<U
|
|
|
203
205
|
}
|
|
204
206
|
|
|
205
207
|
function postCreate<T>(
|
|
206
|
-
parse: (
|
|
208
|
+
parse: (request: Request) => T,
|
|
207
209
|
method: (user: RequestUser, message: T) => Promise<void>
|
|
208
210
|
): Handler {
|
|
209
211
|
return (req, res, next) => {
|
|
210
212
|
const user = <RequestUser>req.user;
|
|
211
|
-
const message = parse(req
|
|
212
|
-
if (!message) {
|
|
213
|
-
throw new Error('Ensure that you have called app.use(express.json()).');
|
|
214
|
-
}
|
|
213
|
+
const message = parse(req);
|
|
215
214
|
method(user, message)
|
|
216
215
|
.then(_ => {
|
|
217
216
|
res.sendStatus(201);
|
|
@@ -274,6 +273,29 @@ function serializeUserIdentity(user: RequestUser | null): UserIdentity | null {
|
|
|
274
273
|
};
|
|
275
274
|
}
|
|
276
275
|
|
|
276
|
+
function inputSaveMessage(req: Request): GraphSource {
|
|
277
|
+
if (req.is('application/x-jinaga-graph-v1')) {
|
|
278
|
+
// Convert the request into a function that reads one line at a time.
|
|
279
|
+
const readLine = createLineReader(req);
|
|
280
|
+
|
|
281
|
+
return new GraphDeserializer(readLine);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
return {
|
|
285
|
+
read: async (onEnvelopes) => {
|
|
286
|
+
const message = parseSaveMessage(req.body);
|
|
287
|
+
if (!message) {
|
|
288
|
+
throw new Error('Ensure that you have called app.use(express.json()).');
|
|
289
|
+
}
|
|
290
|
+
await onEnvelopes(message.facts.map(fact => ({
|
|
291
|
+
fact: fact,
|
|
292
|
+
signatures: []
|
|
293
|
+
})));
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
277
299
|
function outputGraph(result: FactEnvelope[], res: Response, accepts: (type: string) => string | false) {
|
|
278
300
|
if (accepts("application/x-jinaga-graph-v1")) {
|
|
279
301
|
res.type("application/x-jinaga-graph-v1");
|
|
@@ -306,7 +328,12 @@ export interface RequestUser {
|
|
|
306
328
|
export class HttpRouter {
|
|
307
329
|
handler: Handler;
|
|
308
330
|
|
|
309
|
-
constructor(
|
|
331
|
+
constructor(
|
|
332
|
+
private factManager: FactManager,
|
|
333
|
+
private authorization: Authorization,
|
|
334
|
+
private feedCache: FeedCache,
|
|
335
|
+
private allowedOrigin: string | string[] | ((origin: string, callback: (err: Error | null, allow?: boolean) => void) => void)
|
|
336
|
+
) {
|
|
310
337
|
const router = Router();
|
|
311
338
|
router.get('/login', getAuthenticate(user => this.login(user)));
|
|
312
339
|
router.post('/load', post(
|
|
@@ -315,8 +342,8 @@ export class HttpRouter {
|
|
|
315
342
|
outputGraph
|
|
316
343
|
));
|
|
317
344
|
router.post('/save', postCreate(
|
|
318
|
-
|
|
319
|
-
(user,
|
|
345
|
+
inputSaveMessage,
|
|
346
|
+
(user, graphSource) => this.save(user, graphSource)
|
|
320
347
|
));
|
|
321
348
|
|
|
322
349
|
router.post('/read', postString((user, input: string) => this.read(user, input)));
|
|
@@ -330,6 +357,30 @@ export class HttpRouter {
|
|
|
330
357
|
(user, params, query) => this.feed(user, params, query),
|
|
331
358
|
(user, params, query) => this.streamFeed(user, params, query)));
|
|
332
359
|
|
|
360
|
+
// Respond to OPTIONS requests to describe the methods and content types
|
|
361
|
+
// that are supported.
|
|
362
|
+
this.setOptions(router, '/login')
|
|
363
|
+
.intendedForGet()
|
|
364
|
+
.returningContent();
|
|
365
|
+
this.setOptions(router, '/load')
|
|
366
|
+
.intendedForPost('application/json')
|
|
367
|
+
.returningContent();
|
|
368
|
+
this.setOptions(router, '/save')
|
|
369
|
+
.intendedForPost('application/json', 'application/x-jinaga-graph-v1')
|
|
370
|
+
.returningNoContent();
|
|
371
|
+
this.setOptions(router, '/read')
|
|
372
|
+
.intendedForPost('text/plain')
|
|
373
|
+
.returningContent();
|
|
374
|
+
this.setOptions(router, '/write')
|
|
375
|
+
.intendedForPost('text/plain')
|
|
376
|
+
.returningNoContent();
|
|
377
|
+
this.setOptions(router, '/feeds')
|
|
378
|
+
.intendedForPost('text/plain')
|
|
379
|
+
.returningContent();
|
|
380
|
+
this.setOptions(router, '/feeds/:hash')
|
|
381
|
+
.intendedForGet()
|
|
382
|
+
.returningContent();
|
|
383
|
+
|
|
333
384
|
this.handler = router;
|
|
334
385
|
}
|
|
335
386
|
|
|
@@ -347,17 +398,17 @@ export class HttpRouter {
|
|
|
347
398
|
private async load(user: RequestUser, loadMessage: LoadMessage): Promise<FactEnvelope[]> {
|
|
348
399
|
const userIdentity = serializeUserIdentity(user);
|
|
349
400
|
const result = await this.authorization.load(userIdentity, loadMessage.references);
|
|
350
|
-
const facts = result.map(r => r.fact);
|
|
351
401
|
return result;
|
|
352
402
|
}
|
|
353
403
|
|
|
354
|
-
private async save(user: RequestUser | null,
|
|
404
|
+
private async save(user: RequestUser | null, graphSource: GraphSource): Promise<void> {
|
|
355
405
|
const userIdentity = serializeUserIdentity(user);
|
|
356
|
-
await
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
406
|
+
await graphSource.read(async (envelopes) => {
|
|
407
|
+
if (!verifyEnvelopes(envelopes)) {
|
|
408
|
+
throw new Forbidden("The signatures on the facts are invalid.");
|
|
409
|
+
}
|
|
410
|
+
await this.authorization.save(userIdentity, envelopes);
|
|
411
|
+
});
|
|
361
412
|
}
|
|
362
413
|
|
|
363
414
|
private async read(user: RequestUser | null, input: string): Promise<any[]> {
|
|
@@ -550,6 +601,77 @@ export class HttpRouter {
|
|
|
550
601
|
return declaredFact.declared.reference;
|
|
551
602
|
});
|
|
552
603
|
}
|
|
604
|
+
|
|
605
|
+
private setOptions(router: Router, path: string): OptionsConfiguration {
|
|
606
|
+
const addOptions = (allowedMethods: string[], allowedHeaders: string[], allowedTypes: string[]) => {
|
|
607
|
+
router.options(path, this.applyAllowOrigin((req, res) => {
|
|
608
|
+
res.set('Allow', allowedMethods.join(', '));
|
|
609
|
+
res.set('Access-Control-Allow-Methods', allowedMethods.join(', '));
|
|
610
|
+
res.set('Access-Control-Allow-Headers', allowedHeaders.join(', '));
|
|
611
|
+
if (allowedTypes.length > 0) {
|
|
612
|
+
res.set('Accept-Post', allowedTypes.join(', '));
|
|
613
|
+
}
|
|
614
|
+
res.status(204).send();
|
|
615
|
+
}));
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return {
|
|
619
|
+
intendedForGet: () => {
|
|
620
|
+
addOptions(['GET', 'OPTIONS'], ['Accept', 'Authorization'], []);
|
|
621
|
+
return {
|
|
622
|
+
returningContent: () => { },
|
|
623
|
+
returningNoContent: () => { }
|
|
624
|
+
};
|
|
625
|
+
},
|
|
626
|
+
intendedForPost: (...contentTypes: string[]) => {
|
|
627
|
+
return {
|
|
628
|
+
returningContent: () => {
|
|
629
|
+
addOptions(
|
|
630
|
+
['POST', 'OPTIONS'],
|
|
631
|
+
['Content-Type', 'Accept', 'Authorization'],
|
|
632
|
+
contentTypes);
|
|
633
|
+
},
|
|
634
|
+
returningNoContent: () => {
|
|
635
|
+
addOptions(
|
|
636
|
+
['POST', 'OPTIONS'],
|
|
637
|
+
['Content-Type', 'Authorization'],
|
|
638
|
+
contentTypes);
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
private applyAllowOrigin(sendResponse: (req: Request, res: Response) => void) {
|
|
646
|
+
// Specify the allowed origins.
|
|
647
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
648
|
+
let requestOrigin = req.get('Origin');
|
|
649
|
+
|
|
650
|
+
if (typeof this.allowedOrigin === 'string') {
|
|
651
|
+
// If allowedOrigin is a string, use it as the value of the Access-Control-Allow-Origin header
|
|
652
|
+
res.set('Access-Control-Allow-Origin', this.allowedOrigin);
|
|
653
|
+
sendResponse(req, res);
|
|
654
|
+
} else if (Array.isArray(this.allowedOrigin)) {
|
|
655
|
+
// If allowedOrigin is an array, check if the request's origin is in the array
|
|
656
|
+
if (requestOrigin && this.allowedOrigin.includes(requestOrigin)) {
|
|
657
|
+
res.set('Access-Control-Allow-Origin', requestOrigin);
|
|
658
|
+
}
|
|
659
|
+
sendResponse(req, res);
|
|
660
|
+
} else if (typeof this.allowedOrigin === 'function' && requestOrigin) {
|
|
661
|
+
// If allowedOrigin is a function, call it with the request's origin
|
|
662
|
+
this.allowedOrigin(requestOrigin, (err, allow) => {
|
|
663
|
+
if (err) {
|
|
664
|
+
next(err);
|
|
665
|
+
} else if (allow) {
|
|
666
|
+
res.set('Access-Control-Allow-Origin', requestOrigin);
|
|
667
|
+
}
|
|
668
|
+
sendResponse(req, res);
|
|
669
|
+
});
|
|
670
|
+
} else {
|
|
671
|
+
sendResponse(req, res);
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
}
|
|
553
675
|
}
|
|
554
676
|
|
|
555
677
|
function parseString(input: any): string {
|
|
@@ -573,4 +695,14 @@ function extractResults(obj: any): any {
|
|
|
573
695
|
else {
|
|
574
696
|
return obj;
|
|
575
697
|
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
interface OptionsConfiguration {
|
|
701
|
+
intendedForGet(): ResponseConfiguration;
|
|
702
|
+
intendedForPost(...contentTypes: string[]): ResponseConfiguration;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
interface ResponseConfiguration {
|
|
706
|
+
returningContent(): void;
|
|
707
|
+
returningNoContent(): void;
|
|
576
708
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { AuthenticationDevice } from "./authentication/authentication-device";
|
|
|
2
2
|
export { AuthenticationSession } from "./authentication/authentication-session";
|
|
3
3
|
export { AuthorizationKeystore } from "./authorization/authorization-keystore";
|
|
4
4
|
export { DistributedFactCache } from "./authorization/distributed-fact-cache";
|
|
5
|
+
export { createLineReader } from "./http/line-reader";
|
|
5
6
|
export { HttpRouter, RequestUser } from "./http/router";
|
|
6
7
|
export { Stream } from "./http/stream";
|
|
7
8
|
export { JinagaServer, JinagaServerConfig, JinagaServerInstance, tracePool } from "./jinaga-server";
|
package/src/jinaga-server.ts
CHANGED
|
@@ -38,6 +38,7 @@ export type JinagaServerConfig = {
|
|
|
38
38
|
model?: Model,
|
|
39
39
|
authorization?: (a: AuthorizationRules) => AuthorizationRules,
|
|
40
40
|
distribution?: (d: DistributionRules) => DistributionRules,
|
|
41
|
+
origin?: string | string[] | ((origin: string, callback: (err: Error | null, allow?: boolean) => void) => void),
|
|
41
42
|
};
|
|
42
43
|
|
|
43
44
|
export type JinagaServerInstance = {
|
|
@@ -67,7 +68,7 @@ export class JinagaServer {
|
|
|
67
68
|
const network = new NetworkNoOp();
|
|
68
69
|
const factManager = new FactManager(fork, source, store, network);
|
|
69
70
|
const authorization = createAuthorization(authorizationRules, distributionRules, factManager, store, keystore);
|
|
70
|
-
const router = new HttpRouter(factManager, authorization, feedCache);
|
|
71
|
+
const router = new HttpRouter(factManager, authorization, feedCache, config.origin || '*');
|
|
71
72
|
const j: Jinaga = new Jinaga(authentication, factManager, syncStatusNotifier);
|
|
72
73
|
|
|
73
74
|
async function close() {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { computeHash, FactEnvelope, FactRecord, PredecessorCollection, UserIdentity } from "jinaga";
|
|
2
|
-
import { pki } from "node-forge";
|
|
1
|
+
import { computeHash, FactEnvelope, FactRecord, generateKeyPair, KeyPair, PredecessorCollection, UserIdentity } from "jinaga";
|
|
3
2
|
|
|
4
3
|
import { Keystore } from "../keystore";
|
|
5
4
|
|
|
6
5
|
export class MemoryKeystore implements Keystore {
|
|
7
|
-
private keyPairs: { [key: string]:
|
|
6
|
+
private keyPairs: { [key: string]: KeyPair} = {};
|
|
8
7
|
|
|
9
8
|
getOrCreateUserFact(userIdentity: UserIdentity): Promise<FactRecord> {
|
|
10
9
|
return Promise.resolve(this.getOrCreateIdentityFact('Jinaga.User', userIdentity));
|
|
@@ -55,7 +54,7 @@ export class MemoryKeystore implements Keystore {
|
|
|
55
54
|
const key = `${userIdentity.provider}:${userIdentity.id}`;
|
|
56
55
|
const keyPair = this.keyPairs[key];
|
|
57
56
|
if (keyPair) {
|
|
58
|
-
return keyPair.
|
|
57
|
+
return keyPair.publicPem;
|
|
59
58
|
}
|
|
60
59
|
else {
|
|
61
60
|
return this.generateKeyPair(key);
|
|
@@ -66,7 +65,7 @@ export class MemoryKeystore implements Keystore {
|
|
|
66
65
|
const key = `${userIdentity.provider}:${userIdentity.id}`;
|
|
67
66
|
const keyPair = this.keyPairs[key];
|
|
68
67
|
if (keyPair) {
|
|
69
|
-
return keyPair.
|
|
68
|
+
return keyPair.publicPem;
|
|
70
69
|
}
|
|
71
70
|
else {
|
|
72
71
|
throw new Error("Public key not found");
|
|
@@ -74,10 +73,8 @@ export class MemoryKeystore implements Keystore {
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
private generateKeyPair(key: string) {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.keyPairs[key] = { publicKey, privateKey };
|
|
81
|
-
return publicKey;
|
|
76
|
+
const keyPair = generateKeyPair();
|
|
77
|
+
this.keyPairs[key] = keyPair;
|
|
78
|
+
return keyPair.privatePem;
|
|
82
79
|
}
|
|
83
80
|
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { md, pki, util } from "node-forge";
|
|
1
|
+
import { computeHash, FactEnvelope, FactRecord, generateKeyPair, KeyPair, PredecessorCollection, signFacts, UserIdentity } from "jinaga";
|
|
3
2
|
import { Pool, PoolClient } from "pg";
|
|
4
3
|
|
|
5
4
|
import { Keystore } from "../keystore";
|
|
6
5
|
import { ConnectionFactory } from "./connection";
|
|
7
6
|
|
|
8
|
-
interface KeyPair {
|
|
9
|
-
publicPem: string;
|
|
10
|
-
privatePem: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
7
|
export class PostgresKeystore implements Keystore {
|
|
14
8
|
private connectionFactory: ConnectionFactory;
|
|
15
9
|
private cache: Map<string, KeyPair> = new Map();
|
|
@@ -39,10 +33,8 @@ export class PostgresKeystore implements Keystore {
|
|
|
39
33
|
return facts.map(fact => ({ fact, signatures: [] }));
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const envelopes: FactEnvelope[] = facts.map(fact => signFact(fact, publicPem, privateKey));
|
|
45
|
-
return envelopes;
|
|
36
|
+
const keyPair = await this.getKeyPair(userIdentity);
|
|
37
|
+
return signFacts(keyPair, facts);
|
|
46
38
|
}
|
|
47
39
|
|
|
48
40
|
private async getOrCreateIdentityFact(type: string, identity: UserIdentity): Promise<FactRecord> {
|
|
@@ -121,37 +113,13 @@ export class PostgresKeystore implements Keystore {
|
|
|
121
113
|
}
|
|
122
114
|
|
|
123
115
|
private async generateKeyPair(connection: PoolClient, userIdentity: UserIdentity): Promise<KeyPair> {
|
|
124
|
-
const
|
|
125
|
-
const privatePem = pki.privateKeyToPem(keypair.privateKey);
|
|
126
|
-
const publicPem = pki.publicKeyToPem(keypair.publicKey);
|
|
116
|
+
const keyPair = generateKeyPair();
|
|
127
117
|
await connection.query(`INSERT INTO ${this.schema}.user (provider, user_identifier, private_key, public_key) VALUES ($1, $2, $3, $4)`,
|
|
128
|
-
[userIdentity.provider, userIdentity.id, privatePem, publicPem]);
|
|
129
|
-
return
|
|
118
|
+
[userIdentity.provider, userIdentity.id, keyPair.privatePem, keyPair.publicPem]);
|
|
119
|
+
return keyPair;
|
|
130
120
|
}
|
|
131
121
|
}
|
|
132
122
|
|
|
133
123
|
function getUserIdentityKey(userIdentity: UserIdentity) {
|
|
134
124
|
return `${userIdentity.provider}-${userIdentity.id}`;
|
|
135
125
|
}
|
|
136
|
-
|
|
137
|
-
function signFact(fact: FactRecord, publicPem: string, privateKey: pki.rsa.PrivateKey): FactEnvelope {
|
|
138
|
-
const canonicalString = canonicalizeFact(fact.fields, fact.predecessors);
|
|
139
|
-
const encodedString = util.encodeUtf8(canonicalString);
|
|
140
|
-
const digest = md.sha512.create().update(encodedString);
|
|
141
|
-
const hash = util.encode64(digest.digest().getBytes());
|
|
142
|
-
if (fact.hash !== hash) {
|
|
143
|
-
Trace.error(`Hash does not match. "${fact.hash}" !== "${hash}"\nFact: ${canonicalString}`);
|
|
144
|
-
return {
|
|
145
|
-
fact,
|
|
146
|
-
signatures: []
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
const signature = util.encode64(privateKey.sign(digest));
|
|
150
|
-
return {
|
|
151
|
-
fact,
|
|
152
|
-
signatures: [{
|
|
153
|
-
signature,
|
|
154
|
-
publicKey: publicPem
|
|
155
|
-
}]
|
|
156
|
-
};
|
|
157
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { FactEnvelope } from "jinaga";
|
|
2
|
-
export declare class GraphSerializer {
|
|
3
|
-
private readonly write;
|
|
4
|
-
private index;
|
|
5
|
-
private indexByFactReference;
|
|
6
|
-
private publicKeys;
|
|
7
|
-
constructor(write: (chunk: string) => void);
|
|
8
|
-
serialize(result: FactEnvelope[]): void;
|
|
9
|
-
private getPredecessorIndexes;
|
|
10
|
-
private getFactIndex;
|
|
11
|
-
}
|
package/dist/http/serializer.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GraphSerializer = void 0;
|
|
4
|
-
class GraphSerializer {
|
|
5
|
-
constructor(write) {
|
|
6
|
-
this.write = write;
|
|
7
|
-
this.index = 0;
|
|
8
|
-
this.indexByFactReference = {};
|
|
9
|
-
this.publicKeys = [];
|
|
10
|
-
}
|
|
11
|
-
serialize(result) {
|
|
12
|
-
// Write the facts
|
|
13
|
-
for (const fact of result) {
|
|
14
|
-
// Skip facts that have already been written
|
|
15
|
-
const key = fact.fact.type + ":" + fact.fact.hash;
|
|
16
|
-
if (this.indexByFactReference.hasOwnProperty(key)) {
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
// Write any new public keys
|
|
20
|
-
for (const signature of fact.signatures) {
|
|
21
|
-
if (!this.publicKeys.includes(signature.publicKey)) {
|
|
22
|
-
const pkIndex = this.publicKeys.length;
|
|
23
|
-
const publicKey = JSON.stringify(signature.publicKey);
|
|
24
|
-
this.write(`PK${pkIndex.toString()}\n${publicKey}\n\n`);
|
|
25
|
-
this.publicKeys.push(signature.publicKey);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
// Write the fact
|
|
29
|
-
const factType = JSON.stringify(fact.fact.type);
|
|
30
|
-
const predecessorIndexes = JSON.stringify(this.getPredecessorIndexes(fact.fact.predecessors));
|
|
31
|
-
const factFields = JSON.stringify(fact.fact.fields);
|
|
32
|
-
let output = `${factType}\n${predecessorIndexes}\n${factFields}`;
|
|
33
|
-
// Write the signatures
|
|
34
|
-
for (const signature of fact.signatures) {
|
|
35
|
-
const publicKeyIndex = this.publicKeys.indexOf(signature.publicKey);
|
|
36
|
-
const publicKey = `PK${publicKeyIndex.toString()}`;
|
|
37
|
-
const signatureString = JSON.stringify(signature.signature);
|
|
38
|
-
output += `\n${publicKey}\n${signatureString}`;
|
|
39
|
-
}
|
|
40
|
-
output += "\n\n";
|
|
41
|
-
this.write(output);
|
|
42
|
-
this.indexByFactReference[key] = this.index;
|
|
43
|
-
this.index++;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
getPredecessorIndexes(predecessors) {
|
|
47
|
-
const result = {};
|
|
48
|
-
for (const role in predecessors) {
|
|
49
|
-
const reference = predecessors[role];
|
|
50
|
-
if (Array.isArray(reference)) {
|
|
51
|
-
result[role] = reference.map(r => this.getFactIndex(r));
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
result[role] = this.getFactIndex(reference);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return result;
|
|
58
|
-
}
|
|
59
|
-
getFactIndex(reference) {
|
|
60
|
-
const key = reference.type + ":" + reference.hash;
|
|
61
|
-
if (!this.indexByFactReference.hasOwnProperty(key)) {
|
|
62
|
-
throw new Error(`Fact reference not found in graph: ${key}`);
|
|
63
|
-
}
|
|
64
|
-
return this.indexByFactReference[key];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
exports.GraphSerializer = GraphSerializer;
|
|
68
|
-
//# sourceMappingURL=serializer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../../src/http/serializer.ts"],"names":[],"mappings":"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"}
|
package/src/http/serializer.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { FactEnvelope, FactReference, PredecessorCollection } from "jinaga";
|
|
2
|
-
|
|
3
|
-
type IndexPredecessorCollection = {
|
|
4
|
-
[role: string]: number | number[];
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export class GraphSerializer
|
|
8
|
-
{
|
|
9
|
-
private index = 0;
|
|
10
|
-
private indexByFactReference: { [key: string]: number } = {};
|
|
11
|
-
private publicKeys: string[] = [];
|
|
12
|
-
|
|
13
|
-
constructor(
|
|
14
|
-
private readonly write: (chunk: string) => void
|
|
15
|
-
) {}
|
|
16
|
-
|
|
17
|
-
serialize(result: FactEnvelope[]) {
|
|
18
|
-
// Write the facts
|
|
19
|
-
for (const fact of result) {
|
|
20
|
-
// Skip facts that have already been written
|
|
21
|
-
const key = fact.fact.type + ":" + fact.fact.hash;
|
|
22
|
-
if (this.indexByFactReference.hasOwnProperty(key)) {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Write any new public keys
|
|
27
|
-
for (const signature of fact.signatures) {
|
|
28
|
-
if (!this.publicKeys.includes(signature.publicKey)) {
|
|
29
|
-
const pkIndex = this.publicKeys.length;
|
|
30
|
-
const publicKey = JSON.stringify(signature.publicKey);
|
|
31
|
-
this.write(`PK${pkIndex.toString()}\n${publicKey}\n\n`);
|
|
32
|
-
this.publicKeys.push(signature.publicKey);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Write the fact
|
|
37
|
-
const factType = JSON.stringify(fact.fact.type);
|
|
38
|
-
const predecessorIndexes = JSON.stringify(this.getPredecessorIndexes(fact.fact.predecessors));
|
|
39
|
-
const factFields = JSON.stringify(fact.fact.fields);
|
|
40
|
-
|
|
41
|
-
let output = `${factType}\n${predecessorIndexes}\n${factFields}`;
|
|
42
|
-
|
|
43
|
-
// Write the signatures
|
|
44
|
-
for (const signature of fact.signatures) {
|
|
45
|
-
const publicKeyIndex = this.publicKeys.indexOf(signature.publicKey);
|
|
46
|
-
const publicKey = `PK${publicKeyIndex.toString()}`;
|
|
47
|
-
const signatureString = JSON.stringify(signature.signature);
|
|
48
|
-
|
|
49
|
-
output += `\n${publicKey}\n${signatureString}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
output += "\n\n";
|
|
53
|
-
|
|
54
|
-
this.write(output);
|
|
55
|
-
|
|
56
|
-
this.indexByFactReference[key] = this.index;
|
|
57
|
-
this.index++;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private getPredecessorIndexes(predecessors: PredecessorCollection): IndexPredecessorCollection {
|
|
62
|
-
const result: IndexPredecessorCollection = {};
|
|
63
|
-
for (const role in predecessors) {
|
|
64
|
-
const reference = predecessors[role];
|
|
65
|
-
if (Array.isArray(reference)) {
|
|
66
|
-
result[role] = reference.map(r => this.getFactIndex(r));
|
|
67
|
-
} else {
|
|
68
|
-
result[role] = this.getFactIndex(reference);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
private getFactIndex(reference: FactReference): number {
|
|
75
|
-
const key = reference.type + ":" + reference.hash;
|
|
76
|
-
if (!this.indexByFactReference.hasOwnProperty(key)) {
|
|
77
|
-
throw new Error(`Fact reference not found in graph: ${key}`);
|
|
78
|
-
}
|
|
79
|
-
return this.indexByFactReference[key];
|
|
80
|
-
}
|
|
81
|
-
}
|