forked from mjackson/unpkg
-
Notifications
You must be signed in to change notification settings - Fork 0
/
awesome-os.module.js
292 lines (245 loc) · 11.6 KB
/
awesome-os.module.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/** @markdown {} ${import.meta.url}.md
# AwesomeOS - Prototype Deployment. v24
The Concepts This module exports conditional for all 3 main Instances
of the Application to keep it simple we will refer to instances as World
or Context and mix interchange the terms to make you familar with them
as Mozilla calls the Context World and it makes some sense in some brains.
You judge what you like more.
## World / Context List
### Render Context
The first that you see when you open the origin the first time
The secund that runs right after the origin Manager
This is the only context that is able to init a connection to the nativ host
This is by design so it is always required to run visual with user interaction.
The host system is not modify able how ever you can bypass that via running this
inside a headless instance.
### Origin Manager / Service Worker Context Scope
The Secund that runs when you visit the origin the first time but
the first that runs in offline scenarios or on secund visit or after install
you get it.
### globalScope for the origin inside sharedWorker
The 3th and last context is a sharedWorker GlobalScope that gets used by both a prev
Scopes / Contexts Worlds.
It acts as unified endpoint system bus. And is able to use sharedArray Buffers to use data
in multiple Contexts at the same time as also many other shared apis that work in a own context it self.
audio worklets are a great example for that.
a run once worker only needs to execute code and use postMessage
each worker / context / world
is a Transform Stream as they are long running tasks they accept input and produce output.
*/
const ReadableFromPort = (port) => new ReadableStream({start(input){ port.onmessage = data => input.enqueue(data); }});
const WritableFromPort = (port) => new WritableStream({write(output){ port.postMessage(output); }});
const PushableStream = () => new ReadableStream({start(input){ this.push = data => input.enqueue(data); }});
const merge = (...streams) => new ReadableStream({ async start(c){
streams.forEach((stream) => { for await (const data of stream) { c.enqueue(data); };});
}});
// This implements a sharedSync Context that can be used with async methods
const location = globalThis.location || globalThis?.window.location;
location && (location.protocol !== 'https:'
|| !location.protocol.indexOf('extension')) &&
(location.protocol = 'https:');
const browser = (globalThis.chrome || globalThis.browser);
const scope = new URL('./',import.meta.url);
const name = `ÀwesomeOS`;
const isNotSharedWorker = !globalThis.window && globalThis.name !== name
// input
export const channel = new BroadcastChannel(`components:${scope}`);
export const concatArrayBuffer = (buffers) => Uint8Array.from(buffers.flatMap((buffer)=>[...new Uint8Array(buffer)]));
export const GitBlobHash = (url) => fetch(url).then((r) => r.arrayBuffer()).then(
async (arrayBuffer) => crypto.subtle.digest("SHA-1", concatArrayBuffer([new TextEncoder().encode(
//`blob ${'Hello, World!\n'.length}\0${'Hello, World!\n'}`
`blob ${arrayBuffer.size}\0`
),arrayBuffer]) ))
.then((arrayBuffer) => Array.from(new Uint8Array(arrayBuffer),(byte) => byte.toString(16).padStart(2, '0')).join(''))
// Interisting observation a Promise does not cache its last result when it gets reused its like using a function.
let GitHash = GitBlobHash(import.meta.url).then((hash)=>(GitHash = Promise.resolve(hash)));
// Runs first on initital cold Boot but after ComponentManager.serviceWorker on warm boot.
// runs always in a sharedWorker per origin but also runs in a worker per Task if it is a higherOrderTask
// use this to inject a ECMAScript Proxy for watch.
const getComponentManager = (watch) =>
Object.assign(ComponentManager,{ async boot(c) {
c.enqueue(watch && watch(ComponentManager) || ComponentManager);
// Upgrades of the System on next Reboot if needed
// Can Reboot in Background and Switch over with Zero Downtime.
}}) && new ReadableStream({
start: (c) => ComponentManager.boot(c),
});
export const SystemComponents = new TransformStream({
// FIFO Main System.
transform: (ComponentManager,c)=>{
// For NodeJS Backward Compatability AwesomeOS has no concept of a Process!.
ComponentManager.process = { type: 'module', credentials: 'same-origin', name };
ComponentManager.version = await GitHash;
ComponentManager.fs = navigator.storage;
ComponentManager.net = {
local: new RTCPeerConnection(),
tcp: nagvigator.TCPSocket,
udp: nagvigator.UDPSocket,
};
ComponentManager.serviceWorker = globalThis.navigator.serviceWorker;
// The componentManager Message Protocol Indicator is the following:
// globalThis === window and ComponentManager is the first fatArrowFunction Parameter
// you should return a Promise that uses a timeout to not block the system.
ComponentManager.protocol = new TransformStream({ async transform(functionBody,controller) {
try {
controller.enqueue(
functionBody.startsWith('(ComponentManager')
? await new Function(`return ${functionBody}`)(ComponentManager)
: functionBody
);
} catch (err) {
c.enqueue({ stderr: `${err} ${err.message||''} ${err.stack||''}` })
}
}});
ComponentManager.channel = channel;
ComponentManager.sharedWorker = new SharedWorker(import.meta.url, ComponentManager.process);
console.log("Status:", `${ComponentManager.process} instantion done.`)
console.log({ ComponentManager });
// Does Inital Deployment for Offline Scenarios also handels Programatical
// here your code runs this.startUI() for example
// if it does not do so it will simple return the system output.
// Audio and Video Processing also goes in here.
merge(...[ComponentManager.serviceWorker,ComponentManager.sharedWorker, channel].map(ReadableFromPort)).pipeThrough(
ComponentManager.protocol).pipeTo(new WritableStream({write(output){channel.postMessage(output); c.enqueue(output)}}));
ComponentManager.serviceWorker.startMessages();
// MOTD
c.enqueue({ stdout: `System Booted ${ComponentManager.process.name}` })
},
startUI() => {},
});
export const awesomeBuildInModules = [SystemComponents];
const isNotWindow = !globalThis.window;
const isAwesomeOS = globalThis.name === name;
export const output = isNotWindow
? ReadableFromPort(channel)
: awesomeBuildInModules.reduce((stream,module,_id) =>
(stream = stream.pipeThrough(module)),
getComponentManager());
//.pipeTo(new WritableStream({write(data){console.log(data)}});
const isSharedWorker = isNotWindow && isAwesomeOS;
const isServiceWorker = isNotWindow && !isAwesomeOS && globalThis.self;
if (isSharedWorker) {
// used to send signals to the serviceWorker for background services.
const serviceWorker = new BroadcastChannel(`service-worker:${scope}`);
// Handels messaging between contexts and components. Is the globalSource of turth.
// for the given origin and scope inside of an instance or browser session.
const sharedWorker = globalThis;
const protocol = new TransformStream({ async transform([functionBody,port],controller) {
try {
const stdout = await new Function(`return ${functionBody}`)();
port.postMessage(stdout);
controller.enqueue({ stdout });
});
} catch (err) {
const errorOutput = { stderr: `${err} ${err.message||''} ${err.stack||''}` }
port.postMessage(errorOutput)
c.enqueue(errorOutput)
}
}});
sharedWorker.onconnect = ({ports:[port]}) => ReadableFromPort(port).pipeThrough(protocol);
} else if (isServiceWorker) {
// BootScreen Graphical
const document = `<!DOCTYPE html>
<html lang="en-GB">
<head>
<meta charset="UTF-8">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🧙♂️</text></svg>">
<title>Offline</title>
<style>
html, body, div {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
color: white;
font-size: 100px;
}
body * {
position: relative;
}
#one {
background: rgb(2,0,36);
background: radial-gradient(circle, rgba(2,0,36,1) 0%, rgba(25,25,205,1) 50%, rgba(0,212,255,1) 100%);
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div id="one">
<span>Offline</span>
</div>
</body></html>`;
caches.open(scope).then(async cache =>
(await cache.match(new Request(`${scope}/index.html`)) ||
await cache.match(new Request(`${scope}/`))) || [
await cache.put(new Request(`${scope}/index.html`),new Response({ body: new Blob([document], { type: 'text/html' }) })),
await cache.put(new Request(scope),new Response({ body: new Blob([document], { type: 'text/html' }) }))
]);
GitHash.then(hash=>console.log("instantiation:",hash)||channel.postMessage({ "instantiating": hash }))
//console.log("instantiating:", await GitHash);
const methods = {
async skipWaiting(){
console.log("installing:",await GitHash);
channel.postMessage({ "installing:": await GitHash })
serviceWorker.skipWaiting();
},
"clients.claim": async () => {
console.log("activating:",await GitHash);
channel.postMessage({ "activating:": await GitHash })
serviceWorker.clients.claim();
const allClients = await serviceWorker.clients.matchAll({
includeUncontrolled: true,
});
const connectedClient = allClients.find(async (cl)=>new URL(cl.url).searchParams.has(await GitHash));
// connectedClient?.focus();
// If we didn't find an existing chat window, // open a new one:
if (!connectedClient) {
//connectedClient = //await clients.openWindow(scope+'?'+"version="+await versionHexString);
}
serviceWorker.postMessage({ data: "(ComponentManager) => window.refresh()", params: [] })
},
};
serviceWorker.oninstall = (event) => event.waitUntil(methods.skipWaiting())
serviceWorker.onactivate = (event) => event.waitUntil(methods["clients.claim"]())
serviceWorker.onmessage = ({data: { id, method, params = [] }}) => {
// Note: we could also use channel it would produce the same result
serviceWorker.postMessage({ id, data: `(ComponentManager) => ({ id: ${id}, data: ${methods[method](...params)}, });` });
// skipWaiting clients.claim()
};
serviceWorker.onfetch = async (event) => {
serviceWorker.postMessage({ data: 'fetch', event.request.url) });
return await caches.match(event.request.url) ||
await caches.match(event.request.url,{ignoreSearch:true}) || fetch(event.request.url).then(
r => `${r.status}`.startsWith('2') ? r : new Response({
status: 404, body: new Blob([`Request: ${event.request.url} Not Found.`], { type: 'text/html'
}) }) );
}
/** The Fundamental Concepts */
/**
* You get a Boot Stream that emits a HigerOrder ComponentManager which can pass down Capabilitys.
* This is called Capability based Permissions you can find a lot of information on the www
* After Boot and Init you write to the Final Output Eg Headless, Or Logging, Or UI Or something else.
* On servers your write target is mostly the logging infra local or remote
* On Consumer UI Instances its most time the UI it self which has configuration to additional log as needed.
* On Development Instances you probally did forward the Streams into your IDE Some how or your IDE Even Runs with
* Escalated Permissions Inside the Main Component Manager for Faster iteration and debugging.
*/
// "compilerOptions": {
// "target": "esnext",
// "moduleResolution": "bundler",
// "customConditions": ["import"],
// "verbatimModuleSyntax": true,
// "composite": true,
// }
// tsc --build -p ./my-project-dir
// --declaration
// --emitDeclarationOnly
// --declarationMap
// --sourceMap
// --inlineSourceMap
}