Skip to content

Commit f1f3f2f

Browse files
committed
Add intents to customer account extension api
1 parent 092a4ca commit f1f3f2f

2 files changed

Lines changed: 191 additions & 0 deletions

File tree

packages/ui-extensions/src/surfaces/customer-account/api/shared.ts

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,3 +624,185 @@ export interface ToastApiResult {
624624
export interface ToastApi {
625625
show: (content: string) => Promise<ToastApiResult>;
626626
}
627+
628+
/**
629+
* Options for URL-based invocations.
630+
*
631+
* When invoking via URL syntax, `action` and `type` are parsed from the
632+
* string. This companion type captures the remaining optional fields that can
633+
* be provided alongside the URL.
634+
*/
635+
export interface IntentQueryOptions {
636+
/**
637+
* The resource identifier for edit actions (e.g., 'gid://shopify/SubscriptionContract/123').
638+
*/
639+
value?: string;
640+
/**
641+
* Optional input payload passed to the intent.
642+
*
643+
* Used to seed forms or supply parameters. The accepted shape is
644+
* intent-specific. For example:
645+
* - Replacing a payment method on a subscription contract requires
646+
* { field: 'paymentMethod' }
647+
*/
648+
data?: Record<string, unknown>;
649+
}
650+
651+
/**
652+
* Allowed actions that can be performed by an intent.
653+
*
654+
* Common actions include:
655+
* - `'create'`: Initiate creation of a new resource.
656+
* - `'open'`: Modify an existing resource.
657+
*/
658+
export type IntentAction = 'create' | 'open' | string;
659+
660+
/**
661+
* Structured description of an intent to invoke.
662+
*
663+
* Use this object form when programmatically composing an intent at runtime.
664+
* It pairs an action (verb) with a resource type and optional inputs.
665+
*/
666+
export interface IntentQuery extends IntentQueryOptions {
667+
/**
668+
* Verb describing the operation to perform on the target resource.
669+
*
670+
* Common values include `'create'` and `'open'`. The set of
671+
* allowed verbs is intent-specific; unknown verbs will fail validation.
672+
*/
673+
action: IntentAction;
674+
/**
675+
* The resource type (e.g., 'shopify/SubscriptionContract').
676+
*/
677+
type: string;
678+
}
679+
680+
/**
681+
* Successful intent completion.
682+
*
683+
* - `code` is always `'ok'`
684+
* - `data` contains the output payload
685+
*/
686+
export interface SuccessIntentResponse {
687+
code: 'ok';
688+
/**
689+
* Validated output payload produced by the workflow.
690+
*
691+
* The shape is intent-specific. Consumers should narrow by `code === 'ok'` before accessing.
692+
*/
693+
data: Record<string, unknown>;
694+
}
695+
696+
/**
697+
* Failed intent completion.
698+
*
699+
* - `code` is always `'error'`
700+
* - `message` summarizes the failure
701+
* - `issues` optionally provides structured details for validation or
702+
* field-specific problems following the Standard Schema convention
703+
*
704+
*/
705+
export interface ErrorIntentResponse {
706+
code?: 'error';
707+
message?: string;
708+
issues?: {
709+
/**
710+
* The path to the field with the issue.
711+
*/
712+
path?: string[];
713+
/**
714+
* The error message for the issue.
715+
*/
716+
message?: string;
717+
}[];
718+
}
719+
720+
/**
721+
* User dismissed or closed the workflow without completing it.
722+
*
723+
* Distinct from `error`: no failure occurred, the activity was simply
724+
* abandoned by the user.
725+
*/
726+
export interface ClosedIntentResponse {
727+
code: 'closed';
728+
}
729+
730+
/**
731+
* Result of an intent activity.
732+
*
733+
* Discriminated union representing all possible completion outcomes for an
734+
* invoked intent.
735+
*/
736+
export type IntentResponse =
737+
| SuccessIntentResponse
738+
| ErrorIntentResponse
739+
| ClosedIntentResponse;
740+
741+
/**
742+
* Activity handle for tracking intent workflow progress.
743+
*/
744+
export interface IntentActivity {
745+
/**
746+
* A Promise that resolves when the intent workflow completes, returning the response.
747+
*/
748+
complete: Promise<IntentResponse>;
749+
}
750+
751+
/**
752+
* Entry point for Shopify intents.
753+
*
754+
* Intents pair an `action` (verb) with a resource `type` and optional `value`
755+
* and `data` to request a workflow.
756+
*/
757+
export interface Intents {
758+
/**
759+
* Invoke an intent using the object syntax.
760+
*
761+
* @param query - Structured intent description, including `action` and `type`.
762+
* @returns A promise for an {@link IntentActivity} that completes with an
763+
* {@link IntentResponse}.
764+
*
765+
* @example
766+
* ```javascript
767+
* const activity = await shopify.intents.invoke(
768+
* {
769+
* action: 'open',
770+
* type: 'shopify/SubscriptionContract',
771+
* value: 'gid://shopify/SubscriptionContract/69372608568',
772+
* data: { field: 'paymentMethod' },
773+
* }
774+
* );
775+
* ```
776+
*/
777+
invoke(query: IntentQuery): Promise<IntentActivity>;
778+
/**
779+
* Invoke an intent using the URL syntax.
780+
*
781+
* URL format: `action:type[,value][?params]`.
782+
*
783+
* @param intentURL - Intent in URL form
784+
* @param options - Optional supplemental inputs such as `value` or `data`.
785+
* @returns A promise for an {@link IntentActivity} that completes with an
786+
* {@link IntentResponse}.
787+
*
788+
* @example
789+
* ```javascript
790+
* // Using query string syntax
791+
* const activity = await shopify.intents.invoke('open:shopify/SubscriptionContract,gid://shopify/SubscriptionContract/69372608568?field=paymentMethod');
792+
*
793+
* // Or using a query string and options
794+
* const activity = await shopify.intents.invoke(
795+
* 'open:shopify/SubscriptionContract',
796+
* {
797+
* value: 'gid://shopify/SubscriptionContract/69372608568',
798+
* data: { field: 'paymentMethod' },
799+
* }
800+
* );
801+
* const response = await activity.complete;
802+
* ```
803+
*/
804+
invoke(
805+
intentURL: string,
806+
options?: IntentQueryOptions,
807+
): Promise<IntentActivity>;
808+
}

packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
CustomerPrivacy,
1313
ApplyTrackingConsentChangeType,
1414
ToastApi,
15+
Intents,
1516
SubscribableSignalLike,
1617
} from '../shared';
1718

@@ -87,6 +88,14 @@ export interface StandardApi<Target extends ExtensionTarget = ExtensionTarget> {
8788
*/
8889
analytics: Analytics;
8990

91+
/**
92+
* Entry point for Shopify intents.
93+
*
94+
* Intents pair an `action` (verb) with a resource `type` and optional `value`
95+
* and `data` to request a workflow.
96+
*/
97+
intents: Intents;
98+
9099
/**
91100
* The settings matching the settings definition written in the
92101
* [`shopify.ui.extension.toml`](https://shopify.dev/docs/api/customer-account-ui-extensions/configuration) file.

0 commit comments

Comments
 (0)