Skip to content

Expose allowed_domains on the app#7240

Open
elanalynn wants to merge 1 commit into04-09-notify_extension_dev_server_of_app_assets_updatesfrom
04-09-expose_allowed_domains_on_the_app
Open

Expose allowed_domains on the app#7240
elanalynn wants to merge 1 commit into04-09-notify_extension_dev_server_of_app_assets_updatesfrom
04-09-expose_allowed_domains_on_the_app

Conversation

@elanalynn
Copy link
Copy Markdown
Contributor

@elanalynn elanalynn commented Apr 9, 2026

WHY are these changes introduced?

Part 1/2 in https://github.com/shop/issues-admin-extensibility/issues/2397

Exposes allowed_domains in app object sent through extensions websocket so that we can construct a CSP meta tag in dev mode for static apps.

WHAT is this pull request doing?

  • Expose allowed_domains from the admin module config on the App model and pass it through the extension dev server payload
  • Add allowed_domains to the admin spec's transformRemoteToLocal so the field round-trips correctly
  • Thread allowedDomains through the previewable extension process options down to the payload store

How to test your changes?

Step 1: Build the CLI from source

From the repo root:

dev up

Step 2: Set up a test app with allowed_domains

You need a Shopify app directory with a shopify.app.toml that includes the admin config.

The key part of the TOML is:

[admin]                                                                                                                                   
static_root = "./dist"
allowed_domains = ["example.com", "cdn.example.com"]

Step 3: Run shopify app dev using the local CLI

pnpm shopify app dev --path /path/to/your/test/app

This will:

  • Start the extension dev server (HTTP + WebSocket)
  • Print a URL like https://some-tunnel-url.trycloudflare.com

Step 4: Inspect the HTTP payload

You can open this URL in your browser:

https://<your-tunnel>.trycloudflare.com/extensions

This returns the full JSON payload. Look for app.allowedDomains in the response.

You can also pipe it through jq for readability:

curl https://<your-tunnel>.trycloudflare.com/extensions | jq '.app'

You should see:

{                                                                                                                                           
  "apiKey": "...",                                                                                                                        
  "title": "...", 
  "url": "...",  
  "mobileUrl": "...",
  "assets": { ... },                                                                                                                        
  "allowedDomains": ["example.com", "cdn.example.com"]
  }    

Step 5: Inspect WebSocket messages

  1. Open admin and dev tools and go to the network tab
  2. Click the "socket" filter
  3. Navigate to the dev console preview URL that the CLI prints
  4. You'll see an "extensions" websocket connection
  5. Click on it, then click the Messages tab
  6. Confirm that it contains the full app object including allowedDomains

Screenshot 2026-04-09 at 5.13.14 PM.png

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact
  • The change is user-facing, so I've added a changelog entry with pnpm changeset add

Copy link
Copy Markdown
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/private/node/ui.d.ts
 import { Logger, LogLevel } from '../../public/node/output.js';
-import React from 'react';
 import { Key, RenderOptions } from 'ink';
 import { EventEmitter } from 'events';
-/**
- * Signal that the current Ink tree is done. Must be called within an
- * InkLifecycleRoot — throws if the provider is missing so lifecycle
- * bugs surface immediately instead of silently hanging.
- */
-export declare function useComplete(): (error?: Error) => void;
-/**
- * Root wrapper for Ink trees. Owns the single `exit()` call site — children
- * signal completion via `useComplete()`, which sets state here. The `useEffect`
- * fires post-render, guaranteeing all batched state updates have been flushed
- * before the tree is torn down.
- */
-export declare function InkLifecycleRoot({ children }: {
-    children: React.ReactNode;
-}): React.JSX.Element;
 interface RenderOnceOptions {
     logLevel?: LogLevel;
     logger?: Logger;
     renderOptions?: RenderOptions;
 }
 export declare function renderOnce(element: JSX.Element, { logLevel, renderOptions }: RenderOnceOptions): string | undefined;
-export declare function render(element: JSX.Element, options?: RenderOptions): Promise<void>;
+export declare function render(element: JSX.Element, options?: RenderOptions): Promise<unknown>;
 export declare class Stdout extends EventEmitter {
     columns: number;
     rows: number;
     readonly frames: string[];
     private _lastFrame?;
     constructor(options: {
         columns?: number;
         rows?: number;
     });
     write: (frame: string) => void;
     lastFrame: () => string | undefined;
 }
 export declare function handleCtrlC(input: string, key: Key, exit?: () => void): void;
 export {};
packages/cli-kit/dist/public/node/ui.d.ts
@@ -34,7 +34,7 @@ export interface RenderConcurrentOptions extends PartialBy<ConcurrentOutputProps
  * 00:00:00 │ frontend │ third frontend message
  *
  */
-export declare function renderConcurrent({ renderOptions, ...props }: RenderConcurrentOptions): Promise<void>;
+export declare function renderConcurrent({ renderOptions, ...props }: RenderConcurrentOptions): Promise<unknown>;
 export type AlertCustomSection = CustomSection;
 export type RenderAlertOptions = Omit<AlertOptions, 'type'>;
 /**

@elanalynn elanalynn marked this pull request as ready for review April 9, 2026 23:22
@elanalynn elanalynn requested a review from a team as a code owner April 9, 2026 23:22
Copy link
Copy Markdown
Member

@vividviolet vividviolet left a comment

Choose a reason for hiding this comment

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

Could we nest these fields under app.admin instead? I think it would align with the other configs on the app toml better. I think we should do the same with static root too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants