+
+
(flashlightOn = !flashlightOn)}
/>
@@ -66,7 +138,7 @@ let flashlightOn = $state(false);
-
Website URL
- https://www.aave.inc/login
+ {scannedData?.content}
{ codeScannedDrawerOpen = false; }}
+ callback={() => { codeScannedDrawerOpen = false; startScan(); }}
>
Decline
@@ -96,6 +168,7 @@ let flashlightOn = $state(false);
callback={() => {
codeScannedDrawerOpen = false
loggedInDrawerOpen = true
+ startScan();
}}
>
Confirm
@@ -112,7 +185,7 @@ let flashlightOn = $state(false);
-
{ loggedInDrawerOpen = false; }}
+ callback={() => { loggedInDrawerOpen = false; startScan(); }}
>
Close
diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte
index 7d3a9d9f..e88d6712 100644
--- a/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte
@@ -2,15 +2,15 @@
import { SettingsNavigationBtn } from "$lib/fragments";
import { runtime } from "$lib/global/runtime.svelte";
import {
- Key01Icon,
- LanguageSquareIcon,
- Link02Icon,
- PinCodeIcon,
- Shield01Icon,
+ Key01Icon,
+ LanguageSquareIcon,
+ Link02Icon,
+ PinCodeIcon,
+ Shield01Icon,
} from "@hugeicons/core-free-icons";
$effect(() => {
- runtime.header.title = "Settings";
+ runtime.header.title = "Settings";
});
diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/history/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/history/+page.svelte
index bfa2a99e..a54f803e 100644
--- a/infrastructure/eid-wallet/src/routes/(app)/settings/history/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(app)/settings/history/+page.svelte
@@ -2,7 +2,7 @@
import { runtime } from "$lib/global/runtime.svelte";
$effect(() => {
- runtime.header.title = "History";
+ runtime.header.title = "History";
});
diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/language/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/language/+page.svelte
index 1c64d656..b70b794a 100644
--- a/infrastructure/eid-wallet/src/routes/(app)/settings/language/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(app)/settings/language/+page.svelte
@@ -3,15 +3,15 @@ import { runtime } from "$lib/global/runtime.svelte";
import { Selector } from "$lib/ui";
let languages: { name: string; country: string }[] = [
- { name: "English", country: "gb" },
- { name: "Spanish", country: "es" },
- { name: "German", country: "de" },
- { name: "French", country: "fr" },
+ { name: "English", country: "gb" },
+ { name: "Spanish", country: "es" },
+ { name: "German", country: "de" },
+ { name: "French", country: "fr" },
];
let selected = $state("");
$effect(() => {
- runtime.header.title = "Language";
+ runtime.header.title = "Language";
});
diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelte
index 8607c2c1..6a0ba3db 100644
--- a/infrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelte
@@ -11,18 +11,18 @@ let isError = $state(false);
let showDrawer = $state(false);
const handleClose = async () => {
- // close functionality goes here.
- showDrawer = false;
+ // close functionality goes here.
+ showDrawer = false;
};
const handleChangePIN = async () => {
- if (repeatPin.length === 4 && newPin !== repeatPin) isError = true;
- if (!isError) showDrawer = true;
+ if (repeatPin.length === 4 && newPin !== repeatPin) isError = true;
+ if (!isError) showDrawer = true;
};
$effect(() => {
- runtime.header.title = "Change PIN";
- if (repeatPin.length === 4 && newPin === repeatPin) isError = false;
+ runtime.header.title = "Change PIN";
+ if (repeatPin.length === 4 && newPin === repeatPin) isError = false;
});
diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/privacy/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/privacy/+page.svelte
index 452ecfec..9ceac2eb 100644
--- a/infrastructure/eid-wallet/src/routes/(app)/settings/privacy/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(app)/settings/privacy/+page.svelte
@@ -2,7 +2,7 @@
import { runtime } from "$lib/global/runtime.svelte";
$effect(() => {
- runtime.header.title = "Privacy";
+ runtime.header.title = "Privacy";
});
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte
index 90cf9095..9a8b7324 100644
--- a/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte
@@ -5,7 +5,7 @@ import IdentityCard from "$lib/fragments/IdentityCard/IdentityCard.svelte";
import { ButtonAction } from "$lib/ui";
const handleFinish = async () => {
- await goto("/main");
+ await goto("/main");
};
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte
new file mode 100644
index 00000000..4fd62b79
--- /dev/null
+++ b/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte
@@ -0,0 +1,97 @@
+
+
+
+
+
+ handlePinInput(pin)} />
+ Your PIN does not match, try again.
+
+
+ Clear Pin
+
+
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte
index c349b53e..ec88a5f3 100644
--- a/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte
@@ -6,13 +6,13 @@ import { ButtonAction, Drawer } from "$lib/ui";
let isPaneOpen = $state(false);
const handleGetStarted = async () => {
- //get started functionality
- isPaneOpen = true;
+ //get started functionality
+ isPaneOpen = true;
};
const handleNext = async () => {
- //handle next functionlity
- goto("/verify");
+ //handle next functionlity
+ goto("/verify");
};
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/register/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/register/+page.svelte
index e910a576..b9d788a6 100644
--- a/infrastructure/eid-wallet/src/routes/(auth)/register/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(auth)/register/+page.svelte
@@ -1,55 +1,87 @@
@@ -61,7 +93,8 @@ $effect(() => {
subtitle="Enter a 4-digit PIN code"
class="mb-[14vh]"
/>
-
+
+ Your PIN does not match, try again.
Confirm
@@ -73,8 +106,7 @@ $effect(() => {
subtitle="Confirm by entering pin again"
class="mb-[14vh]"
/>
-
- Your PIN does not match, try again.
+
Confirm
@@ -106,10 +138,13 @@ $effect(() => {
{#if !isBiometricsAdded}
Skip
-
Set up
+
+
Set up
+
Biometrics unavailable.
+
{:else}
Continue
{/if}
{/if}
-
\ No newline at end of file
+
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte
index 1490cbc0..93ff78a1 100644
--- a/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte
@@ -4,7 +4,7 @@ import { Hero, IdentityCard } from "$lib/fragments";
import { ButtonAction } from "$lib/ui";
const handleNext = async () => {
- await goto("/e-passport");
+ await goto("/e-passport");
};
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
index 9940fb04..c7fb2de2 100644
--- a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
@@ -1,7 +1,28 @@
@@ -12,5 +33,12 @@ import { ButtonAction } from "$lib/ui";
/>
- goto("/register")}>I'm ready
-
\ No newline at end of file
+ {
+ try {
+ await handleVerification();
+ } catch (error) {
+ console.error("Verification failed:", error);
+ // Consider adding user-facing error handling here
+ }
+ }}>I'm ready
+
diff --git a/infrastructure/eid-wallet/src/routes/+layout.svelte b/infrastructure/eid-wallet/src/routes/+layout.svelte
index 10cf01da..dc6e7a12 100644
--- a/infrastructure/eid-wallet/src/routes/+layout.svelte
+++ b/infrastructure/eid-wallet/src/routes/+layout.svelte
@@ -1,68 +1,93 @@
-
+
{#if showSplashScreen}
{:else}
diff --git a/infrastructure/eid-wallet/src/routes/+page.svelte b/infrastructure/eid-wallet/src/routes/+page.svelte
index b709cedf..0936edc9 100644
--- a/infrastructure/eid-wallet/src/routes/+page.svelte
+++ b/infrastructure/eid-wallet/src/routes/+page.svelte
@@ -1,7 +1,40 @@
+
+
+ Clear Pin
+
+
+{cleared ? "Pin cleared, restart app" : "Login"}
diff --git a/infrastructure/w3id/biome.json b/infrastructure/w3id/biome.json
index f8e45519..860a937f 100644
--- a/infrastructure/w3id/biome.json
+++ b/infrastructure/w3id/biome.json
@@ -1,3 +1,4 @@
{
- "$schema": "./node_modules/@biomejs/biome/configuration_schema.json"
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
+ "extends": ["../../biome.json"]
}
diff --git a/infrastructure/w3id/package.json b/infrastructure/w3id/package.json
index a278bb85..5d09a542 100644
--- a/infrastructure/w3id/package.json
+++ b/infrastructure/w3id/package.json
@@ -1,47 +1,48 @@
{
- "name": "w3id",
- "version": "1.0.0",
- "description": "",
- "scripts": {
- "test": "vitest",
- "dev": "tsc --watch",
- "check-format": "npx @biomejs/biome format ./src",
- "format": "npx @biomejs/biome format --write ./src",
- "lint": "npx @biomejs/biome lint --write ./src",
- "check": "npx @biomejs/biome check --write ./src",
- "check-types": "tsc --noEmit",
- "build": "npm run build:node && npm run build:browser",
- "build:node": "tsc -p tsconfig.node.json",
- "build:browser": "tsc -p tsconfig.browser.json",
- "postinstall": "npm run build"
- },
- "keywords": [],
- "author": "",
- "license": "ISC",
- "dependencies": {
- "canonicalize": "^2.1.0",
- "multiformats": "^13.3.2",
- "tweetnacl": "^1.0.3",
- "uuid": "^11.1.0"
- },
- "devDependencies": {
- "@ngneat/falso": "^7.3.0",
- "@types/node": "^22.13.10",
- "typescript": "^5.8.2",
- "vitest": "^3.0.9"
- },
- "main": "./dist/node/index.js",
- "module": "./dist/browser/index.js",
- "types": "./dist/types/index.d.ts",
- "exports": {
- ".": {
- "require": "./dist/node/index.js",
- "import": "./dist/browser/index.js",
- "types": "./dist/types/index.d.ts"
+ "name": "w3id",
+ "version": "1.0.0",
+ "description": "",
+ "scripts": {
+ "test": "vitest",
+ "dev": "tsc --watch",
+ "check-format": "npx @biomejs/biome format ./src",
+ "format": "npx @biomejs/biome format --write ./src",
+ "lint": "npx @biomejs/biome lint --write ./src",
+ "check": "npx @biomejs/biome check --write ./src",
+ "check-types": "tsc --noEmit",
+ "build": "npm run build:node && npm run build:browser",
+ "build:node": "tsc -p tsconfig.node.json",
+ "build:browser": "tsc -p tsconfig.browser.json",
+ "postinstall": "npm run build"
},
- "./package.json": "./package.json"
- },
- "files": [
- "dist/**/*"
- ]
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "canonicalize": "^2.1.0",
+ "multiformats": "^13.3.2",
+ "tweetnacl": "^1.0.3",
+ "uuid": "^11.1.0"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "^1.9.4",
+ "@ngneat/falso": "^7.3.0",
+ "@types/node": "^22.13.10",
+ "typescript": "^5.8.2",
+ "vitest": "^3.0.9"
+ },
+ "main": "./dist/node/index.js",
+ "module": "./dist/browser/index.js",
+ "types": "./dist/types/index.d.ts",
+ "exports": {
+ ".": {
+ "require": "./dist/node/index.js",
+ "import": "./dist/browser/index.js",
+ "types": "./dist/types/index.d.ts"
+ },
+ "./package.json": "./package.json"
+ },
+ "files": [
+ "dist/**/*"
+ ]
}
diff --git a/infrastructure/w3id/src/errors/errors.ts b/infrastructure/w3id/src/errors/errors.ts
index 31bc90ef..2dafde3b 100644
--- a/infrastructure/w3id/src/errors/errors.ts
+++ b/infrastructure/w3id/src/errors/errors.ts
@@ -1,34 +1,34 @@
export class MalformedIndexChainError extends Error {
- constructor(message = "Malformed index chain detected") {
- super(message);
- this.name = "MalformedIndexChainError";
- }
+ constructor(message = "Malformed index chain detected") {
+ super(message);
+ this.name = "MalformedIndexChainError";
+ }
}
export class MalformedHashChainError extends Error {
- constructor(message = "Malformed hash chain detected") {
- super(message);
- this.name = "MalformedHashChainError";
- }
+ constructor(message = "Malformed hash chain detected") {
+ super(message);
+ this.name = "MalformedHashChainError";
+ }
}
export class BadSignatureError extends Error {
- constructor(message = "Bad signature detected") {
- super(message);
- this.name = "BadSignatureError";
- }
+ constructor(message = "Bad signature detected") {
+ super(message);
+ this.name = "BadSignatureError";
+ }
}
export class BadNextKeySpecifiedError extends Error {
- constructor(message = "Bad next key specified") {
- super(message);
- this.name = "BadNextKeySpecifiedError";
- }
+ constructor(message = "Bad next key specified") {
+ super(message);
+ this.name = "BadNextKeySpecifiedError";
+ }
}
export class BadOptionsSpecifiedError extends Error {
- constructor(message = "Bad options specified") {
- super(message);
- this.name = "BadOptionsSpecifiedError";
- }
+ constructor(message = "Bad options specified") {
+ super(message);
+ this.name = "BadOptionsSpecifiedError";
+ }
}
diff --git a/infrastructure/w3id/src/logs/log-manager.ts b/infrastructure/w3id/src/logs/log-manager.ts
index d7cf2f6b..99a64c4d 100644
--- a/infrastructure/w3id/src/logs/log-manager.ts
+++ b/infrastructure/w3id/src/logs/log-manager.ts
@@ -1,22 +1,22 @@
import canonicalize from "canonicalize";
import {
- BadNextKeySpecifiedError,
- BadOptionsSpecifiedError,
- BadSignatureError,
- MalformedHashChainError,
- MalformedIndexChainError,
+ BadNextKeySpecifiedError,
+ BadOptionsSpecifiedError,
+ BadSignatureError,
+ MalformedHashChainError,
+ MalformedIndexChainError,
} from "../errors/errors";
import { isSubsetOf } from "../utils/array";
import { hash } from "../utils/hash";
import {
- type CreateLogEventOptions,
- type GenesisLogOptions,
- type LogEvent,
- type RotationLogOptions,
- type Signer,
- type VerifierCallback,
- isGenesisOptions,
- isRotationOptions,
+ type CreateLogEventOptions,
+ type GenesisLogOptions,
+ type LogEvent,
+ type RotationLogOptions,
+ type Signer,
+ type VerifierCallback,
+ isGenesisOptions,
+ isRotationOptions,
} from "./log.types";
import type { StorageSpec } from "./storage/storage-spec";
@@ -28,175 +28,185 @@ import type { StorageSpec } from "./storage/storage-spec";
// TODO: Create a specification link inside our docs for how generation of identifier works
export class IDLogManager {
- repository: StorageSpec;
- signer: Signer;
-
- constructor(repository: StorageSpec, signer: Signer) {
- this.repository = repository;
- this.signer = signer;
- }
-
- /**
- * Validate a chain of W3ID logs
- *
- * @param {LogEvent[]} log
- * @param {VerifierCallback} verifyCallback
- * @returns {Promise}
- */
-
- static async validateLogChain(
- log: LogEvent[],
- verifyCallback: VerifierCallback,
- ): Promise {
- let currIndex = 0;
- let currentNextKeyHashesSeen: string[] = [];
- let lastUpdateKeysSeen: string[] = [];
- let lastHash: string | null = null;
-
- for (const e of log) {
- const [_index, _hash] = e.versionId.split("-");
- const index = Number(_index);
- if (currIndex !== index) throw new MalformedIndexChainError();
- const hashedUpdateKeys = await Promise.all(
- e.updateKeys.map(async (k) => await hash(k)),
- );
- if (index > 0) {
- const updateKeysSeen = isSubsetOf(
- hashedUpdateKeys,
- currentNextKeyHashesSeen,
- );
- if (!updateKeysSeen || lastHash !== _hash)
- throw new MalformedHashChainError();
- }
-
- currentNextKeyHashesSeen = e.nextKeyHashes;
- await IDLogManager.verifyLogEventProof(
- e,
- lastUpdateKeysSeen.length > 0 ? lastUpdateKeysSeen : e.updateKeys,
- verifyCallback,
- );
- lastUpdateKeysSeen = e.updateKeys;
- currIndex++;
- lastHash = await hash(canonicalize(e) as string);
- }
- return true;
- }
-
- /**
- * Validate cryptographic signature on a single LogEvent
- *
- * @param {LogEvent} e
- * @param {string[]} currentUpdateKeys
- * @param {VerifierCallback} verifyCallback
- * @returns {Promise}
- */
- private static async verifyLogEventProof(
- e: LogEvent,
- currentUpdateKeys: string[],
- verifyCallback: VerifierCallback,
- ): Promise {
- const proofs = e.proofs;
- const copy = JSON.parse(JSON.stringify(e));
- // biome-ignore lint/performance/noDelete: we need to delete proof completely
- delete copy.proofs;
- const canonicalJson = canonicalize(copy);
- let verified = false;
- if (!proofs)
- throw new BadSignatureError("No proof found in the log event.");
- for (const key of currentUpdateKeys) {
- const signValidates = await verifyCallback(
- canonicalJson as string,
- proofs,
- key,
- );
- if (signValidates) verified = true;
- }
- if (!verified) throw new BadSignatureError();
- }
-
- /**
- * Append a new log entry for a W3ID
- *
- * @param {LogEvent[]} entries
- * @param {RotationLogOptions} options
- * @returns Promise
- */
- private async appendEntry(entries: LogEvent[], options: RotationLogOptions) {
- const { nextKeyHashes, nextKeySigner } = options;
- const latestEntry = entries[entries.length - 1];
- const logHash = await hash(latestEntry);
- const index = Number(latestEntry.versionId.split("-")[0]) + 1;
-
- const currKeyHash = await hash(nextKeySigner.pubKey);
- if (!latestEntry.nextKeyHashes.includes(currKeyHash))
- throw new BadNextKeySpecifiedError();
-
- const logEvent: LogEvent = {
- id: latestEntry.id,
- versionTime: new Date(Date.now()),
- versionId: `${index}-${logHash}`,
- updateKeys: [nextKeySigner.pubKey],
- nextKeyHashes: nextKeyHashes,
- method: "w3id:v0.0.0",
- };
-
- const signature = await this.signer.sign(canonicalize(logEvent) as string);
- logEvent.proofs = [
- {
- kid: `${logEvent.id}#0`,
- alg: this.signer.alg,
- signature,
- },
- ];
-
- await this.repository.create(logEvent);
- this.signer = nextKeySigner;
- return logEvent;
- }
-
- /**
- * Create genesis entry for a W3ID log
- *
- * @param {GenesisLogOptions} options
- * @returns Promise
- */
- private async createGenesisEntry(options: GenesisLogOptions) {
- const { id, nextKeyHashes } = options;
- const idTag = id.includes("@") ? id.split("@")[1] : id;
- const logEvent: LogEvent = {
- id,
- versionId: `0-${idTag}`,
- versionTime: new Date(Date.now()),
- updateKeys: [this.signer.pubKey],
- nextKeyHashes: nextKeyHashes,
- method: "w3id:v0.0.0",
- };
- const signature = await this.signer.sign(canonicalize(logEvent) as string);
- logEvent.proofs = [
- {
- kid: `${id}#0`,
- alg: this.signer.alg,
- signature,
- },
- ];
-
- await this.repository.create(logEvent);
- return logEvent;
- }
-
- /**
- * Create a log event and save it to the repository
- *
- * @param {CreateLogEventOptions} options
- * @returns Promise
- */
- async createLogEvent(options: CreateLogEventOptions): Promise {
- const entries = await this.repository.findMany({});
- if (entries.length > 0) {
- if (!isRotationOptions(options)) throw new BadOptionsSpecifiedError();
- return this.appendEntry(entries, options);
- }
- if (!isGenesisOptions(options)) throw new BadOptionsSpecifiedError();
- return this.createGenesisEntry(options);
- }
+ repository: StorageSpec;
+ signer: Signer;
+
+ constructor(repository: StorageSpec, signer: Signer) {
+ this.repository = repository;
+ this.signer = signer;
+ }
+
+ /**
+ * Validate a chain of W3ID logs
+ *
+ * @param {LogEvent[]} log
+ * @param {VerifierCallback} verifyCallback
+ * @returns {Promise}
+ */
+
+ static async validateLogChain(
+ log: LogEvent[],
+ verifyCallback: VerifierCallback,
+ ): Promise {
+ let currIndex = 0;
+ let currentNextKeyHashesSeen: string[] = [];
+ let lastUpdateKeysSeen: string[] = [];
+ let lastHash: string | null = null;
+
+ for (const e of log) {
+ const [_index, _hash] = e.versionId.split("-");
+ const index = Number(_index);
+ if (currIndex !== index) throw new MalformedIndexChainError();
+ const hashedUpdateKeys = await Promise.all(
+ e.updateKeys.map(async (k) => await hash(k)),
+ );
+ if (index > 0) {
+ const updateKeysSeen = isSubsetOf(
+ hashedUpdateKeys,
+ currentNextKeyHashesSeen,
+ );
+ if (!updateKeysSeen || lastHash !== _hash)
+ throw new MalformedHashChainError();
+ }
+
+ currentNextKeyHashesSeen = e.nextKeyHashes;
+ await IDLogManager.verifyLogEventProof(
+ e,
+ lastUpdateKeysSeen.length > 0
+ ? lastUpdateKeysSeen
+ : e.updateKeys,
+ verifyCallback,
+ );
+ lastUpdateKeysSeen = e.updateKeys;
+ currIndex++;
+ lastHash = await hash(canonicalize(e) as string);
+ }
+ return true;
+ }
+
+ /**
+ * Validate cryptographic signature on a single LogEvent
+ *
+ * @param {LogEvent} e
+ * @param {string[]} currentUpdateKeys
+ * @param {VerifierCallback} verifyCallback
+ * @returns {Promise}
+ */
+ private static async verifyLogEventProof(
+ e: LogEvent,
+ currentUpdateKeys: string[],
+ verifyCallback: VerifierCallback,
+ ): Promise {
+ const proofs = e.proofs;
+ const copy = JSON.parse(JSON.stringify(e));
+ // biome-ignore lint/performance/noDelete: we need to delete proof completely
+ delete copy.proofs;
+ const canonicalJson = canonicalize(copy);
+ let verified = false;
+ if (!proofs)
+ throw new BadSignatureError("No proof found in the log event.");
+ for (const key of currentUpdateKeys) {
+ const signValidates = await verifyCallback(
+ canonicalJson as string,
+ proofs,
+ key,
+ );
+ if (signValidates) verified = true;
+ }
+ if (!verified) throw new BadSignatureError();
+ }
+
+ /**
+ * Append a new log entry for a W3ID
+ *
+ * @param {LogEvent[]} entries
+ * @param {RotationLogOptions} options
+ * @returns Promise
+ */
+ private async appendEntry(
+ entries: LogEvent[],
+ options: RotationLogOptions,
+ ) {
+ const { nextKeyHashes, nextKeySigner } = options;
+ const latestEntry = entries[entries.length - 1];
+ const logHash = await hash(latestEntry);
+ const index = Number(latestEntry.versionId.split("-")[0]) + 1;
+
+ const currKeyHash = await hash(nextKeySigner.pubKey);
+ if (!latestEntry.nextKeyHashes.includes(currKeyHash))
+ throw new BadNextKeySpecifiedError();
+
+ const logEvent: LogEvent = {
+ id: latestEntry.id,
+ versionTime: new Date(Date.now()),
+ versionId: `${index}-${logHash}`,
+ updateKeys: [nextKeySigner.pubKey],
+ nextKeyHashes: nextKeyHashes,
+ method: "w3id:v0.0.0",
+ };
+
+ const signature = await this.signer.sign(
+ canonicalize(logEvent) as string,
+ );
+ logEvent.proofs = [
+ {
+ kid: `${logEvent.id}#0`,
+ alg: this.signer.alg,
+ signature,
+ },
+ ];
+
+ await this.repository.create(logEvent);
+ this.signer = nextKeySigner;
+ return logEvent;
+ }
+
+ /**
+ * Create genesis entry for a W3ID log
+ *
+ * @param {GenesisLogOptions} options
+ * @returns Promise
+ */
+ private async createGenesisEntry(options: GenesisLogOptions) {
+ const { id, nextKeyHashes } = options;
+ const idTag = id.includes("@") ? id.split("@")[1] : id;
+ const logEvent: LogEvent = {
+ id,
+ versionId: `0-${idTag}`,
+ versionTime: new Date(Date.now()),
+ updateKeys: [this.signer.pubKey],
+ nextKeyHashes: nextKeyHashes,
+ method: "w3id:v0.0.0",
+ };
+ const signature = await this.signer.sign(
+ canonicalize(logEvent) as string,
+ );
+ logEvent.proofs = [
+ {
+ kid: `${id}#0`,
+ alg: this.signer.alg,
+ signature,
+ },
+ ];
+
+ await this.repository.create(logEvent);
+ return logEvent;
+ }
+
+ /**
+ * Create a log event and save it to the repository
+ *
+ * @param {CreateLogEventOptions} options
+ * @returns Promise
+ */
+ async createLogEvent(options: CreateLogEventOptions): Promise {
+ const entries = await this.repository.findMany({});
+ if (entries.length > 0) {
+ if (!isRotationOptions(options))
+ throw new BadOptionsSpecifiedError();
+ return this.appendEntry(entries, options);
+ }
+ if (!isGenesisOptions(options)) throw new BadOptionsSpecifiedError();
+ return this.createGenesisEntry(options);
+ }
}
diff --git a/infrastructure/w3id/src/logs/log.types.ts b/infrastructure/w3id/src/logs/log.types.ts
index 0545ed62..a40f3eb4 100644
--- a/infrastructure/w3id/src/logs/log.types.ts
+++ b/infrastructure/w3id/src/logs/log.types.ts
@@ -1,67 +1,67 @@
export type Proof = {
- kid: string;
- signature: string;
- alg: string;
+ kid: string;
+ signature: string;
+ alg: string;
};
export type LogEvent = {
- id: string;
- versionId: string;
- versionTime: Date;
- updateKeys: string[];
- nextKeyHashes: string[];
- method: `w3id:v${string}`;
- proofs?: Proof[];
+ id: string;
+ versionId: string;
+ versionTime: Date;
+ updateKeys: string[];
+ nextKeyHashes: string[];
+ method: `w3id:v${string}`;
+ proofs?: Proof[];
};
export type VerifierCallback = (
- message: string,
- proofs: Proof[],
- pubKey: string,
+ message: string,
+ proofs: Proof[],
+ pubKey: string,
) => Promise;
export type JWTHeader = {
- alg: string;
- typ: "JWT";
- kid?: string;
+ alg: string;
+ typ: "JWT";
+ kid?: string;
};
export type JWTPayload = {
- [key: string]: unknown;
- iat?: number;
- exp?: number;
- nbf?: number;
- iss?: string;
- sub?: string;
- aud?: string;
- jti?: string;
+ [key: string]: unknown;
+ iat?: number;
+ exp?: number;
+ nbf?: number;
+ iss?: string;
+ sub?: string;
+ aud?: string;
+ jti?: string;
};
export type Signer = {
- sign: (message: string) => Promise | string;
- pubKey: string;
- alg: string;
+ sign: (message: string) => Promise | string;
+ pubKey: string;
+ alg: string;
};
export type RotationLogOptions = {
- nextKeyHashes: string[];
- nextKeySigner: Signer;
+ nextKeyHashes: string[];
+ nextKeySigner: Signer;
};
export type GenesisLogOptions = {
- nextKeyHashes: string[];
- id: string;
+ nextKeyHashes: string[];
+ id: string;
};
export function isGenesisOptions(
- options: CreateLogEventOptions,
+ options: CreateLogEventOptions,
): options is GenesisLogOptions {
- return "id" in options;
+ return "id" in options;
}
export function isRotationOptions(
- options: CreateLogEventOptions,
+ options: CreateLogEventOptions,
): options is RotationLogOptions {
- return "nextKeySigner" in options;
+ return "nextKeySigner" in options;
}
export type CreateLogEventOptions = GenesisLogOptions | RotationLogOptions;
diff --git a/infrastructure/w3id/src/logs/storage/storage-spec.ts b/infrastructure/w3id/src/logs/storage/storage-spec.ts
index ab66f9d7..9a41770c 100644
--- a/infrastructure/w3id/src/logs/storage/storage-spec.ts
+++ b/infrastructure/w3id/src/logs/storage/storage-spec.ts
@@ -5,40 +5,40 @@
*/
export declare class StorageSpec {
- /**
- * Build a new storage driver
- *
- * @param {...any[]} props
- * @returns Promise
- */
-
- // biome-ignore lint: lint/suspicious/noExplicitAny
- public static build(...props: any[]): Promise>;
-
- /**
- * Create a new entry in the storage
- *
- * @param body
- * @returns Promise
- */
-
- public create(body: T): Promise;
-
- /**
- * Find one entry in the storage using partial of K
- *
- * @param {Partial} options
- * @returns Promise
- */
-
- public findOne(options: Partial): Promise;
-
- /**
- * Find many entities in the storage using partial of K
- *
- * @param {Partial} options
- * @returns Promise
- */
-
- public findMany(options: Partial): Promise;
+ /**
+ * Build a new storage driver
+ *
+ * @param {...any[]} props
+ * @returns Promise
+ */
+
+ // biome-ignore lint: lint/suspicious/noExplicitAny
+ public static build(...props: any[]): Promise>;
+
+ /**
+ * Create a new entry in the storage
+ *
+ * @param body
+ * @returns Promise
+ */
+
+ public create(body: T): Promise;
+
+ /**
+ * Find one entry in the storage using partial of K
+ *
+ * @param {Partial} options
+ * @returns Promise
+ */
+
+ public findOne(options: Partial): Promise;
+
+ /**
+ * Find many entities in the storage using partial of K
+ *
+ * @param {Partial} options
+ * @returns Promise
+ */
+
+ public findMany(options: Partial): Promise;
}
diff --git a/infrastructure/w3id/src/utils/array.ts b/infrastructure/w3id/src/utils/array.ts
index 19224fec..f1dd49f7 100644
--- a/infrastructure/w3id/src/utils/array.ts
+++ b/infrastructure/w3id/src/utils/array.ts
@@ -11,18 +11,18 @@
*/
export function isSubsetOf(a: unknown[], b: unknown[]) {
- const map = new Map();
+ const map = new Map();
- for (const el of b) {
- map.set(el, (map.get(el) || 0) + 1);
- }
+ for (const el of b) {
+ map.set(el, (map.get(el) || 0) + 1);
+ }
- for (const el of a) {
- if (!map.has(el) || map.get(el) === 0) {
- return false;
- }
- map.set(el, map.get(el) - 1);
- }
+ for (const el of a) {
+ if (!map.has(el) || map.get(el) === 0) {
+ return false;
+ }
+ map.set(el, map.get(el) - 1);
+ }
- return true;
+ return true;
}
diff --git a/infrastructure/w3id/src/utils/codec.ts b/infrastructure/w3id/src/utils/codec.ts
index f05ce212..8181f59e 100644
--- a/infrastructure/w3id/src/utils/codec.ts
+++ b/infrastructure/w3id/src/utils/codec.ts
@@ -1,20 +1,20 @@
export function uint8ArrayToHex(bytes: Uint8Array): string {
- return Array.from(bytes)
- .map((b) => b.toString(16).padStart(2, "0"))
- .join("");
+ return Array.from(bytes)
+ .map((b) => b.toString(16).padStart(2, "0"))
+ .join("");
}
export function hexToUint8Array(hex: string): Uint8Array {
- if (hex.length % 2 !== 0) {
- throw new Error("Hex string must have an even length");
- }
- const bytes = new Uint8Array(hex.length / 2);
- for (let i = 0; i < hex.length; i += 2) {
- bytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);
- }
- return bytes;
+ if (hex.length % 2 !== 0) {
+ throw new Error("Hex string must have an even length");
+ }
+ const bytes = new Uint8Array(hex.length / 2);
+ for (let i = 0; i < hex.length; i += 2) {
+ bytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);
+ }
+ return bytes;
}
export function stringToUint8Array(str: string): Uint8Array {
- return new TextEncoder().encode(str);
+ return new TextEncoder().encode(str);
}
diff --git a/infrastructure/w3id/src/utils/hash.ts b/infrastructure/w3id/src/utils/hash.ts
index d7e8c852..4055a495 100644
--- a/infrastructure/w3id/src/utils/hash.ts
+++ b/infrastructure/w3id/src/utils/hash.ts
@@ -2,25 +2,25 @@ import canonicalize from "canonicalize";
import { uint8ArrayToHex } from "./codec";
export async function hash(
- input: string | Record,
+ input: string | Record,
): Promise {
- let dataToHash: string;
+ let dataToHash: string;
- if (typeof input === "string") {
- dataToHash = input;
- } else {
- const canonical = canonicalize(input);
- if (!canonical) {
- throw new Error(
- `Failed to canonicalize object: ${JSON.stringify(input).substring(0, 100)}...`,
- );
- }
- dataToHash = canonical;
- }
+ if (typeof input === "string") {
+ dataToHash = input;
+ } else {
+ const canonical = canonicalize(input);
+ if (!canonical) {
+ throw new Error(
+ `Failed to canonicalize object: ${JSON.stringify(input).substring(0, 100)}...`,
+ );
+ }
+ dataToHash = canonical;
+ }
- const buffer = new TextEncoder().encode(dataToHash);
- const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
- const hashHex = uint8ArrayToHex(new Uint8Array(hashBuffer));
+ const buffer = new TextEncoder().encode(dataToHash);
+ const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
+ const hashHex = uint8ArrayToHex(new Uint8Array(hashBuffer));
- return hashHex;
+ return hashHex;
}
diff --git a/infrastructure/w3id/src/utils/jwt.ts b/infrastructure/w3id/src/utils/jwt.ts
index c163f937..41868427 100644
--- a/infrastructure/w3id/src/utils/jwt.ts
+++ b/infrastructure/w3id/src/utils/jwt.ts
@@ -4,21 +4,21 @@ import type { JWTHeader, JWTPayload, Signer } from "../logs/log.types";
* Encodes a string to base64url format
*/
function base64urlEncode(str: string): string {
- return Buffer.from(str)
- .toString("base64")
- .replace(/\+/g, "-")
- .replace(/\//g, "_")
- .replace(/=/g, "");
+ return Buffer.from(str)
+ .toString("base64")
+ .replace(/\+/g, "-")
+ .replace(/\//g, "_")
+ .replace(/=/g, "");
}
/**
* Decodes a base64url string
*/
function base64urlDecode(str: string): string {
- return Buffer.from(
- str.replace(/-/g, "+").replace(/_/g, "/"),
- "base64",
- ).toString();
+ return Buffer.from(
+ str.replace(/-/g, "+").replace(/_/g, "/"),
+ "base64",
+ ).toString();
}
/**
@@ -28,9 +28,9 @@ function base64urlDecode(str: string): string {
* @returns The unsigned JWT
*/
export function createJWT(header: JWTHeader, payload: JWTPayload): string {
- const encodedHeader = base64urlEncode(JSON.stringify(header));
- const encodedPayload = base64urlEncode(JSON.stringify(payload));
- return `${encodedHeader}.${encodedPayload}`;
+ const encodedHeader = base64urlEncode(JSON.stringify(header));
+ const encodedPayload = base64urlEncode(JSON.stringify(payload));
+ return `${encodedHeader}.${encodedPayload}`;
}
/**
@@ -42,19 +42,19 @@ export function createJWT(header: JWTHeader, payload: JWTPayload): string {
* @returns The signed JWT
*/
export async function signJWT(
- signer: Signer,
- payload: JWTPayload,
- kid: string,
- header?: JWTHeader,
+ signer: Signer,
+ payload: JWTPayload,
+ kid: string,
+ header?: JWTHeader,
): Promise {
- const jwtHeader = header || {
- alg: signer.alg,
- typ: "JWT",
- kid,
- };
- const jwt = createJWT(jwtHeader, payload);
- const signature = await signer.sign(jwt);
- return `${jwt}.${signature}`;
+ const jwtHeader = header || {
+ alg: signer.alg,
+ typ: "JWT",
+ kid,
+ };
+ const jwt = createJWT(jwtHeader, payload);
+ const signature = await signer.sign(jwt);
+ return `${jwt}.${signature}`;
}
/**
@@ -66,16 +66,16 @@ export async function signJWT(
* @returns True if the signature is valid, false otherwise
*/
export async function verifyJWT(
- jwt: string,
- signature: string,
- verifier: (
- message: string,
- signature: string,
- pubKey: string,
- ) => Promise,
- pubKey: string,
+ jwt: string,
+ signature: string,
+ verifier: (
+ message: string,
+ signature: string,
+ pubKey: string,
+ ) => Promise,
+ pubKey: string,
): Promise {
- return verifier(jwt, signature, pubKey);
+ return verifier(jwt, signature, pubKey);
}
/**
@@ -84,8 +84,8 @@ export async function verifyJWT(
* @returns The JWT header
*/
export function getJWTHeader(jwt: string): JWTHeader {
- const [header] = jwt.split(".");
- return JSON.parse(base64urlDecode(header));
+ const [header] = jwt.split(".");
+ return JSON.parse(base64urlDecode(header));
}
/**
@@ -94,6 +94,6 @@ export function getJWTHeader(jwt: string): JWTHeader {
* @returns The JWT payload
*/
export function getJWTPayload(jwt: string): JWTPayload {
- const [, payload] = jwt.split(".");
- return JSON.parse(base64urlDecode(payload));
+ const [, payload] = jwt.split(".");
+ return JSON.parse(base64urlDecode(payload));
}
diff --git a/infrastructure/w3id/src/utils/rand.ts b/infrastructure/w3id/src/utils/rand.ts
index f9710fe0..8360fa88 100644
--- a/infrastructure/w3id/src/utils/rand.ts
+++ b/infrastructure/w3id/src/utils/rand.ts
@@ -6,17 +6,17 @@
*/
export function generateRandomAlphaNum(length = 16): string {
- const chars =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- let result = "";
- const charsLength = chars.length;
- const randomValues = new Uint32Array(length);
+ const chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ let result = "";
+ const charsLength = chars.length;
+ const randomValues = new Uint32Array(length);
- crypto.getRandomValues(randomValues);
+ crypto.getRandomValues(randomValues);
- for (let i = 0; i < length; i++) {
- result += chars.charAt(randomValues[i] % charsLength);
- }
+ for (let i = 0; i < length; i++) {
+ result += chars.charAt(randomValues[i] % charsLength);
+ }
- return result;
+ return result;
}
diff --git a/infrastructure/w3id/src/utils/uuid.ts b/infrastructure/w3id/src/utils/uuid.ts
index 1ffc1ae1..ecb953b3 100644
--- a/infrastructure/w3id/src/utils/uuid.ts
+++ b/infrastructure/w3id/src/utils/uuid.ts
@@ -10,8 +10,8 @@ import { v4 as uuidv4, v5 as uuidv5 } from "uuid";
*/
export function generateUuid(
- entropy: string,
- namespace: string = uuidv4(),
+ entropy: string,
+ namespace: string = uuidv4(),
): string {
- return uuidv5(entropy, namespace);
+ return uuidv5(entropy, namespace);
}
diff --git a/infrastructure/w3id/src/w3id.ts b/infrastructure/w3id/src/w3id.ts
index b2acb846..c6db4579 100644
--- a/infrastructure/w3id/src/w3id.ts
+++ b/infrastructure/w3id/src/w3id.ts
@@ -7,150 +7,152 @@ import { generateRandomAlphaNum } from "./utils/rand";
import { generateUuid } from "./utils/uuid";
export class W3ID {
- constructor(
- public id: string,
- public logs?: IDLogManager,
- ) {}
+ constructor(
+ public id: string,
+ public logs?: IDLogManager,
+ ) {}
- /**
- * Signs a JWT with the W3ID's signer
- * @param payload - The JWT payload
- * @param header - Optional JWT header (defaults to using the signer's alg and W3ID's id as kid)
- * @returns The signed JWT
- */
- public async signJWT(
- payload: JWTPayload,
- header?: JWTHeader,
- ): Promise {
- if (!this.logs?.signer) {
- throw new Error("W3ID must have a signer to sign JWTs");
- }
- return signJWT(this.logs.signer, payload, `@${this.id}#0`, header);
- }
+ /**
+ * Signs a JWT with the W3ID's signer
+ * @param payload - The JWT payload
+ * @param header - Optional JWT header (defaults to using the signer's alg and W3ID's id as kid)
+ * @returns The signed JWT
+ */
+ public async signJWT(
+ payload: JWTPayload,
+ header?: JWTHeader,
+ ): Promise {
+ if (!this.logs?.signer) {
+ throw new Error("W3ID must have a signer to sign JWTs");
+ }
+ return signJWT(this.logs.signer, payload, `@${this.id}#0`, header);
+ }
}
export class W3IDBuilder {
- private signer?: Signer;
- private repository?: StorageSpec;
- private entropy?: string;
- private namespace?: string;
- private nextKeyHash?: string;
- private global?: boolean = false;
- private id?: string;
+ private signer?: Signer;
+ private repository?: StorageSpec;
+ private entropy?: string;
+ private namespace?: string;
+ private nextKeyHash?: string;
+ private global?: boolean = false;
+ private id?: string;
- /**
- * Specify entropy to create the identity with
- *
- * @param {string} str
- */
- public withEntropy(str: string): W3IDBuilder {
- this.entropy = str;
- return this;
- }
+ /**
+ * Specify entropy to create the identity with
+ *
+ * @param {string} str
+ */
+ public withEntropy(str: string): W3IDBuilder {
+ this.entropy = str;
+ return this;
+ }
- /**
- * Specify namespace to use to generate the UUIDv5
- *
- * @param {string} uuid
- */
- public withNamespace(uuid: string): W3IDBuilder {
- this.namespace = uuid;
- return this;
- }
+ /**
+ * Specify namespace to use to generate the UUIDv5
+ *
+ * @param {string} uuid
+ */
+ public withNamespace(uuid: string): W3IDBuilder {
+ this.namespace = uuid;
+ return this;
+ }
- /**
- * Specify whether to create a global identifier or a local identifer
- *
- * According to the project specification there are supposed to be 2 main types of
- * W3ID's ones which are tied to more permanent entities
- *
- * A global identifer is expected to live at the registry and starts with an \`@\`
- *
- * @param {boolean} isGlobal
- */
- public withGlobal(isGlobal: boolean): W3IDBuilder {
- this.global = isGlobal;
- return this;
- }
+ /**
+ * Specify whether to create a global identifier or a local identifer
+ *
+ * According to the project specification there are supposed to be 2 main types of
+ * W3ID's ones which are tied to more permanent entities
+ *
+ * A global identifer is expected to live at the registry and starts with an \`@\`
+ *
+ * @param {boolean} isGlobal
+ */
+ public withGlobal(isGlobal: boolean): W3IDBuilder {
+ this.global = isGlobal;
+ return this;
+ }
- /**
- * Add a logs repository to the W3ID, a rotateble key attached W3ID would need a
- * repository in which the logs would be stored
- *
- * @param {StorageSpec} storage
- */
- public withRepository(storage: StorageSpec): W3IDBuilder {
- this.repository = storage;
- return this;
- }
+ /**
+ * Add a logs repository to the W3ID, a rotateble key attached W3ID would need a
+ * repository in which the logs would be stored
+ *
+ * @param {StorageSpec} storage
+ */
+ public withRepository(
+ storage: StorageSpec,
+ ): W3IDBuilder {
+ this.repository = storage;
+ return this;
+ }
- /**
- * Pre-specify a UUID to use as the W3ID
- *
- * @param {string} id
- */
- public withId(id: string): W3IDBuilder {
- this.id = id;
- return this;
- }
+ /**
+ * Pre-specify a UUID to use as the W3ID
+ *
+ * @param {string} id
+ */
+ public withId(id: string): W3IDBuilder {
+ this.id = id;
+ return this;
+ }
- /**
- * Attach a keypair to the W3ID, a key attached W3ID would also need a repository
- * to be added.
- *
- * @param {Signer} signer
- */
- public withSigner(signer: Signer): W3IDBuilder {
- this.signer = signer;
- return this;
- }
+ /**
+ * Attach a keypair to the W3ID, a key attached W3ID would also need a repository
+ * to be added.
+ *
+ * @param {Signer} signer
+ */
+ public withSigner(signer: Signer): W3IDBuilder {
+ this.signer = signer;
+ return this;
+ }
- /**
- * Specify the SHA256 hash of the next key which will sign the next log entry after
- * rotation of keys
- *
- * @param {string} hash
- */
- public withNextKeyHash(hash: string): W3IDBuilder {
- this.nextKeyHash = hash;
- return this;
- }
+ /**
+ * Specify the SHA256 hash of the next key which will sign the next log entry after
+ * rotation of keys
+ *
+ * @param {string} hash
+ */
+ public withNextKeyHash(hash: string): W3IDBuilder {
+ this.nextKeyHash = hash;
+ return this;
+ }
- /**
- * Build the W3ID with provided builder options
- *
- * @returns Promise
- */
- public async build(): Promise {
- if ((this.id && this.namespace) || (this.id && this.entropy))
- throw new Error(
- "Namespace and Entropy can't be specified when using pre-defined ID",
- );
- this.entropy = this.entropy ?? generateRandomAlphaNum();
- this.namespace = this.namespace ?? uuidv4();
- this.id = this.id?.includes("@") ? this.id.split("@")[1] : this.id;
- const id = `${
- this.global ? "@" : ""
- }${this.id?.toString() ?? generateUuid(this.entropy, this.namespace)}`;
- if (!this.signer) {
- return new W3ID(id);
- }
- if (!this.repository)
- throw new Error(
- "Repository is required, pass with `withRepository` method",
- );
- if (!this.nextKeyHash)
- throw new Error(
- "NextKeyHash is required pass with `withNextKeyHash` method",
- );
- const logs = new IDLogManager(this.repository, this.signer);
+ /**
+ * Build the W3ID with provided builder options
+ *
+ * @returns Promise
+ */
+ public async build(): Promise {
+ if ((this.id && this.namespace) || (this.id && this.entropy))
+ throw new Error(
+ "Namespace and Entropy can't be specified when using pre-defined ID",
+ );
+ this.entropy = this.entropy ?? generateRandomAlphaNum();
+ this.namespace = this.namespace ?? uuidv4();
+ this.id = this.id?.includes("@") ? this.id.split("@")[1] : this.id;
+ const id = `${
+ this.global ? "@" : ""
+ }${this.id?.toString() ?? generateUuid(this.entropy, this.namespace)}`;
+ if (!this.signer) {
+ return new W3ID(id);
+ }
+ if (!this.repository)
+ throw new Error(
+ "Repository is required, pass with `withRepository` method",
+ );
+ if (!this.nextKeyHash)
+ throw new Error(
+ "NextKeyHash is required pass with `withNextKeyHash` method",
+ );
+ const logs = new IDLogManager(this.repository, this.signer);
- const currentLogs = await logs.repository.findMany({});
- if (currentLogs.length > 0) return new W3ID(id, logs);
- await logs.createLogEvent({
- id,
- nextKeyHashes: [this.nextKeyHash],
- });
- return new W3ID(id, logs);
- }
+ const currentLogs = await logs.repository.findMany({});
+ if (currentLogs.length > 0) return new W3ID(id, logs);
+ await logs.createLogEvent({
+ id,
+ nextKeyHashes: [this.nextKeyHash],
+ });
+ return new W3ID(id, logs);
+ }
}
diff --git a/infrastructure/web3-adapter/package.json b/infrastructure/web3-adapter/package.json
index 645d2f68..23b7325c 100644
--- a/infrastructure/web3-adapter/package.json
+++ b/infrastructure/web3-adapter/package.json
@@ -1,34 +1,38 @@
{
- "name": "web3-adapter",
- "version": "1.0.0",
- "description": "Web3 adapter for platform-specific data mapping to universal schema",
- "type": "module",
- "main": "dist/index.js",
- "types": "dist/index.d.ts",
- "scripts": {
- "build": "tsc",
- "test": "vitest",
- "lint": "eslint src/**/*.ts"
- },
- "dependencies": {
- "evault-core": "workspace:*",
- "test": "^3.3.0",
- "vitest": "^3.1.2"
- },
- "devDependencies": {
- "@types/jest": "^29.5.0",
- "@typescript-eslint/eslint-plugin": "^5.59.0",
- "@typescript-eslint/parser": "^5.59.0",
- "eslint": "^8.38.0",
- "jest": "^29.5.0",
- "ts-jest": "^29.1.0",
- "typescript": "^5.0.4"
- },
- "jest": {
- "preset": "ts-jest",
- "testEnvironment": "node",
- "testMatch": [
- "**/__tests__/**/*.test.ts"
- ]
- }
+ "name": "web3-adapter",
+ "version": "1.0.0",
+ "description": "Web3 adapter for platform-specific data mapping to universal schema",
+ "type": "module",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest",
+ "check-format": "npx @biomejs/biome format ./src",
+ "format": "npx @biomejs/biome format --write ./src",
+ "lint": "npx @biomejs/biome lint --write ./src",
+ "check": "npx @biomejs/biome check --write ./src",
+ "check-types": "tsc --noEmit"
+ },
+ "dependencies": {
+ "evault-core": "workspace:*",
+ "test": "^3.3.0",
+ "vitest": "^3.1.2"
+ },
+ "devDependencies": {
+ "@types/jest": "^29.5.0",
+ "@typescript-eslint/eslint-plugin": "^5.59.0",
+ "@typescript-eslint/parser": "^5.59.0",
+ "eslint": "^8.38.0",
+ "jest": "^29.5.0",
+ "ts-jest": "^29.1.0",
+ "typescript": "^5.0.4"
+ },
+ "jest": {
+ "preset": "ts-jest",
+ "testEnvironment": "node",
+ "testMatch": [
+ "**/__tests__/**/*.test.ts"
+ ]
+ }
}
diff --git a/infrastructure/web3-adapter/src/__tests__/adapter.test.ts b/infrastructure/web3-adapter/src/__tests__/adapter.test.ts
index 8b7491c9..4d384cdd 100644
--- a/infrastructure/web3-adapter/src/__tests__/adapter.test.ts
+++ b/infrastructure/web3-adapter/src/__tests__/adapter.test.ts
@@ -1,73 +1,73 @@
-import { describe, it, expect, beforeEach } from "vitest";
+import { beforeEach, describe, expect, it } from "vitest";
import { Web3Adapter } from "../adapter.js";
describe("Web3Adapter", () => {
- let adapter: Web3Adapter;
+ let adapter: Web3Adapter;
- beforeEach(() => {
- adapter = new Web3Adapter();
- });
+ beforeEach(() => {
+ adapter = new Web3Adapter();
+ });
- it("should transform platform data to universal format", () => {
- // Register mappings for a platform
- adapter.registerMapping("twitter", [
- { sourceField: "tweet", targetField: "content" },
- { sourceField: "likes", targetField: "reactions" },
- { sourceField: "replies", targetField: "comments" },
- ]);
+ it("should transform platform data to universal format", () => {
+ // Register mappings for a platform
+ adapter.registerMapping("twitter", [
+ { sourceField: "tweet", targetField: "content" },
+ { sourceField: "likes", targetField: "reactions" },
+ { sourceField: "replies", targetField: "comments" },
+ ]);
- const twitterData = {
- tweet: "Hello world!",
- likes: 42,
- replies: ["user1", "user2"],
- };
+ const twitterData = {
+ tweet: "Hello world!",
+ likes: 42,
+ replies: ["user1", "user2"],
+ };
- const universalData = adapter.toUniversal("twitter", twitterData);
- expect(universalData).toEqual({
- content: "Hello world!",
- reactions: 42,
- comments: ["user1", "user2"],
+ const universalData = adapter.toUniversal("twitter", twitterData);
+ expect(universalData).toEqual({
+ content: "Hello world!",
+ reactions: 42,
+ comments: ["user1", "user2"],
+ });
});
- });
- it("should transform universal data to platform format", () => {
- // Register mappings for a platform
- adapter.registerMapping("instagram", [
- { sourceField: "caption", targetField: "content" },
- { sourceField: "hearts", targetField: "reactions" },
- { sourceField: "comments", targetField: "comments" },
- ]);
+ it("should transform universal data to platform format", () => {
+ // Register mappings for a platform
+ adapter.registerMapping("instagram", [
+ { sourceField: "caption", targetField: "content" },
+ { sourceField: "hearts", targetField: "reactions" },
+ { sourceField: "comments", targetField: "comments" },
+ ]);
- const universalData = {
- content: "Hello world!",
- reactions: 42,
- comments: ["user1", "user2"],
- };
+ const universalData = {
+ content: "Hello world!",
+ reactions: 42,
+ comments: ["user1", "user2"],
+ };
- const instagramData = adapter.fromUniversal("instagram", universalData);
- expect(instagramData).toEqual({
- caption: "Hello world!",
- hearts: 42,
- comments: ["user1", "user2"],
+ const instagramData = adapter.fromUniversal("instagram", universalData);
+ expect(instagramData).toEqual({
+ caption: "Hello world!",
+ hearts: 42,
+ comments: ["user1", "user2"],
+ });
});
- });
- it("should handle field transformations", () => {
- adapter.registerMapping("custom", [
- {
- sourceField: "timestamp",
- targetField: "date",
- transform: (value: number) => new Date(value).toISOString(),
- },
- ]);
+ it("should handle field transformations", () => {
+ adapter.registerMapping("custom", [
+ {
+ sourceField: "timestamp",
+ targetField: "date",
+ transform: (value: number) => new Date(value).toISOString(),
+ },
+ ]);
- const customData = {
- timestamp: 1677721600000,
- };
+ const customData = {
+ timestamp: 1677721600000,
+ };
- const universalData = adapter.toUniversal("custom", customData);
- expect(universalData).toEqual({
- date: "2023-03-02T01:46:40.000Z",
+ const universalData = adapter.toUniversal("custom", customData);
+ expect(universalData).toEqual({
+ date: "2023-03-02T01:46:40.000Z",
+ });
});
- });
});
diff --git a/infrastructure/web3-adapter/src/__tests__/evault.test.ts b/infrastructure/web3-adapter/src/__tests__/evault.test.ts
index 06e51e99..67aa4d09 100644
--- a/infrastructure/web3-adapter/src/__tests__/evault.test.ts
+++ b/infrastructure/web3-adapter/src/__tests__/evault.test.ts
@@ -1,57 +1,57 @@
-import { describe, it, expect, beforeEach } from "vitest";
+import { beforeEach, describe, expect, it } from "vitest";
import { Web3Adapter } from "../adapter.js";
const EVaultEndpoint = "http://localhost:4000/graphql";
async function queryGraphQL(
- query: string,
- variables: Record = {}
+ query: string,
+ variables: Record = {},
) {
- const response = await fetch(EVaultEndpoint, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ query, variables }),
- });
- return response.json();
+ const response = await fetch(EVaultEndpoint, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ query, variables }),
+ });
+ return response.json();
}
describe("eVault Integration", () => {
- let adapter: Web3Adapter;
- let storedId: string;
-
- beforeEach(() => {
- adapter = new Web3Adapter();
- });
-
- it("should store and retrieve data from eVault", async () => {
- // Register mappings for a platform
- adapter.registerMapping("twitter", [
- { sourceField: "tweet", targetField: "text" },
- { sourceField: "likes", targetField: "userLikes" },
- { sourceField: "replies", targetField: "interactions" },
- { sourceField: "image", targetField: "image" },
- {
- sourceField: "timestamp",
- targetField: "dateCreated",
- transform: (value: number) => new Date(value).toISOString(),
- },
- ]);
-
- // Create platform-specific data
- const twitterData = {
- tweet: "Hello world!",
- likes: ["@user1", "@user2"],
- replies: ["reply1", "reply2"],
- image: "https://example.com/image.jpg",
- };
-
- // Convert to universal format
- const universalData = adapter.toUniversal("twitter", twitterData);
-
- // Store in eVault
- const storeMutation = `
+ let adapter: Web3Adapter;
+ let storedId: string;
+
+ beforeEach(() => {
+ adapter = new Web3Adapter();
+ });
+
+ it("should store and retrieve data from eVault", async () => {
+ // Register mappings for a platform
+ adapter.registerMapping("twitter", [
+ { sourceField: "tweet", targetField: "text" },
+ { sourceField: "likes", targetField: "userLikes" },
+ { sourceField: "replies", targetField: "interactions" },
+ { sourceField: "image", targetField: "image" },
+ {
+ sourceField: "timestamp",
+ targetField: "dateCreated",
+ transform: (value: number) => new Date(value).toISOString(),
+ },
+ ]);
+
+ // Create platform-specific data
+ const twitterData = {
+ tweet: "Hello world!",
+ likes: ["@user1", "@user2"],
+ replies: ["reply1", "reply2"],
+ image: "https://example.com/image.jpg",
+ };
+
+ // Convert to universal format
+ const universalData = adapter.toUniversal("twitter", twitterData);
+
+ // Store in eVault
+ const storeMutation = `
mutation StoreMetaEnvelope($input: MetaEnvelopeInput!) {
storeMetaEnvelope(input: $input) {
metaEnvelope {
@@ -63,20 +63,22 @@ describe("eVault Integration", () => {
}
`;
- const storeResult = await queryGraphQL(storeMutation, {
- input: {
- ontology: "SocialMediaPost",
- payload: universalData,
- acl: ["*"],
- },
- });
-
- expect(storeResult.errors).toBeUndefined();
- expect(storeResult.data.storeMetaEnvelope.metaEnvelope.id).toBeDefined();
- storedId = storeResult.data.storeMetaEnvelope.metaEnvelope.id;
-
- // Retrieve from eVault
- const retrieveQuery = `
+ const storeResult = await queryGraphQL(storeMutation, {
+ input: {
+ ontology: "SocialMediaPost",
+ payload: universalData,
+ acl: ["*"],
+ },
+ });
+
+ expect(storeResult.errors).toBeUndefined();
+ expect(
+ storeResult.data.storeMetaEnvelope.metaEnvelope.id,
+ ).toBeDefined();
+ storedId = storeResult.data.storeMetaEnvelope.metaEnvelope.id;
+
+ // Retrieve from eVault
+ const retrieveQuery = `
query GetMetaEnvelope($id: String!) {
getMetaEnvelopeById(id: $id) {
parsed
@@ -84,55 +86,57 @@ describe("eVault Integration", () => {
}
`;
- const retrieveResult = await queryGraphQL(retrieveQuery, { id: storedId });
- expect(retrieveResult.errors).toBeUndefined();
- const retrievedData = retrieveResult.data.getMetaEnvelopeById.parsed;
-
- // Convert back to platform format
- const platformData = adapter.fromUniversal("twitter", retrievedData);
- });
-
- it("should exchange data between different platforms", async () => {
- // Register mappings for Platform A (Twitter-like)
- adapter.registerMapping("platformA", [
- { sourceField: "post", targetField: "text" },
- { sourceField: "reactions", targetField: "userLikes" },
- { sourceField: "comments", targetField: "interactions" },
- { sourceField: "media", targetField: "image" },
- {
- sourceField: "createdAt",
- targetField: "dateCreated",
- transform: (value: number) => new Date(value).toISOString(),
- },
- ]);
-
- // Register mappings for Platform B (Facebook-like)
- adapter.registerMapping("platformB", [
- { sourceField: "content", targetField: "text" },
- { sourceField: "likes", targetField: "userLikes" },
- { sourceField: "responses", targetField: "interactions" },
- { sourceField: "attachment", targetField: "image" },
- {
- sourceField: "postedAt",
- targetField: "dateCreated",
- transform: (value: string) => new Date(value).getTime(),
- },
- ]);
-
- // Create data in Platform A format
- const platformAData = {
- post: "Cross-platform test post",
- reactions: ["user1", "user2"],
- comments: ["Great post!", "Thanks for sharing"],
- media: "https://example.com/cross-platform.jpg",
- createdAt: Date.now(),
- };
-
- // Convert Platform A data to universal format
- const universalData = adapter.toUniversal("platformA", platformAData);
-
- // Store in eVault
- const storeMutation = `
+ const retrieveResult = await queryGraphQL(retrieveQuery, {
+ id: storedId,
+ });
+ expect(retrieveResult.errors).toBeUndefined();
+ const retrievedData = retrieveResult.data.getMetaEnvelopeById.parsed;
+
+ // Convert back to platform format
+ const platformData = adapter.fromUniversal("twitter", retrievedData);
+ });
+
+ it("should exchange data between different platforms", async () => {
+ // Register mappings for Platform A (Twitter-like)
+ adapter.registerMapping("platformA", [
+ { sourceField: "post", targetField: "text" },
+ { sourceField: "reactions", targetField: "userLikes" },
+ { sourceField: "comments", targetField: "interactions" },
+ { sourceField: "media", targetField: "image" },
+ {
+ sourceField: "createdAt",
+ targetField: "dateCreated",
+ transform: (value: number) => new Date(value).toISOString(),
+ },
+ ]);
+
+ // Register mappings for Platform B (Facebook-like)
+ adapter.registerMapping("platformB", [
+ { sourceField: "content", targetField: "text" },
+ { sourceField: "likes", targetField: "userLikes" },
+ { sourceField: "responses", targetField: "interactions" },
+ { sourceField: "attachment", targetField: "image" },
+ {
+ sourceField: "postedAt",
+ targetField: "dateCreated",
+ transform: (value: string) => new Date(value).getTime(),
+ },
+ ]);
+
+ // Create data in Platform A format
+ const platformAData = {
+ post: "Cross-platform test post",
+ reactions: ["user1", "user2"],
+ comments: ["Great post!", "Thanks for sharing"],
+ media: "https://example.com/cross-platform.jpg",
+ createdAt: Date.now(),
+ };
+
+ // Convert Platform A data to universal format
+ const universalData = adapter.toUniversal("platformA", platformAData);
+
+ // Store in eVault
+ const storeMutation = `
mutation StoreMetaEnvelope($input: MetaEnvelopeInput!) {
storeMetaEnvelope(input: $input) {
metaEnvelope {
@@ -144,20 +148,22 @@ describe("eVault Integration", () => {
}
`;
- const storeResult = await queryGraphQL(storeMutation, {
- input: {
- ontology: "SocialMediaPost",
- payload: universalData,
- acl: ["*"],
- },
- });
-
- expect(storeResult.errors).toBeUndefined();
- expect(storeResult.data.storeMetaEnvelope.metaEnvelope.id).toBeDefined();
- const storedId = storeResult.data.storeMetaEnvelope.metaEnvelope.id;
-
- // Retrieve from eVault
- const retrieveQuery = `
+ const storeResult = await queryGraphQL(storeMutation, {
+ input: {
+ ontology: "SocialMediaPost",
+ payload: universalData,
+ acl: ["*"],
+ },
+ });
+
+ expect(storeResult.errors).toBeUndefined();
+ expect(
+ storeResult.data.storeMetaEnvelope.metaEnvelope.id,
+ ).toBeDefined();
+ const storedId = storeResult.data.storeMetaEnvelope.metaEnvelope.id;
+
+ // Retrieve from eVault
+ const retrieveQuery = `
query GetMetaEnvelope($id: String!) {
getMetaEnvelopeById(id: $id) {
parsed
@@ -165,45 +171,47 @@ describe("eVault Integration", () => {
}
`;
- const retrieveResult = await queryGraphQL(retrieveQuery, { id: storedId });
- expect(retrieveResult.errors).toBeUndefined();
- const retrievedData = retrieveResult.data.getMetaEnvelopeById.parsed;
+ const retrieveResult = await queryGraphQL(retrieveQuery, {
+ id: storedId,
+ });
+ expect(retrieveResult.errors).toBeUndefined();
+ const retrievedData = retrieveResult.data.getMetaEnvelopeById.parsed;
+
+ // Convert to Platform B format
+ const platformBData = adapter.fromUniversal("platformB", retrievedData);
+
+ // Verify Platform B data structure
+ expect(platformBData).toEqual({
+ content: platformAData.post,
+ likes: platformAData.reactions,
+ responses: platformAData.comments,
+ attachment: platformAData.media,
+ postedAt: expect.any(Number), // We expect a timestamp
+ });
+
+ // Verify data integrity
+ expect(platformBData.content).toBe(platformAData.post);
+ expect(platformBData.likes).toEqual(platformAData.reactions);
+ expect(platformBData.responses).toEqual(platformAData.comments);
+ expect(platformBData.attachment).toBe(platformAData.media);
+ });
- // Convert to Platform B format
- const platformBData = adapter.fromUniversal("platformB", retrievedData);
+ it("should search data in eVault", async () => {
+ // Register mappings for a platform
+ adapter.registerMapping("twitter", [
+ { sourceField: "tweet", targetField: "text" },
+ { sourceField: "likes", targetField: "userLikes" },
+ ]);
- // Verify Platform B data structure
- expect(platformBData).toEqual({
- content: platformAData.post,
- likes: platformAData.reactions,
- responses: platformAData.comments,
- attachment: platformAData.media,
- postedAt: expect.any(Number), // We expect a timestamp
- });
+ // Create and store test data
+ const twitterData = {
+ tweet: "Searchable content",
+ likes: ["@user1"],
+ };
- // Verify data integrity
- expect(platformBData.content).toBe(platformAData.post);
- expect(platformBData.likes).toEqual(platformAData.reactions);
- expect(platformBData.responses).toEqual(platformAData.comments);
- expect(platformBData.attachment).toBe(platformAData.media);
- });
-
- it("should search data in eVault", async () => {
- // Register mappings for a platform
- adapter.registerMapping("twitter", [
- { sourceField: "tweet", targetField: "text" },
- { sourceField: "likes", targetField: "userLikes" },
- ]);
-
- // Create and store test data
- const twitterData = {
- tweet: "Searchable content",
- likes: ["@user1"],
- };
-
- const universalData = adapter.toUniversal("twitter", twitterData);
-
- const storeMutation = `
+ const universalData = adapter.toUniversal("twitter", twitterData);
+
+ const storeMutation = `
mutation Store($input: MetaEnvelopeInput!) {
storeMetaEnvelope(input: $input) {
metaEnvelope {
@@ -213,16 +221,16 @@ describe("eVault Integration", () => {
}
`;
- await queryGraphQL(storeMutation, {
- input: {
- ontology: "SocialMediaPost",
- payload: universalData,
- acl: ["*"],
- },
- });
+ await queryGraphQL(storeMutation, {
+ input: {
+ ontology: "SocialMediaPost",
+ payload: universalData,
+ acl: ["*"],
+ },
+ });
- // Search in eVault
- const searchQuery = `
+ // Search in eVault
+ const searchQuery = `
query Search($ontology: String!, $term: String!) {
searchMetaEnvelopes(ontology: $ontology, term: $term) {
id
@@ -231,15 +239,15 @@ describe("eVault Integration", () => {
}
`;
- const searchResult = await queryGraphQL(searchQuery, {
- ontology: "SocialMediaPost",
- term: "Searchable",
- });
+ const searchResult = await queryGraphQL(searchQuery, {
+ ontology: "SocialMediaPost",
+ term: "Searchable",
+ });
- expect(searchResult.errors).toBeUndefined();
- expect(searchResult.data.searchMetaEnvelopes.length).toBeGreaterThan(0);
- expect(searchResult.data.searchMetaEnvelopes[0].parsed.text).toBe(
- "Searchable content"
- );
- });
+ expect(searchResult.errors).toBeUndefined();
+ expect(searchResult.data.searchMetaEnvelopes.length).toBeGreaterThan(0);
+ expect(searchResult.data.searchMetaEnvelopes[0].parsed.text).toBe(
+ "Searchable content",
+ );
+ });
});
diff --git a/infrastructure/web3-adapter/src/adapter.ts b/infrastructure/web3-adapter/src/adapter.ts
index 1a12b4ae..3fbb72bc 100644
--- a/infrastructure/web3-adapter/src/adapter.ts
+++ b/infrastructure/web3-adapter/src/adapter.ts
@@ -1,59 +1,59 @@
export type FieldMapping = {
- sourceField: string;
- targetField: string;
- transform?: (value: any) => any;
+ sourceField: string;
+ targetField: string;
+ transform?: (value: any) => any;
};
export class Web3Adapter {
- private mappings: Map;
+ private mappings: Map;
- constructor() {
- this.mappings = new Map();
- }
-
- public registerMapping(platform: string, mappings: FieldMapping[]): void {
- this.mappings.set(platform, mappings);
- }
-
- public toUniversal(
- platform: string,
- data: Record
- ): Record {
- const mappings = this.mappings.get(platform);
- if (!mappings) {
- throw new Error(`No mappings found for platform: ${platform}`);
+ constructor() {
+ this.mappings = new Map();
}
- const result: Record = {};
- for (const mapping of mappings) {
- if (data[mapping.sourceField] !== undefined) {
- const value = mapping.transform
- ? mapping.transform(data[mapping.sourceField])
- : data[mapping.sourceField];
- result[mapping.targetField] = value;
- }
+ public registerMapping(platform: string, mappings: FieldMapping[]): void {
+ this.mappings.set(platform, mappings);
}
- return result;
- }
- public fromUniversal(
- platform: string,
- data: Record
- ): Record {
- const mappings = this.mappings.get(platform);
- if (!mappings) {
- throw new Error(`No mappings found for platform: ${platform}`);
+ public toUniversal(
+ platform: string,
+ data: Record,
+ ): Record {
+ const mappings = this.mappings.get(platform);
+ if (!mappings) {
+ throw new Error(`No mappings found for platform: ${platform}`);
+ }
+
+ const result: Record = {};
+ for (const mapping of mappings) {
+ if (data[mapping.sourceField] !== undefined) {
+ const value = mapping.transform
+ ? mapping.transform(data[mapping.sourceField])
+ : data[mapping.sourceField];
+ result[mapping.targetField] = value;
+ }
+ }
+ return result;
}
- const result: Record = {};
- for (const mapping of mappings) {
- if (data[mapping.targetField] !== undefined) {
- const value = mapping.transform
- ? mapping.transform(data[mapping.targetField])
- : data[mapping.targetField];
- result[mapping.sourceField] = value;
- }
+ public fromUniversal(
+ platform: string,
+ data: Record,
+ ): Record {
+ const mappings = this.mappings.get(platform);
+ if (!mappings) {
+ throw new Error(`No mappings found for platform: ${platform}`);
+ }
+
+ const result: Record = {};
+ for (const mapping of mappings) {
+ if (data[mapping.targetField] !== undefined) {
+ const value = mapping.transform
+ ? mapping.transform(data[mapping.targetField])
+ : data[mapping.targetField];
+ result[mapping.sourceField] = value;
+ }
+ }
+ return result;
}
- return result;
- }
}
diff --git a/package.json b/package.json
index b9f4a9b2..f28d01b6 100644
--- a/package.json
+++ b/package.json
@@ -1,24 +1,24 @@
{
- "name": "prototype",
- "private": true,
- "scripts": {
- "build": "turbo run build",
- "dev": "turbo run dev",
- "lint": "turbo run lint",
- "check-lint": "turbo run check-lint",
- "format": "turbo run format",
- "check-format": "turbo run check-format",
- "check": "turbo run check",
- "check-types": "turbo run check-types",
- "dev:evault": "docker compose -f evault.docker-compose.yml up --watch"
- },
- "devDependencies": {
- "prettier": "^3.5.3",
- "turbo": "^2.4.4",
- "typescript": "5.8.2"
- },
- "packageManager": "pnpm@10.6.5",
- "engines": {
- "node": ">=18"
- }
+ "name": "prototype",
+ "private": true,
+ "scripts": {
+ "build": "turbo run build",
+ "dev": "turbo run dev",
+ "lint": "turbo run lint",
+ "check-lint": "turbo run check-lint",
+ "format": "turbo run format",
+ "check-format": "turbo run check-format",
+ "check": "turbo run check",
+ "check-types": "turbo run check-types",
+ "dev:evault": "docker compose -f evault.docker-compose.yml up --watch"
+ },
+ "devDependencies": {
+ "turbo": "^2.4.4",
+ "typescript": "5.8.2",
+ "@biomejs/biome": "^1.9.4"
+ },
+ "packageManager": "pnpm@10.6.5",
+ "engines": {
+ "node": ">=18"
+ }
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 34485d2e..97ddc386 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,9 +8,9 @@ importers:
.:
devDependencies:
- prettier:
- specifier: ^3.5.3
- version: 3.5.3
+ '@biomejs/biome':
+ specifier: ^1.9.4
+ version: 1.9.4
turbo:
specifier: ^2.4.4
version: 2.4.4
@@ -20,9 +20,6 @@ importers:
infrastructure/eid-wallet:
dependencies:
- '@biomejs/biome':
- specifier: ^1.9.4
- version: 1.9.4
'@hugeicons/core-free-icons':
specifier: ^1.0.13
version: 1.0.13
@@ -35,9 +32,18 @@ importers:
'@tauri-apps/api':
specifier: ^2
version: 2.3.0
+ '@tauri-apps/plugin-barcode-scanner':
+ specifier: ^2.2.0
+ version: 2.2.0
+ '@tauri-apps/plugin-biometric':
+ specifier: ^2.2.0
+ version: 2.2.0
'@tauri-apps/plugin-opener':
specifier: ^2
version: 2.2.6
+ '@tauri-apps/plugin-store':
+ specifier: ^2.2.0
+ version: 2.2.0
clsx:
specifier: ^2.1.1
version: 2.1.1
@@ -48,6 +54,9 @@ importers:
specifier: ^3.0.2
version: 3.0.2
devDependencies:
+ '@biomejs/biome':
+ specifier: ^1.9.4
+ version: 1.9.4
'@chromatic-com/storybook':
specifier: ^3
version: 3.2.6(react@19.0.0)(storybook@8.6.7(prettier@3.5.3))
@@ -267,6 +276,9 @@ importers:
specifier: ^11.1.0
version: 11.1.0
devDependencies:
+ '@biomejs/biome':
+ specifier: ^1.9.4
+ version: 1.9.4
'@ngneat/falso':
specifier: ^7.3.0
version: 7.3.0
@@ -1151,8 +1163,8 @@ packages:
resolution: {integrity: sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==}
engines: {node: '>=12.10.0'}
- '@grpc/proto-loader@0.7.14':
- resolution: {integrity: sha512-oS0FyK8eGNBJC6aB/qsS4LOxCYQlBniNzp6W8IdjlRVRGs0FOK9dS84OV+kXGaZf8Ozeos8fbUMJUGGzSpOCzQ==}
+ '@grpc/proto-loader@0.7.15':
+ resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==}
engines: {node: '>=6'}
hasBin: true
@@ -2033,9 +2045,18 @@ packages:
engines: {node: '>= 10'}
hasBin: true
+ '@tauri-apps/plugin-barcode-scanner@2.2.0':
+ resolution: {integrity: sha512-16AgAjNZGS790KXTW2oq7K0YKUCxLmlDlizN7+pBvIcZzdR3kYzHSST/CYSXkAkYRParyHmE2nMsMvqJFAHKbA==}
+
+ '@tauri-apps/plugin-biometric@2.2.0':
+ resolution: {integrity: sha512-bxetRUGNGIuH/zMMlZ1VuYG7B0pWA/GkNDyMKmajCC3T3AIgL+siyeorDw+lAcp9KDNnTZ8vSOlxiAcwdbWN4Q==}
+
'@tauri-apps/plugin-opener@2.2.6':
resolution: {integrity: sha512-bSdkuP71ZQRepPOn8BOEdBKYJQvl6+jb160QtJX/i2H9BF6ZySY/kYljh76N2Ne5fJMQRge7rlKoStYQY5Jq1w==}
+ '@tauri-apps/plugin-store@2.2.0':
+ resolution: {integrity: sha512-hJTRtuJis4w5fW1dkcgftsYxKXK0+DbAqurZ3CURHG5WkAyyZgbxpeYctw12bbzF9ZbZREXZklPq8mocCC3Sgg==}
+
'@testcontainers/neo4j@10.24.2':
resolution: {integrity: sha512-Bdhg0HPJYpvnp0PxqubbAITrf87pI5I3KxDAhjMzGdELFHgZltRK4NUNRaHs6R3q0V4vnyHD4Hooci9XA2tVgQ==}
@@ -3128,8 +3149,8 @@ packages:
resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==}
engines: {node: '>= 8.0'}
- dockerode@4.0.5:
- resolution: {integrity: sha512-ZPmKSr1k1571Mrh7oIBS/j0AqAccoecY2yH420ni5j1KyNMgnoTh4Nu4FWunh0HZIJmRSmSysJjBIpa/zyWUEA==}
+ dockerode@4.0.6:
+ resolution: {integrity: sha512-FbVf3Z8fY/kALB9s+P9epCpWhfi/r0N2DgYYcYpsAUlaTxPjdsitsFobnltb+lyCgAIvf9C+4PSWlTnHlJMf1w==}
engines: {node: '>= 8.0'}
doctrine@2.1.0:
@@ -6907,10 +6928,10 @@ snapshots:
'@grpc/grpc-js@1.13.3':
dependencies:
- '@grpc/proto-loader': 0.7.14
+ '@grpc/proto-loader': 0.7.15
'@js-sdsl/ordered-map': 4.4.2
- '@grpc/proto-loader@0.7.14':
+ '@grpc/proto-loader@0.7.15':
dependencies:
lodash.camelcase: 4.3.0
long: 5.3.2
@@ -7908,10 +7929,22 @@ snapshots:
'@tauri-apps/cli-win32-ia32-msvc': 2.3.1
'@tauri-apps/cli-win32-x64-msvc': 2.3.1
+ '@tauri-apps/plugin-barcode-scanner@2.2.0':
+ dependencies:
+ '@tauri-apps/api': 2.3.0
+
+ '@tauri-apps/plugin-biometric@2.2.0':
+ dependencies:
+ '@tauri-apps/api': 2.3.0
+
'@tauri-apps/plugin-opener@2.2.6':
dependencies:
'@tauri-apps/api': 2.3.0
+ '@tauri-apps/plugin-store@2.2.0':
+ dependencies:
+ '@tauri-apps/api': 2.3.0
+
'@testcontainers/neo4j@10.24.2':
dependencies:
testcontainers: 10.24.2
@@ -7922,7 +7955,7 @@ snapshots:
'@testing-library/dom@10.4.0':
dependencies:
'@babel/code-frame': 7.26.2
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.26.10
'@types/aria-query': 5.0.4
aria-query: 5.3.0
chalk: 4.1.2
@@ -9309,11 +9342,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- dockerode@4.0.5:
+ dockerode@4.0.6:
dependencies:
'@balena/dockerignore': 1.0.2
'@grpc/grpc-js': 1.13.3
- '@grpc/proto-loader': 0.7.14
+ '@grpc/proto-loader': 0.7.15
docker-modem: 5.0.6
protobufjs: 7.5.0
tar-fs: 2.1.2
@@ -9679,7 +9712,7 @@ snapshots:
eslint-scope: 7.2.2
eslint-utils: 3.0.0(eslint@8.4.1)
eslint-visitor-keys: 3.4.3
- espree: 9.6.1
+ espree: 9.2.0
esquery: 1.6.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
@@ -11607,7 +11640,8 @@ snapshots:
prelude-ls@1.2.1: {}
- prettier@3.5.3: {}
+ prettier@3.5.3:
+ optional: true
pretty-format@27.5.1:
dependencies:
@@ -12380,7 +12414,7 @@ snapshots:
byline: 5.0.0
debug: 4.4.0(supports-color@5.5.0)
docker-compose: 0.24.8
- dockerode: 4.0.5
+ dockerode: 4.0.6
get-port: 7.1.0
proper-lockfile: 4.1.2
properties-reader: 2.3.0
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 47a3d605..74f12989 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -4,7 +4,10 @@ packages:
- platforms/*
- infrastructure/*
onlyBuiltDependencies:
- - "@biomejs/biome"
+ - '@biomejs/biome'
+ - cpu-features
- esbuild
- msw
+ - protobufjs
+ - ssh2
- svelte-preprocess