diff --git a/apps/tangle-cloud/src/blueprintApps/manifest.ts b/apps/tangle-cloud/src/blueprintApps/manifest.ts
index f49839e46..31700bae8 100644
--- a/apps/tangle-cloud/src/blueprintApps/manifest.ts
+++ b/apps/tangle-cloud/src/blueprintApps/manifest.ts
@@ -198,11 +198,15 @@ function parseExternalApp(
const reasons: string[] = [];
if (requestedMode === 'iframe') {
- reasons.push('Iframe embedding is disabled by policy; verified apps must open in a new tab.');
+ reasons.push(
+ 'Iframe embedding is disabled by policy; verified apps must open in a new tab.',
+ );
}
if (!options.publisherVerified) {
- reasons.push('Publisher is not verified for advanced external app handoff.');
+ reasons.push(
+ 'Publisher is not verified for advanced external app handoff.',
+ );
}
if (!options.metadataVerified) {
@@ -288,7 +292,8 @@ export function buildBlueprintManifestFromMetadata(
? 'verified'
: getPublisherVerificationForNamespace(publisherNamespace),
};
- const metadataVerified = blueprint.metadataVerification?.status === 'verified';
+ const metadataVerified =
+ blueprint.metadataVerification?.status === 'verified';
const publisherVerified = publisher.verification === 'verified';
const slugPolicy = canPublisherClaimSlug(normalizedRequestedSlug, publisher)
? 'publisher-scoped'
@@ -313,9 +318,12 @@ export function buildBlueprintManifestFromMetadata(
};
const allowDeclarativeTier =
- manifestRoot !== null && blueprint.metadataVerification?.productionReady === true;
+ manifestRoot !== null &&
+ blueprint.metadataVerification?.productionReady === true;
const trustedExternalApp =
- manifest.externalApp?.trust === 'trusted' ? manifest.externalApp : undefined;
+ manifest.externalApp?.trust === 'trusted'
+ ? manifest.externalApp
+ : undefined;
const entry: BlueprintAppEntry = {
slug: normalizedRequestedSlug,
diff --git a/apps/tangle-cloud/src/components/blueprintApps/BlueprintHostCard.tsx b/apps/tangle-cloud/src/components/blueprintApps/BlueprintHostCard.tsx
index a80203708..cf9c0d245 100644
--- a/apps/tangle-cloud/src/components/blueprintApps/BlueprintHostCard.tsx
+++ b/apps/tangle-cloud/src/components/blueprintApps/BlueprintHostCard.tsx
@@ -93,8 +93,8 @@ const BlueprintHostCard = ({ blueprint, serviceId }: Props) => {
value={
blueprint.metadataVerification?.status === 'verified'
? 'Verified owner attestation'
- : blueprint.metadataVerification?.reason ??
- 'Unverified metadata'
+ : (blueprint.metadataVerification?.reason ??
+ 'Unverified metadata')
}
/>
@@ -158,10 +158,7 @@ const BlueprintHostCard = ({ blueprint, serviceId }: Props) => {
)}
{blueprintUi.theme.accentColor && (
-
+
)}
{blueprintUi.theme.secondaryColor && (
{
{action.fields && action.fields.length > 0 && (
{action.fields
- .map((field) =>
- `${field.label} (${field.input}${field.required ? ', required' : ''})`,
+ .map(
+ (field) =>
+ `${field.label} (${field.input}${field.required ? ', required' : ''})`,
)
.join(' • ')}
diff --git a/apps/tangle-cloud/src/pages/blueprints/create/page.tsx b/apps/tangle-cloud/src/pages/blueprints/create/page.tsx
index ac0f0c3ae..25273aaca 100644
--- a/apps/tangle-cloud/src/pages/blueprints/create/page.tsx
+++ b/apps/tangle-cloud/src/pages/blueprints/create/page.tsx
@@ -877,8 +877,8 @@ const BasicInfoStep: FC = ({
/>
Publish the JSON preview below at this URI so cloud.tangle.tools can
- resolve your hosted blueprint surfaces and shared runtime metadata.
- New SDK blueprints ship the same contract shape in
+ resolve your hosted blueprint surfaces and shared runtime metadata. New
+ SDK blueprints ship the same contract shape in
`metadata/blueprint-metadata.json`.
{requiresIpfsForBlueprintMetadata()
? ' Production hosting only accepts ipfs:// metadata URIs.'
diff --git a/apps/tangle-cloud/src/pages/blueprints/manage/page.tsx b/apps/tangle-cloud/src/pages/blueprints/manage/page.tsx
index 0abbe3b73..dc3be055e 100644
--- a/apps/tangle-cloud/src/pages/blueprints/manage/page.tsx
+++ b/apps/tangle-cloud/src/pages/blueprints/manage/page.tsx
@@ -170,17 +170,21 @@ const fetchBlueprintMetadataPreview = async (
controller.abort();
}
- const response = await fetch(resolveBlueprintMetadataFetchUrl(metadataUri), {
- signal: controller.signal,
- cache: 'no-store',
- });
+ const response = await fetch(
+ resolveBlueprintMetadataFetchUrl(metadataUri),
+ {
+ signal: controller.signal,
+ cache: 'no-store',
+ },
+ );
if (!response.ok) {
throw new Error(`Failed to fetch metadata: ${response.status}`);
}
const metadataText = await response.text();
- const { parsed, rawMetadata } = parseBlueprintMetadataJsonText(metadataText);
+ const { parsed, rawMetadata } =
+ parseBlueprintMetadataJsonText(metadataText);
const metadataJson = rawMetadata;
if (metadataJson === null) {
throw new Error('Metadata payload must be a JSON object');
@@ -195,7 +199,8 @@ const fetchBlueprintMetadataPreview = async (
codeRepository: parsed.codeUrl ?? undefined,
docs:
metadataJson !== null
- ? readString(metadataJson.docs) ?? readString(metadataJson.documentation)
+ ? (readString(metadataJson.docs) ??
+ readString(metadataJson.documentation))
: undefined,
metadataHash: computeBlueprintMetadataPayloadHash(metadataJson),
};
diff --git a/libs/tangle-shared-ui/src/blueprintApps/authoring.ts b/libs/tangle-shared-ui/src/blueprintApps/authoring.ts
index 130482507..e04be360c 100644
--- a/libs/tangle-shared-ui/src/blueprintApps/authoring.ts
+++ b/libs/tangle-shared-ui/src/blueprintApps/authoring.ts
@@ -142,8 +142,7 @@ const MODULE_SLOT_VALUES = new Set([
'resources',
]);
-const COLOR_PATTERN =
- /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
+const COLOR_PATTERN = /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
const MAX_METADATA_BYTES = 64 * 1024;
const MAX_STRING_LENGTH = 240;
@@ -190,14 +189,6 @@ const readTrimmedString = (
return trimmed.slice(0, maxLength);
};
-const readNullableString = (
- value: unknown,
- maxLength = MAX_STRING_LENGTH,
-): string | null => {
- const trimmed = readTrimmedString(value, maxLength);
- return trimmed ?? null;
-};
-
const readStringArray = (
value: unknown,
maxItems = MAX_STRING_LIST_COUNT,
@@ -341,124 +332,118 @@ const parseTheme = (value: unknown): BlueprintUiTheme | undefined => {
return Object.keys(theme).length > 0 ? theme : undefined;
};
-const parseOverviewCards = (value: unknown): BlueprintUiOverviewCard[] | undefined => {
+const parseOverviewCards = (
+ value: unknown,
+): BlueprintUiOverviewCard[] | undefined => {
if (!Array.isArray(value)) {
return undefined;
}
- const cards = value
- .slice(0, MAX_CARD_COUNT)
- .flatMap((entry, index) => {
- if (!isRecord(entry)) {
- return [];
- }
-
- const title = readTrimmedString(entry.title, 80);
- const kind = readTrimmedString(entry.kind, 32)?.toLowerCase();
- if (!title || !kind || !CARD_KIND_VALUES.has(kind as BlueprintUiCardKind)) {
- return [];
- }
-
- const tone = readTrimmedString(entry.tone, 24)?.toLowerCase();
- const links = Array.isArray(entry.links)
- ? entry.links
- .slice(0, MAX_CARD_LINK_COUNT)
- .flatMap((link) => {
- if (!isRecord(link)) {
- return [];
- }
-
- const label = readTrimmedString(link.label, 60);
- const href = readTrimmedString(link.href, 2_048);
- if (!label || !isValidHttpsUrl(href)) {
- return [];
- }
+ const cards = value.slice(0, MAX_CARD_COUNT).flatMap((entry, index) => {
+ if (!isRecord(entry)) {
+ return [];
+ }
+
+ const title = readTrimmedString(entry.title, 80);
+ const kind = readTrimmedString(entry.kind, 32)?.toLowerCase();
+ if (!title || !kind || !CARD_KIND_VALUES.has(kind as BlueprintUiCardKind)) {
+ return [];
+ }
+
+ const tone = readTrimmedString(entry.tone, 24)?.toLowerCase();
+ const links = Array.isArray(entry.links)
+ ? entry.links.slice(0, MAX_CARD_LINK_COUNT).flatMap((link) => {
+ if (!isRecord(link)) {
+ return [];
+ }
- return [{ label, href }];
- })
- : undefined;
- const items = readStringArray(entry.items, MAX_CARD_ITEM_COUNT, 120);
+ const label = readTrimmedString(link.label, 60);
+ const href = readTrimmedString(link.href, 2_048);
+ if (!label || !isValidHttpsUrl(href)) {
+ return [];
+ }
- return [
- {
- id:
- sanitizeBlueprintSlug(readTrimmedString(entry.id, 60) ?? '') ||
- `card-${index + 1}`,
- kind: kind as BlueprintUiCardKind,
- title,
- description: readTrimmedString(entry.description, 240),
- value: readTrimmedString(entry.value, 80),
- tone: CARD_TONE_VALUES.has(tone as BlueprintUiCardTone)
- ? (tone as BlueprintUiCardTone)
- : undefined,
- ...(links && links.length > 0 ? { links } : {}),
- ...(items.length > 0 ? { items } : {}),
- } satisfies BlueprintUiOverviewCard,
- ];
- });
+ return [{ label, href }];
+ })
+ : undefined;
+ const items = readStringArray(entry.items, MAX_CARD_ITEM_COUNT, 120);
+
+ return [
+ {
+ id:
+ sanitizeBlueprintSlug(readTrimmedString(entry.id, 60) ?? '') ||
+ `card-${index + 1}`,
+ kind: kind as BlueprintUiCardKind,
+ title,
+ description: readTrimmedString(entry.description, 240),
+ value: readTrimmedString(entry.value, 80),
+ tone: CARD_TONE_VALUES.has(tone as BlueprintUiCardTone)
+ ? (tone as BlueprintUiCardTone)
+ : undefined,
+ ...(links && links.length > 0 ? { links } : {}),
+ ...(items.length > 0 ? { items } : {}),
+ } satisfies BlueprintUiOverviewCard,
+ ];
+ });
return cards.length > 0 ? cards : undefined;
};
-const parseActionFields = (value: unknown): BlueprintUiActionField[] | undefined => {
+const parseActionFields = (
+ value: unknown,
+): BlueprintUiActionField[] | undefined => {
if (!Array.isArray(value)) {
return undefined;
}
- const fields = value
- .slice(0, MAX_ACTION_FIELD_COUNT)
- .flatMap((entry) => {
- if (!isRecord(entry)) {
- return [];
- }
-
- const key =
- sanitizeBlueprintSlug(readTrimmedString(entry.key, 60) ?? '').replace(
- /-/g,
- '_',
- );
- const label = readTrimmedString(entry.label, 80);
- const input = readTrimmedString(entry.input, 24)?.toLowerCase();
-
- if (
- !key ||
- !label ||
- !input ||
- !ACTION_FIELD_INPUT_VALUES.has(input as BlueprintUiActionFieldInput)
- ) {
- return [];
- }
-
- const options = Array.isArray(entry.options)
- ? entry.options
- .slice(0, MAX_OPTION_COUNT)
- .flatMap((option) => {
- if (!isRecord(option)) {
- return [];
- }
-
- const optionLabel = readTrimmedString(option.label, 60);
- const optionValue = readTrimmedString(option.value, 60);
- if (!optionLabel || !optionValue) {
- return [];
- }
+ const fields = value.slice(0, MAX_ACTION_FIELD_COUNT).flatMap((entry) => {
+ if (!isRecord(entry)) {
+ return [];
+ }
+
+ const key = sanitizeBlueprintSlug(
+ readTrimmedString(entry.key, 60) ?? '',
+ ).replace(/-/g, '_');
+ const label = readTrimmedString(entry.label, 80);
+ const input = readTrimmedString(entry.input, 24)?.toLowerCase();
+
+ if (
+ !key ||
+ !label ||
+ !input ||
+ !ACTION_FIELD_INPUT_VALUES.has(input as BlueprintUiActionFieldInput)
+ ) {
+ return [];
+ }
+
+ const options = Array.isArray(entry.options)
+ ? entry.options.slice(0, MAX_OPTION_COUNT).flatMap((option) => {
+ if (!isRecord(option)) {
+ return [];
+ }
- return [{ label: optionLabel, value: optionValue }];
- })
- : undefined;
+ const optionLabel = readTrimmedString(option.label, 60);
+ const optionValue = readTrimmedString(option.value, 60);
+ if (!optionLabel || !optionValue) {
+ return [];
+ }
- return [
- {
- key,
- label,
- input: input as BlueprintUiActionFieldInput,
- required: entry.required === true,
- placeholder: readTrimmedString(entry.placeholder, 120),
- helpText: readTrimmedString(entry.helpText, 160),
- ...(options && options.length > 0 ? { options } : {}),
- } satisfies BlueprintUiActionField,
- ];
- });
+ return [{ label: optionLabel, value: optionValue }];
+ })
+ : undefined;
+
+ return [
+ {
+ key,
+ label,
+ input: input as BlueprintUiActionFieldInput,
+ required: entry.required === true,
+ placeholder: readTrimmedString(entry.placeholder, 120),
+ helpText: readTrimmedString(entry.helpText, 160),
+ ...(options && options.length > 0 ? { options } : {}),
+ } satisfies BlueprintUiActionField,
+ ];
+ });
return fields.length > 0 ? fields : undefined;
};
@@ -468,44 +453,42 @@ const parseActions = (value: unknown): BlueprintUiAction[] | undefined => {
return undefined;
}
- const actions = value
- .slice(0, MAX_ACTION_COUNT)
- .flatMap((entry, index) => {
- if (!isRecord(entry)) {
- return [];
- }
-
- const label = readTrimmedString(entry.label, 80);
- const target = readTrimmedString(entry.target, 24)?.toLowerCase();
- if (
- !label ||
- !target ||
- !ACTION_TARGET_VALUES.has(target as BlueprintUiActionTarget)
- ) {
- return [];
- }
-
- const href = readTrimmedString(entry.href, 2_048);
- const fields = parseActionFields(entry.fields);
-
- if (!fields && !isValidHttpsUrl(href)) {
- return [];
- }
-
- return [
- {
- id:
- sanitizeBlueprintSlug(readTrimmedString(entry.id, 60) ?? '') ||
- `action-${index + 1}`,
- label,
- description: readTrimmedString(entry.description, 240),
- target: target as BlueprintUiActionTarget,
- submitLabel: readTrimmedString(entry.submitLabel, 60),
- ...(isValidHttpsUrl(href) ? { href } : {}),
- ...(fields ? { fields } : {}),
- } satisfies BlueprintUiAction,
- ];
- });
+ const actions = value.slice(0, MAX_ACTION_COUNT).flatMap((entry, index) => {
+ if (!isRecord(entry)) {
+ return [];
+ }
+
+ const label = readTrimmedString(entry.label, 80);
+ const target = readTrimmedString(entry.target, 24)?.toLowerCase();
+ if (
+ !label ||
+ !target ||
+ !ACTION_TARGET_VALUES.has(target as BlueprintUiActionTarget)
+ ) {
+ return [];
+ }
+
+ const href = readTrimmedString(entry.href, 2_048);
+ const fields = parseActionFields(entry.fields);
+
+ if (!fields && !isValidHttpsUrl(href)) {
+ return [];
+ }
+
+ return [
+ {
+ id:
+ sanitizeBlueprintSlug(readTrimmedString(entry.id, 60) ?? '') ||
+ `action-${index + 1}`,
+ label,
+ description: readTrimmedString(entry.description, 240),
+ target: target as BlueprintUiActionTarget,
+ submitLabel: readTrimmedString(entry.submitLabel, 60),
+ ...(isValidHttpsUrl(href) ? { href } : {}),
+ ...(fields ? { fields } : {}),
+ } satisfies BlueprintUiAction,
+ ];
+ });
return actions.length > 0 ? actions : undefined;
};
@@ -547,10 +530,9 @@ const parseResourceViews = (
return [];
}
- const key =
- sanitizeBlueprintSlug(
- readTrimmedString(column.key, 60) ?? '',
- ).replace(/-/g, '_');
+ const key = sanitizeBlueprintSlug(
+ readTrimmedString(column.key, 60) ?? '',
+ ).replace(/-/g, '_');
const label = readTrimmedString(column.label, 60);
if (!key || !label) {
@@ -588,7 +570,9 @@ const parseResourceViews = (
return views.length > 0 ? views : undefined;
};
-const parseModules = (value: unknown): BlueprintUiModuleBinding[] | undefined => {
+const parseModules = (
+ value: unknown,
+): BlueprintUiModuleBinding[] | undefined => {
if (!Array.isArray(value)) {
return undefined;
}
@@ -616,8 +600,16 @@ const parseModules = (value: unknown): BlueprintUiModuleBinding[] | undefined =>
module: moduleKey as BlueprintUiModuleKey,
slot: slot as BlueprintUiModuleSlot,
title: readTrimmedString(entry.title, 80),
- metricKeys: readStringArray(entry.metricKeys, MAX_STRING_LIST_COUNT, 48),
- eventKinds: readStringArray(entry.eventKinds, MAX_STRING_LIST_COUNT, 48),
+ metricKeys: readStringArray(
+ entry.metricKeys,
+ MAX_STRING_LIST_COUNT,
+ 48,
+ ),
+ eventKinds: readStringArray(
+ entry.eventKinds,
+ MAX_STRING_LIST_COUNT,
+ 48,
+ ),
} satisfies BlueprintUiModuleBinding,
];
})
@@ -699,7 +691,8 @@ const toCanonicalMetadataJson = (value: Record): string =>
export const computeBlueprintMetadataPayloadHash = (
value: Record,
-): Hex => keccak256(toHex(new TextEncoder().encode(toCanonicalMetadataJson(value))));
+): Hex =>
+ keccak256(toHex(new TextEncoder().encode(toCanonicalMetadataJson(value))));
const parseBlueprintMetadataAttestation = (
value: unknown,
@@ -825,7 +818,10 @@ export const verifyBlueprintMetadataIntegrity = async ({
stripMetadataAttestation(rawMetadata),
);
- if (metadataHash && metadataHash.toLowerCase() !== payloadHash.toLowerCase()) {
+ if (
+ metadataHash &&
+ metadataHash.toLowerCase() !== payloadHash.toLowerCase()
+ ) {
return buildDefaultMetadataVerification({
metadataUri,
status: 'invalid',
@@ -1073,7 +1069,9 @@ export const parseBlueprintMetadataJsonText = (
};
};
-export const resolveBlueprintMetadataFetchUrl = (metadataUri: string): string => {
+export const resolveBlueprintMetadataFetchUrl = (
+ metadataUri: string,
+): string => {
if (!metadataUri.startsWith('ipfs://')) {
return metadataUri;
}
@@ -1140,7 +1138,9 @@ export const buildBlueprintUiMetadataDocument = ({
metrics: draft.surfaces.includes('metrics'),
permissions: draft.surfaces.includes('permissions'),
},
- externalApp: isValidHttpsUrl(readTrimmedString(draft.externalAppUrl, 2_048))
+ externalApp: isValidHttpsUrl(
+ readTrimmedString(draft.externalAppUrl, 2_048),
+ )
? {
url: draft.externalAppUrl.trim(),
mode: 'link',
diff --git a/libs/tangle-shared-ui/src/data/graphql/useBlueprintManagement.ts b/libs/tangle-shared-ui/src/data/graphql/useBlueprintManagement.ts
index f05fcdc00..0996e38b5 100644
--- a/libs/tangle-shared-ui/src/data/graphql/useBlueprintManagement.ts
+++ b/libs/tangle-shared-ui/src/data/graphql/useBlueprintManagement.ts
@@ -217,8 +217,8 @@ const fetchBlueprintsByOwner = async (
const response = await fetch(
resolveBlueprintMetadataFetchUrl(bp.metadataUri),
{
- signal: controller.signal,
- cache: 'no-store',
+ signal: controller.signal,
+ cache: 'no-store',
},
).finally(() => {
clearTimeout(timeoutId);
@@ -244,8 +244,8 @@ const fetchBlueprintsByOwner = async (
codeRepository: parsed.codeUrl ?? undefined,
docs:
metadataJson !== null
- ? readString(metadataJson.docs) ??
- readString(metadataJson.documentation)
+ ? (readString(metadataJson.docs) ??
+ readString(metadataJson.documentation))
: undefined,
};
} catch (error) {
@@ -409,7 +409,11 @@ export const useUpdateBlueprintTx = () => {
address: contracts.tangle,
abi: TANGLE_ABI,
functionName: 'updateBlueprint' as const,
- args: [params.blueprintId, params.metadataUri, params.metadataHash] as const,
+ args: [
+ params.blueprintId,
+ params.metadataUri,
+ params.metadataHash,
+ ] as const,
};
},
{
@@ -424,7 +428,11 @@ export const useUpdateBlueprintTx = () => {
metadataUri: string,
metadataHash: `0x${string}`,
): Promise => {
- const result = await execute?.({ blueprintId, metadataUri, metadataHash });
+ const result = await execute?.({
+ blueprintId,
+ metadataUri,
+ metadataHash,
+ });
return result?.hash ?? null;
},
[execute],
diff --git a/libs/tangle-shared-ui/src/data/graphql/useBlueprints.ts b/libs/tangle-shared-ui/src/data/graphql/useBlueprints.ts
index 2317a7e42..b22a770d8 100644
--- a/libs/tangle-shared-ui/src/data/graphql/useBlueprints.ts
+++ b/libs/tangle-shared-ui/src/data/graphql/useBlueprints.ts
@@ -96,8 +96,7 @@ const fetchBlueprintMetadata = async ({
metadataHash?: `0x${string}` | null;
blueprintId?: bigint;
owner?: Address;
-},
-): Promise<{
+}): Promise<{
name: string;
description: string;
author: string;
@@ -130,9 +129,12 @@ const fetchBlueprintMetadata = async ({
}
try {
- const response = await fetch(resolveBlueprintMetadataFetchUrl(metadataUri), {
- signal: AbortSignal.timeout(5000),
- });
+ const response = await fetch(
+ resolveBlueprintMetadataFetchUrl(metadataUri),
+ {
+ signal: AbortSignal.timeout(5000),
+ },
+ );
if (!response.ok) {
throw new Error(`Failed to fetch metadata: ${response.status}`);
}