@x8r/sapphire 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/package.json +24 -0
- package/src/SapphirePlugin.ts +23 -0
- package/src/autoStub.ts +58 -0
- package/src/background.ts +67 -0
- package/src/chromeApi.ts +1390 -0
- package/src/commands.ts +36 -0
- package/src/contentScripts.ts +103 -0
- package/src/crx.ts +60 -0
- package/src/db.ts +81 -0
- package/src/dnr.ts +88 -0
- package/src/eventHub.ts +56 -0
- package/src/extensions.ts +216 -0
- package/src/fileStore.ts +22 -0
- package/src/htmlInject.ts +84 -0
- package/src/index.ts +12 -0
- package/src/manifest.ts +81 -0
- package/src/newtab.ts +29 -0
- package/src/pageMount.ts +39 -0
- package/src/popup.ts +32 -0
- package/src/registry.ts +174 -0
- package/src/router/SapphireRouter.ts +65 -0
- package/src/router/swEntry.ts +9 -0
- package/src/sapphire.ts +101 -0
- package/src/types.ts +132 -0
- package/src/urlScheme.ts +56 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export interface ChromeManifestIcons {
|
|
2
|
+
[size: string]: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface ChromeManifestAction {
|
|
6
|
+
default_icon?: string | ChromeManifestIcons;
|
|
7
|
+
default_title?: string;
|
|
8
|
+
default_popup?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ChromeManifestBackground {
|
|
12
|
+
service_worker?: string;
|
|
13
|
+
type?: "module" | "classic";
|
|
14
|
+
page?: string;
|
|
15
|
+
scripts?: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ChromeManifestContentScript {
|
|
19
|
+
matches?: string[];
|
|
20
|
+
exclude_matches?: string[];
|
|
21
|
+
js?: string[];
|
|
22
|
+
css?: string[];
|
|
23
|
+
run_at?: "document_start" | "document_end" | "document_idle";
|
|
24
|
+
all_frames?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ChromeManifestRuleResource {
|
|
28
|
+
id?: string;
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
path: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ChromeManifestCommand {
|
|
34
|
+
description?: string;
|
|
35
|
+
suggested_key?: {
|
|
36
|
+
default?: string;
|
|
37
|
+
windows?: string;
|
|
38
|
+
mac?: string;
|
|
39
|
+
linux?: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ChromeManifest {
|
|
44
|
+
name: string;
|
|
45
|
+
short_name?: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
version?: string;
|
|
48
|
+
manifest_version?: number;
|
|
49
|
+
default_locale?: string;
|
|
50
|
+
background?: ChromeManifestBackground;
|
|
51
|
+
content_scripts?: ChromeManifestContentScript[];
|
|
52
|
+
declarative_net_request?: {
|
|
53
|
+
rule_resources?: ChromeManifestRuleResource[];
|
|
54
|
+
};
|
|
55
|
+
icons?: ChromeManifestIcons;
|
|
56
|
+
action?: ChromeManifestAction;
|
|
57
|
+
browser_action?: ChromeManifestAction;
|
|
58
|
+
page_action?: ChromeManifestAction;
|
|
59
|
+
options_page?: string;
|
|
60
|
+
options_ui?: { page?: string };
|
|
61
|
+
chrome_url_overrides?: { newtab?: string; bookmarks?: string; history?: string };
|
|
62
|
+
commands?: Record<string, ChromeManifestCommand>;
|
|
63
|
+
permissions?: string[];
|
|
64
|
+
host_permissions?: string[];
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ExtensionMeta {
|
|
69
|
+
id: string;
|
|
70
|
+
manifest: ChromeManifest;
|
|
71
|
+
enabled: boolean;
|
|
72
|
+
installedAt: number;
|
|
73
|
+
filename: string;
|
|
74
|
+
fileList: string[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface DNRRuleCondition {
|
|
78
|
+
urlFilter?: string;
|
|
79
|
+
regexFilter?: string;
|
|
80
|
+
resourceTypes?: string[];
|
|
81
|
+
initiatorDomains?: string[];
|
|
82
|
+
excludedInitiatorDomains?: string[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface DNRRuleAction {
|
|
86
|
+
type: "block" | "redirect" | "upgradeScheme" | "modifyHeaders" | "allow";
|
|
87
|
+
redirect?: { url?: string; regexSubstitution?: string };
|
|
88
|
+
requestHeaders?: unknown[];
|
|
89
|
+
responseHeaders?: unknown[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface DNRRule {
|
|
93
|
+
id: number;
|
|
94
|
+
priority?: number;
|
|
95
|
+
condition: DNRRuleCondition;
|
|
96
|
+
action: DNRRuleAction;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type DNRDecision =
|
|
100
|
+
| { action: "block" }
|
|
101
|
+
| { action: "redirect"; url: string }
|
|
102
|
+
| { action: "modifyHeaders"; headers: unknown[]; responseHeaders: unknown[] };
|
|
103
|
+
|
|
104
|
+
export interface ContentScriptRegistration {
|
|
105
|
+
extId: string;
|
|
106
|
+
id?: string;
|
|
107
|
+
matches: string[];
|
|
108
|
+
excludeMatches: string[];
|
|
109
|
+
js: string[];
|
|
110
|
+
css: string[];
|
|
111
|
+
runAt: "document_start" | "document_end" | "document_idle";
|
|
112
|
+
allFrames: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface TabInfo {
|
|
116
|
+
id: number;
|
|
117
|
+
windowId: number;
|
|
118
|
+
url: string;
|
|
119
|
+
title: string;
|
|
120
|
+
active: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface SapphireHostBindings {
|
|
124
|
+
getTabId: (win: Window) => number | null;
|
|
125
|
+
getTab: (tabId: number) => TabInfo | null;
|
|
126
|
+
getAllTabs: () => TabInfo[];
|
|
127
|
+
getActiveTabId?: () => number | null;
|
|
128
|
+
getTabWindow?: (tabId: number) => Window | null;
|
|
129
|
+
navigateTab?: (tabId: number | null, url: string) => void;
|
|
130
|
+
openExtensionTab?: (extId: string, page: string, tabId: number | null) => void;
|
|
131
|
+
showNotification?: (title: string, message: string) => void;
|
|
132
|
+
}
|
package/src/urlScheme.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const SAPPHIRE_PREFIX = "/~/sx/";
|
|
2
|
+
|
|
3
|
+
export function sapphireBootstrapUrl(extId: string): string {
|
|
4
|
+
return `${sapphireExtensionBase(extId)}__bootstrap__`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function sapphireExtensionBase(extId: string): string {
|
|
8
|
+
return `${globalThis.location.origin}${SAPPHIRE_PREFIX}${extId}/`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildExtensionUrl(extId: string, path: string): string {
|
|
12
|
+
return `${sapphireExtensionBase(extId)}${path.replace(/^\//, "")}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function chromeExtensionUrl(extId: string, path: string): string {
|
|
16
|
+
return `chrome-extension://${extId}/${path.replace(/^\//, "")}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resolveExtensionResourcePath(pageDir: string, ref: string): string {
|
|
20
|
+
if (ref.startsWith("/")) return ref.replace(/^\//, "");
|
|
21
|
+
const fakeBase = `sapphire://x/${pageDir ? `${pageDir}/` : ""}`;
|
|
22
|
+
const resolved = new URL(ref, fakeBase);
|
|
23
|
+
return decodeURIComponent(resolved.pathname.replace(/^\//, ""));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function extensionPathDir(path: string): string {
|
|
27
|
+
const slash = path.lastIndexOf("/");
|
|
28
|
+
return slash === -1 ? "" : path.slice(0, slash);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function decodeSapphirePath(pathname: string): { extId: string; path: string } | null {
|
|
32
|
+
if (!pathname.startsWith(SAPPHIRE_PREFIX)) return null;
|
|
33
|
+
const rest = pathname.slice(SAPPHIRE_PREFIX.length);
|
|
34
|
+
const slash = rest.indexOf("/");
|
|
35
|
+
if (slash === -1) return null;
|
|
36
|
+
const extId = rest.slice(0, slash);
|
|
37
|
+
const path = decodeURIComponent(rest.slice(slash + 1));
|
|
38
|
+
if (!extId || !path) return null;
|
|
39
|
+
return { extId, path };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function decodeSapphireUrl(input: string): { extId: string; path: string } | null {
|
|
43
|
+
try {
|
|
44
|
+
const url = new URL(input, globalThis.location.href);
|
|
45
|
+
if (url.protocol === "chrome-extension:") {
|
|
46
|
+
const extId = url.hostname;
|
|
47
|
+
const path = decodeURIComponent(url.pathname.replace(/^\//, ""));
|
|
48
|
+
if (!extId || !path) return null;
|
|
49
|
+
return { extId, path };
|
|
50
|
+
}
|
|
51
|
+
if (url.origin !== globalThis.location.origin) return null;
|
|
52
|
+
return decodeSapphirePath(url.pathname);
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|