Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infrastructure/web3-adapter/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,13 @@ import { logger } from "./logging";
import { fromGlobal, toGlobal } from "./mapper/mapper";
import type { IMapping } from "./mapper/mapper.types";

export { EVaultClient } from "./evault/evault";
export type {
MetaEnvelope,
UploadFileInput,
UploadFileResult,
} from "./evault/evault";
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This re-export still pulls the heavy root module into stateless consumers.

Importing EVaultClient from web3-adapter will still evaluate this file first, including the top-level ./db, mapper, fs, and logging imports above. That means the profile-editor API still inherits the adapter’s mapping/sqlite side effects, which defeats the stated goal of exposing a lightweight eVault client. Split EVaultClient into a dedicated entrypoint or subpath export that has no root-module dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/web3-adapter/src/index.ts` around lines 11 - 16, The
root-level export is dragging heavy top-level dependencies into stateless
consumers; create a dedicated lightweight entrypoint that only exposes the
client and its types. Add a new module (e.g., evault/client) that imports and
exports EVaultClient plus MetaEnvelope/UploadFileInput/UploadFileResult and
ensure that module does not import the heavy root-level ./db, mappers, fs, or
logging; then change the current re-exports in index.ts to re-export from
"./evault/client" (and remove any top-level imports of the heavy modules from
the package root) so consumers of EVaultClient do not trigger the adapter’s side
effects.


/**
* Standalone function to spin up an eVault
* @param registryUrl - URL of the registry service
Expand Down
6 changes: 6 additions & 0 deletions platforms/profile-editor/api/nodemon.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node src/index.ts"
}
19 changes: 4 additions & 15 deletions platforms/profile-editor/api/package.json
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
{
"name": "profile-editor-api",
"version": "1.0.0",
"description": "Profile Editor API for the w3ds ecosystem",
"description": "W3DS-native Profile Editor API: reads from AaaS, writes to eVault",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "bash -c 'read -p \"Migration name: \" name && npx typeorm-ts-node-commonjs migration:generate src/database/migrations/$name -d src/database/data-source.ts'",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
"build": "tsc"
Comment on lines 7 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

start currently depends on a dev-only package.

ts-node lives under devDependencies, so a production install that omits dev deps will not start this service even though build passes. Point start at the compiled JS output, or move ts-node into runtime dependencies if that is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/package.json` around lines 7 - 9, The "start"
script currently runs "ts-node src/index.ts" which depends on the dev-only
package "ts-node" (listed under devDependencies), so production installs without
dev deps will fail; fix by changing the "start" script to point to the compiled
output (e.g., "node dist/index.js" or similar matching your build output used by
"build") or, if you truly need to run TypeScript at runtime, move "ts-node" from
devDependencies into dependencies; update the package.json scripts to use
"start" -> node <compiled entry> (and keep "dev" using ts-node) or adjust
dependency placement accordingly so production installs can start.

},
"dependencies": {
"@metastate-foundation/auth": "workspace:*",
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"form-data": "^4.0.5",
"graphql-request": "^6.1.0",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"pg": "^8.20.0",
"reflect-metadata": "^0.2.2",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*",
"typeorm": "^0.3.28",
"uuid": "^9.0.1"
"zod": "^3.23.8"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
"@types/node": "^20.11.24",
"@types/pg": "^8.18.0",
"@types/uuid": "^9.0.8",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
Expand Down
121 changes: 121 additions & 0 deletions platforms/profile-editor/api/src/aaas.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import axios from "axios";
import { env } from "./env";
import {
type Payload,
PROFESSIONAL_PROFILE_ONTOLOGY,
USER_ONTOLOGY,
} from "./ontology";

/** A single packet as returned by AaaS `GET /api/packets`. */
export interface Packet {
id: string;
ontology: string;
w3id: string | null;
data: Payload | null;
receivedAt: string;
}

interface PacketsResponse {
packets: Packet[];
hasMore: boolean;
nextCursor: string | null;
}

/** The current envelope (id + payload) for one ontology. */
export interface EnvelopeState {
id: string;
data: Payload;
}

/**
* Read side of the W3DS-native profile editor: everything is pulled from
* Awareness-as-a-Service. Each MetaEnvelope maps to exactly one packet (AaaS
* upserts by envelope id), so filtering by eVault + ontology yields the current
* state.
*/
export class AaasClient {
private base = env.awarenessServiceUrl.replace(/\/$/, "");
private headers = { Authorization: `Bearer ${env.awarenessApiKey}` };

/** One page of packets for the given filters. */
private async page(
params: Record<string, string | number>,
cursor?: string | null,
): Promise<PacketsResponse> {
const { data } = await axios.get<PacketsResponse>(
`${this.base}/api/packets`,
{
headers: this.headers,
params: { limit: 200, ...params, ...(cursor ? { cursor } : {}) },
timeout: 15000,
},
);
return data;
}

/** Every packet matching the filters, paged to exhaustion (newest last). */
private async all(
params: Record<string, string | number>,
): Promise<Packet[]> {
const out: Packet[] = [];
let cursor: string | null | undefined;
do {
const res = await this.page(params, cursor);
out.push(...(res.packets ?? []));
cursor = res.hasMore ? res.nextCursor : null;
} while (cursor);
return out;
}

/** Latest envelope state per ontology for a single eVault (by eName). */
async getProfileEnvelopes(ename: string): Promise<{
user: EnvelopeState | null;
professional: EnvelopeState | null;
}> {
const packets = await this.all({
evault: ename,
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});

// Packets are ordered oldest→newest, so the last write wins.
let user: EnvelopeState | null = null;
let professional: EnvelopeState | null = null;
for (const p of packets) {
if (!p.data) continue;
if (p.ontology === USER_ONTOLOGY) {
user = { id: p.id, data: p.data };
} else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
professional = { id: p.id, data: p.data };
}
}
return { user, professional };
}

/**
* All User + Professional-Profile envelopes across every eVault, keyed by
* eName — the cross-user source for discovery (no single eVault has them).
*/
async listAllProfiles(): Promise<
Map<string, { user: Payload | null; professional: Payload | null }>
> {
const packets = await this.all({
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});
Comment on lines +98 to +103

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Discovery now scales with total packet history, not current profiles.

listAllProfiles() paginates through every packet ever written and materializes the full history before collapsing it to latest state. Because the discovery flow calls this on demand, latency and memory usage will grow with every profile edit, not just with the number of visible profiles. Cache the collapsed latest envelopes or add an AaaS query that returns current state directly before this path sees production traffic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/src/aaas.ts` around lines 98 - 103,
listAllProfiles() currently pages through every packet history via this.all(...)
and materializes the full history before collapsing to latest state, causing
memory/latency to grow with edits; change it to either (A) perform incremental
collapse while streaming pages (keep a Map keyed by profile id and update latest
envelope per id as each page is processed) or (B) add/consume an AaaS query that
returns only current/latest envelopes for the given ontologies (USER_ONTOLOGY,
PROFESSIONAL_PROFILE_ONTOLOGY) and use that instead of this.all; update the
implementation of listAllProfiles to use the incremental collapse or the new
AaaS query so it only holds the latest envelopes in memory.


const byEname = new Map<
string,
{ user: Payload | null; professional: Payload | null }
>();
for (const p of packets) {
const ename = p.w3id ?? (p.data?.ename as string | undefined);
if (!ename || !p.data) continue;
const entry = byEname.get(ename) ?? { user: null, professional: null };
if (p.ontology === USER_ONTOLOGY) entry.user = p.data;
else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
entry.professional = p.data;
}
byEname.set(ename, entry);
}
return byEname;
}
}
134 changes: 0 additions & 134 deletions platforms/profile-editor/api/src/controllers/AuthController.ts

This file was deleted.

This file was deleted.

Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infrastructure/web3-adapter/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,13 @@ import { logger } from "./logging";
import { fromGlobal, toGlobal } from "./mapper/mapper";
import type { IMapping } from "./mapper/mapper.types";

export { EVaultClient } from "./evault/evault";
export type {
MetaEnvelope,
UploadFileInput,
UploadFileResult,
} from "./evault/evault";
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This re-export still pulls the heavy root module into stateless consumers.

Importing EVaultClient from web3-adapter will still evaluate this file first, including the top-level ./db, mapper, fs, and logging imports above. That means the profile-editor API still inherits the adapter’s mapping/sqlite side effects, which defeats the stated goal of exposing a lightweight eVault client. Split EVaultClient into a dedicated entrypoint or subpath export that has no root-module dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/web3-adapter/src/index.ts` around lines 11 - 16, The
root-level export is dragging heavy top-level dependencies into stateless
consumers; create a dedicated lightweight entrypoint that only exposes the
client and its types. Add a new module (e.g., evault/client) that imports and
exports EVaultClient plus MetaEnvelope/UploadFileInput/UploadFileResult and
ensure that module does not import the heavy root-level ./db, mappers, fs, or
logging; then change the current re-exports in index.ts to re-export from
"./evault/client" (and remove any top-level imports of the heavy modules from
the package root) so consumers of EVaultClient do not trigger the adapter’s side
effects.


/**
* Standalone function to spin up an eVault
* @param registryUrl - URL of the registry service
Expand Down
6 changes: 6 additions & 0 deletions platforms/profile-editor/api/nodemon.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node src/index.ts"
}
19 changes: 4 additions & 15 deletions platforms/profile-editor/api/package.json
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
{
"name": "profile-editor-api",
"version": "1.0.0",
"description": "Profile Editor API for the w3ds ecosystem",
"description": "W3DS-native Profile Editor API: reads from AaaS, writes to eVault",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "bash -c 'read -p \"Migration name: \" name && npx typeorm-ts-node-commonjs migration:generate src/database/migrations/$name -d src/database/data-source.ts'",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
"build": "tsc"
Comment on lines 7 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

start currently depends on a dev-only package.

ts-node lives under devDependencies, so a production install that omits dev deps will not start this service even though build passes. Point start at the compiled JS output, or move ts-node into runtime dependencies if that is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/package.json` around lines 7 - 9, The "start"
script currently runs "ts-node src/index.ts" which depends on the dev-only
package "ts-node" (listed under devDependencies), so production installs without
dev deps will fail; fix by changing the "start" script to point to the compiled
output (e.g., "node dist/index.js" or similar matching your build output used by
"build") or, if you truly need to run TypeScript at runtime, move "ts-node" from
devDependencies into dependencies; update the package.json scripts to use
"start" -> node <compiled entry> (and keep "dev" using ts-node) or adjust
dependency placement accordingly so production installs can start.

},
"dependencies": {
"@metastate-foundation/auth": "workspace:*",
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"form-data": "^4.0.5",
"graphql-request": "^6.1.0",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"pg": "^8.20.0",
"reflect-metadata": "^0.2.2",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*",
"typeorm": "^0.3.28",
"uuid": "^9.0.1"
"zod": "^3.23.8"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
"@types/node": "^20.11.24",
"@types/pg": "^8.18.0",
"@types/uuid": "^9.0.8",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
Expand Down
121 changes: 121 additions & 0 deletions platforms/profile-editor/api/src/aaas.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import axios from "axios";
import { env } from "./env";
import {
type Payload,
PROFESSIONAL_PROFILE_ONTOLOGY,
USER_ONTOLOGY,
} from "./ontology";

/** A single packet as returned by AaaS `GET /api/packets`. */
export interface Packet {
id: string;
ontology: string;
w3id: string | null;
data: Payload | null;
receivedAt: string;
}

interface PacketsResponse {
packets: Packet[];
hasMore: boolean;
nextCursor: string | null;
}

/** The current envelope (id + payload) for one ontology. */
export interface EnvelopeState {
id: string;
data: Payload;
}

/**
* Read side of the W3DS-native profile editor: everything is pulled from
* Awareness-as-a-Service. Each MetaEnvelope maps to exactly one packet (AaaS
* upserts by envelope id), so filtering by eVault + ontology yields the current
* state.
*/
export class AaasClient {
private base = env.awarenessServiceUrl.replace(/\/$/, "");
private headers = { Authorization: `Bearer ${env.awarenessApiKey}` };

/** One page of packets for the given filters. */
private async page(
params: Record<string, string | number>,
cursor?: string | null,
): Promise<PacketsResponse> {
const { data } = await axios.get<PacketsResponse>(
`${this.base}/api/packets`,
{
headers: this.headers,
params: { limit: 200, ...params, ...(cursor ? { cursor } : {}) },
timeout: 15000,
},
);
return data;
}

/** Every packet matching the filters, paged to exhaustion (newest last). */
private async all(
params: Record<string, string | number>,
): Promise<Packet[]> {
const out: Packet[] = [];
let cursor: string | null | undefined;
do {
const res = await this.page(params, cursor);
out.push(...(res.packets ?? []));
cursor = res.hasMore ? res.nextCursor : null;
} while (cursor);
return out;
}

/** Latest envelope state per ontology for a single eVault (by eName). */
async getProfileEnvelopes(ename: string): Promise<{
user: EnvelopeState | null;
professional: EnvelopeState | null;
}> {
const packets = await this.all({
evault: ename,
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});

// Packets are ordered oldest→newest, so the last write wins.
let user: EnvelopeState | null = null;
let professional: EnvelopeState | null = null;
for (const p of packets) {
if (!p.data) continue;
if (p.ontology === USER_ONTOLOGY) {
user = { id: p.id, data: p.data };
} else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
professional = { id: p.id, data: p.data };
}
}
return { user, professional };
}

/**
* All User + Professional-Profile envelopes across every eVault, keyed by
* eName — the cross-user source for discovery (no single eVault has them).
*/
async listAllProfiles(): Promise<
Map<string, { user: Payload | null; professional: Payload | null }>
> {
const packets = await this.all({
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});
Comment on lines +98 to +103

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Discovery now scales with total packet history, not current profiles.

listAllProfiles() paginates through every packet ever written and materializes the full history before collapsing it to latest state. Because the discovery flow calls this on demand, latency and memory usage will grow with every profile edit, not just with the number of visible profiles. Cache the collapsed latest envelopes or add an AaaS query that returns current state directly before this path sees production traffic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/src/aaas.ts` around lines 98 - 103,
listAllProfiles() currently pages through every packet history via this.all(...)
and materializes the full history before collapsing to latest state, causing
memory/latency to grow with edits; change it to either (A) perform incremental
collapse while streaming pages (keep a Map keyed by profile id and update latest
envelope per id as each page is processed) or (B) add/consume an AaaS query that
returns only current/latest envelopes for the given ontologies (USER_ONTOLOGY,
PROFESSIONAL_PROFILE_ONTOLOGY) and use that instead of this.all; update the
implementation of listAllProfiles to use the incremental collapse or the new
AaaS query so it only holds the latest envelopes in memory.


const byEname = new Map<
string,
{ user: Payload | null; professional: Payload | null }
>();
for (const p of packets) {
const ename = p.w3id ?? (p.data?.ename as string | undefined);
if (!ename || !p.data) continue;
const entry = byEname.get(ename) ?? { user: null, professional: null };
if (p.ontology === USER_ONTOLOGY) entry.user = p.data;
else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
entry.professional = p.data;
}
byEname.set(ename, entry);
}
return byEname;
}
}
134 changes: 0 additions & 134 deletions platforms/profile-editor/api/src/controllers/AuthController.ts

This file was deleted.

This file was deleted.

Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infrastructure/web3-adapter/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,13 @@ import { logger } from "./logging";
import { fromGlobal, toGlobal } from "./mapper/mapper";
import type { IMapping } from "./mapper/mapper.types";

export { EVaultClient } from "./evault/evault";
export type {
MetaEnvelope,
UploadFileInput,
UploadFileResult,
} from "./evault/evault";
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This re-export still pulls the heavy root module into stateless consumers.

Importing EVaultClient from web3-adapter will still evaluate this file first, including the top-level ./db, mapper, fs, and logging imports above. That means the profile-editor API still inherits the adapter’s mapping/sqlite side effects, which defeats the stated goal of exposing a lightweight eVault client. Split EVaultClient into a dedicated entrypoint or subpath export that has no root-module dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/web3-adapter/src/index.ts` around lines 11 - 16, The
root-level export is dragging heavy top-level dependencies into stateless
consumers; create a dedicated lightweight entrypoint that only exposes the
client and its types. Add a new module (e.g., evault/client) that imports and
exports EVaultClient plus MetaEnvelope/UploadFileInput/UploadFileResult and
ensure that module does not import the heavy root-level ./db, mappers, fs, or
logging; then change the current re-exports in index.ts to re-export from
"./evault/client" (and remove any top-level imports of the heavy modules from
the package root) so consumers of EVaultClient do not trigger the adapter’s side
effects.


/**
* Standalone function to spin up an eVault
* @param registryUrl - URL of the registry service
Expand Down
6 changes: 6 additions & 0 deletions platforms/profile-editor/api/nodemon.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node src/index.ts"
}
19 changes: 4 additions & 15 deletions platforms/profile-editor/api/package.json
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
{
"name": "profile-editor-api",
"version": "1.0.0",
"description": "Profile Editor API for the w3ds ecosystem",
"description": "W3DS-native Profile Editor API: reads from AaaS, writes to eVault",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "bash -c 'read -p \"Migration name: \" name && npx typeorm-ts-node-commonjs migration:generate src/database/migrations/$name -d src/database/data-source.ts'",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
"build": "tsc"
Comment on lines 7 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

start currently depends on a dev-only package.

ts-node lives under devDependencies, so a production install that omits dev deps will not start this service even though build passes. Point start at the compiled JS output, or move ts-node into runtime dependencies if that is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/package.json` around lines 7 - 9, The "start"
script currently runs "ts-node src/index.ts" which depends on the dev-only
package "ts-node" (listed under devDependencies), so production installs without
dev deps will fail; fix by changing the "start" script to point to the compiled
output (e.g., "node dist/index.js" or similar matching your build output used by
"build") or, if you truly need to run TypeScript at runtime, move "ts-node" from
devDependencies into dependencies; update the package.json scripts to use
"start" -> node <compiled entry> (and keep "dev" using ts-node) or adjust
dependency placement accordingly so production installs can start.

},
"dependencies": {
"@metastate-foundation/auth": "workspace:*",
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"form-data": "^4.0.5",
"graphql-request": "^6.1.0",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"pg": "^8.20.0",
"reflect-metadata": "^0.2.2",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*",
"typeorm": "^0.3.28",
"uuid": "^9.0.1"
"zod": "^3.23.8"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
"@types/node": "^20.11.24",
"@types/pg": "^8.18.0",
"@types/uuid": "^9.0.8",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
Expand Down
121 changes: 121 additions & 0 deletions platforms/profile-editor/api/src/aaas.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import axios from "axios";
import { env } from "./env";
import {
type Payload,
PROFESSIONAL_PROFILE_ONTOLOGY,
USER_ONTOLOGY,
} from "./ontology";

/** A single packet as returned by AaaS `GET /api/packets`. */
export interface Packet {
id: string;
ontology: string;
w3id: string | null;
data: Payload | null;
receivedAt: string;
}

interface PacketsResponse {
packets: Packet[];
hasMore: boolean;
nextCursor: string | null;
}

/** The current envelope (id + payload) for one ontology. */
export interface EnvelopeState {
id: string;
data: Payload;
}

/**
* Read side of the W3DS-native profile editor: everything is pulled from
* Awareness-as-a-Service. Each MetaEnvelope maps to exactly one packet (AaaS
* upserts by envelope id), so filtering by eVault + ontology yields the current
* state.
*/
export class AaasClient {
private base = env.awarenessServiceUrl.replace(/\/$/, "");
private headers = { Authorization: `Bearer ${env.awarenessApiKey}` };

/** One page of packets for the given filters. */
private async page(
params: Record<string, string | number>,
cursor?: string | null,
): Promise<PacketsResponse> {
const { data } = await axios.get<PacketsResponse>(
`${this.base}/api/packets`,
{
headers: this.headers,
params: { limit: 200, ...params, ...(cursor ? { cursor } : {}) },
timeout: 15000,
},
);
return data;
}

/** Every packet matching the filters, paged to exhaustion (newest last). */
private async all(
params: Record<string, string | number>,
): Promise<Packet[]> {
const out: Packet[] = [];
let cursor: string | null | undefined;
do {
const res = await this.page(params, cursor);
out.push(...(res.packets ?? []));
cursor = res.hasMore ? res.nextCursor : null;
} while (cursor);
return out;
}

/** Latest envelope state per ontology for a single eVault (by eName). */
async getProfileEnvelopes(ename: string): Promise<{
user: EnvelopeState | null;
professional: EnvelopeState | null;
}> {
const packets = await this.all({
evault: ename,
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});

// Packets are ordered oldest→newest, so the last write wins.
let user: EnvelopeState | null = null;
let professional: EnvelopeState | null = null;
for (const p of packets) {
if (!p.data) continue;
if (p.ontology === USER_ONTOLOGY) {
user = { id: p.id, data: p.data };
} else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
professional = { id: p.id, data: p.data };
}
}
return { user, professional };
}

/**
* All User + Professional-Profile envelopes across every eVault, keyed by
* eName — the cross-user source for discovery (no single eVault has them).
*/
async listAllProfiles(): Promise<
Map<string, { user: Payload | null; professional: Payload | null }>
> {
const packets = await this.all({
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});
Comment on lines +98 to +103

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Discovery now scales with total packet history, not current profiles.

listAllProfiles() paginates through every packet ever written and materializes the full history before collapsing it to latest state. Because the discovery flow calls this on demand, latency and memory usage will grow with every profile edit, not just with the number of visible profiles. Cache the collapsed latest envelopes or add an AaaS query that returns current state directly before this path sees production traffic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/src/aaas.ts` around lines 98 - 103,
listAllProfiles() currently pages through every packet history via this.all(...)
and materializes the full history before collapsing to latest state, causing
memory/latency to grow with edits; change it to either (A) perform incremental
collapse while streaming pages (keep a Map keyed by profile id and update latest
envelope per id as each page is processed) or (B) add/consume an AaaS query that
returns only current/latest envelopes for the given ontologies (USER_ONTOLOGY,
PROFESSIONAL_PROFILE_ONTOLOGY) and use that instead of this.all; update the
implementation of listAllProfiles to use the incremental collapse or the new
AaaS query so it only holds the latest envelopes in memory.


const byEname = new Map<
string,
{ user: Payload | null; professional: Payload | null }
>();
for (const p of packets) {
const ename = p.w3id ?? (p.data?.ename as string | undefined);
if (!ename || !p.data) continue;
const entry = byEname.get(ename) ?? { user: null, professional: null };
if (p.ontology === USER_ONTOLOGY) entry.user = p.data;
else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
entry.professional = p.data;
}
byEname.set(ename, entry);
}
return byEname;
}
}
134 changes: 0 additions & 134 deletions platforms/profile-editor/api/src/controllers/AuthController.ts

This file was deleted.

This file was deleted.

Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infrastructure/web3-adapter/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,13 @@ import { logger } from "./logging";
import { fromGlobal, toGlobal } from "./mapper/mapper";
import type { IMapping } from "./mapper/mapper.types";

export { EVaultClient } from "./evault/evault";
export type {
MetaEnvelope,
UploadFileInput,
UploadFileResult,
} from "./evault/evault";
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This re-export still pulls the heavy root module into stateless consumers.

Importing EVaultClient from web3-adapter will still evaluate this file first, including the top-level ./db, mapper, fs, and logging imports above. That means the profile-editor API still inherits the adapter’s mapping/sqlite side effects, which defeats the stated goal of exposing a lightweight eVault client. Split EVaultClient into a dedicated entrypoint or subpath export that has no root-module dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/web3-adapter/src/index.ts` around lines 11 - 16, The
root-level export is dragging heavy top-level dependencies into stateless
consumers; create a dedicated lightweight entrypoint that only exposes the
client and its types. Add a new module (e.g., evault/client) that imports and
exports EVaultClient plus MetaEnvelope/UploadFileInput/UploadFileResult and
ensure that module does not import the heavy root-level ./db, mappers, fs, or
logging; then change the current re-exports in index.ts to re-export from
"./evault/client" (and remove any top-level imports of the heavy modules from
the package root) so consumers of EVaultClient do not trigger the adapter’s side
effects.


/**
* Standalone function to spin up an eVault
* @param registryUrl - URL of the registry service
Expand Down
6 changes: 6 additions & 0 deletions platforms/profile-editor/api/nodemon.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node src/index.ts"
}
19 changes: 4 additions & 15 deletions platforms/profile-editor/api/package.json
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
{
"name": "profile-editor-api",
"version": "1.0.0",
"description": "Profile Editor API for the w3ds ecosystem",
"description": "W3DS-native Profile Editor API: reads from AaaS, writes to eVault",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "bash -c 'read -p \"Migration name: \" name && npx typeorm-ts-node-commonjs migration:generate src/database/migrations/$name -d src/database/data-source.ts'",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
"build": "tsc"
Comment on lines 7 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

start currently depends on a dev-only package.

ts-node lives under devDependencies, so a production install that omits dev deps will not start this service even though build passes. Point start at the compiled JS output, or move ts-node into runtime dependencies if that is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/package.json` around lines 7 - 9, The "start"
script currently runs "ts-node src/index.ts" which depends on the dev-only
package "ts-node" (listed under devDependencies), so production installs without
dev deps will fail; fix by changing the "start" script to point to the compiled
output (e.g., "node dist/index.js" or similar matching your build output used by
"build") or, if you truly need to run TypeScript at runtime, move "ts-node" from
devDependencies into dependencies; update the package.json scripts to use
"start" -> node <compiled entry> (and keep "dev" using ts-node) or adjust
dependency placement accordingly so production installs can start.

},
"dependencies": {
"@metastate-foundation/auth": "workspace:*",
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"form-data": "^4.0.5",
"graphql-request": "^6.1.0",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"pg": "^8.20.0",
"reflect-metadata": "^0.2.2",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*",
"typeorm": "^0.3.28",
"uuid": "^9.0.1"
"zod": "^3.23.8"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
"@types/node": "^20.11.24",
"@types/pg": "^8.18.0",
"@types/uuid": "^9.0.8",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
Expand Down
121 changes: 121 additions & 0 deletions platforms/profile-editor/api/src/aaas.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import axios from "axios";
import { env } from "./env";
import {
type Payload,
PROFESSIONAL_PROFILE_ONTOLOGY,
USER_ONTOLOGY,
} from "./ontology";

/** A single packet as returned by AaaS `GET /api/packets`. */
export interface Packet {
id: string;
ontology: string;
w3id: string | null;
data: Payload | null;
receivedAt: string;
}

interface PacketsResponse {
packets: Packet[];
hasMore: boolean;
nextCursor: string | null;
}

/** The current envelope (id + payload) for one ontology. */
export interface EnvelopeState {
id: string;
data: Payload;
}

/**
* Read side of the W3DS-native profile editor: everything is pulled from
* Awareness-as-a-Service. Each MetaEnvelope maps to exactly one packet (AaaS
* upserts by envelope id), so filtering by eVault + ontology yields the current
* state.
*/
export class AaasClient {
private base = env.awarenessServiceUrl.replace(/\/$/, "");
private headers = { Authorization: `Bearer ${env.awarenessApiKey}` };

/** One page of packets for the given filters. */
private async page(
params: Record<string, string | number>,
cursor?: string | null,
): Promise<PacketsResponse> {
const { data } = await axios.get<PacketsResponse>(
`${this.base}/api/packets`,
{
headers: this.headers,
params: { limit: 200, ...params, ...(cursor ? { cursor } : {}) },
timeout: 15000,
},
);
return data;
}

/** Every packet matching the filters, paged to exhaustion (newest last). */
private async all(
params: Record<string, string | number>,
): Promise<Packet[]> {
const out: Packet[] = [];
let cursor: string | null | undefined;
do {
const res = await this.page(params, cursor);
out.push(...(res.packets ?? []));
cursor = res.hasMore ? res.nextCursor : null;
} while (cursor);
return out;
}

/** Latest envelope state per ontology for a single eVault (by eName). */
async getProfileEnvelopes(ename: string): Promise<{
user: EnvelopeState | null;
professional: EnvelopeState | null;
}> {
const packets = await this.all({
evault: ename,
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});

// Packets are ordered oldest→newest, so the last write wins.
let user: EnvelopeState | null = null;
let professional: EnvelopeState | null = null;
for (const p of packets) {
if (!p.data) continue;
if (p.ontology === USER_ONTOLOGY) {
user = { id: p.id, data: p.data };
} else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
professional = { id: p.id, data: p.data };
}
}
return { user, professional };
}

/**
* All User + Professional-Profile envelopes across every eVault, keyed by
* eName — the cross-user source for discovery (no single eVault has them).
*/
async listAllProfiles(): Promise<
Map<string, { user: Payload | null; professional: Payload | null }>
> {
const packets = await this.all({
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});
Comment on lines +98 to +103

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Discovery now scales with total packet history, not current profiles.

listAllProfiles() paginates through every packet ever written and materializes the full history before collapsing it to latest state. Because the discovery flow calls this on demand, latency and memory usage will grow with every profile edit, not just with the number of visible profiles. Cache the collapsed latest envelopes or add an AaaS query that returns current state directly before this path sees production traffic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/src/aaas.ts` around lines 98 - 103,
listAllProfiles() currently pages through every packet history via this.all(...)
and materializes the full history before collapsing to latest state, causing
memory/latency to grow with edits; change it to either (A) perform incremental
collapse while streaming pages (keep a Map keyed by profile id and update latest
envelope per id as each page is processed) or (B) add/consume an AaaS query that
returns only current/latest envelopes for the given ontologies (USER_ONTOLOGY,
PROFESSIONAL_PROFILE_ONTOLOGY) and use that instead of this.all; update the
implementation of listAllProfiles to use the incremental collapse or the new
AaaS query so it only holds the latest envelopes in memory.


const byEname = new Map<
string,
{ user: Payload | null; professional: Payload | null }
>();
for (const p of packets) {
const ename = p.w3id ?? (p.data?.ename as string | undefined);
if (!ename || !p.data) continue;
const entry = byEname.get(ename) ?? { user: null, professional: null };
if (p.ontology === USER_ONTOLOGY) entry.user = p.data;
else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
entry.professional = p.data;
}
byEname.set(ename, entry);
}
return byEname;
}
}
134 changes: 0 additions & 134 deletions platforms/profile-editor/api/src/controllers/AuthController.ts

This file was deleted.

This file was deleted.

Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infrastructure/web3-adapter/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,13 @@ import { logger } from "./logging";
import { fromGlobal, toGlobal } from "./mapper/mapper";
import type { IMapping } from "./mapper/mapper.types";

export { EVaultClient } from "./evault/evault";
export type {
MetaEnvelope,
UploadFileInput,
UploadFileResult,
} from "./evault/evault";
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This re-export still pulls the heavy root module into stateless consumers.

Importing EVaultClient from web3-adapter will still evaluate this file first, including the top-level ./db, mapper, fs, and logging imports above. That means the profile-editor API still inherits the adapter’s mapping/sqlite side effects, which defeats the stated goal of exposing a lightweight eVault client. Split EVaultClient into a dedicated entrypoint or subpath export that has no root-module dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/web3-adapter/src/index.ts` around lines 11 - 16, The
root-level export is dragging heavy top-level dependencies into stateless
consumers; create a dedicated lightweight entrypoint that only exposes the
client and its types. Add a new module (e.g., evault/client) that imports and
exports EVaultClient plus MetaEnvelope/UploadFileInput/UploadFileResult and
ensure that module does not import the heavy root-level ./db, mappers, fs, or
logging; then change the current re-exports in index.ts to re-export from
"./evault/client" (and remove any top-level imports of the heavy modules from
the package root) so consumers of EVaultClient do not trigger the adapter’s side
effects.


/**
* Standalone function to spin up an eVault
* @param registryUrl - URL of the registry service
Expand Down
6 changes: 6 additions & 0 deletions platforms/profile-editor/api/nodemon.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node src/index.ts"
}
19 changes: 4 additions & 15 deletions platforms/profile-editor/api/package.json
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
{
"name": "profile-editor-api",
"version": "1.0.0",
"description": "Profile Editor API for the w3ds ecosystem",
"description": "W3DS-native Profile Editor API: reads from AaaS, writes to eVault",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "bash -c 'read -p \"Migration name: \" name && npx typeorm-ts-node-commonjs migration:generate src/database/migrations/$name -d src/database/data-source.ts'",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
"build": "tsc"
Comment on lines 7 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

start currently depends on a dev-only package.

ts-node lives under devDependencies, so a production install that omits dev deps will not start this service even though build passes. Point start at the compiled JS output, or move ts-node into runtime dependencies if that is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/package.json` around lines 7 - 9, The "start"
script currently runs "ts-node src/index.ts" which depends on the dev-only
package "ts-node" (listed under devDependencies), so production installs without
dev deps will fail; fix by changing the "start" script to point to the compiled
output (e.g., "node dist/index.js" or similar matching your build output used by
"build") or, if you truly need to run TypeScript at runtime, move "ts-node" from
devDependencies into dependencies; update the package.json scripts to use
"start" -> node <compiled entry> (and keep "dev" using ts-node) or adjust
dependency placement accordingly so production installs can start.

},
"dependencies": {
"@metastate-foundation/auth": "workspace:*",
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"form-data": "^4.0.5",
"graphql-request": "^6.1.0",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"pg": "^8.20.0",
"reflect-metadata": "^0.2.2",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*",
"typeorm": "^0.3.28",
"uuid": "^9.0.1"
"zod": "^3.23.8"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
"@types/node": "^20.11.24",
"@types/pg": "^8.18.0",
"@types/uuid": "^9.0.8",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
Expand Down
121 changes: 121 additions & 0 deletions platforms/profile-editor/api/src/aaas.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import axios from "axios";
import { env } from "./env";
import {
type Payload,
PROFESSIONAL_PROFILE_ONTOLOGY,
USER_ONTOLOGY,
} from "./ontology";

/** A single packet as returned by AaaS `GET /api/packets`. */
export interface Packet {
id: string;
ontology: string;
w3id: string | null;
data: Payload | null;
receivedAt: string;
}

interface PacketsResponse {
packets: Packet[];
hasMore: boolean;
nextCursor: string | null;
}

/** The current envelope (id + payload) for one ontology. */
export interface EnvelopeState {
id: string;
data: Payload;
}

/**
* Read side of the W3DS-native profile editor: everything is pulled from
* Awareness-as-a-Service. Each MetaEnvelope maps to exactly one packet (AaaS
* upserts by envelope id), so filtering by eVault + ontology yields the current
* state.
*/
export class AaasClient {
private base = env.awarenessServiceUrl.replace(/\/$/, "");
private headers = { Authorization: `Bearer ${env.awarenessApiKey}` };

/** One page of packets for the given filters. */
private async page(
params: Record<string, string | number>,
cursor?: string | null,
): Promise<PacketsResponse> {
const { data } = await axios.get<PacketsResponse>(
`${this.base}/api/packets`,
{
headers: this.headers,
params: { limit: 200, ...params, ...(cursor ? { cursor } : {}) },
timeout: 15000,
},
);
return data;
}

/** Every packet matching the filters, paged to exhaustion (newest last). */
private async all(
params: Record<string, string | number>,
): Promise<Packet[]> {
const out: Packet[] = [];
let cursor: string | null | undefined;
do {
const res = await this.page(params, cursor);
out.push(...(res.packets ?? []));
cursor = res.hasMore ? res.nextCursor : null;
} while (cursor);
return out;
}

/** Latest envelope state per ontology for a single eVault (by eName). */
async getProfileEnvelopes(ename: string): Promise<{
user: EnvelopeState | null;
professional: EnvelopeState | null;
}> {
const packets = await this.all({
evault: ename,
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});

// Packets are ordered oldest→newest, so the last write wins.
let user: EnvelopeState | null = null;
let professional: EnvelopeState | null = null;
for (const p of packets) {
if (!p.data) continue;
if (p.ontology === USER_ONTOLOGY) {
user = { id: p.id, data: p.data };
} else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
professional = { id: p.id, data: p.data };
}
}
return { user, professional };
}

/**
* All User + Professional-Profile envelopes across every eVault, keyed by
* eName — the cross-user source for discovery (no single eVault has them).
*/
async listAllProfiles(): Promise<
Map<string, { user: Payload | null; professional: Payload | null }>
> {
const packets = await this.all({
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});
Comment on lines +98 to +103

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Discovery now scales with total packet history, not current profiles.

listAllProfiles() paginates through every packet ever written and materializes the full history before collapsing it to latest state. Because the discovery flow calls this on demand, latency and memory usage will grow with every profile edit, not just with the number of visible profiles. Cache the collapsed latest envelopes or add an AaaS query that returns current state directly before this path sees production traffic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/src/aaas.ts` around lines 98 - 103,
listAllProfiles() currently pages through every packet history via this.all(...)
and materializes the full history before collapsing to latest state, causing
memory/latency to grow with edits; change it to either (A) perform incremental
collapse while streaming pages (keep a Map keyed by profile id and update latest
envelope per id as each page is processed) or (B) add/consume an AaaS query that
returns only current/latest envelopes for the given ontologies (USER_ONTOLOGY,
PROFESSIONAL_PROFILE_ONTOLOGY) and use that instead of this.all; update the
implementation of listAllProfiles to use the incremental collapse or the new
AaaS query so it only holds the latest envelopes in memory.


const byEname = new Map<
string,
{ user: Payload | null; professional: Payload | null }
>();
for (const p of packets) {
const ename = p.w3id ?? (p.data?.ename as string | undefined);
if (!ename || !p.data) continue;
const entry = byEname.get(ename) ?? { user: null, professional: null };
if (p.ontology === USER_ONTOLOGY) entry.user = p.data;
else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
entry.professional = p.data;
}
byEname.set(ename, entry);
}
return byEname;
}
}
134 changes: 0 additions & 134 deletions platforms/profile-editor/api/src/controllers/AuthController.ts

This file was deleted.

This file was deleted.

Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infrastructure/web3-adapter/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,13 @@ import { logger } from "./logging";
import { fromGlobal, toGlobal } from "./mapper/mapper";
import type { IMapping } from "./mapper/mapper.types";

export { EVaultClient } from "./evault/evault";
export type {
MetaEnvelope,
UploadFileInput,
UploadFileResult,
} from "./evault/evault";
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This re-export still pulls the heavy root module into stateless consumers.

Importing EVaultClient from web3-adapter will still evaluate this file first, including the top-level ./db, mapper, fs, and logging imports above. That means the profile-editor API still inherits the adapter’s mapping/sqlite side effects, which defeats the stated goal of exposing a lightweight eVault client. Split EVaultClient into a dedicated entrypoint or subpath export that has no root-module dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/web3-adapter/src/index.ts` around lines 11 - 16, The
root-level export is dragging heavy top-level dependencies into stateless
consumers; create a dedicated lightweight entrypoint that only exposes the
client and its types. Add a new module (e.g., evault/client) that imports and
exports EVaultClient plus MetaEnvelope/UploadFileInput/UploadFileResult and
ensure that module does not import the heavy root-level ./db, mappers, fs, or
logging; then change the current re-exports in index.ts to re-export from
"./evault/client" (and remove any top-level imports of the heavy modules from
the package root) so consumers of EVaultClient do not trigger the adapter’s side
effects.


/**
* Standalone function to spin up an eVault
* @param registryUrl - URL of the registry service
Expand Down
6 changes: 6 additions & 0 deletions platforms/profile-editor/api/nodemon.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node src/index.ts"
}
19 changes: 4 additions & 15 deletions platforms/profile-editor/api/package.json
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
{
"name": "profile-editor-api",
"version": "1.0.0",
"description": "Profile Editor API for the w3ds ecosystem",
"description": "W3DS-native Profile Editor API: reads from AaaS, writes to eVault",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "bash -c 'read -p \"Migration name: \" name && npx typeorm-ts-node-commonjs migration:generate src/database/migrations/$name -d src/database/data-source.ts'",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
"build": "tsc"
Comment on lines 7 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

start currently depends on a dev-only package.

ts-node lives under devDependencies, so a production install that omits dev deps will not start this service even though build passes. Point start at the compiled JS output, or move ts-node into runtime dependencies if that is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/package.json` around lines 7 - 9, The "start"
script currently runs "ts-node src/index.ts" which depends on the dev-only
package "ts-node" (listed under devDependencies), so production installs without
dev deps will fail; fix by changing the "start" script to point to the compiled
output (e.g., "node dist/index.js" or similar matching your build output used by
"build") or, if you truly need to run TypeScript at runtime, move "ts-node" from
devDependencies into dependencies; update the package.json scripts to use
"start" -> node <compiled entry> (and keep "dev" using ts-node) or adjust
dependency placement accordingly so production installs can start.

},
"dependencies": {
"@metastate-foundation/auth": "workspace:*",
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"form-data": "^4.0.5",
"graphql-request": "^6.1.0",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"pg": "^8.20.0",
"reflect-metadata": "^0.2.2",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*",
"typeorm": "^0.3.28",
"uuid": "^9.0.1"
"zod": "^3.23.8"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
"@types/node": "^20.11.24",
"@types/pg": "^8.18.0",
"@types/uuid": "^9.0.8",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
Expand Down
121 changes: 121 additions & 0 deletions platforms/profile-editor/api/src/aaas.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import axios from "axios";
import { env } from "./env";
import {
type Payload,
PROFESSIONAL_PROFILE_ONTOLOGY,
USER_ONTOLOGY,
} from "./ontology";

/** A single packet as returned by AaaS `GET /api/packets`. */
export interface Packet {
id: string;
ontology: string;
w3id: string | null;
data: Payload | null;
receivedAt: string;
}

interface PacketsResponse {
packets: Packet[];
hasMore: boolean;
nextCursor: string | null;
}

/** The current envelope (id + payload) for one ontology. */
export interface EnvelopeState {
id: string;
data: Payload;
}

/**
* Read side of the W3DS-native profile editor: everything is pulled from
* Awareness-as-a-Service. Each MetaEnvelope maps to exactly one packet (AaaS
* upserts by envelope id), so filtering by eVault + ontology yields the current
* state.
*/
export class AaasClient {
private base = env.awarenessServiceUrl.replace(/\/$/, "");
private headers = { Authorization: `Bearer ${env.awarenessApiKey}` };

/** One page of packets for the given filters. */
private async page(
params: Record<string, string | number>,
cursor?: string | null,
): Promise<PacketsResponse> {
const { data } = await axios.get<PacketsResponse>(
`${this.base}/api/packets`,
{
headers: this.headers,
params: { limit: 200, ...params, ...(cursor ? { cursor } : {}) },
timeout: 15000,
},
);
return data;
}

/** Every packet matching the filters, paged to exhaustion (newest last). */
private async all(
params: Record<string, string | number>,
): Promise<Packet[]> {
const out: Packet[] = [];
let cursor: string | null | undefined;
do {
const res = await this.page(params, cursor);
out.push(...(res.packets ?? []));
cursor = res.hasMore ? res.nextCursor : null;
} while (cursor);
return out;
}

/** Latest envelope state per ontology for a single eVault (by eName). */
async getProfileEnvelopes(ename: string): Promise<{
user: EnvelopeState | null;
professional: EnvelopeState | null;
}> {
const packets = await this.all({
evault: ename,
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});

// Packets are ordered oldest→newest, so the last write wins.
let user: EnvelopeState | null = null;
let professional: EnvelopeState | null = null;
for (const p of packets) {
if (!p.data) continue;
if (p.ontology === USER_ONTOLOGY) {
user = { id: p.id, data: p.data };
} else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
professional = { id: p.id, data: p.data };
}
}
return { user, professional };
}

/**
* All User + Professional-Profile envelopes across every eVault, keyed by
* eName — the cross-user source for discovery (no single eVault has them).
*/
async listAllProfiles(): Promise<
Map<string, { user: Payload | null; professional: Payload | null }>
> {
const packets = await this.all({
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});
Comment on lines +98 to +103

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Discovery now scales with total packet history, not current profiles.

listAllProfiles() paginates through every packet ever written and materializes the full history before collapsing it to latest state. Because the discovery flow calls this on demand, latency and memory usage will grow with every profile edit, not just with the number of visible profiles. Cache the collapsed latest envelopes or add an AaaS query that returns current state directly before this path sees production traffic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/src/aaas.ts` around lines 98 - 103,
listAllProfiles() currently pages through every packet history via this.all(...)
and materializes the full history before collapsing to latest state, causing
memory/latency to grow with edits; change it to either (A) perform incremental
collapse while streaming pages (keep a Map keyed by profile id and update latest
envelope per id as each page is processed) or (B) add/consume an AaaS query that
returns only current/latest envelopes for the given ontologies (USER_ONTOLOGY,
PROFESSIONAL_PROFILE_ONTOLOGY) and use that instead of this.all; update the
implementation of listAllProfiles to use the incremental collapse or the new
AaaS query so it only holds the latest envelopes in memory.


const byEname = new Map<
string,
{ user: Payload | null; professional: Payload | null }
>();
for (const p of packets) {
const ename = p.w3id ?? (p.data?.ename as string | undefined);
if (!ename || !p.data) continue;
const entry = byEname.get(ename) ?? { user: null, professional: null };
if (p.ontology === USER_ONTOLOGY) entry.user = p.data;
else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
entry.professional = p.data;
}
byEname.set(ename, entry);
}
return byEname;
}
}
134 changes: 0 additions & 134 deletions platforms/profile-editor/api/src/controllers/AuthController.ts

This file was deleted.

This file was deleted.

Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infrastructure/web3-adapter/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,13 @@ import { logger } from "./logging";
import { fromGlobal, toGlobal } from "./mapper/mapper";
import type { IMapping } from "./mapper/mapper.types";

export { EVaultClient } from "./evault/evault";
export type {
MetaEnvelope,
UploadFileInput,
UploadFileResult,
} from "./evault/evault";
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This re-export still pulls the heavy root module into stateless consumers.

Importing EVaultClient from web3-adapter will still evaluate this file first, including the top-level ./db, mapper, fs, and logging imports above. That means the profile-editor API still inherits the adapter’s mapping/sqlite side effects, which defeats the stated goal of exposing a lightweight eVault client. Split EVaultClient into a dedicated entrypoint or subpath export that has no root-module dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/web3-adapter/src/index.ts` around lines 11 - 16, The
root-level export is dragging heavy top-level dependencies into stateless
consumers; create a dedicated lightweight entrypoint that only exposes the
client and its types. Add a new module (e.g., evault/client) that imports and
exports EVaultClient plus MetaEnvelope/UploadFileInput/UploadFileResult and
ensure that module does not import the heavy root-level ./db, mappers, fs, or
logging; then change the current re-exports in index.ts to re-export from
"./evault/client" (and remove any top-level imports of the heavy modules from
the package root) so consumers of EVaultClient do not trigger the adapter’s side
effects.


/**
* Standalone function to spin up an eVault
* @param registryUrl - URL of the registry service
Expand Down
6 changes: 6 additions & 0 deletions platforms/profile-editor/api/nodemon.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node src/index.ts"
}
19 changes: 4 additions & 15 deletions platforms/profile-editor/api/package.json
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
{
"name": "profile-editor-api",
"version": "1.0.0",
"description": "Profile Editor API for the w3ds ecosystem",
"description": "W3DS-native Profile Editor API: reads from AaaS, writes to eVault",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "bash -c 'read -p \"Migration name: \" name && npx typeorm-ts-node-commonjs migration:generate src/database/migrations/$name -d src/database/data-source.ts'",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
"build": "tsc"
Comment on lines 7 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

start currently depends on a dev-only package.

ts-node lives under devDependencies, so a production install that omits dev deps will not start this service even though build passes. Point start at the compiled JS output, or move ts-node into runtime dependencies if that is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/package.json` around lines 7 - 9, The "start"
script currently runs "ts-node src/index.ts" which depends on the dev-only
package "ts-node" (listed under devDependencies), so production installs without
dev deps will fail; fix by changing the "start" script to point to the compiled
output (e.g., "node dist/index.js" or similar matching your build output used by
"build") or, if you truly need to run TypeScript at runtime, move "ts-node" from
devDependencies into dependencies; update the package.json scripts to use
"start" -> node <compiled entry> (and keep "dev" using ts-node) or adjust
dependency placement accordingly so production installs can start.

},
"dependencies": {
"@metastate-foundation/auth": "workspace:*",
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"form-data": "^4.0.5",
"graphql-request": "^6.1.0",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"pg": "^8.20.0",
"reflect-metadata": "^0.2.2",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*",
"typeorm": "^0.3.28",
"uuid": "^9.0.1"
"zod": "^3.23.8"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
"@types/node": "^20.11.24",
"@types/pg": "^8.18.0",
"@types/uuid": "^9.0.8",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
Expand Down
121 changes: 121 additions & 0 deletions platforms/profile-editor/api/src/aaas.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import axios from "axios";
import { env } from "./env";
import {
type Payload,
PROFESSIONAL_PROFILE_ONTOLOGY,
USER_ONTOLOGY,
} from "./ontology";

/** A single packet as returned by AaaS `GET /api/packets`. */
export interface Packet {
id: string;
ontology: string;
w3id: string | null;
data: Payload | null;
receivedAt: string;
}

interface PacketsResponse {
packets: Packet[];
hasMore: boolean;
nextCursor: string | null;
}

/** The current envelope (id + payload) for one ontology. */
export interface EnvelopeState {
id: string;
data: Payload;
}

/**
* Read side of the W3DS-native profile editor: everything is pulled from
* Awareness-as-a-Service. Each MetaEnvelope maps to exactly one packet (AaaS
* upserts by envelope id), so filtering by eVault + ontology yields the current
* state.
*/
export class AaasClient {
private base = env.awarenessServiceUrl.replace(/\/$/, "");
private headers = { Authorization: `Bearer ${env.awarenessApiKey}` };

/** One page of packets for the given filters. */
private async page(
params: Record<string, string | number>,
cursor?: string | null,
): Promise<PacketsResponse> {
const { data } = await axios.get<PacketsResponse>(
`${this.base}/api/packets`,
{
headers: this.headers,
params: { limit: 200, ...params, ...(cursor ? { cursor } : {}) },
timeout: 15000,
},
);
return data;
}

/** Every packet matching the filters, paged to exhaustion (newest last). */
private async all(
params: Record<string, string | number>,
): Promise<Packet[]> {
const out: Packet[] = [];
let cursor: string | null | undefined;
do {
const res = await this.page(params, cursor);
out.push(...(res.packets ?? []));
cursor = res.hasMore ? res.nextCursor : null;
} while (cursor);
return out;
}

/** Latest envelope state per ontology for a single eVault (by eName). */
async getProfileEnvelopes(ename: string): Promise<{
user: EnvelopeState | null;
professional: EnvelopeState | null;
}> {
const packets = await this.all({
evault: ename,
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});

// Packets are ordered oldest→newest, so the last write wins.
let user: EnvelopeState | null = null;
let professional: EnvelopeState | null = null;
for (const p of packets) {
if (!p.data) continue;
if (p.ontology === USER_ONTOLOGY) {
user = { id: p.id, data: p.data };
} else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
professional = { id: p.id, data: p.data };
}
}
return { user, professional };
}

/**
* All User + Professional-Profile envelopes across every eVault, keyed by
* eName — the cross-user source for discovery (no single eVault has them).
*/
async listAllProfiles(): Promise<
Map<string, { user: Payload | null; professional: Payload | null }>
> {
const packets = await this.all({
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});
Comment on lines +98 to +103

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Discovery now scales with total packet history, not current profiles.

listAllProfiles() paginates through every packet ever written and materializes the full history before collapsing it to latest state. Because the discovery flow calls this on demand, latency and memory usage will grow with every profile edit, not just with the number of visible profiles. Cache the collapsed latest envelopes or add an AaaS query that returns current state directly before this path sees production traffic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/src/aaas.ts` around lines 98 - 103,
listAllProfiles() currently pages through every packet history via this.all(...)
and materializes the full history before collapsing to latest state, causing
memory/latency to grow with edits; change it to either (A) perform incremental
collapse while streaming pages (keep a Map keyed by profile id and update latest
envelope per id as each page is processed) or (B) add/consume an AaaS query that
returns only current/latest envelopes for the given ontologies (USER_ONTOLOGY,
PROFESSIONAL_PROFILE_ONTOLOGY) and use that instead of this.all; update the
implementation of listAllProfiles to use the incremental collapse or the new
AaaS query so it only holds the latest envelopes in memory.


const byEname = new Map<
string,
{ user: Payload | null; professional: Payload | null }
>();
for (const p of packets) {
const ename = p.w3id ?? (p.data?.ename as string | undefined);
if (!ename || !p.data) continue;
const entry = byEname.get(ename) ?? { user: null, professional: null };
if (p.ontology === USER_ONTOLOGY) entry.user = p.data;
else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
entry.professional = p.data;
}
byEname.set(ename, entry);
}
return byEname;
}
}
134 changes: 0 additions & 134 deletions platforms/profile-editor/api/src/controllers/AuthController.ts

This file was deleted.

This file was deleted.

Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions infrastructure/web3-adapter/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,13 @@ import { logger } from "./logging";
import { fromGlobal, toGlobal } from "./mapper/mapper";
import type { IMapping } from "./mapper/mapper.types";

export { EVaultClient } from "./evault/evault";
export type {
MetaEnvelope,
UploadFileInput,
UploadFileResult,
} from "./evault/evault";
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This re-export still pulls the heavy root module into stateless consumers.

Importing EVaultClient from web3-adapter will still evaluate this file first, including the top-level ./db, mapper, fs, and logging imports above. That means the profile-editor API still inherits the adapter’s mapping/sqlite side effects, which defeats the stated goal of exposing a lightweight eVault client. Split EVaultClient into a dedicated entrypoint or subpath export that has no root-module dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/web3-adapter/src/index.ts` around lines 11 - 16, The
root-level export is dragging heavy top-level dependencies into stateless
consumers; create a dedicated lightweight entrypoint that only exposes the
client and its types. Add a new module (e.g., evault/client) that imports and
exports EVaultClient plus MetaEnvelope/UploadFileInput/UploadFileResult and
ensure that module does not import the heavy root-level ./db, mappers, fs, or
logging; then change the current re-exports in index.ts to re-export from
"./evault/client" (and remove any top-level imports of the heavy modules from
the package root) so consumers of EVaultClient do not trigger the adapter’s side
effects.


/**
* Standalone function to spin up an eVault
* @param registryUrl - URL of the registry service
Expand Down
6 changes: 6 additions & 0 deletions platforms/profile-editor/api/nodemon.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
{
"watch": ["src"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node src/index.ts"
}
19 changes: 4 additions & 15 deletions platforms/profile-editor/api/package.json
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
{
"name": "profile-editor-api",
"version": "1.0.0",
"description": "Profile Editor API for the w3ds ecosystem",
"description": "W3DS-native Profile Editor API: reads from AaaS, writes to eVault",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "bash -c 'read -p \"Migration name: \" name && npx typeorm-ts-node-commonjs migration:generate src/database/migrations/$name -d src/database/data-source.ts'",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
"build": "tsc"
Comment on lines 7 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

start currently depends on a dev-only package.

ts-node lives under devDependencies, so a production install that omits dev deps will not start this service even though build passes. Point start at the compiled JS output, or move ts-node into runtime dependencies if that is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/package.json` around lines 7 - 9, The "start"
script currently runs "ts-node src/index.ts" which depends on the dev-only
package "ts-node" (listed under devDependencies), so production installs without
dev deps will fail; fix by changing the "start" script to point to the compiled
output (e.g., "node dist/index.js" or similar matching your build output used by
"build") or, if you truly need to run TypeScript at runtime, move "ts-node" from
devDependencies into dependencies; update the package.json scripts to use
"start" -> node <compiled entry> (and keep "dev" using ts-node) or adjust
dependency placement accordingly so production installs can start.

},
"dependencies": {
"@metastate-foundation/auth": "workspace:*",
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"form-data": "^4.0.5",
"graphql-request": "^6.1.0",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"pg": "^8.20.0",
"reflect-metadata": "^0.2.2",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*",
"typeorm": "^0.3.28",
"uuid": "^9.0.1"
"zod": "^3.23.8"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
"@types/node": "^20.11.24",
"@types/pg": "^8.18.0",
"@types/uuid": "^9.0.8",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
Expand Down
121 changes: 121 additions & 0 deletions platforms/profile-editor/api/src/aaas.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
import axios from "axios";
import { env } from "./env";
import {
type Payload,
PROFESSIONAL_PROFILE_ONTOLOGY,
USER_ONTOLOGY,
} from "./ontology";

/** A single packet as returned by AaaS `GET /api/packets`. */
export interface Packet {
id: string;
ontology: string;
w3id: string | null;
data: Payload | null;
receivedAt: string;
}

interface PacketsResponse {
packets: Packet[];
hasMore: boolean;
nextCursor: string | null;
}

/** The current envelope (id + payload) for one ontology. */
export interface EnvelopeState {
id: string;
data: Payload;
}

/**
* Read side of the W3DS-native profile editor: everything is pulled from
* Awareness-as-a-Service. Each MetaEnvelope maps to exactly one packet (AaaS
* upserts by envelope id), so filtering by eVault + ontology yields the current
* state.
*/
export class AaasClient {
private base = env.awarenessServiceUrl.replace(/\/$/, "");
private headers = { Authorization: `Bearer ${env.awarenessApiKey}` };

/** One page of packets for the given filters. */
private async page(
params: Record<string, string | number>,
cursor?: string | null,
): Promise<PacketsResponse> {
const { data } = await axios.get<PacketsResponse>(
`${this.base}/api/packets`,
{
headers: this.headers,
params: { limit: 200, ...params, ...(cursor ? { cursor } : {}) },
timeout: 15000,
},
);
return data;
}

/** Every packet matching the filters, paged to exhaustion (newest last). */
private async all(
params: Record<string, string | number>,
): Promise<Packet[]> {
const out: Packet[] = [];
let cursor: string | null | undefined;
do {
const res = await this.page(params, cursor);
out.push(...(res.packets ?? []));
cursor = res.hasMore ? res.nextCursor : null;
} while (cursor);
return out;
}

/** Latest envelope state per ontology for a single eVault (by eName). */
async getProfileEnvelopes(ename: string): Promise<{
user: EnvelopeState | null;
professional: EnvelopeState | null;
}> {
const packets = await this.all({
evault: ename,
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});

// Packets are ordered oldest→newest, so the last write wins.
let user: EnvelopeState | null = null;
let professional: EnvelopeState | null = null;
for (const p of packets) {
if (!p.data) continue;
if (p.ontology === USER_ONTOLOGY) {
user = { id: p.id, data: p.data };
} else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
professional = { id: p.id, data: p.data };
}
}
return { user, professional };
}

/**
* All User + Professional-Profile envelopes across every eVault, keyed by
* eName — the cross-user source for discovery (no single eVault has them).
*/
async listAllProfiles(): Promise<
Map<string, { user: Payload | null; professional: Payload | null }>
> {
const packets = await this.all({
ontology: `${USER_ONTOLOGY},${PROFESSIONAL_PROFILE_ONTOLOGY}`,
});
Comment on lines +98 to +103

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Discovery now scales with total packet history, not current profiles.

listAllProfiles() paginates through every packet ever written and materializes the full history before collapsing it to latest state. Because the discovery flow calls this on demand, latency and memory usage will grow with every profile edit, not just with the number of visible profiles. Cache the collapsed latest envelopes or add an AaaS query that returns current state directly before this path sees production traffic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@platforms/profile-editor/api/src/aaas.ts` around lines 98 - 103,
listAllProfiles() currently pages through every packet history via this.all(...)
and materializes the full history before collapsing to latest state, causing
memory/latency to grow with edits; change it to either (A) perform incremental
collapse while streaming pages (keep a Map keyed by profile id and update latest
envelope per id as each page is processed) or (B) add/consume an AaaS query that
returns only current/latest envelopes for the given ontologies (USER_ONTOLOGY,
PROFESSIONAL_PROFILE_ONTOLOGY) and use that instead of this.all; update the
implementation of listAllProfiles to use the incremental collapse or the new
AaaS query so it only holds the latest envelopes in memory.


const byEname = new Map<
string,
{ user: Payload | null; professional: Payload | null }
>();
for (const p of packets) {
const ename = p.w3id ?? (p.data?.ename as string | undefined);
if (!ename || !p.data) continue;
const entry = byEname.get(ename) ?? { user: null, professional: null };
if (p.ontology === USER_ONTOLOGY) entry.user = p.data;
else if (p.ontology === PROFESSIONAL_PROFILE_ONTOLOGY) {
entry.professional = p.data;
}
byEname.set(ename, entry);
}
return byEname;
}
}
134 changes: 0 additions & 134 deletions platforms/profile-editor/api/src/controllers/AuthController.ts

This file was deleted.

This file was deleted.

Loading
Loading