Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .changeset/pos-intercept-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/ui-extensions': minor
---

Add `shopify.intercept()` types for POS blocking workflows.
94 changes: 94 additions & 0 deletions packages/ui-extensions/src/surfaces/point-of-sale/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
CashTrackingSessionStartEvent,
CashTrackingSessionCompleteEvent,
} from './events/cash-tracking-session-events';
import type {Cart} from './types/cart';

/**
* Canonical event-name constants for POS host events. Prefer these over string
Expand All @@ -16,6 +17,16 @@ export const POS_EVENT_NAMES = {
CASH_TRACKING_SESSION_COMPLETE: 'cashtrackingsessioncomplete',
} as const;

/**
* Canonical workflow-name constants for POS host interceptions. Prefer these
* over string literals when calling `shopify.intercept`.
*
* @publicDocs
*/
export const POS_INTERCEPT_NAMES = {
BEFORE_CHECKOUT: 'beforecheckout',
} as const;

/**
* Maps Shopify POS event names to their corresponding `Event` subclass types.
*
Expand All @@ -30,6 +41,89 @@ export interface ShopifyEventMap {
[POS_EVENT_NAMES.CASH_TRACKING_SESSION_COMPLETE]: CashTrackingSessionCompleteEvent;
}

/**
* Maps POS interceptable workflow names to their corresponding `Event` types.
*
* Used as the generic type parameter for `shopify.intercept`.
*
* @publicDocs
*/
export interface ShopifyInterceptMap {
[POS_INTERCEPT_NAMES.BEFORE_CHECKOUT]: BeforeCheckoutEvent;
}

/**
* Dispatched when staff attempts to leave the active cart for checkout.
*
* @publicDocs
*/
export interface BeforeCheckoutEvent extends Event {
readonly type: typeof POS_INTERCEPT_NAMES.BEFORE_CHECKOUT;
/** The POS cart at the point checkout was requested. */
readonly cart: Cart;
}

/** @publicDocs */
export type ShopifyInterceptor<TEvent extends Event> = (
event: TEvent,
) => InterceptResult;

/**
* The result an interceptor returns. An empty `operations` list allows the
* workflow; an `ERROR` validation blocks it.
*
* @publicDocs
*/
export interface InterceptResult {
operations: Operation[];
}

/**
* A single host operation produced by an interceptor.
*
* @publicDocs
*/
export interface Operation {
validationAdd?: ValidationAdd;
}

/** @publicDocs */
export type ValidationLevel = 'INFO' | 'WARNING' | 'ERROR';

/**
* Adds a validation to the workflow being intercepted.
*
* @publicDocs
*/
export interface ValidationAdd {
/** `ERROR` blocks the workflow. `WARNING` and `INFO` do not. */
level: ValidationLevel;

/** Stable identifier for this validation. */
handle: string;

/** Host-facing message for support, observability, or staff UX. */
message: string;

/** JSON-path locator for where the validation applies. Defaults to `$.cart`. */
target?: string;

/** Optional structured data for custom UX or order metadata. */
metafields?: Metafield[];
}

/**
* Metafield input attached to a validation.
*
* @publicDocs
*/
export interface Metafield {
namespace: string;
key: string;
value: string;
type: string;
}

export type {
TransactionCompleteEvent,
CashTrackingSessionStartEvent,
Expand Down
15 changes: 14 additions & 1 deletion packages/ui-extensions/src/surfaces/point-of-sale/globals.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type {Navigation} from './api/navigation-api/navigation-api';
import type {ShopifyEventMap} from './events';
import type {
ShopifyEventMap,
ShopifyInterceptMap,
ShopifyInterceptor,
} from './events';

/**
* The `shopify` global provides APIs that are available to all POS extensions
Expand Down Expand Up @@ -36,6 +40,15 @@ export interface BackgroundShopifyGlobal extends ShopifyGlobal {
type: K,
listener: (event: ShopifyEventMap[K]) => void,
): void;

/**
* Register an interceptor for a POS host workflow that can be blocked.
* Returns a function that unregisters the interceptor.
*/
intercept<K extends keyof ShopifyInterceptMap>(
type: K,
interceptor: ShopifyInterceptor<ShopifyInterceptMap[K]>,
): () => void;
}

declare global {
Expand Down
Loading