Breaking Change: Update to latest XO-Engine #2
@@ -22,24 +22,6 @@ import type {
|
|||||||
XOTemplateVariable,
|
XOTemplateVariable,
|
||||||
} from "@xo-cash/types";
|
} from "@xo-cash/types";
|
||||||
|
|
||||||
/**
|
|
||||||
* View metadata copied from a template definition onto a resolved invitation item.
|
|
||||||
*/
|
|
||||||
export interface TemplateViewMetadata {
|
|
||||||
name?: string;
|
|
||||||
description?: string;
|
|
||||||
icon?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Role-specific view metadata from a template output definition.
|
|
||||||
*/
|
|
||||||
export interface ResolvedInvitationOutputRoleMetadata {
|
|
||||||
name?: string;
|
|
||||||
description?: string;
|
|
||||||
icon?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A variable from invitation commits enriched with its template definition.
|
* A variable from invitation commits enriched with its template definition.
|
||||||
*/
|
*/
|
||||||
@@ -74,7 +56,10 @@ export type ResolvedInvitationOutput = XOInvitationOutput & {
|
|||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
roles?: Record<string, ResolvedInvitationOutputRoleMetadata>;
|
roles?: Record<
|
||||||
|
string,
|
||||||
|
{ name?: string; description?: string; icon?: string }
|
||||||
|
>;
|
||||||
lockingScript?: string;
|
lockingScript?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,24 +76,13 @@ export interface ResolvedInvitationData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template display metadata layered onto a committed output.
|
* Picks human-readable view fields from a template definition.
|
||||||
*/
|
*/
|
||||||
export interface TemplateOutputMetadata {
|
export const pickTemplateViewMetadata = (definition?: {
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
roles?: Record<string, ResolvedInvitationOutputRoleMetadata>;
|
}) => {
|
||||||
lockingScript?: string;
|
|
||||||
valueSatoshis?: bigint | string;
|
|
||||||
token?: XOTemplateOutput["token"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Picks human-readable view fields from a template definition.
|
|
||||||
*/
|
|
||||||
export const pickTemplateViewMetadata = (
|
|
||||||
definition: TemplateViewMetadata | undefined,
|
|
||||||
): TemplateViewMetadata => {
|
|
||||||
if (!definition) return {};
|
if (!definition) return {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -124,8 +98,8 @@ export const pickTemplateViewMetadata = (
|
|||||||
* Picks variable metadata from a template variable definition.
|
* Picks variable metadata from a template variable definition.
|
||||||
*/
|
*/
|
||||||
export const pickTemplateVariableMetadata = (
|
export const pickTemplateVariableMetadata = (
|
||||||
definition: XOTemplateVariable | undefined,
|
definition?: XOTemplateVariable,
|
||||||
): Pick<ResolvedInvitationVariable, "name" | "description" | "type" | "hint"> => {
|
) => {
|
||||||
if (!definition) return {};
|
if (!definition) return {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -138,12 +112,7 @@ export const pickTemplateVariableMetadata = (
|
|||||||
/**
|
/**
|
||||||
* Picks input metadata from a template input definition.
|
* Picks input metadata from a template input definition.
|
||||||
*/
|
*/
|
||||||
export const pickTemplateInputMetadata = (
|
export const pickTemplateInputMetadata = (definition?: XOTemplateInput) => {
|
||||||
definition: XOTemplateInput | undefined,
|
|
||||||
): Pick<
|
|
||||||
ResolvedInvitationInput,
|
|
||||||
"name" | "description" | "icon" | "unlockingScript" | "omitChangeAmounts"
|
|
||||||
> => {
|
|
||||||
if (!definition) return {};
|
if (!definition) return {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -164,9 +133,7 @@ export const pickTemplateInputMetadata = (
|
|||||||
* defaults; display-oriented fields like name, description, and template
|
* defaults; display-oriented fields like name, description, and template
|
||||||
* valueSatoshis expressions are layered on for UI rendering.
|
* valueSatoshis expressions are layered on for UI rendering.
|
||||||
*/
|
*/
|
||||||
export const pickTemplateOutputMetadata = (
|
export const pickTemplateOutputMetadata = (definition?: XOTemplateOutput) => {
|
||||||
definition: XOTemplateOutput | undefined,
|
|
||||||
): TemplateOutputMetadata => {
|
|
||||||
if (!definition) return {};
|
if (!definition) return {};
|
||||||
|
|
||||||
const roles = definition.roles
|
const roles = definition.roles
|
||||||
@@ -198,19 +165,17 @@ export const resolveVariable = (
|
|||||||
variable: XOInvitationVariable,
|
variable: XOInvitationVariable,
|
||||||
entityIdentifier: string,
|
entityIdentifier: string,
|
||||||
template: XOTemplate,
|
template: XOTemplate,
|
||||||
): ResolvedInvitationVariable => {
|
): ResolvedInvitationVariable => ({
|
||||||
const definition = template.variables?.[variable.variableIdentifier];
|
entityIdentifier,
|
||||||
|
variableIdentifier: variable.variableIdentifier,
|
||||||
return {
|
...(variable.roleIdentifier !== undefined && {
|
||||||
entityIdentifier,
|
roleIdentifier: variable.roleIdentifier,
|
||||||
variableIdentifier: variable.variableIdentifier,
|
}),
|
||||||
...(variable.roleIdentifier !== undefined && {
|
value: variable.value,
|
||||||
roleIdentifier: variable.roleIdentifier,
|
...pickTemplateVariableMetadata(
|
||||||
}),
|
template.variables?.[variable.variableIdentifier],
|
||||||
value: variable.value,
|
),
|
||||||
...pickTemplateVariableMetadata(definition),
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enriches a committed input with its template definition when an identifier is present.
|
* Enriches a committed input with its template definition when an identifier is present.
|
||||||
@@ -219,17 +184,15 @@ export const resolveInput = (
|
|||||||
input: XOInvitationInput,
|
input: XOInvitationInput,
|
||||||
entityIdentifier: string,
|
entityIdentifier: string,
|
||||||
template: XOTemplate,
|
template: XOTemplate,
|
||||||
): ResolvedInvitationInput => {
|
): ResolvedInvitationInput => ({
|
||||||
const definition = input.inputIdentifier
|
entityIdentifier,
|
||||||
? template.inputs?.[input.inputIdentifier]
|
...input,
|
||||||
: undefined;
|
...pickTemplateInputMetadata(
|
||||||
|
input.inputIdentifier
|
||||||
return {
|
? template.inputs?.[input.inputIdentifier]
|
||||||
entityIdentifier,
|
: undefined,
|
||||||
...input,
|
),
|
||||||
...pickTemplateInputMetadata(definition),
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enriches a committed output with its template definition when an identifier is present.
|
* Enriches a committed output with its template definition when an identifier is present.
|
||||||
@@ -238,25 +201,21 @@ export const resolveOutput = (
|
|||||||
output: XOInvitationOutput,
|
output: XOInvitationOutput,
|
||||||
entityIdentifier: string,
|
entityIdentifier: string,
|
||||||
template: XOTemplate,
|
template: XOTemplate,
|
||||||
): ResolvedInvitationOutput => {
|
): ResolvedInvitationOutput =>
|
||||||
const definition = output.outputIdentifier
|
({
|
||||||
? template.outputs?.[output.outputIdentifier]
|
|
||||||
: undefined;
|
|
||||||
const templateMetadata = pickTemplateOutputMetadata(definition);
|
|
||||||
|
|
||||||
return {
|
|
||||||
entityIdentifier,
|
entityIdentifier,
|
||||||
...output,
|
...output,
|
||||||
...templateMetadata,
|
...pickTemplateOutputMetadata(
|
||||||
} as ResolvedInvitationOutput;
|
output.outputIdentifier
|
||||||
};
|
? template.outputs?.[output.outputIdentifier]
|
||||||
|
: undefined,
|
||||||
|
),
|
||||||
|
}) as ResolvedInvitationOutput;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts hex or binary invitation bytecode fields to hex strings for display.
|
* Converts hex or binary invitation bytecode fields to hex strings for display.
|
||||||
*/
|
*/
|
||||||
export const hexOrBinToHex = (
|
export const hexOrBinToHex = (value?: string | Uint8Array) => {
|
||||||
value: string | Uint8Array | undefined,
|
|
||||||
): string | undefined => {
|
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -267,10 +226,8 @@ export const hexOrBinToHex = (
|
|||||||
/**
|
/**
|
||||||
* Normalizes a merged input row for UI display (hex strings, no encoding placeholders).
|
* Normalizes a merged input row for UI display (hex strings, no encoding placeholders).
|
||||||
*/
|
*/
|
||||||
export const normalizeMergedInputForDisplay = (
|
export const normalizeMergedInputForDisplay = (input: XOInvitationInput) => {
|
||||||
input: XOInvitationInput,
|
const normalized = { ...input };
|
||||||
): XOInvitationInput => {
|
|
||||||
const normalized: XOInvitationInput = { ...input };
|
|
||||||
|
|
||||||
if (input.outpointTransactionHash !== undefined) {
|
if (input.outpointTransactionHash !== undefined) {
|
||||||
normalized.outpointTransactionHash = hexOrBinToHex(
|
normalized.outpointTransactionHash = hexOrBinToHex(
|
||||||
@@ -302,10 +259,8 @@ export const normalizeMergedInputForDisplay = (
|
|||||||
/**
|
/**
|
||||||
* Normalizes a merged output row for UI display (hex strings).
|
* Normalizes a merged output row for UI display (hex strings).
|
||||||
*/
|
*/
|
||||||
export const normalizeMergedOutputForDisplay = (
|
export const normalizeMergedOutputForDisplay = (output: XOInvitationOutput) => {
|
||||||
output: XOInvitationOutput,
|
const normalized = { ...output };
|
||||||
): XOInvitationOutput => {
|
|
||||||
const normalized: XOInvitationOutput = { ...output };
|
|
||||||
|
|
||||||
if (output.lockingBytecode !== undefined) {
|
if (output.lockingBytecode !== undefined) {
|
||||||
normalized.lockingBytecode = hexOrBinToHex(
|
normalized.lockingBytecode = hexOrBinToHex(
|
||||||
@@ -323,7 +278,7 @@ export const normalizeMergedOutputForDisplay = (
|
|||||||
export const findOutputIdentifierForMergedOutput = (
|
export const findOutputIdentifierForMergedOutput = (
|
||||||
commit: XOInvitationCommit | undefined,
|
commit: XOInvitationCommit | undefined,
|
||||||
mergedOutput: XOInvitationOutput,
|
mergedOutput: XOInvitationOutput,
|
||||||
): string | undefined => {
|
) => {
|
||||||
const outputs = commit?.data?.outputs ?? [];
|
const outputs = commit?.data?.outputs ?? [];
|
||||||
const mergedBytecodeHex = hexOrBinToHex(mergedOutput.lockingBytecode);
|
const mergedBytecodeHex = hexOrBinToHex(mergedOutput.lockingBytecode);
|
||||||
|
|
||||||
@@ -348,8 +303,7 @@ export const findOutputIdentifierForMergedOutput = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (outputsWithIdentifier.length === 1) {
|
if (outputsWithIdentifier.length === 1) {
|
||||||
const soleIdentifiedOutput = outputsWithIdentifier[0];
|
return outputsWithIdentifier[0]?.outputIdentifier;
|
||||||
return soleIdentifiedOutput?.outputIdentifier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -361,12 +315,9 @@ export const findOutputIdentifierForMergedOutput = (
|
|||||||
export const matchesInvitationVariable = (
|
export const matchesInvitationVariable = (
|
||||||
left: XOInvitationVariable,
|
left: XOInvitationVariable,
|
||||||
right: XOInvitationVariable,
|
right: XOInvitationVariable,
|
||||||
): boolean => {
|
) =>
|
||||||
return (
|
left.variableIdentifier === right.variableIdentifier &&
|
||||||
left.variableIdentifier === right.variableIdentifier &&
|
left.roleIdentifier === right.roleIdentifier;
|
||||||
left.roleIdentifier === right.roleIdentifier
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the entity that authored a merged variable by scanning invitation commits.
|
* Finds the entity that authored a merged variable by scanning invitation commits.
|
||||||
@@ -376,7 +327,7 @@ export const matchesInvitationVariable = (
|
|||||||
export const findVariableEntityIdentifier = (
|
export const findVariableEntityIdentifier = (
|
||||||
variable: XOInvitationVariable,
|
variable: XOInvitationVariable,
|
||||||
commits: XOInvitationCommit[],
|
commits: XOInvitationCommit[],
|
||||||
): string => {
|
) => {
|
||||||
let entityIdentifier = "";
|
let entityIdentifier = "";
|
||||||
|
|
||||||
for (const commit of commits) {
|
for (const commit of commits) {
|
||||||
@@ -397,10 +348,6 @@ export const findVariableEntityIdentifier = (
|
|||||||
* extensions and transaction indices are resolved. Variables come from the merged
|
* extensions and transaction indices are resolved. Variables come from the merged
|
||||||
* result and are enriched with template metadata. Commit ordering is delegated to
|
* result and are enriched with template metadata. Commit ordering is delegated to
|
||||||
* the engine merger.
|
* the engine merger.
|
||||||
*
|
|
||||||
* @param invitation - The raw invitation in standard XO format.
|
|
||||||
* @param template - The template referenced by the invitation.
|
|
||||||
* @returns Resolved invitation data ready for display.
|
|
||||||
*/
|
*/
|
||||||
export const resolveCommitReferences = (
|
export const resolveCommitReferences = (
|
||||||
invitation: XOInvitation,
|
invitation: XOInvitation,
|
||||||
@@ -436,8 +383,9 @@ export const resolveCommitReferences = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const inputs = merged.inputs.map((mergedInput) => {
|
const inputs = merged.inputs.map((mergedInput) => {
|
||||||
const commit = commitsMap.get(mergedInput.sourceCommitIdentifier);
|
const entityIdentifier =
|
||||||
const entityIdentifier = commit?.entityIdentifier ?? "";
|
commitsMap.get(mergedInput.sourceCommitIdentifier)?.entityIdentifier ??
|
||||||
|
"";
|
||||||
const {
|
const {
|
||||||
sourceCommitIdentifier: _sourceCommitIdentifier,
|
sourceCommitIdentifier: _sourceCommitIdentifier,
|
||||||
mergesWith: _mergesWith,
|
mergesWith: _mergesWith,
|
||||||
@@ -459,7 +407,10 @@ export const resolveCommitReferences = (
|
|||||||
mergesWith: _mergesWith,
|
mergesWith: _mergesWith,
|
||||||
...output
|
...output
|
||||||
} = mergedOutput;
|
} = mergedOutput;
|
||||||
const outputIdentifier = findOutputIdentifierForMergedOutput(commit, output);
|
const outputIdentifier = findOutputIdentifierForMergedOutput(
|
||||||
|
commit,
|
||||||
|
output,
|
||||||
|
);
|
||||||
const outputForDisplay = normalizeMergedOutputForDisplay(
|
const outputForDisplay = normalizeMergedOutputForDisplay(
|
||||||
outputIdentifier !== undefined ? { ...output, outputIdentifier } : output,
|
outputIdentifier !== undefined ? { ...output, outputIdentifier } : output,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ const originalInvitation: XOInvitation = {
|
|||||||
previousCommitIdentifier: undefined,
|
previousCommitIdentifier: undefined,
|
||||||
entityIdentifier: MERCHANT_ENTITY,
|
entityIdentifier: MERCHANT_ENTITY,
|
||||||
data: {},
|
data: {},
|
||||||
signature: "5f487c045657f3939ecfeaaacf239a7cfd44b485c2be591f5280bf0cc3a6e5fe304e8ea23311d82b2afa4f0ad7e0a6d07ec1e0b1aaee9c44097613694390966b",
|
signature:
|
||||||
|
"5f487c045657f3939ecfeaaacf239a7cfd44b485c2be591f5280bf0cc3a6e5fe304e8ea23311d82b2afa4f0ad7e0a6d07ec1e0b1aaee9c44097613694390966b",
|
||||||
expiresAtTimestamp: 1779506689379,
|
expiresAtTimestamp: 1779506689379,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,7 +62,8 @@ const originalInvitation: XOInvitation = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
signature: "7cfc53860ec81403a79a03521a7674ee8d2a11365ee031e4f7f2e36a045bd6e2999510264b29045582a74e1190f0176950a855361f02bc67ff7877fabcf794f4",
|
signature:
|
||||||
|
"7cfc53860ec81403a79a03521a7674ee8d2a11365ee031e4f7f2e36a045bd6e2999510264b29045582a74e1190f0176950a855361f02bc67ff7877fabcf794f4",
|
||||||
expiresAtTimestamp: 1779506689390,
|
expiresAtTimestamp: 1779506689390,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -77,7 +79,8 @@ const originalInvitation: XOInvitation = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
signature: "d9bdd3b24fef6afd13f12da92e832672c6c1b83fb372506faeb7fa4ea0e39e3a32ad74493fbe7a393aed58bc18226431dabae09948ce371ad3f77b0219cb3831",
|
signature:
|
||||||
|
"d9bdd3b24fef6afd13f12da92e832672c6c1b83fb372506faeb7fa4ea0e39e3a32ad74493fbe7a393aed58bc18226431dabae09948ce371ad3f77b0219cb3831",
|
||||||
expiresAtTimestamp: 1779506689412,
|
expiresAtTimestamp: 1779506689412,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -85,7 +88,8 @@ const originalInvitation: XOInvitation = {
|
|||||||
previousCommitIdentifier: "583208aa304c0aa9841d1400efe6b6aa",
|
previousCommitIdentifier: "583208aa304c0aa9841d1400efe6b6aa",
|
||||||
entityIdentifier: CUSTOMER_ENTITY,
|
entityIdentifier: CUSTOMER_ENTITY,
|
||||||
data: {},
|
data: {},
|
||||||
signature: "63be8af81622da4fccc7eb6b81c6174879fe6aa113b8dae794bd42d4d5c87ae550a18be1e6cb5edf231e774bdc7883eb5a78bd02188579dce58da0d449c43865",
|
signature:
|
||||||
|
"63be8af81622da4fccc7eb6b81c6174879fe6aa113b8dae794bd42d4d5c87ae550a18be1e6cb5edf231e774bdc7883eb5a78bd02188579dce58da0d449c43865",
|
||||||
expiresAtTimestamp: 1779506979194,
|
expiresAtTimestamp: 1779506979194,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -101,7 +105,8 @@ const originalInvitation: XOInvitation = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
signature: "e36942eb5f147e620659d20b7059630da871944e74fe5ffb3c4ff0298a5aedb101bc7468b19750114cbcfa56b99bd4a080453a31084f18173adcd9442fca4303",
|
signature:
|
||||||
|
"e36942eb5f147e620659d20b7059630da871944e74fe5ffb3c4ff0298a5aedb101bc7468b19750114cbcfa56b99bd4a080453a31084f18173adcd9442fca4303",
|
||||||
expiresAtTimestamp: 1779507006272,
|
expiresAtTimestamp: 1779507006272,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -117,7 +122,8 @@ const originalInvitation: XOInvitation = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
signature: "2c1d1ed1259a2e4b1bc7187b93029e99e590a4e92ff9c39031319766b7fbcdabab9c3dc20b3d27d05eee198cbc717b9aedfbef92bd3e519c62c60e4731bd936a",
|
signature:
|
||||||
|
"2c1d1ed1259a2e4b1bc7187b93029e99e590a4e92ff9c39031319766b7fbcdabab9c3dc20b3d27d05eee198cbc717b9aedfbef92bd3e519c62c60e4731bd936a",
|
||||||
expiresAtTimestamp: 1779507008169,
|
expiresAtTimestamp: 1779507008169,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -226,8 +232,7 @@ describe("resolveCommitReferences", () => {
|
|||||||
{
|
{
|
||||||
entityIdentifier: MERCHANT_ENTITY,
|
entityIdentifier: MERCHANT_ENTITY,
|
||||||
outputIdentifier: "purchaseOutput",
|
outputIdentifier: "purchaseOutput",
|
||||||
lockingBytecode:
|
lockingBytecode: "76a9146a4715fe1cc1ce228336502f1711b06045ef361088ac",
|
||||||
"76a9146a4715fe1cc1ce228336502f1711b06045ef361088ac",
|
|
||||||
name: "Purchase Payment",
|
name: "Purchase Payment",
|
||||||
description: "$(<totalSatoshis>) sats to $(<merchantName>)",
|
description: "$(<totalSatoshis>) sats to $(<merchantName>)",
|
||||||
icon: "request",
|
icon: "request",
|
||||||
@@ -250,8 +255,7 @@ describe("resolveCommitReferences", () => {
|
|||||||
{
|
{
|
||||||
entityIdentifier: CUSTOMER_ENTITY,
|
entityIdentifier: CUSTOMER_ENTITY,
|
||||||
valueSatoshis: 74881n,
|
valueSatoshis: 74881n,
|
||||||
lockingBytecode:
|
lockingBytecode: "76a9141730ca066d4b9c8d542f8c9bdce645f77697d46088ac",
|
||||||
"76a9141730ca066d4b9c8d542f8c9bdce645f77697d46088ac",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user