Skip to content

Commit

Permalink
Add workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
zjkmxy committed Jan 19, 2024
1 parent d503850 commit ae342f5
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 3 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ echo "@ucla-irl:registry=https://npm.pkg.github.com" >> .npmrc
pnpm add @ucla-irl/ndnts-aux
```

If you are asked to login, create a GitHub access token and use the following command:
```bash
pnpm login --scope=@ucla-irl --auth-type=legacy --registry=https://npm.pkg.github.com
# Use the token for password
```

The current release does not currently mark peer-dependencies.
So please ignore the warnings given by `pnpm`.
Just install NDNts nightly build as usual and it will work.
Expand All @@ -18,7 +24,6 @@ Unfortunately, the denoland release does not work. Please ignore that.

## TODOs

- Add CI for automatic release
- Add more test
- Add a class for NDN workspace
- Add name pattern match and some ntschema for better namespace management.
5 changes: 3 additions & 2 deletions src/security/cert-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { Data, Interest, Name, Signer, Verifier } from '@ndn/packet';
import { Certificate, createSigner, createVerifier, ECDSA } from '@ndn/keychain';
import { Endpoint } from '@ndn/endpoint';
import { Storage } from '../storage/mod.ts';
import { SecurityAgent } from './types.ts';

/**
* A Signer & Verifier that handles security authentication.
* CertStorage itself is not a storage, actually. Depend on an external storage.
* Note: CertStorage will not serve the certificate.
*/
export class CertStorage {
export class CertStorage implements SecurityAgent {
private _signer: Signer | undefined;
readonly readyEvent: Promise<void>;

Expand All @@ -36,7 +37,7 @@ export class CertStorage {

/** Obtain the signer */
get signer() {
return this._signer;
return this._signer!;
}

/** Obtain this node's own certificate */
Expand Down
1 change: 1 addition & 0 deletions src/security/mod.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './cert-storage.ts';
export * from './types.ts';
6 changes: 6 additions & 0 deletions src/security/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Signer, Verifier } from '@ndn/packet';

export interface SecurityAgent {
signer: Signer;
verifier: Verifier;
}
52 changes: 52 additions & 0 deletions src/storage/deno-kv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Storage } from './types.ts';

/**
* A storage based on DenoKV.
* Not actually included in the exported package. May need Custom Shims.
*/
export class DenoKvStorage implements Storage {
constructor(
public readonly kv: Deno.Kv,
) {
}

public static async create(path?: string) {
return new DenoKvStorage(await Deno.openKv(path));
}

async get(key: string): Promise<Uint8Array | undefined> {
const ret = await this.kv.get<Uint8Array>([key]);
return ret.value ?? undefined;
}

async set(key: string, value: Uint8Array | undefined): Promise<void> {
await this.kv.set([key], value);
}

async has(key: string): Promise<boolean> {
const ret = await this.kv.get<Uint8Array>([key]);
return !!ret.value;
}

async delete(key: string): Promise<boolean> {
const ret = await this.kv.get<Uint8Array>([key]);
if (ret.value) {
await this.kv.delete([key]);
return true;
} else {
return false;
}
}

async clear(): Promise<void> {
const entries = this.kv.list({ prefix: [] });
for await (const entry of entries) {
await this.kv.delete(entry.key);
}
}

close(): Promise<void> {
this.kv.close();
return Promise.resolve();
}
}
1 change: 1 addition & 0 deletions src/storage/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './types.ts';
export * from './in-memory.ts';
export * from './file-system.ts';
// export * from './deno-kv.ts';
1 change: 1 addition & 0 deletions src/workspace/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './workspace.ts';
84 changes: 84 additions & 0 deletions src/workspace/workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Storage } from '../storage/mod.ts';
import { Endpoint } from '@ndn/endpoint';
import type { Name, Signer, Verifier } from '@ndn/packet';
import { encodeSyncState, parseSyncState, SyncAgent } from '../sync-agent/mod.ts';
import { NdnSvsAdaptor, YjsStateManager } from '../adaptors/mod.ts';
import * as Y from 'yjs';

export class Workspace {
private constructor(
public readonly nodeId: Name,
public readonly persistStore: Storage,
public readonly endpoint: Endpoint,
public readonly onReset: (() => void) | undefined,
public readonly syncAgent: SyncAgent,
public readonly yjsSnapshotMgr: YjsStateManager,
public readonly yjsAdaptor: NdnSvsAdaptor,
) {
}

public static async create(opts: {
nodeId: Name;
persistStore: Storage;
endpoint: Endpoint;
rootDoc: Y.Doc;
signer: Signer;
verifier: Verifier;
onReset?: () => void;
createNewDoc?: () => Promise<void>;
}) {
// Sync Agents
const syncAgent = await SyncAgent.create(
opts.nodeId,
opts.persistStore,
opts.endpoint,
opts.signer,
opts.verifier,
opts.onReset,
);

// Root doc using CRDT and Sync
const yjsAdaptor = new NdnSvsAdaptor(
syncAgent,
opts.rootDoc,
'doc',
);
const yjsSnapshotMgr = new YjsStateManager(
() => encodeSyncState(syncAgent!.getUpdateSyncSV()),
opts.rootDoc,
// No key conflict in this case. If we are worried, use anothe sub-folder.
opts.persistStore,
);

// Load or create
if (opts.createNewDoc) {
await opts.createNewDoc();
} else {
const state = await yjsSnapshotMgr.loadLocalSnapshot((update) => {
yjsAdaptor!.handleSyncUpdate(update);
return Promise.resolve();
});
await syncAgent.replayUpdates('doc', state ? parseSyncState(state) : undefined);
}

// Start Sync
syncAgent.ready = true;

return new Workspace(
opts.nodeId,
opts.persistStore,
opts.endpoint,
opts.onReset,
syncAgent,
yjsSnapshotMgr,
yjsAdaptor,
);
}

public destroy() {
this.syncAgent.ready = false;
this.yjsSnapshotMgr.destroy();
this.syncAgent.destroy();
this.persistStore.close();
}
}

0 comments on commit ae342f5

Please sign in to comment.