@spicetify/kit 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/bin/spicetify-kit.js +4 -0
- package/dist/build.js +335 -0
- package/dist/check.js +284 -0
- package/dist/classmap.js +187 -0
- package/dist/cli.js +53 -0
- package/dist/create.js +429 -0
- package/dist/dev.js +100 -0
- package/dist/from-theme.js +96 -0
- package/dist/install.js +60 -0
- package/dist/launch.js +122 -0
- package/dist/pack.js +31 -0
- package/dist/push.js +211 -0
- package/dist/vault.js +108 -0
- package/package.json +49 -0
- package/vendor/classmaps/1020096/classmap-19f856aefd5.json +109 -0
- package/vendor/shims/chunks.d.ts +10 -0
- package/vendor/shims/remote-modules.d.ts +6 -0
- package/vendor/shims/spicetify.d.ts +2437 -0
- package/vendor/stdlib/deps.ts +24 -0
- package/vendor/stdlib/index.ts +24 -0
- package/vendor/stdlib/lib/modal.tsx +128 -0
- package/vendor/stdlib/lib/popover.ts +84 -0
- package/vendor/stdlib/lib/primitives-classes.ts +66 -0
- package/vendor/stdlib/lib/primitives-vanilla.ts +277 -0
- package/vendor/stdlib/lib/primitives.tsx +314 -0
- package/vendor/stdlib/load.ts +17 -0
- package/vendor/stdlib/metadata.json +18 -0
- package/vendor/stdlib/mixin.ts +32 -0
- package/vendor/stdlib/mod.ts +26 -0
- package/vendor/stdlib/src/chunks.d.ts +10 -0
- package/vendor/stdlib/src/client.ts +114 -0
- package/vendor/stdlib/src/createIconComponent.tsx +29 -0
- package/vendor/stdlib/src/events.mix.ts +55 -0
- package/vendor/stdlib/src/events.ts +58 -0
- package/vendor/stdlib/src/expose/GraphQL.ts +40 -0
- package/vendor/stdlib/src/expose/Platform.ts +48 -0
- package/vendor/stdlib/src/expose/React.ts +95 -0
- package/vendor/stdlib/src/expose/ReactFlipToolkitSpring.ts +27 -0
- package/vendor/stdlib/src/expose/ReduxStore.ts +39 -0
- package/vendor/stdlib/src/expose/SettingsSection.ts +60 -0
- package/vendor/stdlib/src/expose/Snackbar.ts +34 -0
- package/vendor/stdlib/src/expose/Tippy.ts +26 -0
- package/vendor/stdlib/src/expose/enqueueImageSnackbar.ts +25 -0
- package/vendor/stdlib/src/expose/index.ts +14 -0
- package/vendor/stdlib/src/expose/jsx-runtime.ts +46 -0
- package/vendor/stdlib/src/expose/react-dom-shim.ts +50 -0
- package/vendor/stdlib/src/expose/react-shim.ts +116 -0
- package/vendor/stdlib/src/logger.ts +35 -0
- package/vendor/stdlib/src/playbar-compat.tsx +136 -0
- package/vendor/stdlib/src/registers/index.ts +176 -0
- package/vendor/stdlib/src/registers/menu.ts +160 -0
- package/vendor/stdlib/src/registers/mount.ts +307 -0
- package/vendor/stdlib/src/registers/nativeAnchors.ts +49 -0
- package/vendor/stdlib/src/registers/navlink.tsx +165 -0
- package/vendor/stdlib/src/registers/order.ts +22 -0
- package/vendor/stdlib/src/registers/panel.ts +168 -0
- package/vendor/stdlib/src/registers/playbarButton.tsx +93 -0
- package/vendor/stdlib/src/registers/playbarWidget.tsx +71 -0
- package/vendor/stdlib/src/registers/registry.ts +12 -0
- package/vendor/stdlib/src/registers/root.ts +101 -0
- package/vendor/stdlib/src/registers/route.ts +123 -0
- package/vendor/stdlib/src/registers/settingsRow.tsx +56 -0
- package/vendor/stdlib/src/registers/settingsSection.ts +59 -0
- package/vendor/stdlib/src/registers/topbarLeftButton.tsx +86 -0
- package/vendor/stdlib/src/registers/topbarRightButton.tsx +87 -0
- package/vendor/stdlib/src/storage.ts +163 -0
- package/vendor/stdlib/src/util.ts +62 -0
- package/vendor/stdlib/src/utils/index.ts +16 -0
- package/vendor/stdlib/src/webpack/ClassNames.gen.ts +10 -0
- package/vendor/stdlib/src/webpack/ClassNames.ts +6 -0
- package/vendor/stdlib/src/webpack/ClassNames.xpui.ts +16 -0
- package/vendor/stdlib/src/webpack/ComponentLibrary.gen.ts +10 -0
- package/vendor/stdlib/src/webpack/ComponentLibrary.ts +6 -0
- package/vendor/stdlib/src/webpack/ComponentLibrary.xpui.ts +18 -0
- package/vendor/stdlib/src/webpack/FilterContext.gen.ts +10 -0
- package/vendor/stdlib/src/webpack/FilterContext.ts +6 -0
- package/vendor/stdlib/src/webpack/FilterContext.xpui.ts +10 -0
- package/vendor/stdlib/src/webpack/Mousetrap.gen.ts +10 -0
- package/vendor/stdlib/src/webpack/Mousetrap.ts +6 -0
- package/vendor/stdlib/src/webpack/Mousetrap.xpui.ts +12 -0
- package/vendor/stdlib/src/webpack/React.gen.ts +14 -0
- package/vendor/stdlib/src/webpack/React.ts +6 -0
- package/vendor/stdlib/src/webpack/React.xpui.ts +15 -0
- package/vendor/stdlib/src/webpack/ReactComponents.desktop.ts +17 -0
- package/vendor/stdlib/src/webpack/ReactComponents.gen.ts +74 -0
- package/vendor/stdlib/src/webpack/ReactComponents.panel.ts +19 -0
- package/vendor/stdlib/src/webpack/ReactComponents.ts +50 -0
- package/vendor/stdlib/src/webpack/ReactComponents.xpui.ts +164 -0
- package/vendor/stdlib/src/webpack/ReactFlipToolkit.gen.ts +12 -0
- package/vendor/stdlib/src/webpack/ReactFlipToolkit.ts +6 -0
- package/vendor/stdlib/src/webpack/ReactFlipToolkit.xpui.ts +14 -0
- package/vendor/stdlib/src/webpack/ReactHooks.fullscreen.ts +15 -0
- package/vendor/stdlib/src/webpack/ReactHooks.gen.ts +32 -0
- package/vendor/stdlib/src/webpack/ReactHooks.ts +6 -0
- package/vendor/stdlib/src/webpack/ReactHooks.xpui.ts +38 -0
- package/vendor/stdlib/src/webpack/ReactQuery.gen.ts +26 -0
- package/vendor/stdlib/src/webpack/ReactQuery.ts +6 -0
- package/vendor/stdlib/src/webpack/ReactQuery.xpui.ts +46 -0
- package/vendor/stdlib/src/webpack/ReactRouter.gen.ts +12 -0
- package/vendor/stdlib/src/webpack/ReactRouter.ts +6 -0
- package/vendor/stdlib/src/webpack/ReactRouter.xpui.ts +22 -0
- package/vendor/stdlib/src/webpack/Snackbar.gen.ts +12 -0
- package/vendor/stdlib/src/webpack/Snackbar.ts +6 -0
- package/vendor/stdlib/src/webpack/Snackbar.xpui.ts +27 -0
- package/vendor/stdlib/src/webpack/URI.gen.ts +26 -0
- package/vendor/stdlib/src/webpack/URI.ts +82 -0
- package/vendor/stdlib/src/webpack/URI.xpui.ts +111 -0
- package/vendor/stdlib/src/webpack/capture-readiness.ts +49 -0
- package/vendor/stdlib/src/webpack/index.ts +186 -0
- package/vendor/stdlib/src/webpack/misc.gen.ts +16 -0
- package/vendor/stdlib/src/webpack/misc.ts +6 -0
- package/vendor/stdlib/src/webpack/misc.xpui.ts +27 -0
- package/vendor/stdlib/src/wpunpk.mix.ts +104 -0
- package/vendor/stdlib/src/wpunpk.ts +36 -0
- package/vendor/stdlib/vendor/rxjs.d.ts +5 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type ParsableAsURI = any;
|
|
7
|
+
|
|
8
|
+
export type IsThisURIType<A extends keyof URITypes> = (url: ParsableAsURI) => url is URIClass<A>;
|
|
9
|
+
|
|
10
|
+
export type URIClass<_A extends keyof URITypes> = any;
|
|
11
|
+
|
|
12
|
+
export type URITypes = {
|
|
13
|
+
AD: "ad";
|
|
14
|
+
ALBUM: "album";
|
|
15
|
+
GENRE: "genre";
|
|
16
|
+
QUEUE: "queue";
|
|
17
|
+
APPLICATION: "application";
|
|
18
|
+
ARTIST: "artist";
|
|
19
|
+
ARTIST_TOPLIST: "artist-toplist";
|
|
20
|
+
ARTIST_CONCERTS: "artist-concerts";
|
|
21
|
+
AUDIO_FILE: "audiofile";
|
|
22
|
+
COLLECTION: "collection";
|
|
23
|
+
COLLECTION_ALBUM: "collection-album";
|
|
24
|
+
COLLECTION_ARTIST: "collection-artist";
|
|
25
|
+
COLLECTION_MISSING_ALBUM: "collection-missing-album";
|
|
26
|
+
COLLECTION_TRACK_LIST: "collectiontracklist";
|
|
27
|
+
CONCEPT: "concept";
|
|
28
|
+
CONCERT: "concert";
|
|
29
|
+
CONTEXT_GROUP: "context-group";
|
|
30
|
+
CULTURAL_MOMENT: "cultural-moment";
|
|
31
|
+
DAILY_MIX: "dailymix";
|
|
32
|
+
EMPTY: "empty";
|
|
33
|
+
EPISODE: "episode";
|
|
34
|
+
FACEBOOK: "facebook";
|
|
35
|
+
FOLDER: "folder";
|
|
36
|
+
FOLLOWERS: "followers";
|
|
37
|
+
FOLLOWING: "following";
|
|
38
|
+
IMAGE: "image";
|
|
39
|
+
INBOX: "inbox";
|
|
40
|
+
INTERRUPTION: "interruption";
|
|
41
|
+
LIBRARY: "library";
|
|
42
|
+
LIVE: "live";
|
|
43
|
+
ROOM: "room";
|
|
44
|
+
EXPRESSION: "expression";
|
|
45
|
+
LOCAL: "local";
|
|
46
|
+
LOCAL_TRACK: "local";
|
|
47
|
+
LOCAL_ALBUM: "local-album";
|
|
48
|
+
LOCAL_ARTIST: "local-artist";
|
|
49
|
+
MERCH: "merch";
|
|
50
|
+
MERCHHUB: "merchhub";
|
|
51
|
+
MOSAIC: "mosaic";
|
|
52
|
+
PLAYLIST: "playlist";
|
|
53
|
+
PLAYLIST_V2: "playlist-v2";
|
|
54
|
+
PRERELEASE: "prerelease";
|
|
55
|
+
PROFILE: "profile";
|
|
56
|
+
PUBLISHED_ROOTLIST: "published-rootlist";
|
|
57
|
+
RADIO: "radio";
|
|
58
|
+
ROOTLIST: "rootlist";
|
|
59
|
+
SEARCH: "search";
|
|
60
|
+
SHOW: "show";
|
|
61
|
+
SOCIAL_SESSION: "socialsession";
|
|
62
|
+
SPECIAL: "special";
|
|
63
|
+
STARRED: "starred";
|
|
64
|
+
STATION: "station";
|
|
65
|
+
TEMP_PLAYLIST: "temp-playlist";
|
|
66
|
+
TOPLIST: "toplist";
|
|
67
|
+
TRACK: "track";
|
|
68
|
+
TRACKSET: "trackset";
|
|
69
|
+
USER_TOPLIST: "user-toplist";
|
|
70
|
+
USER_TOP_TRACKS: "user-top-tracks";
|
|
71
|
+
UNKNOWN: "unknown";
|
|
72
|
+
VIDEO: "video";
|
|
73
|
+
MEDIA: "media";
|
|
74
|
+
QUESTION: "question";
|
|
75
|
+
POLL: "poll";
|
|
76
|
+
RESPONSE: "response";
|
|
77
|
+
COURSE: "course";
|
|
78
|
+
LESSON: "lesson";
|
|
79
|
+
CANVAS: "canvas";
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export * from "./URI.gen.ts";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { toPascalCase } from "../util.ts";
|
|
7
|
+
import { warn } from "../logger.ts";
|
|
8
|
+
import { modules, src } from "./index.ts";
|
|
9
|
+
import { webpackRequire } from "../wpunpk.mix.ts";
|
|
10
|
+
import { IsThisURIType, ParsableAsURI, URIClass, URITypes } from "./URI.ts";
|
|
11
|
+
|
|
12
|
+
type Is = {
|
|
13
|
+
Ad: IsThisURIType<any>;
|
|
14
|
+
Album: IsThisURIType<any>;
|
|
15
|
+
Application: IsThisURIType<any>;
|
|
16
|
+
Artist: IsThisURIType<any>;
|
|
17
|
+
CollectionAlbum: IsThisURIType<any>;
|
|
18
|
+
CollectionArtist: IsThisURIType<any>;
|
|
19
|
+
Collection: IsThisURIType<any>;
|
|
20
|
+
Concert: IsThisURIType<any>;
|
|
21
|
+
Episode: IsThisURIType<any>;
|
|
22
|
+
Folder: IsThisURIType<any>;
|
|
23
|
+
LocalTrack: IsThisURIType<any>;
|
|
24
|
+
Playlist: IsThisURIType<any>;
|
|
25
|
+
PlaylistV2: IsThisURIType<any>;
|
|
26
|
+
PlaylistV1OrV2: IsThisURIType<any>;
|
|
27
|
+
Profile: IsThisURIType<any>;
|
|
28
|
+
Radio: IsThisURIType<any>;
|
|
29
|
+
Show: IsThisURIType<any>;
|
|
30
|
+
SocialSession: IsThisURIType<any>;
|
|
31
|
+
Station: IsThisURIType<any>;
|
|
32
|
+
Track: IsThisURIType<any>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type Create = {
|
|
36
|
+
Album: any;
|
|
37
|
+
Application: any;
|
|
38
|
+
Artist: any;
|
|
39
|
+
CollectionAlbum: any;
|
|
40
|
+
CollectionArtist: any;
|
|
41
|
+
Collection: any;
|
|
42
|
+
Concert: any;
|
|
43
|
+
Episode: any;
|
|
44
|
+
Folder: any;
|
|
45
|
+
LocalAlbum: any;
|
|
46
|
+
LocalArtist: any;
|
|
47
|
+
PlaylistV2: any;
|
|
48
|
+
Prerelease: any;
|
|
49
|
+
Profile: any;
|
|
50
|
+
Queue: any;
|
|
51
|
+
Search: any;
|
|
52
|
+
Show: any;
|
|
53
|
+
SocialSession: any;
|
|
54
|
+
Station: any;
|
|
55
|
+
Track: any;
|
|
56
|
+
UserToplist: any;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
await CHUNKS.xpui.promise;
|
|
60
|
+
|
|
61
|
+
// Needle misses are version drift, not fatal errors: a miss costs the
|
|
62
|
+
// affected surface member (undefined), never the whole module — a throw
|
|
63
|
+
// here rejects the lazy URI.gen import and used to crash the client.
|
|
64
|
+
const URIModuleHit = modules.find(
|
|
65
|
+
([id, v]) => src(v).includes("Invalid Spotify URI!") && Object.keys(webpackRequire(id) ?? {}).length > 10,
|
|
66
|
+
);
|
|
67
|
+
if (!URIModuleHit) warn("[stdlib] webpack needle miss: URI module (Invalid Spotify URI!)");
|
|
68
|
+
const URIModule = URIModuleHit ? webpackRequire(URIModuleHit[0]) : {};
|
|
69
|
+
const [_Types, ...vs] = Object.values(URIModule) as [URITypes, ...Function[]];
|
|
70
|
+
export const Types = _Types ?? ({} as URITypes);
|
|
71
|
+
const TypesKeys = Object.keys(Types);
|
|
72
|
+
|
|
73
|
+
const isTestFn = (fn: Function) => TypesKeys.some((t) => src(fn).includes(`${t}}`));
|
|
74
|
+
const isCreateFn = (fn: Function) => TypesKeys.some((t) => src(fn).includes(`${t},`));
|
|
75
|
+
|
|
76
|
+
const fnsByType = Object.groupBy(vs, (fn) => (isTestFn(fn) ? "test" : isCreateFn(fn) ? "create" : undefined!));
|
|
77
|
+
export const is: Is = Object.fromEntries(
|
|
78
|
+
(fnsByType.test ?? []).flatMap((fn) => {
|
|
79
|
+
const name = src(fn).match(/([\w_\d]{2,})\}/)?.[1];
|
|
80
|
+
return name ? [[toPascalCase(name), fn]] : [];
|
|
81
|
+
}),
|
|
82
|
+
) as any;
|
|
83
|
+
export const create: Create = Object.fromEntries(
|
|
84
|
+
(fnsByType.create ?? []).flatMap((fn) => {
|
|
85
|
+
const name = src(fn).match(/([\w_\d]{2,})\,/)?.[1];
|
|
86
|
+
return name ? [[toPascalCase(name), fn]] : [];
|
|
87
|
+
}),
|
|
88
|
+
) as any;
|
|
89
|
+
const uniqueFns = fnsByType[undefined as unknown as keyof typeof fnsByType] ?? [];
|
|
90
|
+
|
|
91
|
+
const findAndExcludeBy = (...strings: string[]) => {
|
|
92
|
+
const i = uniqueFns.findIndex((f) => strings.every((str) => src(f).includes(str)));
|
|
93
|
+
if (i < 0) {
|
|
94
|
+
warn("[stdlib] webpack needle miss: URI helper", strings.join(" + "));
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
return uniqueFns.splice(i, 1)[0];
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const isSameIdentity: (a: ParsableAsURI, b: ParsableAsURI) => boolean = findAndExcludeBy("PLAYLIST") as any;
|
|
101
|
+
export const urlEncode: (str: string) => string = findAndExcludeBy(".URI") as any;
|
|
102
|
+
export const idToHex: (str: string) => string = findAndExcludeBy("22===") as any;
|
|
103
|
+
export const hexToId: (str: string) => string = findAndExcludeBy("32===") as any;
|
|
104
|
+
export const from: (uri: ParsableAsURI) => URIClass<any> = findAndExcludeBy("allowedTypes") as any;
|
|
105
|
+
export const fromString: (str: string) => URIClass<any> = findAndExcludeBy("Argument `uri` must be a string.") as any;
|
|
106
|
+
|
|
107
|
+
is.PlaylistV1OrV2 = (
|
|
108
|
+
is.Playlist && is.PlaylistV2
|
|
109
|
+
? findAndExcludeBy(`${(is.Playlist as Function).name}(e)||${(is.PlaylistV2 as Function).name}(e)`)
|
|
110
|
+
: undefined
|
|
111
|
+
) as any;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 Afonso Jorge Ramos
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface CaptureReadinessOptions {
|
|
7
|
+
timeoutMs: number;
|
|
8
|
+
onTimeout?: () => void;
|
|
9
|
+
scheduleTimeout?: typeof setTimeout;
|
|
10
|
+
clearScheduledTimeout?: typeof clearTimeout;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createCaptureReadiness(options: CaptureReadinessOptions) {
|
|
14
|
+
const settled = Promise.withResolvers<void>();
|
|
15
|
+
let released = false;
|
|
16
|
+
let analyzed = false;
|
|
17
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
18
|
+
const scheduleTimeout = options.scheduleTimeout ?? setTimeout;
|
|
19
|
+
const clearScheduledTimeout = options.clearScheduledTimeout ?? clearTimeout;
|
|
20
|
+
const release = () => {
|
|
21
|
+
if (released) return;
|
|
22
|
+
released = true;
|
|
23
|
+
if (timer !== undefined) clearScheduledTimeout(timer);
|
|
24
|
+
settled.resolve(undefined);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
wait(): Promise<void> {
|
|
29
|
+
if (!released && timer === undefined) {
|
|
30
|
+
timer = scheduleTimeout(() => {
|
|
31
|
+
options.onTimeout?.();
|
|
32
|
+
release();
|
|
33
|
+
}, options.timeoutMs);
|
|
34
|
+
}
|
|
35
|
+
return settled.promise;
|
|
36
|
+
},
|
|
37
|
+
run(work: () => void, onFailure: (error: unknown) => void): void {
|
|
38
|
+
if (analyzed) return;
|
|
39
|
+
analyzed = true;
|
|
40
|
+
try {
|
|
41
|
+
work();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
onFailure(error);
|
|
44
|
+
} finally {
|
|
45
|
+
release();
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { warn } from "../logger.ts";
|
|
7
|
+
import { postWebpackRequireHooks, WebpackModule, WebpackRequire, webpackRequire } from "../wpunpk.mix.ts";
|
|
8
|
+
import { createCaptureReadiness } from "./capture-readiness.ts";
|
|
9
|
+
|
|
10
|
+
export let modules: Array<[PropertyKey, WebpackModule]>;
|
|
11
|
+
export let exports: Array<Record<string, any>>;
|
|
12
|
+
export let exported: Array<any>;
|
|
13
|
+
|
|
14
|
+
export let exportedFunctions: Array<any>;
|
|
15
|
+
|
|
16
|
+
export let exportedReactObjects: Partial<Record<any, any[]>>;
|
|
17
|
+
export let exportedContexts: Array<React.Context<any>>;
|
|
18
|
+
export let exportedForwardRefs: Array<any>;
|
|
19
|
+
export let exportedMemos: React.NamedExoticComponent[];
|
|
20
|
+
|
|
21
|
+
// Some client exports are functions whose own toString is not callable; they
|
|
22
|
+
// can never match a needle, so they stringify to "".
|
|
23
|
+
export const src = (f: unknown): string => {
|
|
24
|
+
try {
|
|
25
|
+
return String(f);
|
|
26
|
+
} catch {
|
|
27
|
+
try {
|
|
28
|
+
return Function.prototype.toString.call(f);
|
|
29
|
+
} catch {
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const analyzeWebpackRequire = (webpackRequire: WebpackRequire) => {
|
|
36
|
+
const modules = Object.entries(webpackRequire.m) as Array<[keyof any, WebpackModule]>;
|
|
37
|
+
const exports = modules.map(([id]) => webpackRequire(id)) as Array<Record<string, any>>;
|
|
38
|
+
const exported = exports
|
|
39
|
+
.filter((module) => typeof module === "object")
|
|
40
|
+
.flatMap((module) => {
|
|
41
|
+
try {
|
|
42
|
+
return Object.values(module);
|
|
43
|
+
} catch (_) {}
|
|
44
|
+
})
|
|
45
|
+
.filter(Boolean) as Array<any>;
|
|
46
|
+
|
|
47
|
+
const isFunction = (obj: any): obj is Function => typeof obj === "function";
|
|
48
|
+
const exportedFunctions = exported.filter(isFunction) as any[];
|
|
49
|
+
|
|
50
|
+
const exportedReactObjects = Object.groupBy(exported, (x) => x.$$typeof);
|
|
51
|
+
const exportedContexts = exportedReactObjects[Symbol.for("react.context") as any]! as Array<React.Context<any>>;
|
|
52
|
+
const exportedForwardRefs = exportedReactObjects[Symbol.for("react.forward_ref") as any]! as any[];
|
|
53
|
+
const exportedMemos = exportedReactObjects[Symbol.for("react.memo") as any]! as React.NamedExoticComponent[];
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
modules,
|
|
57
|
+
exports,
|
|
58
|
+
exported,
|
|
59
|
+
exportedFunctions,
|
|
60
|
+
exportedReactObjects,
|
|
61
|
+
exportedContexts,
|
|
62
|
+
exportedForwardRefs,
|
|
63
|
+
exportedMemos,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
CHUNKS["/vendor~xpui.js"] ??= Promise.withResolvers();
|
|
68
|
+
CHUNKS["/xpui.js"] ??= Promise.withResolvers();
|
|
69
|
+
Object.assign(CHUNKS, {
|
|
70
|
+
xpui: {
|
|
71
|
+
promise: Promise.all([CHUNKS["/vendor~xpui.js"].promise, CHUNKS["/xpui.js"].promise]) as any,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// v3 chunk tracking: the 2024 pre-boot mixin that resolved chunk promises is
|
|
76
|
+
// gone. The loader captures __webpack_require__ once the client is up, and
|
|
77
|
+
// the xpui entry chunks live in the V8 snapshot — they have necessarily
|
|
78
|
+
// executed by capture time. Lazy route chunks go through the runtime's own
|
|
79
|
+
// script loader (wpr.l) from then on.
|
|
80
|
+
postWebpackRequireHooks.push((wpr: any) => {
|
|
81
|
+
if (typeof wpr?.l === "function") {
|
|
82
|
+
const load = wpr.l.bind(wpr);
|
|
83
|
+
wpr.l = (url: string, done: (event: unknown) => unknown, key?: string, chunkId?: unknown) =>
|
|
84
|
+
load(
|
|
85
|
+
url,
|
|
86
|
+
(event: unknown) => {
|
|
87
|
+
(CHUNKS[new URL(url, location.href).pathname] ??= Promise.withResolvers()).resolve(undefined);
|
|
88
|
+
return done(event);
|
|
89
|
+
},
|
|
90
|
+
key,
|
|
91
|
+
chunkId,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
// Chunk scripts that finished loading before capture. The xpui entry
|
|
95
|
+
// chunks are deliberately excluded: they are gated below on registry
|
|
96
|
+
// quiescence, because "script fetched" is not "all modules registered".
|
|
97
|
+
for (const entry of performance.getEntriesByType("resource")) {
|
|
98
|
+
const url = new URL(entry.name, location.href);
|
|
99
|
+
if (url.pathname === "/vendor~xpui.js" || url.pathname === "/xpui.js") continue;
|
|
100
|
+
if (url.origin === location.origin && url.pathname.endsWith(".js")) {
|
|
101
|
+
(CHUNKS[url.pathname] ??= Promise.withResolvers()).resolve(undefined);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Capture can fire while boot is still registering modules (xpui-modules
|
|
105
|
+
// and friends land after the runtime is up, and the analysis needles live
|
|
106
|
+
// there). The analysis below snapshots wpr.m once, so hold the xpui
|
|
107
|
+
// promises until the registry has been quiet for a few ticks — with a
|
|
108
|
+
// hard cap so a pathological boot still resolves.
|
|
109
|
+
let last = -1;
|
|
110
|
+
let stable = 0;
|
|
111
|
+
let ticks = 0;
|
|
112
|
+
const settle = setInterval(() => {
|
|
113
|
+
const count = Object.keys(wpr?.m ?? {}).length;
|
|
114
|
+
stable = count === last ? stable + 1 : 0;
|
|
115
|
+
last = count;
|
|
116
|
+
if (stable >= 3 || ++ticks > 100) {
|
|
117
|
+
clearInterval(settle);
|
|
118
|
+
CHUNKS["/vendor~xpui.js"].resolve(undefined);
|
|
119
|
+
CHUNKS["/xpui.js"].resolve(undefined);
|
|
120
|
+
}
|
|
121
|
+
}, 100);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Capture subscribers run synchronously right after the analysis lands, so
|
|
125
|
+
// the expose shims can populate their live bindings from a real capture
|
|
126
|
+
// instead of snapshotting the lazy proxy at module init (which freezes
|
|
127
|
+
// undefined for the whole session when anything evaluates a shim
|
|
128
|
+
// pre-capture — the Fragment/react-shim class of bug).
|
|
129
|
+
let captured = false;
|
|
130
|
+
const captureSubscribers: Array<() => void> = [];
|
|
131
|
+
const webpackCaptureReadiness = createCaptureReadiness({
|
|
132
|
+
// Registry quiescence caps at about 10.1s. Leave room for its last tick,
|
|
133
|
+
// then release degraded so a changed runtime cannot deadlock all modules.
|
|
134
|
+
timeoutMs: 12000,
|
|
135
|
+
onTimeout: () => warn("[stdlib] capture health: webpack capture timed out; module surfaces will be degraded"),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Module preload uses this boundary to keep every later module out of the
|
|
139
|
+
// gap between capturing webpack's require function and finishing the export
|
|
140
|
+
// analysis. It settles on failure too: a degraded stdlib must not hang the
|
|
141
|
+
// loader forever.
|
|
142
|
+
export function waitForWebpackCapture(): Promise<void> {
|
|
143
|
+
return webpackCaptureReadiness.wait();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function onWebpackCaptured(cb: () => void): void {
|
|
147
|
+
if (captured) {
|
|
148
|
+
cb();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
captureSubscribers.push(cb);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
CHUNKS.xpui.promise.then(() => {
|
|
155
|
+
webpackCaptureReadiness.run(
|
|
156
|
+
() => {
|
|
157
|
+
// A single throwing client-module factory must not abort the whole
|
|
158
|
+
// capture: with no capture, every live binding in the react shims stays
|
|
159
|
+
// undefined for the session and the failure surfaces as nothing but an
|
|
160
|
+
// unhandled rejection.
|
|
161
|
+
const analysis = analyzeWebpackRequire(webpackRequire);
|
|
162
|
+
modules = analysis.modules;
|
|
163
|
+
exports = analysis.exports;
|
|
164
|
+
exported = analysis.exported;
|
|
165
|
+
exportedFunctions = analysis.exportedFunctions;
|
|
166
|
+
exportedReactObjects = analysis.exportedReactObjects;
|
|
167
|
+
exportedContexts = analysis.exportedContexts;
|
|
168
|
+
exportedForwardRefs = analysis.exportedForwardRefs;
|
|
169
|
+
exportedMemos = analysis.exportedMemos;
|
|
170
|
+
captured = true;
|
|
171
|
+
if (!analysis.exported.length) {
|
|
172
|
+
warn(
|
|
173
|
+
"[stdlib] capture health: the webpack capture yielded no exports — every needle-backed surface is degraded",
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
for (const cb of captureSubscribers.splice(0)) {
|
|
177
|
+
try {
|
|
178
|
+
cb();
|
|
179
|
+
} catch (e) {
|
|
180
|
+
console.error("[stdlib] capture subscriber failed:", e);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
(e) => warn("[stdlib] capture health: webpack capture analysis failed; module surfaces will be degraded:", e),
|
|
185
|
+
);
|
|
186
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type * as misc_xpui_ts from "./misc.xpui.ts";
|
|
7
|
+
export let Color: typeof misc_xpui_ts.Color;
|
|
8
|
+
export let Locale: typeof misc_xpui_ts.Locale;
|
|
9
|
+
export let createUrlLocale: typeof misc_xpui_ts.createUrlLocale;
|
|
10
|
+
export let InternalPropetyMap: typeof misc_xpui_ts.InternalPropetyMap;
|
|
11
|
+
import("./misc.xpui.ts").then(m => {
|
|
12
|
+
Color = m.Color;
|
|
13
|
+
Locale = m.Locale;
|
|
14
|
+
createUrlLocale = m.createUrlLocale;
|
|
15
|
+
InternalPropetyMap = m.InternalPropetyMap;
|
|
16
|
+
}).catch((e) => console.warn("[stdlib] lazy webpack surface ./misc.xpui.ts failed:", e));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { exported, exportedFunctions } from "./index.ts";
|
|
7
|
+
import { findBy } from "../util.ts";
|
|
8
|
+
|
|
9
|
+
await CHUNKS.xpui.promise;
|
|
10
|
+
|
|
11
|
+
// The client's own color class; no static types exist for it.
|
|
12
|
+
export type Color = any;
|
|
13
|
+
|
|
14
|
+
export const Color: Function & {
|
|
15
|
+
Format: any;
|
|
16
|
+
[member: string]: any;
|
|
17
|
+
} = Object.assign(findBy("this.rgb")(exportedFunctions)!, {
|
|
18
|
+
// Typed predicate: some client exports are get-trap proxies that answer
|
|
19
|
+
// truthy for any key, so "has RGBA" alone can latch onto the wrong one.
|
|
20
|
+
Format: exported.find((m) => typeof m?.RGBA === "number" && typeof m?.HEX === "number")!,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const Locale: any = exported.find((m) => typeof m.getTranslations === "function");
|
|
24
|
+
|
|
25
|
+
export const createUrlLocale: Function = findBy("has", "baseName", "language")(exportedFunctions);
|
|
26
|
+
|
|
27
|
+
export const InternalPropetyMap: any = exported.find((o) => typeof o.Builder === "function");
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* Copyright (C) 2026 Afonso Jorge Ramos
|
|
4
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// wpunpk.mix: webpack require access for the v3 modular runtime.
|
|
8
|
+
// The modular loader captures __webpack_require__ itself after the client is
|
|
9
|
+
// up (pre-boot chunk registration is fatal to the snapshot runtime). This
|
|
10
|
+
// module forwards to it lazily and keeps the hooks-era contract intact.
|
|
11
|
+
|
|
12
|
+
class Subject<T = unknown> {
|
|
13
|
+
private observers = new Set<(value: T) => void>();
|
|
14
|
+
subscribe(fn: (value: T) => void) {
|
|
15
|
+
this.observers.add(fn);
|
|
16
|
+
return { unsubscribe: () => this.observers.delete(fn) };
|
|
17
|
+
}
|
|
18
|
+
next(value: T) {
|
|
19
|
+
for (const fn of this.observers) fn(value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class BehaviorSubject<T> extends Subject<T> {
|
|
24
|
+
constructor(private current: T) {
|
|
25
|
+
super();
|
|
26
|
+
}
|
|
27
|
+
override subscribe(fn: (value: T) => void) {
|
|
28
|
+
const sub = super.subscribe(fn);
|
|
29
|
+
fn(this.current);
|
|
30
|
+
return sub;
|
|
31
|
+
}
|
|
32
|
+
override next(value: T) {
|
|
33
|
+
this.current = value;
|
|
34
|
+
super.next(value);
|
|
35
|
+
}
|
|
36
|
+
getValue() {
|
|
37
|
+
return this.current;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { Subject, BehaviorSubject };
|
|
42
|
+
|
|
43
|
+
export const chunkLoadedSubjectPre = new Subject<unknown>();
|
|
44
|
+
export const chunkLoadedSubjectPost = new Subject<unknown>();
|
|
45
|
+
export const moduleLoadedSubject = new Subject<unknown>();
|
|
46
|
+
|
|
47
|
+
const pendingHooks: Array<(wpr: any) => void> = [];
|
|
48
|
+
export const postWebpackRequireHooks = {
|
|
49
|
+
push(hook: (wpr: any) => void) {
|
|
50
|
+
if (typeof globalThis.__webpack_require__ === "function") {
|
|
51
|
+
try {
|
|
52
|
+
hook(globalThis.__webpack_require__);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.error(e);
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
pendingHooks.push(hook);
|
|
59
|
+
ensureDrainTimer();
|
|
60
|
+
return pendingHooks.length;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type WebpackModule = (module: any, exports: any, require: any) => void;
|
|
65
|
+
|
|
66
|
+
export type WebpackRequire = {
|
|
67
|
+
(id: PropertyKey): any;
|
|
68
|
+
m: Record<string, unknown>;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const webpackRequire: WebpackRequire = new Proxy(function () {}, {
|
|
72
|
+
get: (_, k) => globalThis.__webpack_require__?.[k as never] ?? (k === "m" ? {} : undefined),
|
|
73
|
+
apply: (_, __, args) => (globalThis.__webpack_require__ as any)(...args),
|
|
74
|
+
}) as never;
|
|
75
|
+
|
|
76
|
+
const drain = () => {
|
|
77
|
+
if (typeof globalThis.__webpack_require__ !== "function") return false;
|
|
78
|
+
for (const hook of pendingHooks.splice(0)) {
|
|
79
|
+
try {
|
|
80
|
+
hook(globalThis.__webpack_require__);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
console.error(e);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
};
|
|
87
|
+
// Hooks can be pushed at mixin time (pre-boot) or load time (post-boot),
|
|
88
|
+
// and capture can land arbitrarily late — each pending push (re)arms a
|
|
89
|
+
// bounded poll instead of relying on one module-eval-time window.
|
|
90
|
+
let drainTimer: ReturnType<typeof setInterval> | undefined;
|
|
91
|
+
const ensureDrainTimer = () => {
|
|
92
|
+
if (drainTimer !== undefined) return;
|
|
93
|
+
let tries = 0;
|
|
94
|
+
drainTimer = setInterval(() => {
|
|
95
|
+
if (drain() || ++tries > 400) {
|
|
96
|
+
clearInterval(drainTimer);
|
|
97
|
+
drainTimer = undefined;
|
|
98
|
+
}
|
|
99
|
+
}, 50);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
declare global {
|
|
103
|
+
var __webpack_require__: WebpackRequire | undefined;
|
|
104
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { moduleLoadedSubject, type WebpackModule, webpackRequire } from "./wpunpk.mix.ts";
|
|
7
|
+
|
|
8
|
+
type ModuleMatcher = (id: string, module: WebpackModule) => boolean;
|
|
9
|
+
type ModulePair = [keyof any, WebpackModule];
|
|
10
|
+
|
|
11
|
+
export function matchWebpackModuleSync(moduleMatcher: ModuleMatcher): ModulePair | null {
|
|
12
|
+
if (webpackRequire) {
|
|
13
|
+
for (const [id, module] of Object.entries(webpackRequire!.m) as Array<[string, WebpackModule]>) {
|
|
14
|
+
if (moduleMatcher(id, module)) {
|
|
15
|
+
return [id, module] as ModulePair;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function matchWebpackModule(moduleMatcher: ModuleMatcher, callback: (...args: ModulePair) => void) {
|
|
24
|
+
const sync = matchWebpackModuleSync(moduleMatcher);
|
|
25
|
+
if (sync) {
|
|
26
|
+
callback(...sync);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const subscription = moduleLoadedSubject.subscribe(([id, module]) => {
|
|
31
|
+
if (moduleMatcher(id, module)) {
|
|
32
|
+
subscription.unsubscribe();
|
|
33
|
+
callback(id, module);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|