1import * as URI from "uri-js";
2
3import { groupTracksPerScheme } from "@common/utils.js";
4import { ostiary, rpc, workerProxy } from "@common/worker.js";
5
6/**
7 * @import {Track} from "@definitions/types.d.ts";
8 * @import {GroupConsult, InputActions} from "@components/input/types.d.ts"
9 * @import {ActionsWithTunnel, ProxiedActions} from "@common/worker.d.ts"
10 */
11
12////////////////////////////////////////////
13// INPUT ACTIONS
14////////////////////////////////////////////
15
16/**
17 * @type {ActionsWithTunnel<InputActions>['consult']}
18 */
19export async function consult({ data, ports }) {
20 const fileUriOrScheme = data;
21 const scheme = fileUriOrScheme.includes(":")
22 ? URI.parse(fileUriOrScheme).scheme || fileUriOrScheme
23 : fileUriOrScheme;
24
25 const input = grabInput(scheme, ports);
26
27 if (!input) {
28 return { supported: false, reason: "Unsupported scheme" };
29 }
30
31 return await input.consult(fileUriOrScheme);
32}
33
34/**
35 * @type {ActionsWithTunnel<InputActions>['detach']}
36 */
37export async function detach({ data, ports }) {
38 const cachedTracks = data.tracks;
39 const groups = groupTracks(cachedTracks, ports);
40
41 const promises = Object.entries(groups).map(
42 async ([scheme, tracksGroup]) => {
43 const input = grabInput(scheme, ports);
44 if (!input || tracksGroup.length === 0) return tracksGroup;
45 if (
46 data.fileUriOrScheme.includes("://")
47 ? data.fileUriOrScheme.startsWith(`${scheme}://`) === false
48 : data.fileUriOrScheme !== scheme
49 ) return tracksGroup;
50
51 return await input.detach({
52 fileUriOrScheme: data.fileUriOrScheme,
53 tracks: tracksGroup,
54 });
55 },
56 );
57
58 const nested = await Promise.all(promises);
59 const tracks = nested.flat(1);
60
61 return tracks;
62}
63
64/**
65 * @type {ActionsWithTunnel<InputActions>['groupConsult']}
66 */
67export async function groupConsult({ data, ports }) {
68 const tracks = data;
69 const groups = groupTracksPerScheme(tracks);
70
71 /** @type {GroupConsult[]} */
72 const consultations = await Promise.all(
73 Object.keys(groups).map(async (scheme) => {
74 const input = grabInput(scheme, ports);
75
76 if (!input) {
77 return {
78 [scheme]: {
79 available: false,
80 reason: "Unsupported scheme",
81 scheme,
82 tracks: groups[scheme] ?? [],
83 },
84 };
85 }
86
87 return await input.groupConsult(groups[scheme] ?? {});
88 }),
89 );
90
91 return consultations.reduce((acc, c) => {
92 return { ...acc, ...c };
93 }, {});
94}
95
96/**
97 * @type {ActionsWithTunnel<InputActions>['list']}
98 */
99export async function list({ data, ports }) {
100 const groups = await groupConsult({ data, ports });
101
102 const promises = Object.values(groups).map(
103 async ({ available, scheme, tracks }) => {
104 if (!available) return tracks;
105
106 const input = grabInput(scheme, ports);
107 if (!input) return tracks;
108 return await input.list(tracks);
109 },
110 );
111
112 const nested = await Promise.all(promises);
113 const tracks = nested.flat(1);
114
115 return tracks;
116}
117
118/**
119 * @type {ActionsWithTunnel<InputActions>['resolve']}
120 */
121export async function resolve({ data, ports }) {
122 const uri = data.uri;
123 const scheme = uri.split(":", 1)[0];
124 const input = grabInput(scheme, ports);
125 if (!input) return undefined;
126
127 const result = await input.resolve(data);
128 return result;
129}
130
131////////////////////////////////////////////
132// ⚡️
133////////////////////////////////////////////
134
135ostiary((context) => {
136 rpc(context, {
137 consult,
138 detach,
139 groupConsult,
140 list,
141 resolve,
142 });
143});
144
145////////////////////////////////////////////
146// 🛠️
147////////////////////////////////////////////
148
149/**
150 * @param {string} scheme
151 * @param {Record<string, MessagePort>} ports
152 * @returns {ProxiedActions<InputActions> | null}
153 */
154function grabInput(scheme, ports) {
155 const port = ports[scheme];
156 if (!port) return null;
157
158 return workerProxy(() => {
159 port.start();
160 return port;
161 });
162}
163
164/**
165 * @param {Track[]} tracks
166 * @param {Record<string, MessagePort>} ports
167 */
168function groupTracks(tracks, ports) {
169 const grouped = groupTracksPerScheme(
170 tracks,
171 Object.fromEntries(
172 Object.keys(ports).map((k) => {
173 return [k, []];
174 }),
175 ),
176 );
177
178 return grouped;
179}