@scarletgeek/web-kernel 1.0.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.
@@ -0,0 +1,9 @@
1
+ export default class MyProgram {
2
+ onStart(args) {
3
+ console.log("My Program is started :: ", args);
4
+ }
5
+
6
+ onDestroy() {
7
+ console.log("My Program is destroyed");
8
+ }
9
+ }
@@ -0,0 +1,7 @@
1
+ import MyProgram from "./myprogram.js";
2
+
3
+ export function registerPrograms(kernel) {
4
+ kernel.registerPrograms({
5
+ "my-program": MyProgram
6
+ });
7
+ }
@@ -0,0 +1,23 @@
1
+ import { Request } from "/dist/index.js";
2
+
3
+ export default class GetPosts extends Request {
4
+ get url() {
5
+ return "https://jsonplaceholder.typicode.com/posts";
6
+ }
7
+
8
+ get method() {
9
+ return "GET";
10
+ }
11
+
12
+ onProcessing() {
13
+ console.log("Get posts is processing");
14
+ }
15
+
16
+ onSuccess(response) {
17
+ console.log("Get posts is success :: ", response);
18
+ }
19
+
20
+ onError(error) {
21
+ console.error("Get Posts is error :: ", error);
22
+ }
23
+ }
@@ -0,0 +1,7 @@
1
+ import GetPosts from "./getposts.js";
2
+
3
+ export function registerRequests(kernel) {
4
+ kernel.registerRequests({
5
+ "getposts" : GetPosts
6
+ });
7
+ }
@@ -0,0 +1,50 @@
1
+ import { registerPrograms } from "./programs/register.js";
2
+ import { registerRequests } from "./requests/register.js";
3
+ import { Kernel } from "/dist/index.js";
4
+
5
+ // Initiate kernel
6
+ window.kernel = new Kernel();
7
+
8
+ // Programs
9
+ registerPrograms(window.kernel);
10
+
11
+ window.kernel.onBoot((k) => {
12
+ k.start("my-program", {
13
+ message: "Kernel is booted.",
14
+ });
15
+ })
16
+
17
+ // window.kernel.start("my-program", {
18
+ // message: "Message is registered."
19
+ // });
20
+
21
+ document.getElementById("link-button").addEventListener("click", () => window.kernel.destroy("my-program"));
22
+
23
+
24
+ // Events
25
+ window.kernel.on("kernel:test", (e) => {
26
+ console.log("Kernel test event fired :: ", e.detail);
27
+ });
28
+
29
+ document.getElementById("second-link-button").addEventListener("click", () => {
30
+ window.kernel.emit("kernel:test", {
31
+ name: "Naruto Uzumaki",
32
+ age: 18,
33
+ residence: "Konoha village"
34
+ });
35
+ });
36
+
37
+ // Requests
38
+ registerRequests(window.kernel);
39
+
40
+ document.getElementById("trail-button").addEventListener("click", () => {
41
+ window.kernel.send("getposts");
42
+ });
43
+
44
+ document.getElementById("metrics-button").addEventListener("click", () => {
45
+ window.kernel.metrics();
46
+ });
47
+
48
+ document.addEventListener("DOMContentLoaded", () => {
49
+ window.kernel.boot();
50
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "/dist/index.js": "/dist/index.js"
3
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@scarletgeek/web-kernel",
3
+ "version": "1.0.0",
4
+ "description": "A kernel for web to handle scripting",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "directories": {
8
+ "example": "example"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/thescarletgeek/web-kernel"
13
+ },
14
+ "scripts": {
15
+ "dev": "npm run development",
16
+ "development": "mix",
17
+ "watch": "mix watch",
18
+ "watch-poll": "mix watch -- --watch-options-poll=1000",
19
+ "hot": "mix watch --hot",
20
+ "prod": "npm run production",
21
+ "production": "mix --production"
22
+ },
23
+ "keywords": [
24
+ "kernel",
25
+ "web-kernel",
26
+ "opensource",
27
+ "webjs"
28
+ ],
29
+ "author": "",
30
+ "license": "MIT",
31
+ "devDependencies": {
32
+ "laravel-mix": "^6.0.49",
33
+ "ts-loader": "^9.5.4",
34
+ "typescript": "^5.9.3"
35
+ },
36
+ "homepage": "https://github.com/thescarletgeek/web-kernel",
37
+ "bugs": {
38
+ "url": "https://github.com/thescarletgeek/web-kernel/issues"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ }
43
+ }
package/readme.md ADDED
@@ -0,0 +1,221 @@
1
+ # Web Kernel
2
+ Web Kernel is an experimental runtime layer inspired by the core concepts of operating system kernels. While it is not a real kernel, it is designed to feel like one, focusing on structure, control and clarity without relying on shiny frameworks.
3
+
4
+ The idea originated while I am studying how OS kernels manage processes, states and system coordination.
5
+
6
+ As applications grow, scripting often becomes duplicated, tightly coupled, and hard to reason about. Web kernel explores whether a central control layer can manage programs, events and requests in a structured and predictable manner, while keeping the system minimal and extensible.
7
+
8
+ ## Philosophy
9
+ - Structure programs, events, and requests without relying on frameworks.
10
+ - Enable scripting using Javascript/Typescript.
11
+ - Reduce duplication in execution and orchestration logic.
12
+ - Follow lifecycle-based design for programs and requests.
13
+
14
+ ## What web kernel is (and isn't)
15
+ ### What it is
16
+ - A runtime coordination layer.
17
+ - A structured way to manage programs.
18
+ - A state-aware execution controller.
19
+ - A foundation for custom architecture.
20
+
21
+ ### What it is not
22
+ - A web framework.
23
+ - A replacement for React, Vue, Express, etc.
24
+ - A dependency-heavy abstraction layer.
25
+ - A magic solution for bad architecture.
26
+
27
+ ---
28
+ ## Quickstart
29
+ To install web kernel, either install through npm or directly incldue the script.
30
+
31
+ Install through **npm** :
32
+ ```
33
+ npm install @scarletgeek/web-kernel
34
+ ```
35
+
36
+ To initialize the kernel :
37
+
38
+ ```js
39
+ import { Kernel } from "@scarletgeek/web-kernel";
40
+
41
+ // Initialize the kernel
42
+ window.kernel = new Kernel();
43
+
44
+ // Run programs on boot
45
+ window.kernel.onBoot((k) => {
46
+ k.start("program-name");
47
+ });
48
+
49
+ // Run boot() method to boot the kernel
50
+ document.addEventListener("DOMContentLoaded", () => {
51
+ window.kernel.boot();
52
+ });
53
+ ```
54
+
55
+ ---
56
+ ## Programs
57
+ Programs are first-class execution units, similar to processes in an OS.
58
+
59
+ **Each Program :**
60
+ - Has a defined lifecycle.
61
+ - Can be executed, tracked and observed.
62
+ - Exists independently of framework logic.
63
+
64
+ To create and initialize programs :
65
+
66
+ ```js
67
+ export default class MyProgram {
68
+ onStart(args) {
69
+ console.log("My Program is started :: ", args);
70
+ }
71
+
72
+ onDestroy() {
73
+ console.log("My Program is destroyed");
74
+ }
75
+ }
76
+ ```
77
+
78
+ ```js
79
+ // Register programs by passing program object and a name.
80
+ window.kernel.registerPrograms({
81
+ "program-name": MyProgram
82
+ });
83
+ ```
84
+
85
+ Run programs from kernel :
86
+
87
+ ```js
88
+ // Run program using kernel
89
+ // Arguments can be passed in start() method.
90
+ // Arguments are optional
91
+ window.kernel.start("program-name", {})
92
+ ```
93
+ Run programs on kernel startup :
94
+
95
+ ```js
96
+ // Run programs on startup using onBoot() method
97
+ window.kernel.onBoot((k) => {
98
+ k.start("program--name");
99
+ });
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Events
105
+
106
+ - Events represent signals within the system.
107
+ - Used to communicate between programs without tight coupling.
108
+ - Help reduce duplication in cross-program communication.
109
+
110
+ Fire events from kernel :
111
+ ```js
112
+ // Fire events using kernel
113
+ window.kernel.emit("kernel:test", {
114
+ name: "Naruto Uzumaki",
115
+ age: 18,
116
+ residence: "Konoha village"
117
+ });
118
+ ```
119
+
120
+ To listen to events from kernel :
121
+ ```js
122
+ // Add event listener
123
+ const listener = window.kernel.on("kernel:test", (e) => {
124
+ console.log("Kernel test event fired :: ", e.detail);
125
+ });
126
+
127
+ // Remove the defined listener
128
+ listener();
129
+
130
+ // To listen to event only once
131
+ window.kernel.once("kernel:test", (e) => {
132
+ console.log("Kernel test event fired :: ", e.detail);
133
+ });
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Request
139
+ Requests can be used for API calls.
140
+
141
+ **Request Types** :
142
+
143
+ - GET
144
+ - POST
145
+ - PUT
146
+ - PATCH
147
+ - DELETE
148
+
149
+ Create Request :
150
+
151
+ ```js
152
+ import { Request } from "/dist/index.js";
153
+
154
+ export default class GetPosts extends Request {
155
+ get url() {
156
+ return "https://jsonplaceholder.typicode.com/posts";
157
+ }
158
+
159
+ get method() {
160
+ return "GET";
161
+ }
162
+
163
+ get headers() {
164
+ // Headers can be defined here
165
+ return {}
166
+ }
167
+
168
+ get payload() {
169
+ // Request payload can be defined here
170
+ return {}
171
+ }
172
+
173
+ onProcessing() {
174
+ // Runs while request is running
175
+ console.log("Get posts is processing");
176
+ }
177
+
178
+ onSuccess(response) {
179
+ // When request is succeeded
180
+ console.log("Get posts is success :: ", response);
181
+ }
182
+
183
+ onError(error) {
184
+ // When request is error
185
+ console.error("Get Posts is error :: ", error);
186
+ }
187
+ }
188
+
189
+ ```
190
+
191
+ Register request on kernel :
192
+
193
+ ```js
194
+ window.kernel.registerRequest({
195
+ "get-posts": GetPosts
196
+ });
197
+ ```
198
+
199
+ Run request using kernel :
200
+
201
+ ```js
202
+ window.kernel.send("get-posts");
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Contributing
208
+
209
+ Web kernel is an experimental project. Contributions are welcome for experimentation, improvements, and exploration of ideas.
210
+
211
+ 1. Fork the repository
212
+ 2. Create a new branch from **develop**.
213
+ 3. Make your changes with clear, focused commits.
214
+ 4. Ensure existing functionality is not broken.
215
+ 5. Submit a pull request to the **develop** branch.
216
+ 6. Describe what has changed and why.
217
+
218
+ ---
219
+ ## License
220
+
221
+ [MIT License](https://opensource.org/license/MIT)
File without changes
@@ -0,0 +1,42 @@
1
+ import { IEventMap, IEventHandler, IUnsubscribeEvent } from "./types/IKernel";
2
+
3
+ class KernelEvents extends EventTarget {
4
+ on<K extends keyof IEventMap>(
5
+ type: K,
6
+ handler: IEventHandler<IEventMap[K]>,
7
+ options?: AddEventListenerOptions
8
+ ): IUnsubscribeEvent {
9
+ this.addEventListener(type, handler, options);
10
+
11
+ return () => this.off(type, handler);
12
+ }
13
+
14
+ once<K extends keyof IEventMap>(
15
+ type: K,
16
+ handler: IEventHandler<IEventMap[K]>
17
+ ) {
18
+ this.addEventListener(type, handler, {
19
+ once: true
20
+ });
21
+ }
22
+
23
+ off<K extends keyof IEventMap>(
24
+ type: K,
25
+ handler: IEventHandler<IEventMap[K]>
26
+ ) {
27
+ this.removeEventListener(type, handler);
28
+ }
29
+
30
+ emit<K extends keyof IEventMap>(
31
+ type: K,
32
+ detail: IEventMap[K] = {}
33
+ ) {
34
+ return this.dispatchEvent(new CustomEvent(type, { detail }));
35
+ }
36
+
37
+ destroy() {
38
+ // this.replaceWith?.(null);
39
+ }
40
+ }
41
+
42
+ export default KernelEvents;
@@ -0,0 +1,106 @@
1
+ import { ProgramState } from "./types/IKernel";
2
+ import { logger, LoggerLevel } from "./utils";
3
+
4
+ class ProgramHandler {
5
+ private programs = new Map();
6
+ private programState = new Map();
7
+
8
+ constructor() {
9
+ //
10
+ }
11
+
12
+ isProgramExists(key: string) {
13
+ return (this.programs.has(key) && this.programState.has(key));
14
+ }
15
+
16
+ addProgram(key: string, program: any) {
17
+ if(this.programs.has(key)) {
18
+ logger(LoggerLevel.WARNING, `Program ${key} already exists.`);
19
+ return;
20
+ }
21
+
22
+ this.programs.set(key, new program());
23
+ this.programState.set(key, ProgramState.IDLE);
24
+ }
25
+
26
+ startProgram(key: string, args: any = null) {
27
+ const program = this.programs.get(key);
28
+ if(!program) {
29
+ logger(LoggerLevel.WARNING, `Program ${key} not found.`);
30
+ return;
31
+ }
32
+
33
+ const status = this.programState.get(key);
34
+ if(status === ProgramState.RUNNING) {
35
+ logger(LoggerLevel.WARNING, `Program ${key} is already running.`);
36
+ return;
37
+ }
38
+
39
+ if(typeof program.onStart !== "function") {
40
+ logger(LoggerLevel.ERROR, `onStart method is not defined in the program ${key}.`);
41
+ return;
42
+ }
43
+
44
+ try {
45
+ program.onStart(args);
46
+ this.programState.set(key, ProgramState.RUNNING);
47
+ } catch(error) {
48
+ this.programState.set(key, ProgramState.ERROR);
49
+ logger(LoggerLevel.ERROR, `Error occured in program ${key} - `, error);
50
+ }
51
+ }
52
+
53
+ endProgram(key: string) {
54
+ const program = this.programs.get(key);
55
+ if(!program) {
56
+ logger(LoggerLevel.WARNING, `Program ${key} not found.`);
57
+ return;
58
+ }
59
+
60
+ if(this.programState.get(key) !== ProgramState.RUNNING) {
61
+ logger(LoggerLevel.WARNING, `Program ${key} is not running.`);
62
+ return;
63
+ }
64
+
65
+ if(typeof program.onDestroy !== "function") {
66
+ logger(LoggerLevel.ERROR, `onDestroy method is not defined in the program ${key}.`);
67
+ return;
68
+ }
69
+
70
+ try {
71
+ program.onDestroy();
72
+ this.programState.set(key, ProgramState.STOPPED);
73
+ } catch(error) {
74
+ this.programState.set(key, ProgramState.ERROR);
75
+ logger(LoggerLevel.ERROR, `Error occured in program ${key} - `, error);
76
+ }
77
+ }
78
+
79
+ getProgramsByState(programState: ProgramState) {
80
+ const status: any = {};
81
+
82
+ for(const [key, state] of this.programState) {
83
+ if(state === programState) {
84
+ status[key] = state;
85
+ }
86
+ }
87
+
88
+ return status;
89
+ }
90
+
91
+ getAllProgramStatus() {
92
+ const status: any = {};
93
+
94
+ for(const [key, state] of this.programState) {
95
+ status[key] = state;
96
+ }
97
+
98
+ return status;
99
+ }
100
+
101
+ getProgramStatus(key: string) {
102
+ return this.programs.get(key);
103
+ }
104
+ }
105
+
106
+ export default ProgramHandler;
@@ -0,0 +1,31 @@
1
+ import { fetchAdapter } from "./request/adapters";
2
+ import Request from "./request/Request";
3
+ import { IKernelRequest } from "./types/IKernel";
4
+ import { logger, LoggerLevel } from "./utils";
5
+
6
+ class RequestHandler {
7
+ requests: IKernelRequest = {};
8
+
9
+ addRequest(key: string, RequestClass: any) {
10
+ const instance = new RequestClass(fetchAdapter);
11
+ if(!(instance instanceof Request)) {
12
+ throw new Error(`Invalid request "${key}". It must extend kernel Request class.`);
13
+ }
14
+
15
+ this.requests[key] = instance;
16
+ }
17
+
18
+ startRequest(key: string) {
19
+ if(!this.requests[key]) {
20
+ logger(LoggerLevel.WARNING, "Request not found.");
21
+ }
22
+
23
+ if(typeof this.requests[key].send !== "function") {
24
+ logger(LoggerLevel.ERROR, "Send method not available for the request.");
25
+ }
26
+
27
+ this.requests[key].send();
28
+ }
29
+ }
30
+
31
+ export default RequestHandler;
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ import Kernel from "./kernel";
2
+ import Request from "./request/Request";
3
+
4
+ import { LoggerLevel } from "./utils";
5
+
6
+ export {
7
+ Kernel,
8
+ LoggerLevel,
9
+ Request
10
+ };
package/src/kernel.ts ADDED
@@ -0,0 +1,94 @@
1
+ import ProgramHandler from "./ProgramHandler";
2
+ import { IKernelProgram, IEventMap, IEventHandler, IUnsubscribeEvent, IKernelRequest, KernelState } from "./types/IKernel";
3
+ import KernelEvents from "./KernelEvents";
4
+ import RequestHandler from "./RequestHandler";
5
+
6
+ class Kernel {
7
+ programHandler;
8
+ eventHandler;
9
+ requestHandler;
10
+ state: KernelState;
11
+ bootHandlers: any[];
12
+
13
+ constructor() {
14
+ this.programHandler = new ProgramHandler();
15
+ this.eventHandler = new KernelEvents();
16
+ this.requestHandler = new RequestHandler();
17
+ this.state = KernelState.CREATED;
18
+ this.bootHandlers = [];
19
+ }
20
+
21
+ onBoot(callback: any) {
22
+ if(this.state == KernelState.BOOTED) {
23
+ callback(this);
24
+ return;
25
+ }
26
+
27
+ this.bootHandlers.push(callback);
28
+ }
29
+
30
+ boot() {
31
+ if(this.state !== KernelState.CREATED) {
32
+ return;
33
+ }
34
+
35
+ this.state = KernelState.BOOTING;
36
+
37
+ for(const handler of this.bootHandlers) {
38
+ handler(this);
39
+ }
40
+
41
+ this.bootHandlers.length = 0;
42
+ this.state = KernelState.BOOTED;
43
+ }
44
+
45
+ registerPrograms(programs: IKernelProgram) {
46
+ if(Object.keys(programs).length) {
47
+ Object.keys(programs).forEach(key => {
48
+ this.programHandler.addProgram(key, programs[key]);
49
+ })
50
+ }
51
+ }
52
+
53
+ start(programName: string, args: any = null) {
54
+ this.programHandler.startProgram(programName, args);
55
+ }
56
+
57
+ destroy(programName: string) {
58
+ this.programHandler.endProgram(programName);
59
+ }
60
+
61
+ emit<K extends keyof IEventMap>(type: K, detail: IEventMap[K] = {}) {
62
+ this.eventHandler.emit(type, detail);
63
+ }
64
+
65
+ on<K extends keyof IEventMap>(type: K, handler: IEventHandler<IEventMap[K]>, options?: AddEventListenerOptions) {
66
+ return this.eventHandler.on(type, handler, options);
67
+ }
68
+
69
+ once<K extends keyof IEventMap>(type: K, handler: IEventHandler<IEventMap[K]>) {
70
+ this.eventHandler.once(type, handler);
71
+ }
72
+
73
+ registerRequests(requests: IKernelRequest) {
74
+ if(Object.keys(requests).length) {
75
+ Object.keys(requests).forEach(key => {
76
+ this.requestHandler.addRequest(key, requests[key]);
77
+ });
78
+ }
79
+ }
80
+
81
+ send(key: string) {
82
+ this.requestHandler.startRequest(key);
83
+ }
84
+
85
+ status() {
86
+ const data: any = {};
87
+
88
+ data["programs"] = this.programHandler.getAllProgramStatus();
89
+
90
+ console.log(data);
91
+ }
92
+ }
93
+
94
+ export default Kernel;
@@ -0,0 +1,19 @@
1
+ import { ResponseTransformer } from "./interfaces";
2
+
3
+ class DataTransformer<T = Response> implements ResponseTransformer<T> {
4
+ async transform(response: Response): Promise<T> {
5
+ const contentType = response.headers.get("content-type") || "";
6
+
7
+ if(contentType.includes("application/json")) {
8
+ return await response.json();
9
+ }
10
+
11
+ if(contentType.includes("text/")) {
12
+ return await response.text() as unknown as T;
13
+ }
14
+
15
+ return await response.blob() as unknown as T;
16
+ }
17
+ }
18
+
19
+ export default DataTransformer;