Skip to content

Commit

Permalink
export better manifest type (#1486)
Browse files Browse the repository at this point in the history
  • Loading branch information
turbocrime authored Jul 19, 2024
1 parent 7787ef1 commit 978efe6
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 49 deletions.
5 changes: 5 additions & 0 deletions .changeset/fresh-camels-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@penumbra-zone/client': minor
---

PenumbraManifest refers to chrome.runtime.ManifestV3
3 changes: 2 additions & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"devDependencies": {
"@connectrpc/connect": "^1.4.0",
"@penumbra-zone/protobuf": "workspace:*",
"@penumbra-zone/transport-dom": "workspace:*"
"@penumbra-zone/transport-dom": "workspace:*",
"@types/chrome": "^0.0.268"
},
"peerDependencies": {
"@connectrpc/connect": "^1.4.0",
Expand Down
3 changes: 3 additions & 0 deletions packages/client/src/assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export const assertProviderConnected = (providerOrigin?: string) => {
* Given a specific origin, identify the relevant injection, and confirm its
* manifest is actually present or throw. An `undefined` origin is accepted but
* will throw.
*
* The manifest will be fetched and returned as parsed json. The `signal`
* parameter may be used to abort the fetch.
*/
export const assertProviderManifest = async (providerOrigin?: string, signal?: AbortSignal) => {
// confirm the provider injection is present
Expand Down
23 changes: 12 additions & 11 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ import { PenumbraSymbol } from './symbol.js';

declare global {
interface Window {
/** Records injected upon this global should identify themselves by a field
* name matching the origin of the provider. */
/** Records injected upon this global should be identified by a name matching
* the origin segment of their manifest href `PenumbraProvider['manifest']`. */
readonly [PenumbraSymbol]?: undefined | Readonly<Record<string, PenumbraProvider>>;
}
}

/** Synchronously return the specified provider, without verifying anything. */
export const getPenumbraUnsafe = (penumbraOrigin: string) =>
/** Return the specified provider, without verifying anything. */
export const getPenumbraUnsafe = (penumbraOrigin: string): PenumbraProvider | undefined =>
window[PenumbraSymbol]?.[penumbraOrigin];

/** Return the specified provider after confirming presence of its manifest. */
export const getPenumbra = (penumbraOrigin: string) => assertProvider(penumbraOrigin);
export const getPenumbra = (penumbraOrigin: string): Promise<PenumbraProvider> =>
assertProvider(penumbraOrigin);

/** Return the specified provider's manifest. */
/** Fetch the specified provider's manifest. */
export const getPenumbraManifest = async (
penumbraOrigin: string,
signal?: AbortSignal,
Expand All @@ -30,14 +31,14 @@ export const getPenumbraManifest = async (
return manifestJson;
};

export const getAllPenumbraManifests = (): Record<
keyof (typeof window)[typeof PenumbraSymbol],
Promise<PenumbraManifest>
> =>
/** Fetch all manifests for all providers available on the page. */
export const getAllPenumbraManifests = (
signal?: AbortSignal,
): Record<string, Promise<PenumbraManifest>> =>
Object.fromEntries(
Object.keys(assertGlobalPresent()).map(providerOrigin => [
providerOrigin,
getPenumbraManifest(providerOrigin),
getPenumbraManifest(providerOrigin, signal),
]),
);

Expand Down
55 changes: 18 additions & 37 deletions packages/client/src/manifest.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,24 @@
/** Currently, Penumbra manifests are chrome extension manifest v3. There's no type
* guard because manifest format is enforced by chrome. This type only describes
* fields we're interested in as a client.
/// <reference types="chrome" />

/**
* Currently, Penumbra manifests are expected to be chrome extension manifest
* v3. This type just requires a few fields of ManifestV3 that apps might use
* to display provider information to the user.
*
* @see https://developer.chrome.com/docs/extensions/reference/manifest#keys
*
* For chrome extensions, the extension `id` will be the host of the extension
* origin. The `id` is added to the manifest by the chrome store, so will be
* missing from a locally-built extension in development. Developers may
* configure a public `key` field to ensure the `id` field matches in
* development builds, but `id` will still not be present in the manifest.
*
* If necessary, `id` could be calculated from your key.
*
* @see https://web.archive.org/web/20120606044635/http://supercollider.dk/2010/01/calculating-chrome-extension-id-from-your-private-key-233
*/
export interface PenumbraManifest {
/**
* manifest id is present in production, but generally not in dev, because
* they are inserted by chrome store tooling. chrome extension id are simple
* hashes of the 'key' field, an extension-specific public key.
*
* developers may configure a public key in dev, and the extension id will
* match appropriately, but will not be present in the manifest.
*
* the extension id is also part of the extension's origin URI.
*
* @see https://developer.chrome.com/docs/extensions/reference/manifest/key
* @see https://web.archive.org/web/20120606044635/http://supercollider.dk/2010/01/calculating-chrome-extension-id-from-your-private-key-233
*/
id?: string;
key?: string;

// these are required
name: string;
version: string;
description: string;

// these are optional, but might be nice to have
homepage_url?: string;
options_ui?: { page: string };
options_page?: string;

// icons are not indexed by number, but by a stringified number. they may be
// any square size but the power-of-two sizes are typical. the chrome store
// requires a '128' icon.
icons: Record<`${number}`, string> & {
['128']: string;
};
}
export type PenumbraManifest = Partial<chrome.runtime.ManifestV3> &
Required<Pick<chrome.runtime.ManifestV3, 'name' | 'version' | 'description' | 'icons'>>;

export const isPenumbraManifest = (mf: unknown): mf is PenumbraManifest =>
mf !== null &&
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 978efe6

Please sign in to comment.